diff --git a/lib/std/http.zig b/lib/std/http.zig index 291e22539b..300ae397ec 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -443,7 +443,7 @@ pub const Reader = struct { }, .none => { if (content_length) |len| { - reader.state = .{ .body_remaining_content_length = len }; + reader.state = if (len == 0) .ready else .{ .body_remaining_content_length = len }; reader.interface = .{ .buffer = transfer_buffer, .seek = 0, @@ -509,27 +509,29 @@ pub const Reader = struct { limit: std.Io.Limit, ) std.Io.Reader.StreamError!usize { const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); + if (reader.state == .ready) return error.EndOfStream; const remaining_content_length = &reader.state.body_remaining_content_length; const remaining = remaining_content_length.*; - if (remaining == 0) { - reader.state = .ready; - return error.EndOfStream; - } const n = try reader.in.stream(w, limit.min(.limited64(remaining))); - remaining_content_length.* = remaining - n; + if (n == remaining) { + reader.state = .ready; + } else { + remaining_content_length.* = remaining - n; + } return n; } fn contentLengthDiscard(io_r: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize { const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); + if (reader.state == .ready) return error.EndOfStream; const remaining_content_length = &reader.state.body_remaining_content_length; const remaining = remaining_content_length.*; - if (remaining == 0) { - reader.state = .ready; - return error.EndOfStream; - } const n = try reader.in.discard(limit.min(.limited64(remaining))); - remaining_content_length.* = remaining - n; + if (n == remaining) { + reader.state = .ready; + } else { + remaining_content_length.* = remaining - n; + } return n; } diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig index b061f4b2ac..42fd9f8fc9 100644 --- a/lib/std/http/test.zig +++ b/lib/std/http/test.zig @@ -11,6 +11,28 @@ const expectEqual = std.testing.expectEqual; const expectEqualStrings = std.testing.expectEqualStrings; const expectError = std.testing.expectError; +test "content length reader state update" { + var in = Io.Reader.fixed("HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nHello!\r\nHTTP/1.1 200 OK\r\n\r\n"); + var reader: http.Reader = .{ + .in = &in, + .interface = undefined, + .state = .ready, + .max_head_len = 1024, + }; + + _ = try reader.receiveHead(); + var body: [6]u8 = undefined; + _ = try reader.bodyReader(&.{}, .none, body.len).readSliceAll(&body); + try expectEqual(.ready, reader.state); + _ = try reader.receiveHead(); + + in.seek = 0; + _ = try reader.receiveHead(); + try reader.bodyReader(&.{}, .none, body.len).discardAll(body.len); + try expectEqual(.ready, reader.state); + _ = try reader.receiveHead(); +} + test "trailers" { if (builtin.cpu.arch.isPowerPC64() and builtin.mode != .Debug) return error.SkipZigTest; // https://github.com/llvm/llvm-project/issues/171879 if (builtin.os.tag == .openbsd) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/30806