mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(fetch): respect --max-http-header-size for response headers
The --max-http-header-size CLI flag was only being enforced for server-side request headers but not for client-side response headers in fetch(). This adds validation to reject responses with headers larger than the configured limit. Fixes #20488 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
12
src/http.zig
12
src/http.zig
@@ -1420,6 +1420,12 @@ inline fn handleShortRead(
|
||||
}
|
||||
}
|
||||
|
||||
// Check if buffered headers already exceed max allowed size
|
||||
if (this.state.response_message_buffer.list.items.len > max_http_header_size) {
|
||||
this.closeAndFail(error.HeadersOverflow, is_ssl, socket);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setTimeout(socket, 5);
|
||||
}
|
||||
|
||||
@@ -1470,6 +1476,12 @@ pub fn handleOnDataHeaders(
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if headers exceed max allowed size (matches --max-http-header-size CLI flag)
|
||||
if (@as(usize, @intCast(response.bytes_read)) > max_http_header_size) {
|
||||
this.closeAndFail(error.HeadersOverflow, is_ssl, socket);
|
||||
return;
|
||||
}
|
||||
|
||||
// we save the successful parsed response
|
||||
this.state.pending_response = response;
|
||||
|
||||
|
||||
109
test/regression/issue/20488.test.ts
Normal file
109
test/regression/issue/20488.test.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, tempDir } from "harness";
|
||||
|
||||
describe("--max-http-header-size for response headers", () => {
|
||||
test("fetch should fail when response headers exceed limit", async () => {
|
||||
// Use Bun.serve to create a server that returns large headers
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(_req) {
|
||||
// Create response with 18KB header (larger than 16KB limit)
|
||||
return new Response("body", {
|
||||
headers: {
|
||||
"Large-Header": "a".repeat(18 * 1024),
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Create a client script that uses fetch
|
||||
using clientDir = tempDir("max-header-client", {
|
||||
"client.js": `
|
||||
try {
|
||||
const res = await fetch("${server.url}");
|
||||
console.log("SUCCESS");
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
if (error.code === "HeadersOverflow") {
|
||||
console.log("HEADERS_OVERFLOW");
|
||||
process.exit(0);
|
||||
}
|
||||
console.log("ERROR:" + error.message + " code:" + error.code);
|
||||
process.exit(1);
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
// Run the client with --max-http-header-size=16384 (16KB)
|
||||
await using clientProc = Bun.spawn({
|
||||
cmd: [bunExe(), "--max-http-header-size=16384", "client.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(clientDir),
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [clientStdout, clientStderr, clientExitCode] = await Promise.all([
|
||||
clientProc.stdout.text(),
|
||||
clientProc.stderr.text(),
|
||||
clientProc.exited,
|
||||
]);
|
||||
|
||||
// With the fix, the client should fail with a headers overflow error
|
||||
expect(clientStdout.trim()).toBe("HEADERS_OVERFLOW");
|
||||
expect(clientExitCode).toBe(0);
|
||||
});
|
||||
|
||||
test("fetch should succeed when response headers are within limit", async () => {
|
||||
// Use Bun.serve to create a server that returns headers within limit
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(_req) {
|
||||
// Create response with 8KB header (smaller than 16KB limit)
|
||||
return new Response("body", {
|
||||
headers: {
|
||||
"Large-Header": "a".repeat(8 * 1024),
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Create a client script that uses fetch
|
||||
using clientDir = tempDir("max-header-client-ok", {
|
||||
"client.js": `
|
||||
try {
|
||||
const res = await fetch("${server.url}");
|
||||
const headerValue = res.headers.get("large-header");
|
||||
if (headerValue && headerValue.length === 8 * 1024) {
|
||||
console.log("SUCCESS");
|
||||
process.exit(0);
|
||||
}
|
||||
console.log("UNEXPECTED:" + (headerValue ? headerValue.length : "null"));
|
||||
process.exit(1);
|
||||
} catch (error) {
|
||||
console.log("ERROR:" + error.message + " code:" + error.code);
|
||||
process.exit(1);
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
// Run the client with --max-http-header-size=16384 (16KB)
|
||||
await using clientProc = Bun.spawn({
|
||||
cmd: [bunExe(), "--max-http-header-size=16384", "client.js"],
|
||||
env: bunEnv,
|
||||
cwd: String(clientDir),
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [clientStdout, clientStderr, clientExitCode] = await Promise.all([
|
||||
clientProc.stdout.text(),
|
||||
clientProc.stderr.text(),
|
||||
clientProc.exited,
|
||||
]);
|
||||
|
||||
// The client should succeed because headers are within limit
|
||||
expect(clientStdout.trim()).toBe("SUCCESS");
|
||||
expect(clientExitCode).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user