mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
## Summary Fixes #18413 - Empty chunked gzip responses were causing `Decompression error: ShortRead` ## The Issue When a server sends an empty response with `Content-Encoding: gzip` and `Transfer-Encoding: chunked`, Bun was throwing a `ShortRead` error. This occurred because the code was checking if `avail_in == 0` (no input data) and immediately returning an error, without attempting to decompress what could be a valid empty gzip stream. ## The Fix Instead of checking `avail_in == 0` before calling `inflate()`, we now: 1. Always call `inflate()` even when `avail_in == 0` 2. Check the return code from `inflate()` 3. If it returns `BufError` with `avail_in == 0`, then we truly need more data and return `ShortRead` 4. If it returns `StreamEnd`, it was a valid empty gzip stream and we finish successfully This approach correctly distinguishes between "no data yet" and "valid empty gzip stream". ## Why This Works - A valid empty gzip stream still has headers and trailers (~20 bytes) - The zlib `inflate()` function can handle empty streams correctly - `BufError` with `avail_in == 0` specifically means "need more input data" ## Test Plan ✅ Added regression test in `test/regression/issue/18413.test.ts` covering: - Empty chunked gzip response - Empty non-chunked gzip response - Empty chunked response without gzip ✅ Verified all existing gzip-related tests still pass ✅ Tested with the original failing case from the issue 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
35 lines
1010 B
Zig
35 lines
1010 B
Zig
fn initMutableString(allocator: std.mem.Allocator) anyerror!MutableString {
|
|
return MutableString.initEmpty(allocator);
|
|
}
|
|
|
|
const BufferPool = bun.ObjectPool(MutableString, initMutableString, false, 4);
|
|
pub fn get(allocator: std.mem.Allocator) *MutableString {
|
|
return &BufferPool.get(allocator).data;
|
|
}
|
|
|
|
pub fn put(mutable: *MutableString) void {
|
|
mutable.reset();
|
|
var node: BufferPool.Node = @fieldParentPtr("data", mutable);
|
|
node.release();
|
|
}
|
|
|
|
pub fn decompress(compressed_data: []const u8, output: *MutableString, allocator: std.mem.Allocator) Zlib.ZlibError!void {
|
|
var reader = try Zlib.ZlibReaderArrayList.initWithOptionsAndListAllocator(
|
|
compressed_data,
|
|
&output.list,
|
|
output.allocator,
|
|
allocator,
|
|
.{
|
|
.windowBits = 15 + 32,
|
|
},
|
|
);
|
|
try reader.readAll(true);
|
|
reader.deinit();
|
|
}
|
|
|
|
const Zlib = @import("../zlib.zig");
|
|
const std = @import("std");
|
|
|
|
const bun = @import("bun");
|
|
const MutableString = bun.MutableString;
|