| ... | ... | @@ -553,44 +553,64 @@ pub const Reader = struct { |
| 553 | 553 | .body_remaining_chunk_len => |*x| x, |
| 554 | 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 | 576 | const in = reader.in; |
| 557 | 577 | len: switch (chunk_len_ptr.*) { |
| 558 | 578 | .head => { |
| 559 | 579 | var cp: ChunkParser = .init; |
| 560 | 580 | const i = cp.feed(in.bufferContents()); |
| 561 | 581 | switch (cp.state) { |
| 562 | | .invalid => return reader.failBody(error.HttpChunkInvalid), |
| 582 | .invalid => return error.HttpChunkInvalid, |
| 563 | 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 | 585 | in.toss(i); |
| 566 | 586 | }, |
| 567 | 587 | else => { |
| 568 | | try endless(reader, in.fill(max_chunk_header_len)); |
| 588 | try in.fill(max_chunk_header_len); |
| 569 | 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 | 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 | 593 | in.toss(header_len); |
| 574 | 594 | }, |
| 575 | 595 | } |
| 576 | 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 | 598 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); |
| 579 | 599 | return n; |
| 580 | 600 | }, |
| 581 | 601 | .n => { |
| 582 | | if ((try in.peekByte()) != '\n') return reader.failBody(error.HttpChunkInvalid); |
| 602 | if ((try in.peekByte()) != '\n') return error.HttpChunkInvalid; |
| 583 | 603 | in.toss(1); |
| 584 | 604 | continue :len .head; |
| 585 | 605 | }, |
| 586 | 606 | .rn => { |
| 587 | | const rn = try endless(reader, in.peekArray(2)); |
| 588 | | if (rn[0] != '\r' or rn[1] != '\n') return reader.failBody(error.HttpChunkInvalid); |
| 607 | const rn = try in.peekArray(2); |
| 608 | if (rn[0] != '\r' or rn[1] != '\n') return error.HttpChunkInvalid; |
| 589 | 609 | in.toss(2); |
| 590 | 610 | continue :len .head; |
| 591 | 611 | }, |
| 592 | 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 | 614 | chunk_len_ptr.* = .init(@intFromEnum(remaining_chunk_len) - n); |
| 595 | 615 | return n; |
| 596 | 616 | }, |
| ... | ... | @@ -604,6 +624,24 @@ pub const Reader = struct { |
| 604 | 624 | .body_remaining_chunk_len => |*x| x, |
| 605 | 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 | 645 | const in = reader.in; |
| 608 | 646 | var already_requested_more = false; |
| 609 | 647 | var amt_read: usize = 0; |
| ... | ... | @@ -614,21 +652,21 @@ pub const Reader = struct { |
| 614 | 652 | var cp: ChunkParser = .init; |
| 615 | 653 | const available_buffer = in.bufferContents(); |
| 616 | 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 | 656 | if (i == available_buffer.len) { |
| 619 | 657 | if (already_requested_more) { |
| 620 | 658 | chunk_len_ptr.* = .head; |
| 621 | 659 | return amt_read; |
| 622 | 660 | } |
| 623 | 661 | already_requested_more = true; |
| 624 | | try endless(reader, in.fill(max_chunk_header_len)); |
| 662 | try in.fill(max_chunk_header_len); |
| 625 | 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 | 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 | 667 | in.toss(header_len); |
| 630 | 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 | 670 | in.toss(i); |
| 633 | 671 | } |
| 634 | 672 | if (cp.chunk_len == 0) return parseTrailers(reader, amt_read); |
| ... | ... | @@ -636,13 +674,13 @@ pub const Reader = struct { |
| 636 | 674 | }, |
| 637 | 675 | .n => { |
| 638 | 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 | 678 | continue :len .head; |
| 641 | 679 | }, |
| 642 | 680 | .rn => { |
| 643 | 681 | if (in.bufferContents().len < 2) already_requested_more = true; |
| 644 | | const rn = try endless(reader, in.takeArray(2)); |
| 645 | | if (rn[0] != '\r' or rn[1] != '\n') return reader.failBody(error.HttpChunkInvalid); |
| 682 | const rn = try in.takeArray(2); |
| 683 | if (rn[0] != '\r' or rn[1] != '\n') return error.HttpChunkInvalid; |
| 646 | 684 | continue :len .head; |
| 647 | 685 | }, |
| 648 | 686 | else => |remaining_chunk_len| { |
| ... | ... | @@ -657,12 +695,14 @@ pub const Reader = struct { |
| 657 | 695 | chunk_len_ptr.* = next_chunk_len; |
| 658 | 696 | continue :data; |
| 659 | 697 | } |
| 660 | | if (already_requested_more) { |
| 661 | | chunk_len_ptr.* = next_chunk_len; |
| 662 | | return amt_read; |
| 698 | if (available_buffer.len - copy_len == 0) { |
| 699 | if (already_requested_more) { |
| 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 | 706 | continue :len next_chunk_len; |
| 667 | 707 | }, |
| 668 | 708 | } |
| ... | ... | @@ -677,44 +717,62 @@ pub const Reader = struct { |
| 677 | 717 | .body_remaining_chunk_len => |*x| x, |
| 678 | 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 | 738 | const in = reader.in; |
| 681 | 739 | len: switch (chunk_len_ptr.*) { |
| 682 | 740 | .head => { |
| 683 | 741 | var cp: ChunkParser = .init; |
| 684 | 742 | const i = cp.feed(in.bufferContents()); |
| 685 | 743 | switch (cp.state) { |
| 686 | | .invalid => return reader.failBody(error.HttpChunkInvalid), |
| 744 | .invalid => return error.HttpChunkInvalid, |
| 687 | 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 | 747 | in.toss(i); |
| 690 | 748 | }, |
| 691 | 749 | else => { |
| 692 | | try endless(reader, in.fill(max_chunk_header_len)); |
| 750 | try in.fill(max_chunk_header_len); |
| 693 | 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 | 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 | 755 | in.toss(header_len); |
| 698 | 756 | }, |
| 699 | 757 | } |
| 700 | 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 | 760 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); |
| 703 | 761 | return n; |
| 704 | 762 | }, |
| 705 | 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 | 765 | in.toss(1); |
| 708 | 766 | continue :len .head; |
| 709 | 767 | }, |
| 710 | 768 | .rn => { |
| 711 | | const rn = try endless(reader, in.peekArray(2)); |
| 712 | | if (rn[0] != '\r' or rn[1] != '\n') return reader.failBody(error.HttpChunkInvalid); |
| 769 | const rn = try in.peekArray(2); |
| 770 | if (rn[0] != '\r' or rn[1] != '\n') return error.HttpChunkInvalid; |
| 713 | 771 | in.toss(2); |
| 714 | 772 | continue :len .head; |
| 715 | 773 | }, |
| 716 | 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 | 776 | chunk_len_ptr.* = .init(remaining_chunk_len.int() - n); |
| 719 | 777 | return n; |
| 720 | 778 | }, |
| ... | ... | @@ -723,33 +781,29 @@ pub const Reader = struct { |
| 723 | 781 | |
| 724 | 782 | /// Called when next bytes in the stream are trailers, or "\r\n" to indicate |
| 725 | 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 | 785 | const in = reader.in; |
| 728 | | var hp: HeadParser = .{}; |
| 729 | | var trailers_len: usize = 0; |
| 786 | const rn = try in.peekArray(2); |
| 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 | 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 | 797 | try in.fill(trailers_len + 1); |
| 733 | 798 | trailers_len += hp.feed(in.bufferContents()[trailers_len..]); |
| 734 | 799 | if (hp.state == .finished) { |
| 735 | 800 | reader.state = .ready; |
| 736 | 801 | reader.trailers = in.bufferContents()[0..trailers_len]; |
| 802 | in.toss(trailers_len); |
| 737 | 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 | 809 | pub const Decompressor = struct { |