Skip to content

Commit 3670a49

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 3670a49

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-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: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { test, expect } from "bun:test";
2+
import { bunEnv, bunExe, tempDir, normalizeBunSnapshot } from "harness";
3+
4+
test("issue #22353 - server handles oversized requests correctly", async () => {
5+
using dir = tempDir("oversized-request", {
6+
"server.ts": `
7+
const server = Bun.serve({
8+
port: 0,
9+
maxRequestBodySize: 1024, // 1KB limit
10+
async fetch(req) {
11+
console.log("Processing request");
12+
const body = await req.text();
13+
return new Response(JSON.stringify({
14+
received: true,
15+
size: body.length
16+
}), {
17+
headers: { "Content-Type": "application/json" }
18+
});
19+
}
20+
});
21+
22+
console.log(JSON.stringify({ port: server.port }));
23+
console.error("Server started");
24+
`,
25+
});
26+
27+
// Start server
28+
await using proc = Bun.spawn({
29+
cmd: [bunExe(), "server.ts"],
30+
env: bunEnv,
31+
cwd: String(dir),
32+
stdout: "pipe",
33+
stderr: "pipe",
34+
});
35+
36+
// Get port from server output
37+
const reader = proc.stdout.getReader();
38+
const { value } = await reader.read();
39+
const decoder = new TextDecoder();
40+
const portLine = decoder.decode(value);
41+
const portJson = portLine.split('\n')[0]; // Get just the first line with the port
42+
const { port } = JSON.parse(portJson.trim());
43+
reader.releaseLock();
44+
45+
expect(port).toBeGreaterThan(0);
46+
47+
// Send request larger than limit (2KB)
48+
const largeBody = "x".repeat(2048);
49+
50+
let largeResponse;
51+
try {
52+
largeResponse = await fetch(`http://localhost:${port}`, {
53+
method: "POST",
54+
body: largeBody,
55+
});
56+
} catch (e) {
57+
// Server may reject with 413 or close connection
58+
}
59+
60+
// Small delay to ensure server processes the rejection
61+
await Bun.sleep(100);
62+
63+
// Send normal request - server should still be working
64+
const normalResponse = await fetch(`http://localhost:${port}`, {
65+
method: "POST",
66+
body: JSON.stringify({ test: "normal" }),
67+
});
68+
69+
expect(normalResponse.ok).toBe(true);
70+
const json = await normalResponse.json();
71+
expect(json.received).toBe(true);
72+
expect(json.size).toBe(17);
73+
74+
// Kill server gracefully
75+
proc.kill();
76+
77+
// Collect remaining output
78+
let remainingStdout = "";
79+
let remainingStderr = "";
80+
81+
try {
82+
const stdoutReader = proc.stdout.getReader();
83+
while (true) {
84+
const { done, value } = await stdoutReader.read();
85+
if (done) break;
86+
remainingStdout += decoder.decode(value);
87+
}
88+
stdoutReader.releaseLock();
89+
} catch {}
90+
91+
try {
92+
const stderrReader = proc.stderr.getReader();
93+
while (true) {
94+
const { done, value } = await stderrReader.read();
95+
if (done) break;
96+
remainingStderr += decoder.decode(value);
97+
}
98+
stderrReader.releaseLock();
99+
} catch {}
100+
101+
const exitCode = await proc.exited;
102+
103+
// Server should exit cleanly with SIGTERM
104+
expect(exitCode).toBe(143); // 128 + 15 (SIGTERM)
105+
106+
// Verify stdout contains expected output
107+
expect(remainingStdout).toContain("Processing request");
108+
109+
// Verify stderr
110+
expect(normalizeBunSnapshot(remainingStderr, dir)).toMatchInlineSnapshot(`"Server started"`);
111+
}, 10000);

0 commit comments

Comments
 (0)