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