| ... | @@ -22,9 +22,6 @@ out: *std.io.BufferedWriter, | ... | @@ -22,9 +22,6 @@ out: *std.io.BufferedWriter, |
| 22 | state: State, | 22 | state: State, |
| 23 | head_parse_err: Request.Head.ParseError, | 23 | head_parse_err: Request.Head.ParseError, |
| 24 | | 24 | |
| 25 | /// being deleted... | | |
| 26 | next_request_start: usize = 0, | | |
| 27 | | | |
| 28 | pub const State = enum { | 25 | pub const State = enum { |
| 29 | /// The connection is available to be used for the first time, or reused. | 26 | /// The connection is available to be used for the first time, or reused. |
| 30 | ready, | 27 | ready, |
| ... | @@ -95,6 +92,8 @@ pub fn receiveHead(s: *Server) ReceiveHeadError!Request { | ... | @@ -95,6 +92,8 @@ pub fn receiveHead(s: *Server) ReceiveHeadError!Request { |
| 95 | if (hp.state == .finished) return .{ | 92 | if (hp.state == .finished) return .{ |
| 96 | .server = s, | 93 | .server = s, |
| 97 | .head_end = head_end, | 94 | .head_end = head_end, |
| | 95 | .trailers_len = 0, |
| | 96 | .read_err = null, |
| 98 | .head = Request.Head.parse(buf[0..head_end]) catch |err| { | 97 | .head = Request.Head.parse(buf[0..head_end]) catch |err| { |
| 99 | s.head_parse_err = err; | 98 | s.head_parse_err = err; |
| 100 | return error.HttpHeadersInvalid; | 99 | return error.HttpHeadersInvalid; |
| ... | @@ -108,11 +107,38 @@ pub const Request = struct { | ... | @@ -108,11 +107,38 @@ pub const Request = struct { |
| 108 | server: *Server, | 107 | server: *Server, |
| 109 | /// Index into `Server.in` internal buffer. | 108 | /// Index into `Server.in` internal buffer. |
| 110 | head_end: usize, | 109 | head_end: usize, |
| | 110 | /// Number of bytes of HTTP trailers. These are at the end of a |
| | 111 | /// transfer-encoding: chunked message. |
| | 112 | trailers_len: usize, |
| 111 | head: Head, | 113 | head: Head, |
| 112 | reader_state: union { | 114 | reader_state: union { |
| 113 | remaining_content_length: u64, | 115 | remaining_content_length: u64, |
| 114 | chunk_parser: http.ChunkParser, | 116 | remaining_chunk_len: RemainingChunkLen, |
| 115 | }, | 117 | }, |
| | 118 | read_err: ?ReadError, |
| | 119 | |
| | 120 | pub const ReadError = error{ |
| | 121 | HttpChunkInvalid, |
| | 122 | HttpHeadersOversize, |
| | 123 | }; |
| | 124 | |
| | 125 | pub const max_chunk_header_len = 22; |
| | 126 | |
| | 127 | pub const RemainingChunkLen = enum(u64) { |
| | 128 | head = 0, |
| | 129 | n = 1, |
| | 130 | rn = 2, |
| | 131 | done = std.math.maxInt(u64), |
| | 132 | _, |
| | 133 | |
| | 134 | pub fn init(integer: u64) RemainingChunkLen { |
| | 135 | return @enumFromInt(integer); |
| | 136 | } |
| | 137 | |
| | 138 | pub fn int(rcl: RemainingChunkLen) u64 { |
| | 139 | return @intFromEnum(rcl); |
| | 140 | } |
| | 141 | }; |
| 116 | | 142 | |
| 117 | pub const Compression = union(enum) { | 143 | pub const Compression = union(enum) { |
| 118 | deflate: std.compress.zlib.Decompressor, | 144 | deflate: std.compress.zlib.Decompressor, |
| ... | @@ -559,177 +585,240 @@ pub const Request = struct { | ... | @@ -559,177 +585,240 @@ pub const Request = struct { |
| 559 | }; | 585 | }; |
| 560 | } | 586 | } |
| 561 | | 587 | |
| 562 | pub const ReadError = net.Stream.ReadError || error{ | 588 | fn contentLengthRead( |
| 563 | HttpChunkInvalid, | | |
| 564 | HttpHeadersOversize, | | |
| 565 | }; | | |
| 566 | | | |
| 567 | fn contentLengthReader_read( | | |
| 568 | ctx: ?*anyopaque, | 589 | ctx: ?*anyopaque, |
| 569 | bw: *std.io.BufferedWriter, | 590 | bw: *std.io.BufferedWriter, |
| 570 | limit: std.io.Reader.Limit, | 591 | limit: std.io.Reader.Limit, |
| 571 | ) std.io.Reader.Error!usize { | 592 | ) std.io.Reader.RwError!usize { |
| 572 | const request: *Request = @alignCast(@ptrCast(ctx)); | 593 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 573 | _ = request; | 594 | const remaining_content_length = &request.reader_state.remaining_content_length; |
| 574 | _ = bw; | 595 | const remaining = remaining_content_length.*; |
| 575 | _ = limit; | 596 | const server = request.server; |
| 576 | @panic("TODO"); | 597 | if (remaining == 0) { |
| | 598 | server.state = .ready; |
| | 599 | return error.EndOfStream; |
| | 600 | } |
| | 601 | const n = try server.in.read(bw, limit.min(.limited(remaining))); |
| | 602 | const new_remaining = remaining - n; |
| | 603 | remaining_content_length.* = new_remaining; |
| | 604 | return n; |
| 577 | } | 605 | } |
| 578 | | 606 | |
| 579 | fn contentLengthReader_readVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | 607 | fn contentLengthReadVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 580 | const request: *Request = @alignCast(@ptrCast(ctx)); | 608 | const request: *Request = @alignCast(@ptrCast(context)); |
| 581 | _ = request; | 609 | const remaining_content_length = &request.reader_state.remaining_content_length; |
| 582 | _ = data; | 610 | const server = request.server; |
| 583 | @panic("TODO"); | 611 | const remaining = remaining_content_length.*; |
| | 612 | if (remaining == 0) { |
| | 613 | server.state = .ready; |
| | 614 | return error.EndOfStream; |
| | 615 | } |
| | 616 | const n = try server.in.readVecLimit(data, .limited(remaining)); |
| | 617 | const new_remaining = remaining - n; |
| | 618 | remaining_content_length.* = new_remaining; |
| | 619 | return n; |
| 584 | } | 620 | } |
| 585 | | 621 | |
| 586 | fn contentLengthReader_discard(ctx: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize { | 622 | fn contentLengthDiscard(ctx: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize { |
| 587 | const request: *Request = @alignCast(@ptrCast(ctx)); | 623 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 588 | _ = request; | 624 | const remaining_content_length = &request.reader_state.remaining_content_length; |
| 589 | _ = limit; | 625 | const server = request.server; |
| 590 | @panic("TODO"); | 626 | const remaining = remaining_content_length.*; |
| | 627 | if (remaining == 0) { |
| | 628 | server.state = .ready; |
| | 629 | return error.EndOfStream; |
| | 630 | } |
| | 631 | const n = try server.in.discard(limit.min(.limited(remaining))); |
| | 632 | const new_remaining = remaining - n; |
| | 633 | remaining_content_length.* = new_remaining; |
| | 634 | return n; |
| 591 | } | 635 | } |
| 592 | | 636 | |
| 593 | fn chunkedReader_read( | 637 | fn chunkedRead( |
| 594 | ctx: ?*anyopaque, | 638 | ctx: ?*anyopaque, |
| 595 | bw: *std.io.BufferedWriter, | 639 | bw: *std.io.BufferedWriter, |
| 596 | limit: std.io.Reader.Limit, | 640 | limit: std.io.Reader.Limit, |
| 597 | ) std.io.Reader.Error!usize { | 641 | ) std.io.Reader.RwError!usize { |
| 598 | const request: *Request = @alignCast(@ptrCast(ctx)); | 642 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 599 | _ = request; | 643 | const chunk_len_ptr = &request.reader_state.remaining_chunk_len; |
| 600 | _ = bw; | 644 | const in = request.server.in; |
| 601 | _ = limit; | 645 | len: switch (chunk_len_ptr.*) { |
| 602 | @panic("TODO"); | 646 | .head => { |
| | 647 | var cp: http.ChunkParser = .init; |
| | 648 | const i = cp.feed(in.bufferContents()); |
| | 649 | switch (cp.state) { |
| | 650 | .invalid => return request.failRead(error.HttpChunkInvalid), |
| | 651 | .data => { |
| | 652 | if (i > max_chunk_header_len) return request.failRead(error.HttpChunkInvalid); |
| | 653 | in.toss(i); |
| | 654 | }, |
| | 655 | else => { |
| | 656 | try in.fill(max_chunk_header_len); |
| | 657 | const next_i = cp.feed(in.bufferContents()[i..]); |
| | 658 | if (cp.state != .data) return request.failRead(error.HttpChunkInvalid); |
| | 659 | const header_len = i + next_i; |
| | 660 | if (header_len > max_chunk_header_len) return request.failRead(error.HttpChunkInvalid); |
| | 661 | in.toss(header_len); |
| | 662 | }, |
| | 663 | } |
| | 664 | if (cp.chunk_len == 0) return parseTrailers(request, 0); |
| | 665 | const n = try in.read(bw, limit.min(.limited(cp.chunk_len))); |
| | 666 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); |
| | 667 | return n; |
| | 668 | }, |
| | 669 | .n => { |
| | 670 | if ((try in.peekByte()) != '\n') return request.failRead(error.HttpChunkInvalid); |
| | 671 | in.toss(1); |
| | 672 | continue :len .head; |
| | 673 | }, |
| | 674 | .rn => { |
| | 675 | const rn = try in.peekArray(2); |
| | 676 | if (rn[0] != '\r' or rn[1] != '\n') return request.failRead(error.HttpChunkInvalid); |
| | 677 | in.toss(2); |
| | 678 | continue :len .head; |
| | 679 | }, |
| | 680 | else => |remaining_chunk_len| { |
| | 681 | const n = try in.read(bw, limit.min(.limited(@intFromEnum(remaining_chunk_len) - 2))); |
| | 682 | chunk_len_ptr.* = .init(@intFromEnum(remaining_chunk_len) - n); |
| | 683 | return n; |
| | 684 | }, |
| | 685 | .done => return error.EndOfStream, |
| | 686 | } |
| 603 | } | 687 | } |
| 604 | | 688 | |
| 605 | fn chunkedReader_readVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | 689 | fn chunkedReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 606 | const request: *Request = @alignCast(@ptrCast(ctx)); | 690 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 607 | _ = request; | 691 | const chunk_len_ptr = &request.reader_state.remaining_chunk_len; |
| 608 | _ = data; | 692 | const in = request.server.in; |
| 609 | @panic("TODO"); | 693 | var already_requested_more = false; |
| | 694 | var amt_read: usize = 0; |
| | 695 | data: for (data) |d| { |
| | 696 | len: switch (chunk_len_ptr.*) { |
| | 697 | .head => { |
| | 698 | var cp: http.ChunkParser = .init; |
| | 699 | const available_buffer = in.bufferContents(); |
| | 700 | const i = cp.feed(available_buffer); |
| | 701 | if (cp.state == .invalid) return request.failRead(error.HttpChunkInvalid); |
| | 702 | if (i == available_buffer.len) { |
| | 703 | if (already_requested_more) { |
| | 704 | chunk_len_ptr.* = .head; |
| | 705 | return amt_read; |
| | 706 | } |
| | 707 | already_requested_more = true; |
| | 708 | try in.fill(max_chunk_header_len); |
| | 709 | const next_i = cp.feed(in.bufferContents()[i..]); |
| | 710 | if (cp.state != .data) return request.failRead(error.HttpChunkInvalid); |
| | 711 | const header_len = i + next_i; |
| | 712 | if (header_len > max_chunk_header_len) return request.failRead(error.HttpChunkInvalid); |
| | 713 | in.toss(header_len); |
| | 714 | } else { |
| | 715 | if (i > max_chunk_header_len) return request.failRead(error.HttpChunkInvalid); |
| | 716 | in.toss(i); |
| | 717 | } |
| | 718 | if (cp.chunk_len == 0) return parseTrailers(request, amt_read); |
| | 719 | continue :len .init(cp.chunk_len + 2); |
| | 720 | }, |
| | 721 | .n => { |
| | 722 | if (in.bufferContents().len < 1) already_requested_more = true; |
| | 723 | if ((try in.takeByte()) != '\n') return request.failRead(error.HttpChunkInvalid); |
| | 724 | continue :len .head; |
| | 725 | }, |
| | 726 | .rn => { |
| | 727 | if (in.bufferContents().len < 2) already_requested_more = true; |
| | 728 | const rn = try in.takeArray(2); |
| | 729 | if (rn[0] != '\r' or rn[1] != '\n') return request.failRead(error.HttpChunkInvalid); |
| | 730 | continue :len .head; |
| | 731 | }, |
| | 732 | else => |remaining_chunk_len| { |
| | 733 | const available_buffer = in.bufferContents(); |
| | 734 | const copy_len = @min(available_buffer.len, d.len, remaining_chunk_len.int() - 2); |
| | 735 | @memcpy(d[0..copy_len], available_buffer[0..copy_len]); |
| | 736 | amt_read += copy_len; |
| | 737 | in.toss(copy_len); |
| | 738 | const next_chunk_len: RemainingChunkLen = .init(remaining_chunk_len.int() - copy_len); |
| | 739 | if (copy_len == d.len) { |
| | 740 | chunk_len_ptr.* = next_chunk_len; |
| | 741 | continue :data; |
| | 742 | } |
| | 743 | if (already_requested_more) { |
| | 744 | chunk_len_ptr.* = next_chunk_len; |
| | 745 | return amt_read; |
| | 746 | } |
| | 747 | already_requested_more = true; |
| | 748 | try in.fill(3); |
| | 749 | continue :len next_chunk_len; |
| | 750 | }, |
| | 751 | .done => return error.EndOfStream, |
| | 752 | } |
| | 753 | } |
| | 754 | return amt_read; |
| 610 | } | 755 | } |
| 611 | | 756 | |
| 612 | fn chunkedReader_discard(ctx: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize { | 757 | fn chunkedDiscard(ctx: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize { |
| 613 | const request: *Request = @alignCast(@ptrCast(ctx)); | 758 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 614 | _ = request; | 759 | const chunk_len_ptr = &request.reader_state.remaining_chunk_len; |
| 615 | _ = limit; | 760 | const in = request.server.in; |
| 616 | @panic("TODO"); | 761 | len: switch (chunk_len_ptr.*) { |
| 617 | } | 762 | .head => { |
| 618 | | 763 | var cp: http.ChunkParser = .init; |
| 619 | fn read_cl(context: *const anyopaque, buffer: []u8) ReadError!usize { | 764 | const i = cp.feed(in.bufferContents()); |
| 620 | const request: *Request = @alignCast(@ptrCast(context)); | 765 | switch (cp.state) { |
| 621 | const s = request.server; | 766 | .invalid => return request.failRead(error.HttpChunkInvalid), |
| 622 | | 767 | .data => { |
| 623 | const remaining_content_length = &request.reader_state.remaining_content_length; | 768 | if (i > max_chunk_header_len) return request.failRead(error.HttpChunkInvalid); |
| 624 | if (remaining_content_length.* == 0) { | 769 | in.toss(i); |
| 625 | s.state = .ready; | 770 | }, |
| 626 | return 0; | 771 | else => { |
| | 772 | try in.fill(max_chunk_header_len); |
| | 773 | const next_i = cp.feed(in.bufferContents()[i..]); |
| | 774 | if (cp.state != .data) return request.failRead(error.HttpChunkInvalid); |
| | 775 | const header_len = i + next_i; |
| | 776 | if (header_len > max_chunk_header_len) return request.failRead(error.HttpChunkInvalid); |
| | 777 | in.toss(header_len); |
| | 778 | }, |
| | 779 | } |
| | 780 | if (cp.chunk_len == 0) return parseTrailers(request, 0); |
| | 781 | const n = try in.discard(limit.min(.limited(cp.chunk_len))); |
| | 782 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); |
| | 783 | return n; |
| | 784 | }, |
| | 785 | .n => { |
| | 786 | if ((try in.peekByte()) != '\n') return request.failRead(error.HttpChunkInvalid); |
| | 787 | in.toss(1); |
| | 788 | continue :len .head; |
| | 789 | }, |
| | 790 | .rn => { |
| | 791 | const rn = try in.peekArray(2); |
| | 792 | if (rn[0] != '\r' or rn[1] != '\n') return request.failRead(error.HttpChunkInvalid); |
| | 793 | in.toss(2); |
| | 794 | continue :len .head; |
| | 795 | }, |
| | 796 | else => |remaining_chunk_len| { |
| | 797 | const n = try in.discard(limit.min(.limited(remaining_chunk_len.int() - 2))); |
| | 798 | chunk_len_ptr.* = .init(remaining_chunk_len.int() - n); |
| | 799 | return n; |
| | 800 | }, |
| | 801 | .done => return error.EndOfStream, |
| 627 | } | 802 | } |
| 628 | assert(s.state == .receiving_body); | | |
| 629 | const available = try fill(s, request.head_end); | | |
| 630 | const len = @min(remaining_content_length.*, available.len, buffer.len); | | |
| 631 | @memcpy(buffer[0..len], available[0..len]); | | |
| 632 | remaining_content_length.* -= len; | | |
| 633 | s.next_request_start += len; | | |
| 634 | if (remaining_content_length.* == 0) | | |
| 635 | s.state = .ready; | | |
| 636 | return len; | | |
| 637 | } | 803 | } |
| 638 | | 804 | |
| 639 | fn fill(s: *Server, head_end: usize) ReadError![]u8 { | 805 | /// Called when next bytes in the stream are trailers, or "\r\n" to indicate |
| 640 | const available = s.read_buffer[s.next_request_start..s.read_buffer_len]; | 806 | /// end of chunked body. |
| 641 | if (available.len > 0) return available; | 807 | fn parseTrailers(request: *Request, amt_read: usize) std.io.Reader.Error!usize { |
| 642 | s.next_request_start = head_end; | 808 | const in = request.server.in; |
| 643 | s.read_buffer_len = head_end + try s.connection.stream.read(s.read_buffer[head_end..]); | 809 | var hp: http.HeadParser = .{}; |
| 644 | return s.read_buffer[head_end..s.read_buffer_len]; | 810 | var trailers_len: usize = 0; |
| 645 | } | 811 | while (true) { |
| 646 | | 812 | if (trailers_len >= in.buffer.len) return request.failRead(error.HttpHeadersOversize); |
| 647 | fn read_chunked(context: *const anyopaque, buffer: []u8) ReadError!usize { | 813 | try in.fill(trailers_len + 1); |
| 648 | const request: *Request = @alignCast(@ptrCast(context)); | 814 | trailers_len += hp.feed(in.bufferContents()[trailers_len..]); |
| 649 | const s = request.server; | 815 | if (hp.state == .finished) { |
| 650 | | 816 | request.reader_state.remaining_chunk_len = .done; |
| 651 | const cp = &request.reader_state.chunk_parser; | 817 | request.server.state = .ready; |
| 652 | const head_end = request.head_end; | 818 | request.trailers_len = trailers_len; |
| 653 | | 819 | return amt_read; |
| 654 | // Protect against returning 0 before the end of stream. | | |
| 655 | var out_end: usize = 0; | | |
| 656 | while (out_end == 0) { | | |
| 657 | switch (cp.state) { | | |
| 658 | .invalid => return 0, | | |
| 659 | .data => { | | |
| 660 | assert(s.state == .receiving_body); | | |
| 661 | const available = try fill(s, head_end); | | |
| 662 | const len = @min(cp.chunk_len, available.len, buffer.len); | | |
| 663 | @memcpy(buffer[0..len], available[0..len]); | | |
| 664 | cp.chunk_len -= len; | | |
| 665 | if (cp.chunk_len == 0) | | |
| 666 | cp.state = .data_suffix; | | |
| 667 | out_end += len; | | |
| 668 | s.next_request_start += len; | | |
| 669 | continue; | | |
| 670 | }, | | |
| 671 | else => { | | |
| 672 | assert(s.state == .receiving_body); | | |
| 673 | const available = try fill(s, head_end); | | |
| 674 | const n = cp.feed(available); | | |
| 675 | switch (cp.state) { | | |
| 676 | .invalid => return error.HttpChunkInvalid, | | |
| 677 | .data => { | | |
| 678 | if (cp.chunk_len == 0) { | | |
| 679 | // The next bytes in the stream are trailers, | | |
| 680 | // or \r\n to indicate end of chunked body. | | |
| 681 | // | | |
| 682 | // This function must append the trailers at | | |
| 683 | // head_end so that headers and trailers are | | |
| 684 | // together. | | |
| 685 | // | | |
| 686 | // Since returning 0 would indicate end of | | |
| 687 | // stream, this function must read all the | | |
| 688 | // trailers before returning. | | |
| 689 | if (s.next_request_start > head_end) rebase(s, head_end); | | |
| 690 | var hp: http.HeadParser = .{}; | | |
| 691 | { | | |
| 692 | const bytes = s.read_buffer[head_end..s.read_buffer_len]; | | |
| 693 | const end = hp.feed(bytes); | | |
| 694 | if (hp.state == .finished) { | | |
| 695 | cp.state = .invalid; | | |
| 696 | s.state = .ready; | | |
| 697 | s.next_request_start = s.read_buffer_len - bytes.len + end; | | |
| 698 | return out_end; | | |
| 699 | } | | |
| 700 | } | | |
| 701 | while (true) { | | |
| 702 | const buf = s.read_buffer[s.read_buffer_len..]; | | |
| 703 | if (buf.len == 0) | | |
| 704 | return error.HttpHeadersOversize; | | |
| 705 | const read_n = try s.connection.stream.read(buf); | | |
| 706 | s.read_buffer_len += read_n; | | |
| 707 | const bytes = buf[0..read_n]; | | |
| 708 | const end = hp.feed(bytes); | | |
| 709 | if (hp.state == .finished) { | | |
| 710 | cp.state = .invalid; | | |
| 711 | s.state = .ready; | | |
| 712 | s.next_request_start = s.read_buffer_len - bytes.len + end; | | |
| 713 | return out_end; | | |
| 714 | } | | |
| 715 | } | | |
| 716 | } | | |
| 717 | const data = available[n..]; | | |
| 718 | const len = @min(cp.chunk_len, data.len, buffer.len); | | |
| 719 | @memcpy(buffer[0..len], data[0..len]); | | |
| 720 | cp.chunk_len -= len; | | |
| 721 | if (cp.chunk_len == 0) | | |
| 722 | cp.state = .data_suffix; | | |
| 723 | out_end += len; | | |
| 724 | s.next_request_start += n + len; | | |
| 725 | continue; | | |
| 726 | }, | | |
| 727 | else => continue, | | |
| 728 | } | | |
| 729 | }, | | |
| 730 | } | 820 | } |
| 731 | } | 821 | } |
| 732 | return out_end; | | |
| 733 | } | 822 | } |
| 734 | | 823 | |
| 735 | pub const ReaderError = error{ | 824 | pub const ReaderError = error{ |
| ... | @@ -752,7 +841,6 @@ pub const Request = struct { | ... | @@ -752,7 +841,6 @@ pub const Request = struct { |
| 752 | const s = request.server; | 841 | const s = request.server; |
| 753 | assert(s.state == .received_head); | 842 | assert(s.state == .received_head); |
| 754 | s.state = .receiving_body; | 843 | s.state = .receiving_body; |
| 755 | s.next_request_start = request.head_end; | | |
| 756 | | 844 | |
| 757 | if (request.head.expect) |expect| { | 845 | if (request.head.expect) |expect| { |
| 758 | if (mem.eql(u8, expect, "100-continue")) { | 846 | if (mem.eql(u8, expect, "100-continue")) { |
| ... | @@ -765,13 +853,13 @@ pub const Request = struct { | ... | @@ -765,13 +853,13 @@ pub const Request = struct { |
| 765 | | 853 | |
| 766 | switch (request.head.transfer_encoding) { | 854 | switch (request.head.transfer_encoding) { |
| 767 | .chunked => { | 855 | .chunked => { |
| 768 | request.reader_state = .{ .chunk_parser = http.ChunkParser.init }; | 856 | request.reader_state = .{ .remaining_chunk_len = .head }; |
| 769 | return .{ | 857 | return .{ |
| 770 | .context = request, | 858 | .context = request, |
| 771 | .vtable = &.{ | 859 | .vtable = &.{ |
| 772 | .read = &chunkedReader_read, | 860 | .read = &chunkedRead, |
| 773 | .readVec = &chunkedReader_readVec, | 861 | .readVec = &chunkedReadVec, |
| 774 | .discard = &chunkedReader_discard, | 862 | .discard = &chunkedDiscard, |
| 775 | }, | 863 | }, |
| 776 | }; | 864 | }; |
| 777 | }, | 865 | }, |
| ... | @@ -782,9 +870,9 @@ pub const Request = struct { | ... | @@ -782,9 +870,9 @@ pub const Request = struct { |
| 782 | return .{ | 870 | return .{ |
| 783 | .context = request, | 871 | .context = request, |
| 784 | .vtable = &.{ | 872 | .vtable = &.{ |
| 785 | .read = &contentLengthReader_read, | 873 | .read = &contentLengthRead, |
| 786 | .readVec = &contentLengthReader_readVec, | 874 | .readVec = &contentLengthReadVec, |
| 787 | .discard = &contentLengthReader_discard, | 875 | .discard = &contentLengthDiscard, |
| 788 | }, | 876 | }, |
| 789 | }; | 877 | }; |
| 790 | }, | 878 | }, |
| ... | @@ -822,6 +910,11 @@ pub const Request = struct { | ... | @@ -822,6 +910,11 @@ pub const Request = struct { |
| 822 | } | 910 | } |
| 823 | return false; | 911 | return false; |
| 824 | } | 912 | } |
| | 913 | |
| | 914 | fn failRead(r: *Request, err: ReadError) error{ReadFailed} { |
| | 915 | r.read_err = err; |
| | 916 | return error.ReadFailed; |
| | 917 | } |
| 825 | }; | 918 | }; |
| 826 | | 919 | |
| 827 | pub const Response = struct { | 920 | pub const Response = struct { |
| ... | @@ -1165,14 +1258,3 @@ pub const Response = struct { | ... | @@ -1165,14 +1258,3 @@ pub const Response = struct { |
| 1165 | }; | 1258 | }; |
| 1166 | } | 1259 | } |
| 1167 | }; | 1260 | }; |
| 1168 | | | |
| 1169 | fn rebase(s: *Server, index: usize) void { | | |
| 1170 | const leftover = s.read_buffer[s.next_request_start..s.read_buffer_len]; | | |
| 1171 | const dest = s.read_buffer[index..][0..leftover.len]; | | |
| 1172 | if (leftover.len <= s.next_request_start - index) { | | |
| 1173 | @memcpy(dest, leftover); | | |
| 1174 | } else { | | |
| 1175 | mem.copyBackwards(u8, dest, leftover); | | |
| 1176 | } | | |
| 1177 | s.read_buffer_len = index + leftover.len; | | |
| 1178 | } | | |