diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/std/http/Server.zig | 71 | ||||
| -rw-r--r-- | lib/std/http/protocol.zig | 8 |
2 files changed, 77 insertions, 2 deletions
diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig index 59b76ea20d..66adb1bb96 100644 --- a/lib/std/http/Server.zig +++ b/lib/std/http/Server.zig @@ -661,3 +661,74 @@ pub fn accept(server: *Server, options: HeaderStrategy) AcceptError!*Response { return res; } + +test "HTTP server handles a chunked transfer coding request" { + const builtin = @import("builtin"); + + // This test requires spawning threads. + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const native_endian = comptime builtin.cpu.arch.endian(); + if (builtin.zig_backend == .stage2_llvm and native_endian == .Big) { + // https://github.com/ziglang/zig/issues/13782 + return error.SkipZigTest; + } + + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const allocator = std.testing.allocator; + const expect = std.testing.expect; + + const max_header_size = 8192; + var server = std.http.Server.init(allocator, .{ .reuse_address = true }); + defer server.deinit(); + + const address = try std.net.Address.parseIp("127.0.0.1", 0); + try server.listen(address); + const server_port = server.socket.listen_address.in.getPort(); + + const server_thread = try std.Thread.spawn(.{}, (struct { + fn apply(s: *std.http.Server) !void { + const res = try s.accept(.{ .dynamic = max_header_size }); + defer res.deinit(); + defer res.reset(); + try res.wait(); + + try expect(res.request.transfer_encoding.? == .chunked); + + const server_body: []const u8 = "message from server!\n"; + res.transfer_encoding = .{ .content_length = server_body.len }; + try res.headers.append("content-type", "text/plain"); + try res.headers.append("connection", "close"); + try res.do(); + + var buf: [128]u8 = undefined; + const n = try res.readAll(&buf); + try expect(std.mem.eql(u8, buf[0..n], "ABCD")); + _ = try res.writer().writeAll(server_body); + try res.finish(); + } + }).apply, .{&server}); + + const request_bytes = + "POST / HTTP/1.1\r\n" ++ + "Content-Type: text/plain\r\n" ++ + "Transfer-Encoding: chunked\r\n" ++ + "\r\n" ++ + "1\r\n" ++ + "A\r\n" ++ + "1\r\n" ++ + "B\r\n" ++ + "2\r\n" ++ + "CD\r\n" ++ + "0\r\n" ++ + "\r\n"; + + const stream = try std.net.tcpConnectToHost(allocator, "127.0.0.1", server_port); + defer stream.close(); + _ = try stream.writeAll(request_bytes[0..]); + + server_thread.join(); +} diff --git a/lib/std/http/protocol.zig b/lib/std/http/protocol.zig index 0d661bb31f..962376ce82 100644 --- a/lib/std/http/protocol.zig +++ b/lib/std/http/protocol.zig @@ -556,8 +556,12 @@ pub const HeadersParser = struct { switch (r.state) { .invalid => return error.HttpChunkInvalid, .chunk_data => if (r.next_chunk_length == 0) { - // The trailer section is formatted identically to the header section. - r.state = .seen_rn; + if (std.mem.eql(u8, bconn.peek(), "\r\n")) { + r.state = .finished; + } else { + // The trailer section is formatted identically to the header section. + r.state = .seen_rn; + } r.done = true; return out_index; |
