@@ -27,31 +27,38 @@ export class WebIncomingMessage extends IncomingMessage {
2727 // `rawHeaders` is the flat `[name, value, name, value, …]` list Node exposes.
2828 // Web `Headers` lower-cases names and has no wire order, so this is a
2929 // best-effort reconstruction; `set-cookie` is expanded to one entry each.
30+ //
31+ // The map is built locally and assigned once rather than mutated in place:
32+ // Node materializes `headers` lazily from `rawHeaders` behind a prototype
33+ // accessor, while Bun's `IncomingMessage` leaves it `undefined` until
34+ // something assigns it, so there is no object to mutate there at all.
35+ const headers : Record < string , string | string [ ] > = { } ;
3036 const rawHeaders = this . rawHeaders ;
3137 for ( const [ key , value ] of req . headers . entries ( ) ) {
3238 const lowerKey = key . toLowerCase ( ) ;
3339 if ( lowerKey === "set-cookie" ) {
3440 continue ;
3541 }
36- this . headers [ lowerKey ] = value ;
42+ headers [ lowerKey ] = value ;
3743 rawHeaders . push ( key , value ) ;
3844 }
3945 const setCookie = req . headers . getSetCookie ?.( ) ?? [ ] ;
4046 if ( setCookie . length > 0 ) {
4147 // Node keeps `set-cookie` as an array on `headers` and one raw entry each.
42- ( this . headers as Record < string , string | string [ ] > ) [ "set-cookie" ] = setCookie ;
48+ headers [ "set-cookie" ] = setCookie ;
4349 for ( const cookie of setCookie ) {
4450 rawHeaders . push ( "set-cookie" , cookie ) ;
4551 }
4652 }
4753 if (
4854 req . method !== "GET" &&
4955 req . method !== "HEAD" &&
50- ! this . headers [ "content-length" ] &&
51- ! this . headers [ "transfer-encoding" ]
56+ ! headers [ "content-length" ] &&
57+ ! headers [ "transfer-encoding" ]
5258 ) {
53- this . headers [ "transfer-encoding" ] = "chunked" ;
59+ headers [ "transfer-encoding" ] = "chunked" ;
5460 }
61+ this . headers = headers ;
5562
5663 const onData = ( chunk : any ) => {
5764 // Honor backpressure: if the readable buffer is full, pause the source
0 commit comments