Closed
Description
Zig Version
0.11.0-dev.3107+6547d2331
Steps to Reproduce and Observed Behavior
Make a https request using the std.http.Client
and set transfer_encoding
to .chunked
on the request like so:
var client: http.Client = .{ .allocator = allocator };
var req = try client.request(http.Method.POST, http_endpoint, default_http_headers, .{});
req.transfer_encoding = .chunked;
defer req.deinit();
try req.start();
try req.writer().writeAll(reqBody); // reqBody is just []u8
try req.finish();
try req.wait();
if (req.response.status != http.Status.ok) {
return Error.ResponseNotStatusOk;
}
const respBody = try req.reader().readAllAlloc(self.allocator, 12 * 1024 * 1024); // 12Mb max size set
defer self.allocator.free(body);
// do something with respBody
Once you surpass a certain body size (greater than 4096
i believe), it will remain stuck in an infinite loop. The response body size of the request which causes this issue is much smaller than 12Mb
- it's around 3.4Mb
. If I make request under 4Kb
it seems to work fine.
I tracked the bug to BufferedConnection.readAtLeast and it seems it's stuck in the while (out_index < len)
loop.
I logged values of the vars that affect this loop like so:
pub fn readAtLeast(bconn: *BufferedConnection, buffer: []u8, len: usize) ReadError!usize {
var out_index: u16 = 0;
while (out_index < len) {
std.log.debug("readAtLeast.while - begin", .{});
const available = bconn.read_end - bconn.read_start;
const left = buffer.len - out_index;
if (available > 0) {
const can_read = @intCast(u16, @min(available, left));
std.log.debug("readAtLeast.while - available: {}, left: {}, can_read: {}", .{ available, left, can_read });
@memcpy(buffer[out_index..][0..can_read], bconn.read_buf[bconn.read_start..][0..can_read]);
out_index += can_read;
bconn.read_start += can_read;
continue;
}
std.log.debug("readAtLeast.while - passed continue", .{});
if (left > bconn.read_buf.len) {
// skip the buffer if the output is large enough
return bconn.conn.read(buffer[out_index..]);
}
try bconn.fill();
std.log.debug("readAtLeast.while - end", .{});
}
std.log.debug("readAtLeast - returning", .{});
return out_index;
}
and it produces these logs continuously:
...
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
debug: readAtLeast.while - begin
debug: readAtLeast.while - available: 12536, left: 0, can_read: 0
...
Also, sometimes it doesn't get stuck in loop and returns this error instead (~20% of the time):
thread 1290939 panic: @memcpy arguments have non-equal lengths
/Users/bkk/bin/zig/lib/std/crypto/tls/Client.zig:1062:25: 0x104c0671b in readvAdvanced__anon_13130 (main)
@memcpy(frag[0..in], first);
^
/Users/bkk/bin/zig/lib/std/crypto/tls/Client.zig:898:53: 0x104c046cf in readvAtLeast__anon_13129 (main)
var amt = try c.readvAdvanced(stream, iovecs[vec_i..]);
^
/Users/bkk/bin/zig/lib/std/crypto/tls/Client.zig:859:24: 0x104c044b3 in readAtLeast__anon_13127 (main)
return readvAtLeast(c, stream, &iovecs, len);
^
/Users/bkk/bin/zig/lib/std/crypto/tls/Client.zig:864:23: 0x104c043d3 in read__anon_13126 (main)
return readAtLeast(c, stream, buffer, 1);
^
/Users/bkk/bin/zig/lib/std/http/Client.zig:173:46: 0x104bc9e77 in read (main)
.tls => conn.tls_client.read(conn.stream, buffer),
^
/Users/bkk/bin/zig/lib/std/http/Client.zig:267:57: 0x104b7966b in fill (main)
const nread = try bconn.conn.read(bconn.read_buf[0..]);
^
/Users/bkk/bin/zig/lib/std/http/protocol.zig:553:35: 0x104b78f9f in read__anon_7953 (main)
try bconn.fill();
^
/Users/bkk/bin/zig/lib/std/http/Client.zig:686:111: 0x104b7a87b in transferRead (main)
const amt = try req.response.parser.read(&req.connection.data.buffered, buf[index..], req.response.skip);
^
/Users/bkk/bin/zig/lib/std/io/reader.zig:27:31: 0x104bdee07 in read (main)
return readFn(self.context, buffer);
^
/Users/bkk/bin/zig/lib/std/io/reader.zig:243:50: 0x104d1240b in readByte (main)
const amt_read = try self.read(result[0..]);
^
/Users/bkk/bin/zig/lib/std/compress/deflate/decompressor.zig:822:47: 0x104bdb8d7 in moreBits (main)
var c = self.inner_reader.readByte() catch |e| {
^
/Users/bkk/bin/zig/lib/std/compress/deflate/decompressor.zig:412:30: 0x104b8443f in nextBlock (main)
self.moreBits() catch |e| {
^
/Users/bkk/bin/zig/lib/std/compress/deflate/decompressor.zig:471:26: 0x104be05db in read (main)
self.step(self) catch |e| {
^
/Users/bkk/bin/zig/lib/std/compress/gzip.zig:128:45: 0x104b88573 in read (main)
const r = try self.inflater.read(buffer);
^
/Users/bkk/bin/zig/lib/std/http/Client.zig:814:39: 0x104b87c17 in read (main)
.gzip => |*gzip| gzip.read(buffer) catch return error.DecompressionFailure,
^
/Users/bkk/bin/zig/lib/std/io/reader.zig:27:31: 0x104be46b3 in read (main)
return readFn(self.context, buffer);
^
/Users/bkk/bin/zig/lib/std/io/reader.zig:46:49: 0x104b8929f in readAtLeast (main)
const amt = try self.read(buffer[index..]);
^
/Users/bkk/bin/zig/lib/std/io/reader.zig:34:52: 0x104b69727 in readAll (main)
return readAtLeast(self, buffer, buffer.len);
^
/Users/bkk/bin/zig/lib/std/io/reader.zig:80:52: 0x104b6913b in readAllArrayListAligned__anon_6214 (main)
const bytes_read = try self.readAll(dest_slice);
^
/Users/bkk/bin/zig/lib/std/io/reader.zig:65:48: 0x104b68f03 in readAllArrayList (main)
return self.readAllArrayListAligned(null, array_list, max_append_size);
^
/Users/bkk/bin/zig/lib/std/io/reader.zig:105:38: 0x104b698e3 in readAllAlloc (main)
try self.readAllArrayList(&array_list, max_size);
Expected Behavior
It should not hang indefinitely.