@@ -69,7 +69,7 @@ export function XHeaders(req: Request, _res: Response, options: ServerOptions) {
6969 ( req . headers [ "x-forwarded-" + header ] || "" ) + ( req . headers [ "x-forwarded-" + header ] ? "," : "" ) + values [ header ] ;
7070 }
7171
72- req . headers [ "x-forwarded-host" ] = req . headers [ "x-forwarded-host" ] || req . headers [ "host" ] || "" ;
72+ req . headers [ "x-forwarded-host" ] = req . headers [ "x-forwarded-host" ] || req . headers [ "host" ] || req . headers [ ":authority" ] || "" ;
7373}
7474
7575// Does the actual proxying. If `forward` is enabled fires up
@@ -208,6 +208,12 @@ async function stream2(
208208) {
209209 // Helper function to handle errors consistently throughout the fetch path
210210 const handleError = ( err : Error , target ?: ProxyTargetUrl ) => {
211+ const e = err as any ;
212+ // Copy code from cause if available and missing on err
213+ if ( e . code === undefined && e . cause ?. code ) {
214+ e . code = e . cause . code ;
215+ }
216+
211217 if ( cb ) {
212218 cb ( err , req , res , target ) ;
213219 } else {
@@ -229,9 +235,27 @@ async function stream2(
229235 const customFetch = options . fetch || fetch ;
230236 const fetchOptions = options . fetchOptions ?? { } as FetchOptions ;
231237
238+ const controller = new AbortController ( ) ;
239+ const { signal } = controller ;
240+
241+ if ( options . proxyTimeout ) {
242+ setTimeout ( ( ) => {
243+ controller . abort ( ) ;
244+ } , options . proxyTimeout ) ;
245+ }
246+
247+ // Ensure we abort proxy if request is aborted
248+ res . on ( "close" , ( ) => {
249+ const aborted = ! res . writableFinished ;
250+ if ( aborted ) {
251+ controller . abort ( ) ;
252+ }
253+ } ) ;
254+
232255 const prepareRequest = ( outgoing : common . Outgoing ) => {
233256 const requestOptions : RequestInit = {
234257 method : outgoing . method ,
258+ signal,
235259 ...fetchOptions . requestOptions ,
236260 } ;
237261
@@ -294,6 +318,16 @@ async function stream2(
294318 }
295319 }
296320 } catch ( err ) {
321+ if ( ( err as Error ) . name === "AbortError" ) {
322+ // Handle aborts (timeout or client disconnect)
323+ if ( options . proxyTimeout && signal . aborted ) {
324+ const proxyTimeoutErr = new Error ( "Proxy timeout" ) ;
325+ ( proxyTimeoutErr as any ) . code = "ECONNRESET" ;
326+ handleError ( proxyTimeoutErr , options . forward ) ;
327+ }
328+ // If aborted by client (res.close), we might not want to emit an error or maybe just log it
329+ return ;
330+ }
297331 handleError ( err as Error , options . forward ) ;
298332 }
299333
@@ -385,6 +419,14 @@ async function stream2(
385419 server ?. emit ( "end" , req , res , fakeProxyRes ) ;
386420 }
387421 } catch ( err ) {
422+ if ( ( err as Error ) . name === "AbortError" ) {
423+ if ( options . proxyTimeout && signal . aborted ) {
424+ const proxyTimeoutErr = new Error ( "Proxy timeout" ) ;
425+ ( proxyTimeoutErr as any ) . code = "ECONNRESET" ;
426+ handleError ( proxyTimeoutErr , options . target ) ;
427+ }
428+ return ;
429+ }
388430 handleError ( err as Error , options . target ) ;
389431 }
390432}
0 commit comments