fix HTTP server to handle a chunked transfer coding request
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user