| ... | @@ -553,44 +553,64 @@ pub const Reader = struct { | ... | @@ -553,44 +553,64 @@ pub const Reader = struct { |
| 553 | .body_remaining_chunk_len => |*x| x, | 553 | .body_remaining_chunk_len => |*x| x, |
| 554 | else => unreachable, | 554 | else => unreachable, |
| 555 | }; | 555 | }; |
| | 556 | return chunkedReadEndless(reader, bw, limit, chunk_len_ptr) catch |err| switch (err) { |
| | 557 | error.ReadFailed => return error.ReadFailed, |
| | 558 | error.WriteFailed => return error.WriteFailed, |
| | 559 | error.EndOfStream => { |
| | 560 | reader.body_err = error.HttpChunkTruncated; |
| | 561 | return error.ReadFailed; |
| | 562 | }, |
| | 563 | else => |e| { |
| | 564 | reader.body_err = e; |
| | 565 | return error.ReadFailed; |
| | 566 | }, |
| | 567 | }; |
| | 568 | } |
| | 569 | |
| | 570 | fn chunkedReadEndless( |
| | 571 | reader: *Reader, |
| | 572 | bw: *std.io.BufferedWriter, |
| | 573 | limit: std.io.Reader.Limit, |
| | 574 | chunk_len_ptr: *RemainingChunkLen, |
| | 575 | ) (BodyError || std.io.Reader.RwError)!usize { |
| 556 | const in = reader.in; | 576 | const in = reader.in; |
| 557 | len: switch (chunk_len_ptr.*) { | 577 | len: switch (chunk_len_ptr.*) { |
| 558 | .head => { | 578 | .head => { |
| 559 | var cp: ChunkParser = .init; | 579 | var cp: ChunkParser = .init; |
| 560 | const i = cp.feed(in.bufferContents()); | 580 | const i = cp.feed(in.bufferContents()); |
| 561 | switch (cp.state) { | 581 | switch (cp.state) { |
| 562 | .invalid => return reader.failBody(error.HttpChunkInvalid), | 582 | .invalid => return error.HttpChunkInvalid, |
| 563 | .data => { | 583 | .data => { |
| 564 | if (i > max_chunk_header_len) return reader.failBody(error.HttpChunkInvalid); | 584 | if (i > max_chunk_header_len) return error.HttpChunkInvalid; |
| 565 | in.toss(i); | 585 | in.toss(i); |
| 566 | }, | 586 | }, |
| 567 | else => { | 587 | else => { |
| 568 | try endless(reader, in.fill(max_chunk_header_len)); | 588 | try in.fill(max_chunk_header_len); |
| 569 | const next_i = cp.feed(in.bufferContents()[i..]); | 589 | const next_i = cp.feed(in.bufferContents()[i..]); |
| 570 | if (cp.state != .data) return reader.failBody(error.HttpChunkInvalid); | 590 | if (cp.state != .data) return error.HttpChunkInvalid; |
| 571 | const header_len = i + next_i; | 591 | const header_len = i + next_i; |
| 572 | if (header_len > max_chunk_header_len) return reader.failBody(error.HttpChunkInvalid); | 592 | if (header_len > max_chunk_header_len) return error.HttpChunkInvalid; |
| 573 | in.toss(header_len); | 593 | in.toss(header_len); |
| 574 | }, | 594 | }, |
| 575 | } | 595 | } |
| 576 | if (cp.chunk_len == 0) return parseTrailers(reader, 0); | 596 | if (cp.chunk_len == 0) return parseTrailers(reader, 0); |
| 577 | const n = try endless(reader, in.read(bw, limit.min(.limited(cp.chunk_len)))); | 597 | const n = try in.read(bw, limit.min(.limited(cp.chunk_len))); |
| 578 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); | 598 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); |
| 579 | return n; | 599 | return n; |
| 580 | }, | 600 | }, |
| 581 | .n => { | 601 | .n => { |
| 582 | if ((try in.peekByte()) != '\n') return reader.failBody(error.HttpChunkInvalid); | 602 | if ((try in.peekByte()) != '\n') return error.HttpChunkInvalid; |
| 583 | in.toss(1); | 603 | in.toss(1); |
| 584 | continue :len .head; | 604 | continue :len .head; |
| 585 | }, | 605 | }, |
| 586 | .rn => { | 606 | .rn => { |
| 587 | const rn = try endless(reader, in.peekArray(2)); | 607 | const rn = try in.peekArray(2); |
| 588 | if (rn[0] != '\r' or rn[1] != '\n') return reader.failBody(error.HttpChunkInvalid); | 608 | if (rn[0] != '\r' or rn[1] != '\n') return error.HttpChunkInvalid; |
| 589 | in.toss(2); | 609 | in.toss(2); |
| 590 | continue :len .head; | 610 | continue :len .head; |
| 591 | }, | 611 | }, |
| 592 | else => |remaining_chunk_len| { | 612 | else => |remaining_chunk_len| { |
| 593 | const n = try endless(reader, in.read(bw, limit.min(.limited(@intFromEnum(remaining_chunk_len) - 2)))); | 613 | const n = try in.read(bw, limit.min(.limited(@intFromEnum(remaining_chunk_len) - 2))); |
| 594 | chunk_len_ptr.* = .init(@intFromEnum(remaining_chunk_len) - n); | 614 | chunk_len_ptr.* = .init(@intFromEnum(remaining_chunk_len) - n); |
| 595 | return n; | 615 | return n; |
| 596 | }, | 616 | }, |
| ... | @@ -604,6 +624,24 @@ pub const Reader = struct { | ... | @@ -604,6 +624,24 @@ pub const Reader = struct { |
| 604 | .body_remaining_chunk_len => |*x| x, | 624 | .body_remaining_chunk_len => |*x| x, |
| 605 | else => unreachable, | 625 | else => unreachable, |
| 606 | }; | 626 | }; |
| | 627 | return chunkedReadVecEndless(reader, data, chunk_len_ptr) catch |err| switch (err) { |
| | 628 | error.ReadFailed => return error.ReadFailed, |
| | 629 | error.EndOfStream => { |
| | 630 | reader.body_err = error.HttpChunkTruncated; |
| | 631 | return error.ReadFailed; |
| | 632 | }, |
| | 633 | else => |e| { |
| | 634 | reader.body_err = e; |
| | 635 | return error.ReadFailed; |
| | 636 | }, |
| | 637 | }; |
| | 638 | } |
| | 639 | |
| | 640 | fn chunkedReadVecEndless( |
| | 641 | reader: *Reader, |
| | 642 | data: []const []u8, |
| | 643 | chunk_len_ptr: *RemainingChunkLen, |
| | 644 | ) (BodyError || std.io.Reader.Error)!usize { |
| 607 | const in = reader.in; | 645 | const in = reader.in; |
| 608 | var already_requested_more = false; | 646 | var already_requested_more = false; |
| 609 | var amt_read: usize = 0; | 647 | var amt_read: usize = 0; |
| ... | @@ -614,21 +652,21 @@ pub const Reader = struct { | ... | @@ -614,21 +652,21 @@ pub const Reader = struct { |
| 614 | var cp: ChunkParser = .init; | 652 | var cp: ChunkParser = .init; |
| 615 | const available_buffer = in.bufferContents(); | 653 | const available_buffer = in.bufferContents(); |
| 616 | const i = cp.feed(available_buffer); | 654 | const i = cp.feed(available_buffer); |
| 617 | if (cp.state == .invalid) return reader.failBody(error.HttpChunkInvalid); | 655 | if (cp.state == .invalid) return error.HttpChunkInvalid; |
| 618 | if (i == available_buffer.len) { | 656 | if (i == available_buffer.len) { |
| 619 | if (already_requested_more) { | 657 | if (already_requested_more) { |
| 620 | chunk_len_ptr.* = .head; | 658 | chunk_len_ptr.* = .head; |
| 621 | return amt_read; | 659 | return amt_read; |
| 622 | } | 660 | } |
| 623 | already_requested_more = true; | 661 | already_requested_more = true; |
| 624 | try endless(reader, in.fill(max_chunk_header_len)); | 662 | try in.fill(max_chunk_header_len); |
| 625 | const next_i = cp.feed(in.bufferContents()[i..]); | 663 | const next_i = cp.feed(in.bufferContents()[i..]); |
| 626 | if (cp.state != .data) return reader.failBody(error.HttpChunkInvalid); | 664 | if (cp.state != .data) return error.HttpChunkInvalid; |
| 627 | const header_len = i + next_i; | 665 | const header_len = i + next_i; |
| 628 | if (header_len > max_chunk_header_len) return reader.failBody(error.HttpChunkInvalid); | 666 | if (header_len > max_chunk_header_len) return error.HttpChunkInvalid; |
| 629 | in.toss(header_len); | 667 | in.toss(header_len); |
| 630 | } else { | 668 | } else { |
| 631 | if (i > max_chunk_header_len) return reader.failBody(error.HttpChunkInvalid); | 669 | if (i > max_chunk_header_len) return error.HttpChunkInvalid; |
| 632 | in.toss(i); | 670 | in.toss(i); |
| 633 | } | 671 | } |
| 634 | if (cp.chunk_len == 0) return parseTrailers(reader, amt_read); | 672 | if (cp.chunk_len == 0) return parseTrailers(reader, amt_read); |
| ... | @@ -636,13 +674,13 @@ pub const Reader = struct { | ... | @@ -636,13 +674,13 @@ pub const Reader = struct { |
| 636 | }, | 674 | }, |
| 637 | .n => { | 675 | .n => { |
| 638 | if (in.bufferContents().len < 1) already_requested_more = true; | 676 | if (in.bufferContents().len < 1) already_requested_more = true; |
| 639 | if ((try endless(reader, in.takeByte())) != '\n') return reader.failBody(error.HttpChunkInvalid); | 677 | if ((try in.takeByte()) != '\n') return error.HttpChunkInvalid; |
| 640 | continue :len .head; | 678 | continue :len .head; |
| 641 | }, | 679 | }, |
| 642 | .rn => { | 680 | .rn => { |
| 643 | if (in.bufferContents().len < 2) already_requested_more = true; | 681 | if (in.bufferContents().len < 2) already_requested_more = true; |
| 644 | const rn = try endless(reader, in.takeArray(2)); | 682 | const rn = try in.takeArray(2); |
| 645 | if (rn[0] != '\r' or rn[1] != '\n') return reader.failBody(error.HttpChunkInvalid); | 683 | if (rn[0] != '\r' or rn[1] != '\n') return error.HttpChunkInvalid; |
| 646 | continue :len .head; | 684 | continue :len .head; |
| 647 | }, | 685 | }, |
| 648 | else => |remaining_chunk_len| { | 686 | else => |remaining_chunk_len| { |
| ... | @@ -657,12 +695,14 @@ pub const Reader = struct { | ... | @@ -657,12 +695,14 @@ pub const Reader = struct { |
| 657 | chunk_len_ptr.* = next_chunk_len; | 695 | chunk_len_ptr.* = next_chunk_len; |
| 658 | continue :data; | 696 | continue :data; |
| 659 | } | 697 | } |
| 660 | if (already_requested_more) { | 698 | if (available_buffer.len - copy_len == 0) { |
| 661 | chunk_len_ptr.* = next_chunk_len; | 699 | if (already_requested_more) { |
| 662 | return amt_read; | 700 | chunk_len_ptr.* = next_chunk_len; |
| | 701 | return amt_read; |
| | 702 | } |
| | 703 | already_requested_more = true; |
| | 704 | try in.fillMore(); |
| 663 | } | 705 | } |
| 664 | already_requested_more = true; | | |
| 665 | try endless(reader, in.fillMore()); | | |
| 666 | continue :len next_chunk_len; | 706 | continue :len next_chunk_len; |
| 667 | }, | 707 | }, |
| 668 | } | 708 | } |
| ... | @@ -677,44 +717,62 @@ pub const Reader = struct { | ... | @@ -677,44 +717,62 @@ pub const Reader = struct { |
| 677 | .body_remaining_chunk_len => |*x| x, | 717 | .body_remaining_chunk_len => |*x| x, |
| 678 | else => unreachable, | 718 | else => unreachable, |
| 679 | }; | 719 | }; |
| | 720 | return chunkedDiscardEndless(reader, limit, chunk_len_ptr) catch |err| switch (err) { |
| | 721 | error.ReadFailed => return error.ReadFailed, |
| | 722 | error.EndOfStream => { |
| | 723 | reader.body_err = error.HttpChunkTruncated; |
| | 724 | return error.ReadFailed; |
| | 725 | }, |
| | 726 | else => |e| { |
| | 727 | reader.body_err = e; |
| | 728 | return error.ReadFailed; |
| | 729 | }, |
| | 730 | }; |
| | 731 | } |
| | 732 | |
| | 733 | fn chunkedDiscardEndless( |
| | 734 | reader: *Reader, |
| | 735 | limit: std.io.Reader.Limit, |
| | 736 | chunk_len_ptr: *RemainingChunkLen, |
| | 737 | ) (BodyError || std.io.Reader.Error)!usize { |
| 680 | const in = reader.in; | 738 | const in = reader.in; |
| 681 | len: switch (chunk_len_ptr.*) { | 739 | len: switch (chunk_len_ptr.*) { |
| 682 | .head => { | 740 | .head => { |
| 683 | var cp: ChunkParser = .init; | 741 | var cp: ChunkParser = .init; |
| 684 | const i = cp.feed(in.bufferContents()); | 742 | const i = cp.feed(in.bufferContents()); |
| 685 | switch (cp.state) { | 743 | switch (cp.state) { |
| 686 | .invalid => return reader.failBody(error.HttpChunkInvalid), | 744 | .invalid => return error.HttpChunkInvalid, |
| 687 | .data => { | 745 | .data => { |
| 688 | if (i > max_chunk_header_len) return reader.failBody(error.HttpChunkInvalid); | 746 | if (i > max_chunk_header_len) return error.HttpChunkInvalid; |
| 689 | in.toss(i); | 747 | in.toss(i); |
| 690 | }, | 748 | }, |
| 691 | else => { | 749 | else => { |
| 692 | try endless(reader, in.fill(max_chunk_header_len)); | 750 | try in.fill(max_chunk_header_len); |
| 693 | const next_i = cp.feed(in.bufferContents()[i..]); | 751 | const next_i = cp.feed(in.bufferContents()[i..]); |
| 694 | if (cp.state != .data) return reader.failBody(error.HttpChunkInvalid); | 752 | if (cp.state != .data) return error.HttpChunkInvalid; |
| 695 | const header_len = i + next_i; | 753 | const header_len = i + next_i; |
| 696 | if (header_len > max_chunk_header_len) return reader.failBody(error.HttpChunkInvalid); | 754 | if (header_len > max_chunk_header_len) return error.HttpChunkInvalid; |
| 697 | in.toss(header_len); | 755 | in.toss(header_len); |
| 698 | }, | 756 | }, |
| 699 | } | 757 | } |
| 700 | if (cp.chunk_len == 0) return parseTrailers(reader, 0); | 758 | if (cp.chunk_len == 0) return parseTrailers(reader, 0); |
| 701 | const n = try endless(reader, in.discard(limit.min(.limited(cp.chunk_len)))); | 759 | const n = try in.discard(limit.min(.limited(cp.chunk_len))); |
| 702 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); | 760 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); |
| 703 | return n; | 761 | return n; |
| 704 | }, | 762 | }, |
| 705 | .n => { | 763 | .n => { |
| 706 | if ((try endless(reader, in.peekByte())) != '\n') return reader.failBody(error.HttpChunkInvalid); | 764 | if ((try in.peekByte()) != '\n') return error.HttpChunkInvalid; |
| 707 | in.toss(1); | 765 | in.toss(1); |
| 708 | continue :len .head; | 766 | continue :len .head; |
| 709 | }, | 767 | }, |
| 710 | .rn => { | 768 | .rn => { |
| 711 | const rn = try endless(reader, in.peekArray(2)); | 769 | const rn = try in.peekArray(2); |
| 712 | if (rn[0] != '\r' or rn[1] != '\n') return reader.failBody(error.HttpChunkInvalid); | 770 | if (rn[0] != '\r' or rn[1] != '\n') return error.HttpChunkInvalid; |
| 713 | in.toss(2); | 771 | in.toss(2); |
| 714 | continue :len .head; | 772 | continue :len .head; |
| 715 | }, | 773 | }, |
| 716 | else => |remaining_chunk_len| { | 774 | else => |remaining_chunk_len| { |
| 717 | const n = try endless(reader, in.discard(limit.min(.limited(remaining_chunk_len.int() - 2)))); | 775 | const n = try in.discard(limit.min(.limited(remaining_chunk_len.int() - 2))); |
| 718 | chunk_len_ptr.* = .init(remaining_chunk_len.int() - n); | 776 | chunk_len_ptr.* = .init(remaining_chunk_len.int() - n); |
| 719 | return n; | 777 | return n; |
| 720 | }, | 778 | }, |
| ... | @@ -723,33 +781,29 @@ pub const Reader = struct { | ... | @@ -723,33 +781,29 @@ pub const Reader = struct { |
| 723 | | 781 | |
| 724 | /// Called when next bytes in the stream are trailers, or "\r\n" to indicate | 782 | /// Called when next bytes in the stream are trailers, or "\r\n" to indicate |
| 725 | /// end of chunked body. | 783 | /// end of chunked body. |
| 726 | fn parseTrailers(reader: *Reader, amt_read: usize) std.io.Reader.Error!usize { | 784 | fn parseTrailers(reader: *Reader, amt_read: usize) (BodyError || std.io.Reader.Error)!usize { |
| 727 | const in = reader.in; | 785 | const in = reader.in; |
| 728 | var hp: HeadParser = .{}; | 786 | const rn = try in.peekArray(2); |
| 729 | var trailers_len: usize = 0; | 787 | if (rn[0] == '\r' and rn[1] == '\n') { |
| | 788 | in.toss(2); |
| | 789 | reader.state = .ready; |
| | 790 | assert(reader.trailers.len == 0); |
| | 791 | return amt_read; |
| | 792 | } |
| | 793 | var hp: HeadParser = .{ .state = .seen_rn }; |
| | 794 | var trailers_len: usize = 2; |
| 730 | while (true) { | 795 | while (true) { |
| 731 | if (trailers_len >= in.buffer.len) return reader.failBody(error.HttpHeadersOversize); | 796 | if (trailers_len >= in.buffer.len) return error.HttpHeadersOversize; |
| 732 | try in.fill(trailers_len + 1); | 797 | try in.fill(trailers_len + 1); |
| 733 | trailers_len += hp.feed(in.bufferContents()[trailers_len..]); | 798 | trailers_len += hp.feed(in.bufferContents()[trailers_len..]); |
| 734 | if (hp.state == .finished) { | 799 | if (hp.state == .finished) { |
| 735 | reader.state = .ready; | 800 | reader.state = .ready; |
| 736 | reader.trailers = in.bufferContents()[0..trailers_len]; | 801 | reader.trailers = in.bufferContents()[0..trailers_len]; |
| | 802 | in.toss(trailers_len); |
| 737 | return amt_read; | 803 | return amt_read; |
| 738 | } | 804 | } |
| 739 | } | 805 | } |
| 740 | } | 806 | } |
| 741 | | | |
| 742 | fn failBody(r: *Reader, err: BodyError) error{ReadFailed} { | | |
| 743 | r.body_err = err; | | |
| 744 | return error.ReadFailed; | | |
| 745 | } | | |
| 746 | | | |
| 747 | fn endless(r: *Reader, x: anytype) @TypeOf(x) { | | |
| 748 | return x catch |err| switch (err) { | | |
| 749 | error.EndOfStream => return failBody(r, error.HttpChunkTruncated), | | |
| 750 | else => return err, | | |
| 751 | }; | | |
| 752 | } | | |
| 753 | }; | 807 | }; |
| 754 | | 808 | |
| 755 | pub const Decompressor = struct { | 809 | pub const Decompressor = struct { |