@@ -108,7 +108,11 @@ class HeldRequest {
108108 private releaseHeld : ( ( ) => void ) | undefined
109109 private released = false
110110
111- constructor ( readonly matches : ( method : string , path : string ) => boolean ) {
111+ constructor (
112+ readonly matches : ( method : string , path : string ) => boolean ,
113+ /** Deliver the request to Sim on release even if its client gave up, as a late request would arrive. */
114+ readonly deliverIfAbandoned = false
115+ ) {
112116 this . reached = new Promise ( ( resolve ) => {
113117 this . reach = resolve
114118 } )
@@ -214,8 +218,11 @@ export class SimProxy {
214218 }
215219
216220 /** Holds the next request that matches until the returned handle releases it. */
217- hold ( matches : ( method : string , path : string ) => boolean ) : HeldRequest {
218- const held = new HeldRequest ( matches )
221+ hold (
222+ matches : ( method : string , path : string ) => boolean ,
223+ options : { deliverIfAbandoned ?: boolean } = { }
224+ ) : HeldRequest {
225+ const held = new HeldRequest ( matches , options . deliverIfAbandoned )
219226 this . holds . push ( held )
220227 return held
221228 }
@@ -258,7 +265,7 @@ export class SimProxy {
258265 const held = this . holds . find ( ( candidate ) => candidate . matches ( method , url . pathname ) )
259266 if ( held ) {
260267 this . holds = this . holds . filter ( ( candidate ) => candidate !== held )
261- if ( ! ( await held . hold ( entry , response ) ) ) return
268+ if ( ! ( await held . hold ( entry , response ) ) && ! held . deliverIfAbandoned ) return
262269 }
263270 if ( this . chatBodyRewrite && method === 'POST' && url . pathname === '/api/mothership/chat' ) {
264271 const parsed : Record < string , unknown > = JSON . parse ( body . toString ( 'utf8' ) )
@@ -271,14 +278,20 @@ export class SimProxy {
271278 const { 'transfer-encoding' : _chunked , ...forwarded } = request . headers
272279 const headers : IncomingHttpHeaders = { ...forwarded , 'content-length' : String ( body . length ) }
273280 await new Promise < void > ( ( resolve , reject ) => {
281+ const clientGone = response . destroyed
274282 const upstream = httpRequest ( target , { method, headers } , ( upstreamResponse ) => {
275283 entry . status = upstreamResponse . statusCode
276- response . writeHead ( upstreamResponse . statusCode ?? 502 , upstreamResponse . headers )
277- upstreamResponse . pipe ( response )
278284 upstreamResponse . on ( 'end' , resolve )
279285 upstreamResponse . on ( 'error' , reject )
286+ // A request delivered after its client gave up is answered to no one.
287+ if ( clientGone ) {
288+ upstreamResponse . resume ( )
289+ return
290+ }
291+ response . writeHead ( upstreamResponse . statusCode ?? 502 , upstreamResponse . headers )
292+ upstreamResponse . pipe ( response )
280293 } )
281- response . on ( 'close' , ( ) => upstream . destroy ( ) )
294+ if ( ! clientGone ) response . on ( 'close' , ( ) => upstream . destroy ( ) )
282295 upstream . on ( 'error' , reject )
283296 upstream . end ( body )
284297 } )
0 commit comments