mirror of
https://github.com/ziglang/zig.git
synced 2025-12-23 22:53:06 +00:00
update build system to new http.Server API
This commit is contained in:
parent
e2d81bf6c0
commit
fef41c66db
@ -234,7 +234,7 @@ pub const Previous = struct {
|
|||||||
};
|
};
|
||||||
pub fn sendUpdate(
|
pub fn sendUpdate(
|
||||||
fuzz: *Fuzz,
|
fuzz: *Fuzz,
|
||||||
socket: *std.http.WebSocket,
|
socket: *std.http.Server.WebSocket,
|
||||||
prev: *Previous,
|
prev: *Previous,
|
||||||
) !void {
|
) !void {
|
||||||
fuzz.coverage_mutex.lock();
|
fuzz.coverage_mutex.lock();
|
||||||
@ -263,36 +263,36 @@ pub fn sendUpdate(
|
|||||||
.string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len),
|
.string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len),
|
||||||
.start_timestamp = coverage_map.start_timestamp,
|
.start_timestamp = coverage_map.start_timestamp,
|
||||||
};
|
};
|
||||||
const iovecs: [5]std.posix.iovec_const = .{
|
var iovecs: [5][]const u8 = .{
|
||||||
makeIov(@ptrCast(&header)),
|
@ptrCast(&header),
|
||||||
makeIov(@ptrCast(coverage_map.coverage.directories.keys())),
|
@ptrCast(coverage_map.coverage.directories.keys()),
|
||||||
makeIov(@ptrCast(coverage_map.coverage.files.keys())),
|
@ptrCast(coverage_map.coverage.files.keys()),
|
||||||
makeIov(@ptrCast(coverage_map.source_locations)),
|
@ptrCast(coverage_map.source_locations),
|
||||||
makeIov(coverage_map.coverage.string_bytes.items),
|
coverage_map.coverage.string_bytes.items,
|
||||||
};
|
};
|
||||||
try socket.writeMessagev(&iovecs, .binary);
|
try socket.writeMessageVec(&iovecs, .binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
const header: abi.CoverageUpdateHeader = .{
|
const header: abi.CoverageUpdateHeader = .{
|
||||||
.n_runs = n_runs,
|
.n_runs = n_runs,
|
||||||
.unique_runs = unique_runs,
|
.unique_runs = unique_runs,
|
||||||
};
|
};
|
||||||
const iovecs: [2]std.posix.iovec_const = .{
|
var iovecs: [2][]const u8 = .{
|
||||||
makeIov(@ptrCast(&header)),
|
@ptrCast(&header),
|
||||||
makeIov(@ptrCast(seen_pcs)),
|
@ptrCast(seen_pcs),
|
||||||
};
|
};
|
||||||
try socket.writeMessagev(&iovecs, .binary);
|
try socket.writeMessageVec(&iovecs, .binary);
|
||||||
|
|
||||||
prev.unique_runs = unique_runs;
|
prev.unique_runs = unique_runs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prev.entry_points != coverage_map.entry_points.items.len) {
|
if (prev.entry_points != coverage_map.entry_points.items.len) {
|
||||||
const header: abi.EntryPointHeader = .init(@intCast(coverage_map.entry_points.items.len));
|
const header: abi.EntryPointHeader = .init(@intCast(coverage_map.entry_points.items.len));
|
||||||
const iovecs: [2]std.posix.iovec_const = .{
|
var iovecs: [2][]const u8 = .{
|
||||||
makeIov(@ptrCast(&header)),
|
@ptrCast(&header),
|
||||||
makeIov(@ptrCast(coverage_map.entry_points.items)),
|
@ptrCast(coverage_map.entry_points.items),
|
||||||
};
|
};
|
||||||
try socket.writeMessagev(&iovecs, .binary);
|
try socket.writeMessageVec(&iovecs, .binary);
|
||||||
|
|
||||||
prev.entry_points = coverage_map.entry_points.items.len;
|
prev.entry_points = coverage_map.entry_points.items.len;
|
||||||
}
|
}
|
||||||
@ -448,10 +448,3 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte
|
|||||||
}
|
}
|
||||||
try coverage_map.entry_points.append(fuzz.ws.gpa, @intCast(index));
|
try coverage_map.entry_points.append(fuzz.ws.gpa, @intCast(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeIov(s: []const u8) std.posix.iovec_const {
|
|
||||||
return .{
|
|
||||||
.base = s.ptr,
|
|
||||||
.len = s.len,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@ -251,48 +251,44 @@ pub fn now(s: *const WebServer) i64 {
|
|||||||
fn accept(ws: *WebServer, connection: std.net.Server.Connection) void {
|
fn accept(ws: *WebServer, connection: std.net.Server.Connection) void {
|
||||||
defer connection.stream.close();
|
defer connection.stream.close();
|
||||||
|
|
||||||
var read_buf: [0x4000]u8 = undefined;
|
var send_buffer: [4096]u8 = undefined;
|
||||||
var server: std.http.Server = .init(connection, &read_buf);
|
var recv_buffer: [4096]u8 = undefined;
|
||||||
|
var connection_reader = connection.stream.reader(&recv_buffer);
|
||||||
|
var connection_writer = connection.stream.writer(&send_buffer);
|
||||||
|
var server: http.Server = .init(connection_reader.interface(), &connection_writer.interface);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var request = server.receiveHead() catch |err| switch (err) {
|
var request = server.receiveHead() catch |err| switch (err) {
|
||||||
error.HttpConnectionClosing => return,
|
error.HttpConnectionClosing => return,
|
||||||
else => {
|
else => return log.err("failed to receive http request: {t}", .{err}),
|
||||||
log.err("failed to receive http request: {s}", .{@errorName(err)});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
var ws_send_buf: [0x4000]u8 = undefined;
|
switch (request.upgradeRequested()) {
|
||||||
var ws_recv_buf: [0x4000]u8 align(4) = undefined;
|
.websocket => |opt_key| {
|
||||||
if (std.http.WebSocket.init(&request, &ws_send_buf, &ws_recv_buf) catch |err| {
|
const key = opt_key orelse return log.err("missing websocket key", .{});
|
||||||
log.err("failed to initialize websocket connection: {s}", .{@errorName(err)});
|
var web_socket = request.respondWebSocket(.{ .key = key }) catch {
|
||||||
return;
|
return log.err("failed to respond web socket: {t}", .{connection_writer.err.?});
|
||||||
}) |ws_init| {
|
};
|
||||||
var web_socket = ws_init;
|
ws.serveWebSocket(&web_socket) catch |err| {
|
||||||
ws.serveWebSocket(&web_socket) catch |err| {
|
log.err("failed to serve websocket: {t}", .{err});
|
||||||
log.err("failed to serve websocket: {s}", .{@errorName(err)});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
comptime unreachable;
|
|
||||||
} else {
|
|
||||||
ws.serveRequest(&request) catch |err| switch (err) {
|
|
||||||
error.AlreadyReported => return,
|
|
||||||
else => {
|
|
||||||
log.err("failed to serve '{s}': {s}", .{ request.head.target, @errorName(err) });
|
|
||||||
return;
|
return;
|
||||||
},
|
};
|
||||||
};
|
comptime unreachable;
|
||||||
|
},
|
||||||
|
.other => |name| return log.err("unknown upgrade request: {s}", .{name}),
|
||||||
|
.none => {
|
||||||
|
ws.serveRequest(&request) catch |err| switch (err) {
|
||||||
|
error.AlreadyReported => return,
|
||||||
|
else => {
|
||||||
|
log.err("failed to serve '{s}': {t}", .{ request.head.target, err });
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeIov(s: []const u8) std.posix.iovec_const {
|
fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
|
||||||
return .{
|
|
||||||
.base = s.ptr,
|
|
||||||
.len = s.len,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn {
|
|
||||||
var prev_build_status = ws.build_status.load(.monotonic);
|
var prev_build_status = ws.build_status.load(.monotonic);
|
||||||
|
|
||||||
const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len);
|
const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len);
|
||||||
@ -312,11 +308,8 @@ fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn {
|
|||||||
.timestamp = ws.now(),
|
.timestamp = ws.now(),
|
||||||
.steps_len = @intCast(ws.all_steps.len),
|
.steps_len = @intCast(ws.all_steps.len),
|
||||||
};
|
};
|
||||||
try sock.writeMessagev(&.{
|
var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), ws.step_names_trailing, prev_step_status_bits };
|
||||||
makeIov(@ptrCast(&hello_header)),
|
try sock.writeMessageVec(&bufs, .binary);
|
||||||
makeIov(ws.step_names_trailing),
|
|
||||||
makeIov(prev_step_status_bits),
|
|
||||||
}, .binary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var prev_fuzz: Fuzz.Previous = .init;
|
var prev_fuzz: Fuzz.Previous = .init;
|
||||||
@ -380,7 +373,7 @@ fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn {
|
|||||||
std.Thread.Futex.timedWait(&ws.update_id, start_update_id, std.time.ns_per_ms * default_update_interval_ms) catch {};
|
std.Thread.Futex.timedWait(&ws.update_id, start_update_id, std.time.ns_per_ms * default_update_interval_ms) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void {
|
fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {
|
||||||
while (true) {
|
while (true) {
|
||||||
const msg = sock.readSmallMessage() catch return;
|
const msg = sock.readSmallMessage() catch return;
|
||||||
if (msg.opcode != .binary) continue;
|
if (msg.opcode != .binary) continue;
|
||||||
@ -402,7 +395,7 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serveRequest(ws: *WebServer, req: *std.http.Server.Request) !void {
|
fn serveRequest(ws: *WebServer, req: *http.Server.Request) !void {
|
||||||
// Strip an optional leading '/debug' component from the request.
|
// Strip an optional leading '/debug' component from the request.
|
||||||
const target: []const u8, const debug: bool = target: {
|
const target: []const u8, const debug: bool = target: {
|
||||||
if (mem.eql(u8, req.head.target, "/debug")) break :target .{ "/", true };
|
if (mem.eql(u8, req.head.target, "/debug")) break :target .{ "/", true };
|
||||||
@ -431,7 +424,7 @@ fn serveRequest(ws: *WebServer, req: *std.http.Server.Request) !void {
|
|||||||
|
|
||||||
fn serveLibFile(
|
fn serveLibFile(
|
||||||
ws: *WebServer,
|
ws: *WebServer,
|
||||||
request: *std.http.Server.Request,
|
request: *http.Server.Request,
|
||||||
sub_path: []const u8,
|
sub_path: []const u8,
|
||||||
content_type: []const u8,
|
content_type: []const u8,
|
||||||
) !void {
|
) !void {
|
||||||
@ -442,7 +435,7 @@ fn serveLibFile(
|
|||||||
}
|
}
|
||||||
fn serveClientWasm(
|
fn serveClientWasm(
|
||||||
ws: *WebServer,
|
ws: *WebServer,
|
||||||
req: *std.http.Server.Request,
|
req: *http.Server.Request,
|
||||||
optimize_mode: std.builtin.OptimizeMode,
|
optimize_mode: std.builtin.OptimizeMode,
|
||||||
) !void {
|
) !void {
|
||||||
var arena_state: std.heap.ArenaAllocator = .init(ws.gpa);
|
var arena_state: std.heap.ArenaAllocator = .init(ws.gpa);
|
||||||
@ -456,12 +449,12 @@ fn serveClientWasm(
|
|||||||
|
|
||||||
pub fn serveFile(
|
pub fn serveFile(
|
||||||
ws: *WebServer,
|
ws: *WebServer,
|
||||||
request: *std.http.Server.Request,
|
request: *http.Server.Request,
|
||||||
path: Cache.Path,
|
path: Cache.Path,
|
||||||
content_type: []const u8,
|
content_type: []const u8,
|
||||||
) !void {
|
) !void {
|
||||||
const gpa = ws.gpa;
|
const gpa = ws.gpa;
|
||||||
// The desired API is actually sendfile, which will require enhancing std.http.Server.
|
// The desired API is actually sendfile, which will require enhancing http.Server.
|
||||||
// We load the file with every request so that the user can make changes to the file
|
// We load the file with every request so that the user can make changes to the file
|
||||||
// and refresh the HTML page without restarting this server.
|
// and refresh the HTML page without restarting this server.
|
||||||
const file_contents = path.root_dir.handle.readFileAlloc(gpa, path.sub_path, 10 * 1024 * 1024) catch |err| {
|
const file_contents = path.root_dir.handle.readFileAlloc(gpa, path.sub_path, 10 * 1024 * 1024) catch |err| {
|
||||||
@ -478,14 +471,13 @@ pub fn serveFile(
|
|||||||
}
|
}
|
||||||
pub fn serveTarFile(
|
pub fn serveTarFile(
|
||||||
ws: *WebServer,
|
ws: *WebServer,
|
||||||
request: *std.http.Server.Request,
|
request: *http.Server.Request,
|
||||||
paths: []const Cache.Path,
|
paths: []const Cache.Path,
|
||||||
) !void {
|
) !void {
|
||||||
const gpa = ws.gpa;
|
const gpa = ws.gpa;
|
||||||
|
|
||||||
var send_buf: [0x4000]u8 = undefined;
|
var send_buffer: [0x4000]u8 = undefined;
|
||||||
var response = request.respondStreaming(.{
|
var response = try request.respondStreaming(&send_buffer, .{
|
||||||
.send_buffer = &send_buf,
|
|
||||||
.respond_options = .{
|
.respond_options = .{
|
||||||
.extra_headers = &.{
|
.extra_headers = &.{
|
||||||
.{ .name = "Content-Type", .value = "application/x-tar" },
|
.{ .name = "Content-Type", .value = "application/x-tar" },
|
||||||
@ -497,10 +489,7 @@ pub fn serveTarFile(
|
|||||||
var cached_cwd_path: ?[]const u8 = null;
|
var cached_cwd_path: ?[]const u8 = null;
|
||||||
defer if (cached_cwd_path) |p| gpa.free(p);
|
defer if (cached_cwd_path) |p| gpa.free(p);
|
||||||
|
|
||||||
var response_buf: [1024]u8 = undefined;
|
var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer };
|
||||||
var adapter = response.writer().adaptToNewApi();
|
|
||||||
adapter.new_interface.buffer = &response_buf;
|
|
||||||
var archiver: std.tar.Writer = .{ .underlying_writer = &adapter.new_interface };
|
|
||||||
|
|
||||||
for (paths) |path| {
|
for (paths) |path| {
|
||||||
var file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| {
|
var file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| {
|
||||||
@ -526,7 +515,6 @@ pub fn serveTarFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// intentionally not calling `archiver.finishPedantically`
|
// intentionally not calling `archiver.finishPedantically`
|
||||||
try adapter.new_interface.flush();
|
|
||||||
try response.end();
|
try response.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,7 +792,7 @@ pub fn wait(ws: *WebServer) RunnerRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cache_control_header: std.http.Header = .{
|
const cache_control_header: http.Header = .{
|
||||||
.name = "Cache-Control",
|
.name = "Cache-Control",
|
||||||
.value = "max-age=0, must-revalidate",
|
.value = "max-age=0, must-revalidate",
|
||||||
};
|
};
|
||||||
@ -819,5 +807,6 @@ const Build = std.Build;
|
|||||||
const Cache = Build.Cache;
|
const Cache = Build.Cache;
|
||||||
const Fuzz = Build.Fuzz;
|
const Fuzz = Build.Fuzz;
|
||||||
const abi = Build.abi;
|
const abi = Build.abi;
|
||||||
|
const http = std.http;
|
||||||
|
|
||||||
const WebServer = @This();
|
const WebServer = @This();
|
||||||
|
|||||||
@ -7,6 +7,7 @@ const Uri = std.Uri;
|
|||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const Writer = std.Io.Writer;
|
const Writer = std.Io.Writer;
|
||||||
|
const Reader = std.Io.Reader;
|
||||||
|
|
||||||
const Server = @This();
|
const Server = @This();
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ reader: http.Reader,
|
|||||||
/// header, otherwise `receiveHead` returns `error.HttpHeadersOversize`.
|
/// header, otherwise `receiveHead` returns `error.HttpHeadersOversize`.
|
||||||
///
|
///
|
||||||
/// The returned `Server` is ready for `receiveHead` to be called.
|
/// The returned `Server` is ready for `receiveHead` to be called.
|
||||||
pub fn init(in: *std.Io.Reader, out: *Writer) Server {
|
pub fn init(in: *Reader, out: *Writer) Server {
|
||||||
return .{
|
return .{
|
||||||
.reader = .{
|
.reader = .{
|
||||||
.in = in,
|
.in = in,
|
||||||
@ -225,7 +226,7 @@ pub const Request = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn iterateHeaders(r: *Request) http.HeaderIterator {
|
pub fn iterateHeaders(r: *const Request) http.HeaderIterator {
|
||||||
assert(r.server.reader.state == .received_head);
|
assert(r.server.reader.state == .received_head);
|
||||||
return http.HeaderIterator.init(r.head_buffer);
|
return http.HeaderIterator.init(r.head_buffer);
|
||||||
}
|
}
|
||||||
@ -486,10 +487,11 @@ pub const Request = struct {
|
|||||||
none,
|
none,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Does not invalidate `request.head`.
|
||||||
pub fn upgradeRequested(request: *const Request) UpgradeRequest {
|
pub fn upgradeRequested(request: *const Request) UpgradeRequest {
|
||||||
switch (request.head.version) {
|
switch (request.head.version) {
|
||||||
.@"HTTP/1.0" => return null,
|
.@"HTTP/1.0" => return .none,
|
||||||
.@"HTTP/1.1" => if (request.head.method != .GET) return null,
|
.@"HTTP/1.1" => if (request.head.method != .GET) return .none,
|
||||||
}
|
}
|
||||||
|
|
||||||
var sec_websocket_key: ?[]const u8 = null;
|
var sec_websocket_key: ?[]const u8 = null;
|
||||||
@ -517,7 +519,7 @@ pub const Request = struct {
|
|||||||
|
|
||||||
/// The header is not guaranteed to be sent until `WebSocket.flush` is
|
/// The header is not guaranteed to be sent until `WebSocket.flush` is
|
||||||
/// called on the returned struct.
|
/// called on the returned struct.
|
||||||
pub fn respondWebSocket(request: *Request, options: WebSocketOptions) Writer.Error!WebSocket {
|
pub fn respondWebSocket(request: *Request, options: WebSocketOptions) ExpectContinueError!WebSocket {
|
||||||
if (request.head.expect != null) return error.HttpExpectationFailed;
|
if (request.head.expect != null) return error.HttpExpectationFailed;
|
||||||
|
|
||||||
const out = request.server.out;
|
const out = request.server.out;
|
||||||
@ -536,16 +538,14 @@ pub const Request = struct {
|
|||||||
try out.print("{s} {d} {s}\r\n", .{ @tagName(version), @intFromEnum(status), phrase });
|
try out.print("{s} {d} {s}\r\n", .{ @tagName(version), @intFromEnum(status), phrase });
|
||||||
try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: ");
|
try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: ");
|
||||||
const base64_digest = try out.writableArray(28);
|
const base64_digest = try out.writableArray(28);
|
||||||
assert(std.base64.standard.Encoder.encode(&base64_digest, &digest).len == base64_digest.len);
|
assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len);
|
||||||
out.advance(base64_digest.len);
|
out.advance(base64_digest.len);
|
||||||
try out.writeAll("\r\n");
|
try out.writeAll("\r\n");
|
||||||
|
|
||||||
for (options.extra_headers) |header| {
|
for (options.extra_headers) |header| {
|
||||||
assert(header.name.len != 0);
|
assert(header.name.len != 0);
|
||||||
try out.writeAll(header.name);
|
var bufs: [4][]const u8 = .{ header.name, ": ", header.value, "\r\n" };
|
||||||
try out.writeAll(": ");
|
try out.writeVecAll(&bufs);
|
||||||
try out.writeAll(header.value);
|
|
||||||
try out.writeAll("\r\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try out.writeAll("\r\n");
|
try out.writeAll("\r\n");
|
||||||
@ -566,7 +566,7 @@ pub const Request = struct {
|
|||||||
///
|
///
|
||||||
/// See `readerExpectNone` for an infallible alternative that cannot write
|
/// See `readerExpectNone` for an infallible alternative that cannot write
|
||||||
/// to the server output stream.
|
/// to the server output stream.
|
||||||
pub fn readerExpectContinue(request: *Request, buffer: []u8) ExpectContinueError!*std.Io.Reader {
|
pub fn readerExpectContinue(request: *Request, buffer: []u8) ExpectContinueError!*Reader {
|
||||||
const flush = request.head.expect != null;
|
const flush = request.head.expect != null;
|
||||||
try writeExpectContinue(request);
|
try writeExpectContinue(request);
|
||||||
if (flush) try request.server.out.flush();
|
if (flush) try request.server.out.flush();
|
||||||
@ -578,7 +578,7 @@ pub const Request = struct {
|
|||||||
/// this function.
|
/// this function.
|
||||||
///
|
///
|
||||||
/// Asserts that this function is only called once.
|
/// Asserts that this function is only called once.
|
||||||
pub fn readerExpectNone(request: *Request, buffer: []u8) *std.Io.Reader {
|
pub fn readerExpectNone(request: *Request, buffer: []u8) *Reader {
|
||||||
assert(request.server.reader.state == .received_head);
|
assert(request.server.reader.state == .received_head);
|
||||||
assert(request.head.expect == null);
|
assert(request.head.expect == null);
|
||||||
if (!request.head.method.requestHasBody()) return .ending;
|
if (!request.head.method.requestHasBody()) return .ending;
|
||||||
@ -642,7 +642,7 @@ pub const Request = struct {
|
|||||||
/// See https://tools.ietf.org/html/rfc6455
|
/// See https://tools.ietf.org/html/rfc6455
|
||||||
pub const WebSocket = struct {
|
pub const WebSocket = struct {
|
||||||
key: []const u8,
|
key: []const u8,
|
||||||
input: *std.Io.Reader,
|
input: *Reader,
|
||||||
output: *Writer,
|
output: *Writer,
|
||||||
|
|
||||||
pub const Header0 = packed struct(u8) {
|
pub const Header0 = packed struct(u8) {
|
||||||
@ -679,6 +679,8 @@ pub const WebSocket = struct {
|
|||||||
UnexpectedOpCode,
|
UnexpectedOpCode,
|
||||||
MessageTooBig,
|
MessageTooBig,
|
||||||
MissingMaskBit,
|
MissingMaskBit,
|
||||||
|
ReadFailed,
|
||||||
|
EndOfStream,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SmallMessage = struct {
|
pub const SmallMessage = struct {
|
||||||
@ -693,8 +695,9 @@ pub const WebSocket = struct {
|
|||||||
pub fn readSmallMessage(ws: *WebSocket) ReadSmallTextMessageError!SmallMessage {
|
pub fn readSmallMessage(ws: *WebSocket) ReadSmallTextMessageError!SmallMessage {
|
||||||
const in = ws.input;
|
const in = ws.input;
|
||||||
while (true) {
|
while (true) {
|
||||||
const h0 = in.takeStruct(Header0);
|
const header = try in.takeArray(2);
|
||||||
const h1 = in.takeStruct(Header1);
|
const h0: Header0 = @bitCast(header[0]);
|
||||||
|
const h1: Header1 = @bitCast(header[1]);
|
||||||
|
|
||||||
switch (h0.opcode) {
|
switch (h0.opcode) {
|
||||||
.text, .binary, .pong, .ping => {},
|
.text, .binary, .pong, .ping => {},
|
||||||
@ -734,47 +737,49 @@ pub const WebSocket = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeMessage(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void {
|
pub fn writeMessage(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void {
|
||||||
try writeMessageVecUnflushed(ws, &.{data}, op);
|
var bufs: [1][]const u8 = .{data};
|
||||||
|
try writeMessageVecUnflushed(ws, &bufs, op);
|
||||||
try ws.output.flush();
|
try ws.output.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeMessageUnflushed(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void {
|
pub fn writeMessageUnflushed(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void {
|
||||||
try writeMessageVecUnflushed(ws, &.{data}, op);
|
var bufs: [1][]const u8 = .{data};
|
||||||
|
try writeMessageVecUnflushed(ws, &bufs, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeMessageVec(ws: *WebSocket, data: []const []const u8, op: Opcode) Writer.Error!void {
|
pub fn writeMessageVec(ws: *WebSocket, data: [][]const u8, op: Opcode) Writer.Error!void {
|
||||||
try writeMessageVecUnflushed(ws, data, op);
|
try writeMessageVecUnflushed(ws, data, op);
|
||||||
try ws.output.flush();
|
try ws.output.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeMessageVecUnflushed(ws: *WebSocket, data: []const []const u8, op: Opcode) Writer.Error!void {
|
pub fn writeMessageVecUnflushed(ws: *WebSocket, data: [][]const u8, op: Opcode) Writer.Error!void {
|
||||||
const total_len = l: {
|
const total_len = l: {
|
||||||
var total_len: u64 = 0;
|
var total_len: u64 = 0;
|
||||||
for (data) |iovec| total_len += iovec.len;
|
for (data) |iovec| total_len += iovec.len;
|
||||||
break :l total_len;
|
break :l total_len;
|
||||||
};
|
};
|
||||||
const out = ws.output;
|
const out = ws.output;
|
||||||
try out.writeStruct(@as(Header0, .{
|
try out.writeByte(@bitCast(@as(Header0, .{
|
||||||
.opcode = op,
|
.opcode = op,
|
||||||
.fin = true,
|
.fin = true,
|
||||||
}));
|
})));
|
||||||
switch (total_len) {
|
switch (total_len) {
|
||||||
0...125 => try out.writeStruct(@as(Header1, .{
|
0...125 => try out.writeByte(@bitCast(@as(Header1, .{
|
||||||
.payload_len = @enumFromInt(total_len),
|
.payload_len = @enumFromInt(total_len),
|
||||||
.mask = false,
|
.mask = false,
|
||||||
})),
|
}))),
|
||||||
126...0xffff => {
|
126...0xffff => {
|
||||||
try out.writeStruct(@as(Header1, .{
|
try out.writeByte(@bitCast(@as(Header1, .{
|
||||||
.payload_len = .len16,
|
.payload_len = .len16,
|
||||||
.mask = false,
|
.mask = false,
|
||||||
}));
|
})));
|
||||||
try out.writeInt(u16, @intCast(total_len), .big);
|
try out.writeInt(u16, @intCast(total_len), .big);
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
try out.writeStruct(@as(Header1, .{
|
try out.writeByte(@bitCast(@as(Header1, .{
|
||||||
.payload_len = .len64,
|
.payload_len = .len64,
|
||||||
.mask = false,
|
.mask = false,
|
||||||
}));
|
})));
|
||||||
try out.writeInt(u64, total_len, .big);
|
try out.writeInt(u64, total_len, .big);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user