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