| author | |
| committer | |
| log | 0bbf0461d9800ce5eba28cd32ef10492b2f80639 |
| tree | 228500b88381759742764348878cd1255f28d8d4 |
| parent | 29225ae11b93404f2d823ae45bfe68ce882706d1 |
Content length based reading would only set the reader state to `ready`
once it returned EOF, but wrapping readers (such as decompressors)
may stop reading from the underlying source without receiving EOF.
In such cases the http reader state would stay set to
`body_remaining_content_length`, even though the entire body had been
read.
Fixes #30060
Co-authored-by: Andrew Kelley <andre@ziglang.org>2 files changed, 33 insertions(+), 9 deletions(-)
lib/std/http.zig+11-9| ... | ... | @@ -443,7 +443,7 @@ pub const Reader = struct { |
| 443 | 443 | }, |
| 444 | 444 | .none => { |
| 445 | 445 | if (content_length) |len| { |
| 446 | reader.state = .{ .body_remaining_content_length = len }; | |
| 446 | reader.state = if (len == 0) .ready else .{ .body_remaining_content_length = len }; | |
| 447 | 447 | reader.interface = .{ |
| 448 | 448 | .buffer = transfer_buffer, |
| 449 | 449 | .seek = 0, |
| ... | ... | @@ -509,27 +509,29 @@ pub const Reader = struct { |
| 509 | 509 | limit: std.Io.Limit, |
| 510 | 510 | ) std.Io.Reader.StreamError!usize { |
| 511 | 511 | const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); |
| 512 | if (reader.state == .ready) return error.EndOfStream; | |
| 512 | 513 | const remaining_content_length = &reader.state.body_remaining_content_length; |
| 513 | 514 | const remaining = remaining_content_length.*; |
| 514 | if (remaining == 0) { | |
| 515 | const n = try reader.in.stream(w, limit.min(.limited64(remaining))); | |
| 516 | if (n == remaining) { | |
| 515 | 517 | reader.state = .ready; |
| 516 | return error.EndOfStream; | |
| 518 | } else { | |
| 519 | remaining_content_length.* = remaining - n; | |
| 517 | 520 | } |
| 518 | const n = try reader.in.stream(w, limit.min(.limited64(remaining))); | |
| 519 | remaining_content_length.* = remaining - n; | |
| 520 | 521 | return n; |
| 521 | 522 | } |
| 522 | 523 | |
| 523 | 524 | fn contentLengthDiscard(io_r: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize { |
| 524 | 525 | const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); |
| 526 | if (reader.state == .ready) return error.EndOfStream; | |
| 525 | 527 | const remaining_content_length = &reader.state.body_remaining_content_length; |
| 526 | 528 | const remaining = remaining_content_length.*; |
| 527 | if (remaining == 0) { | |
| 529 | const n = try reader.in.discard(limit.min(.limited64(remaining))); | |
| 530 | if (n == remaining) { | |
| 528 | 531 | reader.state = .ready; |
| 529 | return error.EndOfStream; | |
| 532 | } else { | |
| 533 | remaining_content_length.* = remaining - n; | |
| 530 | 534 | } |
| 531 | const n = try reader.in.discard(limit.min(.limited64(remaining))); | |
| 532 | remaining_content_length.* = remaining - n; | |
| 533 | 535 | return n; |
| 534 | 536 | } |
| 535 | 537 |
lib/std/http/test.zig+22| ... | ... | @@ -11,6 +11,28 @@ const expectEqual = std.testing.expectEqual; |
| 11 | 11 | const expectEqualStrings = std.testing.expectEqualStrings; |
| 12 | 12 | const expectError = std.testing.expectError; |
| 13 | 13 | |
| 14 | test "content length reader state update" { | |
| 15 | 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"); | |
| 16 | var reader: http.Reader = .{ | |
| 17 | .in = &in, | |
| 18 | .interface = undefined, | |
| 19 | .state = .ready, | |
| 20 | .max_head_len = 1024, | |
| 21 | }; | |
| 22 | ||
| 23 | _ = try reader.receiveHead(); | |
| 24 | var body: [6]u8 = undefined; | |
| 25 | _ = try reader.bodyReader(&.{}, .none, body.len).readSliceAll(&body); | |
| 26 | try expectEqual(.ready, reader.state); | |
| 27 | _ = try reader.receiveHead(); | |
| 28 | ||
| 29 | in.seek = 0; | |
| 30 | _ = try reader.receiveHead(); | |
| 31 | try reader.bodyReader(&.{}, .none, body.len).discardAll(body.len); | |
| 32 | try expectEqual(.ready, reader.state); | |
| 33 | _ = try reader.receiveHead(); | |
| 34 | } | |
| 35 | ||
| 14 | 36 | test "trailers" { |
| 15 | 37 | if (builtin.cpu.arch.isPowerPC64() and builtin.mode != .Debug) return error.SkipZigTest; // https://github.com/llvm/llvm-project/issues/171879 |
| 16 | 38 | if (builtin.os.tag == .openbsd) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/30806 |