| ... | @@ -309,14 +309,13 @@ pub const Request = struct { | ... | @@ -309,14 +309,13 @@ pub const Request = struct { |
| 309 | | 309 | |
| 310 | var first_buffer: [500]u8 = undefined; | 310 | var first_buffer: [500]u8 = undefined; |
| 311 | var h = std.ArrayListUnmanaged(u8).initBuffer(&first_buffer); | 311 | var h = std.ArrayListUnmanaged(u8).initBuffer(&first_buffer); |
| 312 | h.writerAssumeCapacity().print("{s} {d} {s}\r\n", .{ | 312 | h.fixedWriter().print("{s} {d} {s}\r\n", .{ |
| 313 | @tagName(options.version), @intFromEnum(options.status), phrase, | 313 | @tagName(options.version), @intFromEnum(options.status), phrase, |
| 314 | }) catch |err| switch (err) {}; | 314 | }) catch unreachable; |
| 315 | if (keep_alive) | 315 | if (keep_alive) |
| 316 | h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); | 316 | h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); |
| 317 | if (content.len > 0) | 317 | if (content.len > 0) |
| 318 | h.writerAssumeCapacity().print("content-length: {d}\r\n", .{content.len}) catch |err| | 318 | h.fixedWriter().print("content-length: {d}\r\n", .{content.len}) catch unreachable; |
| 319 | switch (err) {}; | | |
| 320 | | 319 | |
| 321 | var iovecs: [max_extra_headers * 4 + 3]std.posix.iovec_const = undefined; | 320 | var iovecs: [max_extra_headers * 4 + 3]std.posix.iovec_const = undefined; |
| 322 | var iovecs_len: usize = 0; | 321 | var iovecs_len: usize = 0; |
| ... | @@ -407,13 +406,13 @@ pub const Request = struct { | ... | @@ -407,13 +406,13 @@ pub const Request = struct { |
| 407 | | 406 | |
| 408 | var h = std.ArrayListUnmanaged(u8).initBuffer(options.send_buffer); | 407 | var h = std.ArrayListUnmanaged(u8).initBuffer(options.send_buffer); |
| 409 | | 408 | |
| 410 | h.writerAssumeCapacity().print("{s} {d} {s}\r\n", .{ | 409 | h.fixedWriter().print("{s} {d} {s}\r\n", .{ |
| 411 | @tagName(o.version), @intFromEnum(o.status), phrase, | 410 | @tagName(o.version), @intFromEnum(o.status), phrase, |
| 412 | }) catch |err| switch (err) {}; | 411 | }) catch unreachable; |
| 413 | if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); | 412 | if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); |
| 414 | | 413 | |
| 415 | if (options.content_length) |len| { | 414 | if (options.content_length) |len| { |
| 416 | h.writerAssumeCapacity().print("content-length: {d}\r\n", .{len}) catch |err| switch (err) {}; | 415 | h.fixedWriter().print("content-length: {d}\r\n", .{len}) catch unreachable; |
| 417 | } else { | 416 | } else { |
| 418 | h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n"); | 417 | h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n"); |
| 419 | } | 418 | } |
| ... | @@ -443,12 +442,12 @@ pub const Request = struct { | ... | @@ -443,12 +442,12 @@ pub const Request = struct { |
| 443 | fn read_cl(context: *const anyopaque, buffer: []u8) ReadError!usize { | 442 | fn read_cl(context: *const anyopaque, buffer: []u8) ReadError!usize { |
| 444 | const request: *Request = @constCast(@alignCast(@ptrCast(context))); | 443 | const request: *Request = @constCast(@alignCast(@ptrCast(context))); |
| 445 | const s = request.server; | 444 | const s = request.server; |
| 446 | assert(s.state == .receiving_body); | | |
| 447 | const remaining_content_length = &request.reader_state.remaining_content_length; | 445 | const remaining_content_length = &request.reader_state.remaining_content_length; |
| 448 | if (remaining_content_length.* == 0) { | 446 | if (remaining_content_length.* == 0) { |
| 449 | s.state = .ready; | 447 | s.state = .ready; |
| 450 | return 0; | 448 | return 0; |
| 451 | } | 449 | } |
| | 450 | assert(s.state == .receiving_body); |
| 452 | const available = try fill(s, request.head_end); | 451 | const available = try fill(s, request.head_end); |
| 453 | const len = @min(remaining_content_length.*, available.len, buffer.len); | 452 | const len = @min(remaining_content_length.*, available.len, buffer.len); |
| 454 | @memcpy(buffer[0..len], available[0..len]); | 453 | @memcpy(buffer[0..len], available[0..len]); |
| ... | @@ -470,8 +469,6 @@ pub const Request = struct { | ... | @@ -470,8 +469,6 @@ pub const Request = struct { |
| 470 | fn read_chunked(context: *const anyopaque, buffer: []u8) ReadError!usize { | 469 | fn read_chunked(context: *const anyopaque, buffer: []u8) ReadError!usize { |
| 471 | const request: *Request = @constCast(@alignCast(@ptrCast(context))); | 470 | const request: *Request = @constCast(@alignCast(@ptrCast(context))); |
| 472 | const s = request.server; | 471 | const s = request.server; |
| 473 | assert(s.state == .receiving_body); | | |
| 474 | | | |
| 475 | const cp = &request.reader_state.chunk_parser; | 472 | const cp = &request.reader_state.chunk_parser; |
| 476 | const head_end = request.head_end; | 473 | const head_end = request.head_end; |
| 477 | | 474 | |
| ... | @@ -481,6 +478,7 @@ pub const Request = struct { | ... | @@ -481,6 +478,7 @@ pub const Request = struct { |
| 481 | switch (cp.state) { | 478 | switch (cp.state) { |
| 482 | .invalid => return 0, | 479 | .invalid => return 0, |
| 483 | .data => { | 480 | .data => { |
| | 481 | assert(s.state == .receiving_body); |
| 484 | const available = try fill(s, head_end); | 482 | const available = try fill(s, head_end); |
| 485 | const len = @min(cp.chunk_len, available.len, buffer.len); | 483 | const len = @min(cp.chunk_len, available.len, buffer.len); |
| 486 | @memcpy(buffer[0..len], available[0..len]); | 484 | @memcpy(buffer[0..len], available[0..len]); |
| ... | @@ -492,6 +490,7 @@ pub const Request = struct { | ... | @@ -492,6 +490,7 @@ pub const Request = struct { |
| 492 | continue; | 490 | continue; |
| 493 | }, | 491 | }, |
| 494 | else => { | 492 | else => { |
| | 493 | assert(s.state == .receiving_body); |
| 495 | const available = try fill(s, head_end); | 494 | const available = try fill(s, head_end); |
| 496 | const n = cp.feed(available); | 495 | const n = cp.feed(available); |
| 497 | switch (cp.state) { | 496 | switch (cp.state) { |
| ... | @@ -514,6 +513,8 @@ pub const Request = struct { | ... | @@ -514,6 +513,8 @@ pub const Request = struct { |
| 514 | const bytes = s.read_buffer[head_end..s.read_buffer_len]; | 513 | const bytes = s.read_buffer[head_end..s.read_buffer_len]; |
| 515 | const end = hp.feed(bytes); | 514 | const end = hp.feed(bytes); |
| 516 | if (hp.state == .finished) { | 515 | if (hp.state == .finished) { |
| | 516 | cp.state = .invalid; |
| | 517 | s.state = .ready; |
| 517 | s.next_request_start = s.read_buffer_len - bytes.len + end; | 518 | s.next_request_start = s.read_buffer_len - bytes.len + end; |
| 518 | return out_end; | 519 | return out_end; |
| 519 | } | 520 | } |
| ... | @@ -527,6 +528,8 @@ pub const Request = struct { | ... | @@ -527,6 +528,8 @@ pub const Request = struct { |
| 527 | const bytes = buf[0..read_n]; | 528 | const bytes = buf[0..read_n]; |
| 528 | const end = hp.feed(bytes); | 529 | const end = hp.feed(bytes); |
| 529 | if (hp.state == .finished) { | 530 | if (hp.state == .finished) { |
| | 531 | cp.state = .invalid; |
| | 532 | s.state = .ready; |
| 530 | s.next_request_start = s.read_buffer_len - bytes.len + end; | 533 | s.next_request_start = s.read_buffer_len - bytes.len + end; |
| 531 | return out_end; | 534 | return out_end; |
| 532 | } | 535 | } |
| ... | @@ -625,14 +628,13 @@ pub const Response = struct { | ... | @@ -625,14 +628,13 @@ pub const Response = struct { |
| 625 | /// the value sent in the header, then calls `flush`. | 628 | /// the value sent in the header, then calls `flush`. |
| 626 | /// Otherwise, transfer-encoding: chunked is being used, and it writes the | 629 | /// Otherwise, transfer-encoding: chunked is being used, and it writes the |
| 627 | /// end-of-stream message, then flushes the stream to the system. | 630 | /// end-of-stream message, then flushes the stream to the system. |
| 628 | /// When request method is HEAD, does not write anything to the stream. | 631 | /// Respects the value of `elide_body` to omit all data after the headers. |
| 629 | pub fn end(r: *Response) WriteError!void { | 632 | pub fn end(r: *Response) WriteError!void { |
| 630 | if (r.content_length) |len| { | 633 | if (r.content_length) |len| { |
| 631 | assert(len == 0); // Trips when end() called before all bytes written. | 634 | assert(len == 0); // Trips when end() called before all bytes written. |
| 632 | return flush_cl(r); | 635 | try flush_cl(r); |
| 633 | } | 636 | } else { |
| 634 | if (!r.elide_body) { | 637 | try flush_chunked(r, &.{}); |
| 635 | return flush_chunked(r, &.{}); | | |
| 636 | } | 638 | } |
| 637 | r.* = undefined; | 639 | r.* = undefined; |
| 638 | } | 640 | } |
| ... | @@ -644,11 +646,10 @@ pub const Response = struct { | ... | @@ -644,11 +646,10 @@ pub const Response = struct { |
| 644 | /// Asserts that the Response is using transfer-encoding: chunked. | 646 | /// Asserts that the Response is using transfer-encoding: chunked. |
| 645 | /// Writes the end-of-stream message and any optional trailers, then | 647 | /// Writes the end-of-stream message and any optional trailers, then |
| 646 | /// flushes the stream to the system. | 648 | /// flushes the stream to the system. |
| 647 | /// When request method is HEAD, does not write anything to the stream. | 649 | /// Respects the value of `elide_body` to omit all data after the headers. |
| 648 | /// Asserts there are at most 25 trailers. | 650 | /// Asserts there are at most 25 trailers. |
| 649 | pub fn endChunked(r: *Response, options: EndChunkedOptions) WriteError!void { | 651 | pub fn endChunked(r: *Response, options: EndChunkedOptions) WriteError!void { |
| 650 | assert(r.content_length == null); | 652 | assert(r.content_length == null); |
| 651 | if (r.elide_body) return; | | |
| 652 | try flush_chunked(r, options.trailers); | 653 | try flush_chunked(r, options.trailers); |
| 653 | r.* = undefined; | 654 | r.* = undefined; |
| 654 | } | 655 | } |
| ... | @@ -771,6 +772,7 @@ pub const Response = struct { | ... | @@ -771,6 +772,7 @@ pub const Response = struct { |
| 771 | | 772 | |
| 772 | /// Sends all buffered data to the client. | 773 | /// Sends all buffered data to the client. |
| 773 | /// This is redundant after calling `end`. | 774 | /// This is redundant after calling `end`. |
| | 775 | /// Respects the value of `elide_body` to omit all data after the headers. |
| 774 | pub fn flush(r: *Response) WriteError!void { | 776 | pub fn flush(r: *Response) WriteError!void { |
| 775 | if (r.content_length != null) { | 777 | if (r.content_length != null) { |
| 776 | return flush_cl(r); | 778 | return flush_cl(r); |
| ... | @@ -790,7 +792,17 @@ pub const Response = struct { | ... | @@ -790,7 +792,17 @@ pub const Response = struct { |
| 790 | const max_trailers = 25; | 792 | const max_trailers = 25; |
| 791 | if (end_trailers) |trailers| assert(trailers.len <= max_trailers); | 793 | if (end_trailers) |trailers| assert(trailers.len <= max_trailers); |
| 792 | assert(r.content_length == null); | 794 | assert(r.content_length == null); |
| 793 | const send_buffer_len = r.send_buffer_end - r.send_buffer_start; | 795 | |
| | 796 | const http_headers = r.send_buffer[r.send_buffer_start .. r.send_buffer_end - r.chunk_len]; |
| | 797 | |
| | 798 | if (r.elide_body) { |
| | 799 | try r.stream.writeAll(http_headers); |
| | 800 | r.send_buffer_start = 0; |
| | 801 | r.send_buffer_end = 0; |
| | 802 | r.chunk_len = 0; |
| | 803 | return; |
| | 804 | } |
| | 805 | |
| 794 | var header_buf: [18]u8 = undefined; | 806 | var header_buf: [18]u8 = undefined; |
| 795 | const chunk_header = std.fmt.bufPrint(&header_buf, "{x}\r\n", .{r.chunk_len}) catch unreachable; | 807 | const chunk_header = std.fmt.bufPrint(&header_buf, "{x}\r\n", .{r.chunk_len}) catch unreachable; |
| 796 | | 808 | |
| ... | @@ -798,8 +810,8 @@ pub const Response = struct { | ... | @@ -798,8 +810,8 @@ pub const Response = struct { |
| 798 | var iovecs_len: usize = 0; | 810 | var iovecs_len: usize = 0; |
| 799 | | 811 | |
| 800 | iovecs[iovecs_len] = .{ | 812 | iovecs[iovecs_len] = .{ |
| 801 | .iov_base = r.send_buffer.ptr + r.send_buffer_start, | 813 | .iov_base = http_headers.ptr, |
| 802 | .iov_len = send_buffer_len - r.chunk_len, | 814 | .iov_len = http_headers.len, |
| 803 | }; | 815 | }; |
| 804 | iovecs_len += 1; | 816 | iovecs_len += 1; |
| 805 | | 817 | |