Skip to content

Commit 7a94b5a

Browse files
Claude Botclaude
andcommitted
Fix server stability issue with oversized requests
Improves request handling for edge cases. Fixes #22353 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 6bafe26 commit 7a94b5a

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

src/bun.js/api/server.zig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2221,9 +2221,11 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d
22212221
};
22222222

22232223
if (req_len > this.config.max_request_body_size) {
2224+
// Set up abort handler before ending the response to ensure proper cleanup
2225+
ctx.setAbortHandler();
22242226
resp.writeStatus("413 Request Entity Too Large");
22252227
resp.endWithoutBody(true);
2226-
this.finalize();
2228+
// Don't call finalize() here - let the abort handler clean up
22272229
return null;
22282230
}
22292231

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { test, expect } from "bun:test";
2+
import { bunEnv, bunExe, tempDir } from "harness";
3+
4+
test("issue #22353 - server should handle oversized request without crashing", async () => {
5+
// This test reproduces a crash that occurs in debug builds when the server
6+
// handles a request after rejecting an oversized request
7+
8+
using dir = tempDir("oversized-request", {
9+
"server.ts": `
10+
const server = Bun.serve({
11+
port: 0,
12+
maxRequestBodySize: 1024, // 1KB limit
13+
async fetch(req) {
14+
const body = await req.text();
15+
return new Response(JSON.stringify({
16+
received: true,
17+
size: body.length
18+
}), {
19+
headers: { "Content-Type": "application/json" }
20+
});
21+
}
22+
});
23+
24+
console.log(JSON.stringify({ port: server.port }));
25+
26+
// Process stays alive for tests
27+
setTimeout(() => {}, 10000);
28+
`,
29+
});
30+
31+
// Start server
32+
const proc = Bun.spawn({
33+
cmd: [bunExe(), "server.ts"],
34+
env: bunEnv,
35+
cwd: String(dir),
36+
stdout: "pipe",
37+
stderr: "pipe",
38+
});
39+
40+
// Read port from initial output
41+
const reader = proc.stdout.getReader();
42+
const { value } = await reader.read();
43+
const decoder = new TextDecoder();
44+
const portLine = decoder.decode(value);
45+
const { port } = JSON.parse(portLine.trim());
46+
reader.releaseLock();
47+
48+
expect(port).toBeGreaterThan(0);
49+
50+
// Send request larger than limit (2KB) - this triggers the 413 error
51+
const largeBody = "x".repeat(2048);
52+
53+
const largeResponse = await fetch(`http://localhost:${port}`, {
54+
method: "POST",
55+
body: largeBody,
56+
});
57+
58+
// Server should reject with 413
59+
expect(largeResponse.status).toBe(413);
60+
61+
// Important: await the response body to ensure the request is fully processed
62+
await largeResponse.text();
63+
64+
// Send normal request - on buggy version this causes crash/ECONNRESET in debug builds
65+
const normalResponse = await fetch(`http://localhost:${port}`, {
66+
method: "POST",
67+
body: JSON.stringify({ test: "normal" }),
68+
});
69+
70+
expect(normalResponse.ok).toBe(true);
71+
const json = await normalResponse.json();
72+
expect(json.received).toBe(true);
73+
expect(json.size).toBe(17);
74+
75+
// Clean up - kill and wait for process
76+
proc.kill();
77+
const exitCode = await proc.exited;
78+
79+
// Collect any stderr output
80+
let stderrOutput = "";
81+
try {
82+
const stderrReader = proc.stderr.getReader();
83+
while (true) {
84+
const { done, value } = await stderrReader.read();
85+
if (done) break;
86+
stderrOutput += decoder.decode(value);
87+
}
88+
stderrReader.releaseLock();
89+
} catch {}
90+
91+
// Should not have panic message
92+
expect(stderrOutput).not.toContain("panic");
93+
expect(stderrOutput).not.toContain("reached unreachable code");
94+
}, 10000);

0 commit comments

Comments
 (0)