| author | |
| committer | |
| log | fdf0e4612e4441d2f0386839078d8a7adf738a3e |
| tree | 910bf136b35d14c32c1965e24085c383b1dbc23d |
| parent | 98ee9360555730328621aa8b9b9170a4e2b0df7b |
3 files changed, 90 insertions(+), 103 deletions(-)
lib/std/Build/Fuzz.zig+16-23| ... | @@ -234,7 +234,7 @@ pub const Previous = struct { | ... | @@ -234,7 +234,7 @@ pub const Previous = struct { |
| 234 | }; | 234 | }; |
| 235 | pub fn sendUpdate( | 235 | pub fn sendUpdate( |
| 236 | fuzz: *Fuzz, | 236 | fuzz: *Fuzz, |
| 237 | socket: *std.http.WebSocket, | 237 | socket: *std.http.Server.WebSocket, |
| 238 | prev: *Previous, | 238 | prev: *Previous, |
| 239 | ) !void { | 239 | ) !void { |
| 240 | fuzz.coverage_mutex.lock(); | 240 | fuzz.coverage_mutex.lock(); |
| ... | @@ -263,36 +263,36 @@ pub fn sendUpdate( | ... | @@ -263,36 +263,36 @@ pub fn sendUpdate( |
| 263 | .string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len), | 263 | .string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len), |
| 264 | .start_timestamp = coverage_map.start_timestamp, | 264 | .start_timestamp = coverage_map.start_timestamp, |
| 265 | }; | 265 | }; |
| 266 | const iovecs: [5]std.posix.iovec_const = .{ | 266 | var iovecs: [5][]const u8 = .{ |
| 267 | makeIov(@ptrCast(&header)), | 267 | @ptrCast(&header), |
| 268 | makeIov(@ptrCast(coverage_map.coverage.directories.keys())), | 268 | @ptrCast(coverage_map.coverage.directories.keys()), |
| 269 | makeIov(@ptrCast(coverage_map.coverage.files.keys())), | 269 | @ptrCast(coverage_map.coverage.files.keys()), |
| 270 | makeIov(@ptrCast(coverage_map.source_locations)), | 270 | @ptrCast(coverage_map.source_locations), |
| 271 | makeIov(coverage_map.coverage.string_bytes.items), | 271 | coverage_map.coverage.string_bytes.items, |
| 272 | }; | 272 | }; |
| 273 | try socket.writeMessagev(&iovecs, .binary); | 273 | try socket.writeMessageVec(&iovecs, .binary); |
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | const header: abi.CoverageUpdateHeader = .{ | 276 | const header: abi.CoverageUpdateHeader = .{ |
| 277 | .n_runs = n_runs, | 277 | .n_runs = n_runs, |
| 278 | .unique_runs = unique_runs, | 278 | .unique_runs = unique_runs, |
| 279 | }; | 279 | }; |
| 280 | const iovecs: [2]std.posix.iovec_const = .{ | 280 | var iovecs: [2][]const u8 = .{ |
| 281 | makeIov(@ptrCast(&header)), | 281 | @ptrCast(&header), |
| 282 | makeIov(@ptrCast(seen_pcs)), | 282 | @ptrCast(seen_pcs), |
| 283 | }; | 283 | }; |
| 284 | try socket.writeMessagev(&iovecs, .binary); | 284 | try socket.writeMessageVec(&iovecs, .binary); |
| 285 | 285 | ||
| 286 | prev.unique_runs = unique_runs; | 286 | prev.unique_runs = unique_runs; |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | if (prev.entry_points != coverage_map.entry_points.items.len) { | 289 | if (prev.entry_points != coverage_map.entry_points.items.len) { |
| 290 | const header: abi.EntryPointHeader = .init(@intCast(coverage_map.entry_points.items.len)); | 290 | const header: abi.EntryPointHeader = .init(@intCast(coverage_map.entry_points.items.len)); |
| 291 | const iovecs: [2]std.posix.iovec_const = .{ | 291 | var iovecs: [2][]const u8 = .{ |
| 292 | makeIov(@ptrCast(&header)), | 292 | @ptrCast(&header), |
| 293 | makeIov(@ptrCast(coverage_map.entry_points.items)), | 293 | @ptrCast(coverage_map.entry_points.items), |
| 294 | }; | 294 | }; |
| 295 | try socket.writeMessagev(&iovecs, .binary); | 295 | try socket.writeMessageVec(&iovecs, .binary); |
| 296 | 296 | ||
| 297 | prev.entry_points = coverage_map.entry_points.items.len; | 297 | prev.entry_points = coverage_map.entry_points.items.len; |
| 298 | } | 298 | } |
| ... | @@ -448,10 +448,3 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte | ... | @@ -448,10 +448,3 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte |
| 448 | } | 448 | } |
| 449 | try coverage_map.entry_points.append(fuzz.ws.gpa, @intCast(index)); | 449 | try coverage_map.entry_points.append(fuzz.ws.gpa, @intCast(index)); |
| 450 | } | 450 | } |
| 451 | |||
| 452 | fn makeIov(s: []const u8) std.posix.iovec_const { | ||
| 453 | return .{ | ||
| 454 | .base = s.ptr, | ||
| 455 | .len = s.len, | ||
| 456 | }; | ||
| 457 | } |
lib/std/Build/WebServer.zig+42-53| ... | @@ -251,48 +251,44 @@ pub fn now(s: *const WebServer) i64 { | ... | @@ -251,48 +251,44 @@ pub fn now(s: *const WebServer) i64 { |
| 251 | fn accept(ws: *WebServer, connection: std.net.Server.Connection) void { | 251 | fn accept(ws: *WebServer, connection: std.net.Server.Connection) void { |
| 252 | defer connection.stream.close(); | 252 | defer connection.stream.close(); |
| 253 | 253 | ||
| 254 | var read_buf: [0x4000]u8 = undefined; | 254 | var send_buffer: [4096]u8 = undefined; |
| 255 | var server: std.http.Server = .init(connection, &read_buf); | 255 | var recv_buffer: [4096]u8 = undefined; |
| 256 | var connection_reader = connection.stream.reader(&recv_buffer); | ||
| 257 | var connection_writer = connection.stream.writer(&send_buffer); | ||
| 258 | var server: http.Server = .init(connection_reader.interface(), &connection_writer.interface); | ||
| 256 | 259 | ||
| 257 | while (true) { | 260 | while (true) { |
| 258 | var request = server.receiveHead() catch |err| switch (err) { | 261 | var request = server.receiveHead() catch |err| switch (err) { |
| 259 | error.HttpConnectionClosing => return, | 262 | error.HttpConnectionClosing => return, |
| 260 | else => { | 263 | else => return log.err("failed to receive http request: {t}", .{err}), |
| 261 | log.err("failed to receive http request: {s}", .{@errorName(err)}); | ||
| 262 | return; | ||
| 263 | }, | ||
| 264 | }; | 264 | }; |
| 265 | var ws_send_buf: [0x4000]u8 = undefined; | 265 | switch (request.upgradeRequested()) { |
| 266 | var ws_recv_buf: [0x4000]u8 align(4) = undefined; | 266 | .websocket => |opt_key| { |
| 267 | if (std.http.WebSocket.init(&request, &ws_send_buf, &ws_recv_buf) catch |err| { | 267 | const key = opt_key orelse return log.err("missing websocket key", .{}); |
| 268 | log.err("failed to initialize websocket connection: {s}", .{@errorName(err)}); | 268 | var web_socket = request.respondWebSocket(.{ .key = key }) catch { |
| 269 | return; | 269 | return log.err("failed to respond web socket: {t}", .{connection_writer.err.?}); |
| 270 | }) |ws_init| { | 270 | }; |
| 271 | var web_socket = ws_init; | 271 | ws.serveWebSocket(&web_socket) catch |err| { |
| 272 | ws.serveWebSocket(&web_socket) catch |err| { | 272 | log.err("failed to serve websocket: {t}", .{err}); |
| 273 | log.err("failed to serve websocket: {s}", .{@errorName(err)}); | ||
| 274 | return; | ||
| 275 | }; | ||
| 276 | comptime unreachable; | ||
| 277 | } else { | ||
| 278 | ws.serveRequest(&request) catch |err| switch (err) { | ||
| 279 | error.AlreadyReported => return, | ||
| 280 | else => { | ||
| 281 | log.err("failed to serve '{s}': {s}", .{ request.head.target, @errorName(err) }); | ||
| 282 | return; | 273 | return; |
| 283 | }, | 274 | }; |
| 284 | }; | 275 | comptime unreachable; |
| 276 | }, | ||
| 277 | .other => |name| return log.err("unknown upgrade request: {s}", .{name}), | ||
| 278 | .none => { | ||
| 279 | ws.serveRequest(&request) catch |err| switch (err) { | ||
| 280 | error.AlreadyReported => return, | ||
| 281 | else => { | ||
| 282 | log.err("failed to serve '{s}': {t}", .{ request.head.target, err }); | ||
| 283 | return; | ||
| 284 | }, | ||
| 285 | }; | ||
| 286 | }, | ||
| 285 | } | 287 | } |
| 286 | } | 288 | } |
| 287 | } | 289 | } |
| 288 | 290 | ||
| 289 | fn makeIov(s: []const u8) std.posix.iovec_const { | 291 | fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { |
| 290 | return .{ | ||
| 291 | .base = s.ptr, | ||
| 292 | .len = s.len, | ||
| 293 | }; | ||
| 294 | } | ||
| 295 | fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn { | ||
| 296 | var prev_build_status = ws.build_status.load(.monotonic); | 292 | var prev_build_status = ws.build_status.load(.monotonic); |
| 297 | 293 | ||
| 298 | const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len); | 294 | 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 { | ... | @@ -312,11 +308,8 @@ fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn { |
| 312 | .timestamp = ws.now(), | 308 | .timestamp = ws.now(), |
| 313 | .steps_len = @intCast(ws.all_steps.len), | 309 | .steps_len = @intCast(ws.all_steps.len), |
| 314 | }; | 310 | }; |
| 315 | try sock.writeMessagev(&.{ | 311 | var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), ws.step_names_trailing, prev_step_status_bits }; |
| 316 | makeIov(@ptrCast(&hello_header)), | 312 | try sock.writeMessageVec(&bufs, .binary); |
| 317 | makeIov(ws.step_names_trailing), | ||
| 318 | makeIov(prev_step_status_bits), | ||
| 319 | }, .binary); | ||
| 320 | } | 313 | } |
| 321 | 314 | ||
| 322 | var prev_fuzz: Fuzz.Previous = .init; | 315 | var prev_fuzz: Fuzz.Previous = .init; |
| ... | @@ -380,7 +373,7 @@ fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn { | ... | @@ -380,7 +373,7 @@ fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn { |
| 380 | std.Thread.Futex.timedWait(&ws.update_id, start_update_id, std.time.ns_per_ms * default_update_interval_ms) catch {}; | 373 | std.Thread.Futex.timedWait(&ws.update_id, start_update_id, std.time.ns_per_ms * default_update_interval_ms) catch {}; |
| 381 | } | 374 | } |
| 382 | } | 375 | } |
| 383 | fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void { | 376 | fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void { |
| 384 | while (true) { | 377 | while (true) { |
| 385 | const msg = sock.readSmallMessage() catch return; | 378 | const msg = sock.readSmallMessage() catch return; |
| 386 | if (msg.opcode != .binary) continue; | 379 | if (msg.opcode != .binary) continue; |
| ... | @@ -402,7 +395,7 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void { | ... | @@ -402,7 +395,7 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void { |
| 402 | } | 395 | } |
| 403 | } | 396 | } |
| 404 | 397 | ||
| 405 | fn serveRequest(ws: *WebServer, req: *std.http.Server.Request) !void { | 398 | fn serveRequest(ws: *WebServer, req: *http.Server.Request) !void { |
| 406 | // Strip an optional leading '/debug' component from the request. | 399 | // Strip an optional leading '/debug' component from the request. |
| 407 | const target: []const u8, const debug: bool = target: { | 400 | const target: []const u8, const debug: bool = target: { |
| 408 | if (mem.eql(u8, req.head.target, "/debug")) break :target .{ "/", true }; | 401 | 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 { | ... | @@ -431,7 +424,7 @@ fn serveRequest(ws: *WebServer, req: *std.http.Server.Request) !void { |
| 431 | 424 | ||
| 432 | fn serveLibFile( | 425 | fn serveLibFile( |
| 433 | ws: *WebServer, | 426 | ws: *WebServer, |
| 434 | request: *std.http.Server.Request, | 427 | request: *http.Server.Request, |
| 435 | sub_path: []const u8, | 428 | sub_path: []const u8, |
| 436 | content_type: []const u8, | 429 | content_type: []const u8, |
| 437 | ) !void { | 430 | ) !void { |
| ... | @@ -442,7 +435,7 @@ fn serveLibFile( | ... | @@ -442,7 +435,7 @@ fn serveLibFile( |
| 442 | } | 435 | } |
| 443 | fn serveClientWasm( | 436 | fn serveClientWasm( |
| 444 | ws: *WebServer, | 437 | ws: *WebServer, |
| 445 | req: *std.http.Server.Request, | 438 | req: *http.Server.Request, |
| 446 | optimize_mode: std.builtin.OptimizeMode, | 439 | optimize_mode: std.builtin.OptimizeMode, |
| 447 | ) !void { | 440 | ) !void { |
| 448 | var arena_state: std.heap.ArenaAllocator = .init(ws.gpa); | 441 | var arena_state: std.heap.ArenaAllocator = .init(ws.gpa); |
| ... | @@ -456,12 +449,12 @@ fn serveClientWasm( | ... | @@ -456,12 +449,12 @@ fn serveClientWasm( |
| 456 | 449 | ||
| 457 | pub fn serveFile( | 450 | pub fn serveFile( |
| 458 | ws: *WebServer, | 451 | ws: *WebServer, |
| 459 | request: *std.http.Server.Request, | 452 | request: *http.Server.Request, |
| 460 | path: Cache.Path, | 453 | path: Cache.Path, |
| 461 | content_type: []const u8, | 454 | content_type: []const u8, |
| 462 | ) !void { | 455 | ) !void { |
| 463 | const gpa = ws.gpa; | 456 | const gpa = ws.gpa; |
| 464 | // The desired API is actually sendfile, which will require enhancing std.http.Server. | 457 | // The desired API is actually sendfile, which will require enhancing http.Server. |
| 465 | // We load the file with every request so that the user can make changes to the file | 458 | // We load the file with every request so that the user can make changes to the file |
| 466 | // and refresh the HTML page without restarting this server. | 459 | // and refresh the HTML page without restarting this server. |
| 467 | const file_contents = path.root_dir.handle.readFileAlloc(gpa, path.sub_path, 10 * 1024 * 1024) catch |err| { | 460 | const file_contents = path.root_dir.handle.readFileAlloc(gpa, path.sub_path, 10 * 1024 * 1024) catch |err| { |
| ... | @@ -478,14 +471,13 @@ pub fn serveFile( | ... | @@ -478,14 +471,13 @@ pub fn serveFile( |
| 478 | } | 471 | } |
| 479 | pub fn serveTarFile( | 472 | pub fn serveTarFile( |
| 480 | ws: *WebServer, | 473 | ws: *WebServer, |
| 481 | request: *std.http.Server.Request, | 474 | request: *http.Server.Request, |
| 482 | paths: []const Cache.Path, | 475 | paths: []const Cache.Path, |
| 483 | ) !void { | 476 | ) !void { |
| 484 | const gpa = ws.gpa; | 477 | const gpa = ws.gpa; |
| 485 | 478 | ||
| 486 | var send_buf: [0x4000]u8 = undefined; | 479 | var send_buffer: [0x4000]u8 = undefined; |
| 487 | var response = request.respondStreaming(.{ | 480 | var response = try request.respondStreaming(&send_buffer, .{ |
| 488 | .send_buffer = &send_buf, | ||
| 489 | .respond_options = .{ | 481 | .respond_options = .{ |
| 490 | .extra_headers = &.{ | 482 | .extra_headers = &.{ |
| 491 | .{ .name = "Content-Type", .value = "application/x-tar" }, | 483 | .{ .name = "Content-Type", .value = "application/x-tar" }, |
| ... | @@ -497,10 +489,7 @@ pub fn serveTarFile( | ... | @@ -497,10 +489,7 @@ pub fn serveTarFile( |
| 497 | var cached_cwd_path: ?[]const u8 = null; | 489 | var cached_cwd_path: ?[]const u8 = null; |
| 498 | defer if (cached_cwd_path) |p| gpa.free(p); | 490 | defer if (cached_cwd_path) |p| gpa.free(p); |
| 499 | 491 | ||
| 500 | var response_buf: [1024]u8 = undefined; | 492 | var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer }; |
| 501 | var adapter = response.writer().adaptToNewApi(); | ||
| 502 | adapter.new_interface.buffer = &response_buf; | ||
| 503 | var archiver: std.tar.Writer = .{ .underlying_writer = &adapter.new_interface }; | ||
| 504 | 493 | ||
| 505 | for (paths) |path| { | 494 | for (paths) |path| { |
| 506 | var file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| { | 495 | var file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| { |
| ... | @@ -526,7 +515,6 @@ pub fn serveTarFile( | ... | @@ -526,7 +515,6 @@ pub fn serveTarFile( |
| 526 | } | 515 | } |
| 527 | 516 | ||
| 528 | // intentionally not calling `archiver.finishPedantically` | 517 | // intentionally not calling `archiver.finishPedantically` |
| 529 | try adapter.new_interface.flush(); | ||
| 530 | try response.end(); | 518 | try response.end(); |
| 531 | } | 519 | } |
| 532 | 520 | ||
| ... | @@ -804,7 +792,7 @@ pub fn wait(ws: *WebServer) RunnerRequest { | ... | @@ -804,7 +792,7 @@ pub fn wait(ws: *WebServer) RunnerRequest { |
| 804 | } | 792 | } |
| 805 | } | 793 | } |
| 806 | 794 | ||
| 807 | const cache_control_header: std.http.Header = .{ | 795 | const cache_control_header: http.Header = .{ |
| 808 | .name = "Cache-Control", | 796 | .name = "Cache-Control", |
| 809 | .value = "max-age=0, must-revalidate", | 797 | .value = "max-age=0, must-revalidate", |
| 810 | }; | 798 | }; |
| ... | @@ -819,5 +807,6 @@ const Build = std.Build; | ... | @@ -819,5 +807,6 @@ const Build = std.Build; |
| 819 | const Cache = Build.Cache; | 807 | const Cache = Build.Cache; |
| 820 | const Fuzz = Build.Fuzz; | 808 | const Fuzz = Build.Fuzz; |
| 821 | const abi = Build.abi; | 809 | const abi = Build.abi; |
| 810 | const http = std.http; | ||
| 822 | 811 | ||
| 823 | const WebServer = @This(); | 812 | const WebServer = @This(); |
lib/std/http/Server.zig+32-27| ... | @@ -7,6 +7,7 @@ const Uri = std.Uri; | ... | @@ -7,6 +7,7 @@ const Uri = std.Uri; |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | const testing = std.testing; | 8 | const testing = std.testing; |
| 9 | const Writer = std.Io.Writer; | 9 | const Writer = std.Io.Writer; |
| 10 | const Reader = std.Io.Reader; | ||
| 10 | 11 | ||
| 11 | const Server = @This(); | 12 | const Server = @This(); |
| 12 | 13 | ||
| ... | @@ -21,7 +22,7 @@ reader: http.Reader, | ... | @@ -21,7 +22,7 @@ reader: http.Reader, |
| 21 | /// header, otherwise `receiveHead` returns `error.HttpHeadersOversize`. | 22 | /// header, otherwise `receiveHead` returns `error.HttpHeadersOversize`. |
| 22 | /// | 23 | /// |
| 23 | /// The returned `Server` is ready for `receiveHead` to be called. | 24 | /// The returned `Server` is ready for `receiveHead` to be called. |
| 24 | pub fn init(in: *std.Io.Reader, out: *Writer) Server { | 25 | pub fn init(in: *Reader, out: *Writer) Server { |
| 25 | return .{ | 26 | return .{ |
| 26 | .reader = .{ | 27 | .reader = .{ |
| 27 | .in = in, | 28 | .in = in, |
| ... | @@ -225,7 +226,7 @@ pub const Request = struct { | ... | @@ -225,7 +226,7 @@ pub const Request = struct { |
| 225 | } | 226 | } |
| 226 | }; | 227 | }; |
| 227 | 228 | ||
| 228 | pub fn iterateHeaders(r: *Request) http.HeaderIterator { | 229 | pub fn iterateHeaders(r: *const Request) http.HeaderIterator { |
| 229 | assert(r.server.reader.state == .received_head); | 230 | assert(r.server.reader.state == .received_head); |
| 230 | return http.HeaderIterator.init(r.head_buffer); | 231 | return http.HeaderIterator.init(r.head_buffer); |
| 231 | } | 232 | } |
| ... | @@ -486,10 +487,11 @@ pub const Request = struct { | ... | @@ -486,10 +487,11 @@ pub const Request = struct { |
| 486 | none, | 487 | none, |
| 487 | }; | 488 | }; |
| 488 | 489 | ||
| 490 | /// Does not invalidate `request.head`. | ||
| 489 | pub fn upgradeRequested(request: *const Request) UpgradeRequest { | 491 | pub fn upgradeRequested(request: *const Request) UpgradeRequest { |
| 490 | switch (request.head.version) { | 492 | switch (request.head.version) { |
| 491 | .@"HTTP/1.0" => return null, | 493 | .@"HTTP/1.0" => return .none, |
| 492 | .@"HTTP/1.1" => if (request.head.method != .GET) return null, | 494 | .@"HTTP/1.1" => if (request.head.method != .GET) return .none, |
| 493 | } | 495 | } |
| 494 | 496 | ||
| 495 | var sec_websocket_key: ?[]const u8 = null; | 497 | var sec_websocket_key: ?[]const u8 = null; |
| ... | @@ -517,7 +519,7 @@ pub const Request = struct { | ... | @@ -517,7 +519,7 @@ pub const Request = struct { |
| 517 | 519 | ||
| 518 | /// The header is not guaranteed to be sent until `WebSocket.flush` is | 520 | /// The header is not guaranteed to be sent until `WebSocket.flush` is |
| 519 | /// called on the returned struct. | 521 | /// called on the returned struct. |
| 520 | pub fn respondWebSocket(request: *Request, options: WebSocketOptions) Writer.Error!WebSocket { | 522 | pub fn respondWebSocket(request: *Request, options: WebSocketOptions) ExpectContinueError!WebSocket { |
| 521 | if (request.head.expect != null) return error.HttpExpectationFailed; | 523 | if (request.head.expect != null) return error.HttpExpectationFailed; |
| 522 | 524 | ||
| 523 | const out = request.server.out; | 525 | const out = request.server.out; |
| ... | @@ -536,16 +538,14 @@ pub const Request = struct { | ... | @@ -536,16 +538,14 @@ pub const Request = struct { |
| 536 | try out.print("{s} {d} {s}\r\n", .{ @tagName(version), @intFromEnum(status), phrase }); | 538 | try out.print("{s} {d} {s}\r\n", .{ @tagName(version), @intFromEnum(status), phrase }); |
| 537 | try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); | 539 | try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); |
| 538 | const base64_digest = try out.writableArray(28); | 540 | const base64_digest = try out.writableArray(28); |
| 539 | assert(std.base64.standard.Encoder.encode(&base64_digest, &digest).len == base64_digest.len); | 541 | assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len); |
| 540 | out.advance(base64_digest.len); | 542 | out.advance(base64_digest.len); |
| 541 | try out.writeAll("\r\n"); | 543 | try out.writeAll("\r\n"); |
| 542 | 544 | ||
| 543 | for (options.extra_headers) |header| { | 545 | for (options.extra_headers) |header| { |
| 544 | assert(header.name.len != 0); | 546 | assert(header.name.len != 0); |
| 545 | try out.writeAll(header.name); | 547 | var bufs: [4][]const u8 = .{ header.name, ": ", header.value, "\r\n" }; |
| 546 | try out.writeAll(": "); | 548 | try out.writeVecAll(&bufs); |
| 547 | try out.writeAll(header.value); | ||
| 548 | try out.writeAll("\r\n"); | ||
| 549 | } | 549 | } |
| 550 | 550 | ||
| 551 | try out.writeAll("\r\n"); | 551 | try out.writeAll("\r\n"); |
| ... | @@ -566,7 +566,7 @@ pub const Request = struct { | ... | @@ -566,7 +566,7 @@ pub const Request = struct { |
| 566 | /// | 566 | /// |
| 567 | /// See `readerExpectNone` for an infallible alternative that cannot write | 567 | /// See `readerExpectNone` for an infallible alternative that cannot write |
| 568 | /// to the server output stream. | 568 | /// to the server output stream. |
| 569 | pub fn readerExpectContinue(request: *Request, buffer: []u8) ExpectContinueError!*std.Io.Reader { | 569 | pub fn readerExpectContinue(request: *Request, buffer: []u8) ExpectContinueError!*Reader { |
| 570 | const flush = request.head.expect != null; | 570 | const flush = request.head.expect != null; |
| 571 | try writeExpectContinue(request); | 571 | try writeExpectContinue(request); |
| 572 | if (flush) try request.server.out.flush(); | 572 | if (flush) try request.server.out.flush(); |
| ... | @@ -578,7 +578,7 @@ pub const Request = struct { | ... | @@ -578,7 +578,7 @@ pub const Request = struct { |
| 578 | /// this function. | 578 | /// this function. |
| 579 | /// | 579 | /// |
| 580 | /// Asserts that this function is only called once. | 580 | /// Asserts that this function is only called once. |
| 581 | pub fn readerExpectNone(request: *Request, buffer: []u8) *std.Io.Reader { | 581 | pub fn readerExpectNone(request: *Request, buffer: []u8) *Reader { |
| 582 | assert(request.server.reader.state == .received_head); | 582 | assert(request.server.reader.state == .received_head); |
| 583 | assert(request.head.expect == null); | 583 | assert(request.head.expect == null); |
| 584 | if (!request.head.method.requestHasBody()) return .ending; | 584 | if (!request.head.method.requestHasBody()) return .ending; |
| ... | @@ -642,7 +642,7 @@ pub const Request = struct { | ... | @@ -642,7 +642,7 @@ pub const Request = struct { |
| 642 | /// See https://tools.ietf.org/html/rfc6455 | 642 | /// See https://tools.ietf.org/html/rfc6455 |
| 643 | pub const WebSocket = struct { | 643 | pub const WebSocket = struct { |
| 644 | key: []const u8, | 644 | key: []const u8, |
| 645 | input: *std.Io.Reader, | 645 | input: *Reader, |
| 646 | output: *Writer, | 646 | output: *Writer, |
| 647 | 647 | ||
| 648 | pub const Header0 = packed struct(u8) { | 648 | pub const Header0 = packed struct(u8) { |
| ... | @@ -679,6 +679,8 @@ pub const WebSocket = struct { | ... | @@ -679,6 +679,8 @@ pub const WebSocket = struct { |
| 679 | UnexpectedOpCode, | 679 | UnexpectedOpCode, |
| 680 | MessageTooBig, | 680 | MessageTooBig, |
| 681 | MissingMaskBit, | 681 | MissingMaskBit, |
| 682 | ReadFailed, | ||
| 683 | EndOfStream, | ||
| 682 | }; | 684 | }; |
| 683 | 685 | ||
| 684 | pub const SmallMessage = struct { | 686 | pub const SmallMessage = struct { |
| ... | @@ -693,8 +695,9 @@ pub const WebSocket = struct { | ... | @@ -693,8 +695,9 @@ pub const WebSocket = struct { |
| 693 | pub fn readSmallMessage(ws: *WebSocket) ReadSmallTextMessageError!SmallMessage { | 695 | pub fn readSmallMessage(ws: *WebSocket) ReadSmallTextMessageError!SmallMessage { |
| 694 | const in = ws.input; | 696 | const in = ws.input; |
| 695 | while (true) { | 697 | while (true) { |
| 696 | const h0 = in.takeStruct(Header0); | 698 | const header = try in.takeArray(2); |
| 697 | const h1 = in.takeStruct(Header1); | 699 | const h0: Header0 = @bitCast(header[0]); |
| 700 | const h1: Header1 = @bitCast(header[1]); | ||
| 698 | 701 | ||
| 699 | switch (h0.opcode) { | 702 | switch (h0.opcode) { |
| 700 | .text, .binary, .pong, .ping => {}, | 703 | .text, .binary, .pong, .ping => {}, |
| ... | @@ -734,47 +737,49 @@ pub const WebSocket = struct { | ... | @@ -734,47 +737,49 @@ pub const WebSocket = struct { |
| 734 | } | 737 | } |
| 735 | 738 | ||
| 736 | pub fn writeMessage(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void { | 739 | pub fn writeMessage(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void { |
| 737 | try writeMessageVecUnflushed(ws, &.{data}, op); | 740 | var bufs: [1][]const u8 = .{data}; |
| 741 | try writeMessageVecUnflushed(ws, &bufs, op); | ||
| 738 | try ws.output.flush(); | 742 | try ws.output.flush(); |
| 739 | } | 743 | } |
| 740 | 744 | ||
| 741 | pub fn writeMessageUnflushed(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void { | 745 | pub fn writeMessageUnflushed(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void { |
| 742 | try writeMessageVecUnflushed(ws, &.{data}, op); | 746 | var bufs: [1][]const u8 = .{data}; |
| 747 | try writeMessageVecUnflushed(ws, &bufs, op); | ||
| 743 | } | 748 | } |
| 744 | 749 | ||
| 745 | pub fn writeMessageVec(ws: *WebSocket, data: []const []const u8, op: Opcode) Writer.Error!void { | 750 | pub fn writeMessageVec(ws: *WebSocket, data: [][]const u8, op: Opcode) Writer.Error!void { |
| 746 | try writeMessageVecUnflushed(ws, data, op); | 751 | try writeMessageVecUnflushed(ws, data, op); |
| 747 | try ws.output.flush(); | 752 | try ws.output.flush(); |
| 748 | } | 753 | } |
| 749 | 754 | ||
| 750 | pub fn writeMessageVecUnflushed(ws: *WebSocket, data: []const []const u8, op: Opcode) Writer.Error!void { | 755 | pub fn writeMessageVecUnflushed(ws: *WebSocket, data: [][]const u8, op: Opcode) Writer.Error!void { |
| 751 | const total_len = l: { | 756 | const total_len = l: { |
| 752 | var total_len: u64 = 0; | 757 | var total_len: u64 = 0; |
| 753 | for (data) |iovec| total_len += iovec.len; | 758 | for (data) |iovec| total_len += iovec.len; |
| 754 | break :l total_len; | 759 | break :l total_len; |
| 755 | }; | 760 | }; |
| 756 | const out = ws.output; | 761 | const out = ws.output; |
| 757 | try out.writeStruct(@as(Header0, .{ | 762 | try out.writeByte(@bitCast(@as(Header0, .{ |
| 758 | .opcode = op, | 763 | .opcode = op, |
| 759 | .fin = true, | 764 | .fin = true, |
| 760 | })); | 765 | }))); |
| 761 | switch (total_len) { | 766 | switch (total_len) { |
| 762 | 0...125 => try out.writeStruct(@as(Header1, .{ | 767 | 0...125 => try out.writeByte(@bitCast(@as(Header1, .{ |
| 763 | .payload_len = @enumFromInt(total_len), | 768 | .payload_len = @enumFromInt(total_len), |
| 764 | .mask = false, | 769 | .mask = false, |
| 765 | })), | 770 | }))), |
| 766 | 126...0xffff => { | 771 | 126...0xffff => { |
| 767 | try out.writeStruct(@as(Header1, .{ | 772 | try out.writeByte(@bitCast(@as(Header1, .{ |
| 768 | .payload_len = .len16, | 773 | .payload_len = .len16, |
| 769 | .mask = false, | 774 | .mask = false, |
| 770 | })); | 775 | }))); |
| 771 | try out.writeInt(u16, @intCast(total_len), .big); | 776 | try out.writeInt(u16, @intCast(total_len), .big); |
| 772 | }, | 777 | }, |
| 773 | else => { | 778 | else => { |
| 774 | try out.writeStruct(@as(Header1, .{ | 779 | try out.writeByte(@bitCast(@as(Header1, .{ |
| 775 | .payload_len = .len64, | 780 | .payload_len = .len64, |
| 776 | .mask = false, | 781 | .mask = false, |
| 777 | })); | 782 | }))); |
| 778 | try out.writeInt(u64, total_len, .big); | 783 | try out.writeInt(u64, total_len, .big); |
| 779 | }, | 784 | }, |
| 780 | } | 785 | } |