authorgravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-04-08 09:58:48-05:00
committergravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-04-08 09:59:37-05:00
log7f9a4625fda0b1a33177cdd66819f0a061c6b2da
tree2d8c06e089365bd8b5a5a8992c8562a3878cd099
parentef6d58ed3b03b2a5d08489daee0c232d5e113d55
signaturelock-open Commit is signed but in an unrecognized format.

std.http: reenable protocol read tests, add missing branch in findHeaders end


1 files changed, 131 insertions(+), 12 deletions(-)

lib/std/http/protocol.zig+131-12
......@@ -82,7 +82,7 @@ pub const HeadersParser = struct {
8282 /// If the amount returned is less than `bytes.len`, you may assume that the parser is in a content state and the
8383 /// first byte of content is located at `bytes[result]`.
8484 pub fn findHeadersEnd(r: *HeadersParser, bytes: []const u8) u32 {
85 const vector_len: comptime_int = comptime std.simd.suggestVectorSize(u8) orelse 8;
85 const vector_len: comptime_int = comptime std.math.max(std.simd.suggestVectorSize(u8) orelse 1, 8);
8686 const len = @intCast(u32, bytes.len);
8787 var index: u32 = 0;
8888
......@@ -232,7 +232,7 @@ pub const HeadersParser = struct {
232232 else => {},
233233 }
234234 },
235 4...vector_len - 1 => {
235 4...vector_len => {
236236 inline for (0..vector_len - 3) |i_usize| {
237237 const i = @truncate(u32, i_usize);
238238
......@@ -311,6 +311,7 @@ pub const HeadersParser = struct {
311311
312312 switch (b16) {
313313 int16("\r\n") => r.state = .seen_rn,
314 int16("\n\r") => r.state = .seen_rnr,
314315 int16("\n\n") => r.state = .finished,
315316 else => {},
316317 }
......@@ -614,6 +615,86 @@ inline fn intShift(comptime T: type, x: anytype) T {
614615 }
615616}
616617
618/// A buffered (and peekable) Connection.
619const MockBufferedConnection = struct {
620 pub const buffer_size = 0x2000;
621
622 conn: std.io.FixedBufferStream([]const u8),
623 buf: [buffer_size]u8 = undefined,
624 start: u16 = 0,
625 end: u16 = 0,
626
627 pub fn fill(bconn: *MockBufferedConnection) ReadError!void {
628 if (bconn.end != bconn.start) return;
629
630 const nread = try bconn.conn.read(bconn.buf[0..]);
631 if (nread == 0) return error.EndOfStream;
632 bconn.start = 0;
633 bconn.end = @truncate(u16, nread);
634 }
635
636 pub fn peek(bconn: *MockBufferedConnection) []const u8 {
637 return bconn.buf[bconn.start..bconn.end];
638 }
639
640 pub fn clear(bconn: *MockBufferedConnection, num: u16) void {
641 bconn.start += num;
642 }
643
644 pub fn readAtLeast(bconn: *MockBufferedConnection, buffer: []u8, len: usize) ReadError!usize {
645 var out_index: u16 = 0;
646 while (out_index < len) {
647 const available = bconn.end - bconn.start;
648 const left = buffer.len - out_index;
649
650 if (available > 0) {
651 const can_read = @truncate(u16, @min(available, left));
652
653 std.mem.copy(u8, buffer[out_index..], bconn.buf[bconn.start..][0..can_read]);
654 out_index += can_read;
655 bconn.start += can_read;
656
657 continue;
658 }
659
660 if (left > bconn.buf.len) {
661 // skip the buffer if the output is large enough
662 return bconn.conn.read(buffer[out_index..]);
663 }
664
665 try bconn.fill();
666 }
667
668 return out_index;
669 }
670
671 pub fn read(bconn: *MockBufferedConnection, buffer: []u8) ReadError!usize {
672 return bconn.readAtLeast(buffer, 1);
673 }
674
675 pub const ReadError = std.io.FixedBufferStream([]const u8).ReadError || error{EndOfStream};
676 pub const Reader = std.io.Reader(*MockBufferedConnection, ReadError, read);
677
678 pub fn reader(bconn: *MockBufferedConnection) Reader {
679 return Reader{ .context = bconn };
680 }
681
682 pub fn writeAll(bconn: *MockBufferedConnection, buffer: []const u8) WriteError!void {
683 return bconn.conn.writeAll(buffer);
684 }
685
686 pub fn write(bconn: *MockBufferedConnection, buffer: []const u8) WriteError!usize {
687 return bconn.conn.write(buffer);
688 }
689
690 pub const WriteError = std.io.FixedBufferStream([]const u8).WriteError;
691 pub const Writer = std.io.Writer(*MockBufferedConnection, WriteError, write);
692
693 pub fn writer(bconn: *MockBufferedConnection) Writer {
694 return Writer{ .context = bconn };
695 }
696};
697
617698test "HeadersParser.findHeadersEnd" {
618699 var r: HeadersParser = undefined;
619700 const data = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\nHello";
......@@ -662,18 +743,29 @@ test "HeadersParser.findChunkedLen" {
662743
663744test "HeadersParser.read length" {
664745 // mock BufferedConnection for read
665 if (true) return error.SkipZigTest;
666746
667747 var r = HeadersParser.initDynamic(256);
668748 defer r.header_bytes.deinit(std.testing.allocator);
669749 const data = "GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 5\r\n\r\nHello";
670750 var fbs = std.io.fixedBufferStream(data);
671751
672 try r.waitForCompleteHead(fbs.reader(), std.testing.allocator);
752 var bconn = MockBufferedConnection{
753 .conn = fbs,
754 };
755
756 while (true) { // read headers
757 try bconn.fill();
758
759 const nchecked = try r.checkCompleteHead(std.testing.allocator, bconn.peek());
760 bconn.clear(@intCast(u16, nchecked));
761
762 if (r.state.isContent()) break;
763 }
764
673765 var buf: [8]u8 = undefined;
674766
675767 r.next_chunk_length = 5;
676 const len = try r.read(fbs.reader(), &buf, false);
768 const len = try r.read(&bconn, &buf, false);
677769 try std.testing.expectEqual(@as(usize, 5), len);
678770 try std.testing.expectEqualStrings("Hello", buf[0..len]);
679771
......@@ -682,18 +774,28 @@ test "HeadersParser.read length" {
682774
683775test "HeadersParser.read chunked" {
684776 // mock BufferedConnection for read
685 if (true) return error.SkipZigTest;
686777
687778 var r = HeadersParser.initDynamic(256);
688779 defer r.header_bytes.deinit(std.testing.allocator);
689780 const data = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n2\r\nHe\r\n2\r\nll\r\n1\r\no\r\n0\r\n\r\n";
690781 var fbs = std.io.fixedBufferStream(data);
691782
692 try r.waitForCompleteHead(fbs.reader(), std.testing.allocator);
783 var bconn = MockBufferedConnection{
784 .conn = fbs,
785 };
786
787 while (true) { // read headers
788 try bconn.fill();
789
790 const nchecked = try r.checkCompleteHead(std.testing.allocator, bconn.peek());
791 bconn.clear(@intCast(u16, nchecked));
792
793 if (r.state.isContent()) break;
794 }
693795 var buf: [8]u8 = undefined;
694796
695797 r.state = .chunk_head_size;
696 const len = try r.read(fbs.reader(), &buf, false);
798 const len = try r.read(&bconn, &buf, false);
697799 try std.testing.expectEqual(@as(usize, 5), len);
698800 try std.testing.expectEqualStrings("Hello", buf[0..len]);
699801
......@@ -702,22 +804,39 @@ test "HeadersParser.read chunked" {
702804
703805test "HeadersParser.read chunked trailer" {
704806 // mock BufferedConnection for read
705 if (true) return error.SkipZigTest;
706807
707808 var r = HeadersParser.initDynamic(256);
708809 defer r.header_bytes.deinit(std.testing.allocator);
709810 const data = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n2\r\nHe\r\n2\r\nll\r\n1\r\no\r\n0\r\nContent-Type: text/plain\r\n\r\n";
710811 var fbs = std.io.fixedBufferStream(data);
711812
712 try r.waitForCompleteHead(fbs.reader(), std.testing.allocator);
813 var bconn = MockBufferedConnection{
814 .conn = fbs,
815 };
816
817 while (true) { // read headers
818 try bconn.fill();
819
820 const nchecked = try r.checkCompleteHead(std.testing.allocator, bconn.peek());
821 bconn.clear(@intCast(u16, nchecked));
822
823 if (r.state.isContent()) break;
824 }
713825 var buf: [8]u8 = undefined;
714826
715827 r.state = .chunk_head_size;
716 const len = try r.read(fbs.reader(), &buf, false);
828 const len = try r.read(&bconn, &buf, false);
717829 try std.testing.expectEqual(@as(usize, 5), len);
718830 try std.testing.expectEqualStrings("Hello", buf[0..len]);
719831
720 try r.waitForCompleteHead(fbs.reader(), std.testing.allocator);
832 while (true) { // read headers
833 try bconn.fill();
834
835 const nchecked = try r.checkCompleteHead(std.testing.allocator, bconn.peek());
836 bconn.clear(@intCast(u16, nchecked));
837
838 if (r.state.isContent()) break;
839 }
721840
722841 try std.testing.expectEqualStrings("GET / HTTP/1.1\r\nHost: localhost\r\n\r\nContent-Type: text/plain\r\n\r\n", r.header_bytes.items);
723842}