-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
Zig Version
0.16.0-dev.195+ac42eaaad
Steps to Reproduce and Observed Behavior
The HTTP Client will try to read/discard response body even if no body is expected to appear, which will hang forever/until the server closes the connection. Which can take a while with Connection: keep-alive.
const std = @import("std");
test {
var client: std.http.Client = .{
.allocator = std.testing.allocator,
};
defer client.deinit();
for ([_]bool{ false, true }) |keep_alive| {// The problem does not happen when keep_alive is false
for ([_][]const u8{ "http", "https" }) |protocol| {
const host_url = "httpbun.org";// Arbitrary service to reproduce the problem
const method: std.http.Method = .DELETE;// Zig expects DELETE response to have a body
const path = "status/204";// No Content, causes the server to return no Content-Length header
var response: std.Io.Writer.Allocating = .init(std.testing.allocator);
defer response.deinit();
var url_buffer: [512]u8 = undefined;
const result = try client.fetch(.{
.response_writer = &response.writer,
.location = .{ .url = try std.fmt.bufPrint(&url_buffer, "{s}://{s}/{s}", .{ protocol, host_url, path }) },
.method = method,
.payload = null,
.keep_alive = keep_alive,
});
try std.testing.expectEqual(.no_content, result.status);
try std.testing.expectEqual(0, response.written().len);
std.debug.print("Done {s} {}\n", .{protocol, keep_alive});
}
}
}In this example, when keep_alive is false, everything will work as expected, because while Client will attempt to errorniously discard the body, the server will close the connection and no hang will occur.
But with keep_alive true, the connection will stay open, with no traffic. Because the response code is 204 No Content, the response will not even contain Content-Length: 0 header. The Client will still assume that the response body is everything left in the stream (despite response headers containing HTTP 1.1 and Connection: keep-alive). It will then try to read the body forever and hang here.
Expected Behavior
zig.http.Client (probably function bodyReader in particular) should respect HTTP 1.1 specification on when the response contains the body.
For response messages, whether or not a message-body is included with a message is dependent on both the request method and the response status code. All responses to the HEAD request method MUST NOT include a message-body, even though the presence of entity-header fields might lead one to believe they do. All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a message-body. All other responses do include a message-body, although it MAY be of zero length. (RFC-2616, section 4.3)