@@ -59,14 +59,17 @@ import {
5959import {
6060 buildRawSocketEventData ,
6161 buildTlsSocketEventData ,
62+ isSocketLoop ,
63+ resetOrDestroy
64+ } from "../util/socket-util" ;
65+ import {
6266 ClientErrorInProgress ,
6367 LastHopEncrypted ,
6468 LastTunnelAddress ,
6569 TlsSetupCompleted ,
66- isSocketLoop ,
67- resetOrDestroy ,
68- getSocketMetadataTags
69- } from "../util/socket-util" ;
70+ SocketMetadata
71+ } from '../util/socket-extensions' ;
72+ import { getSocketMetadataTags , getSocketMetadataFromProxyAuth } from '../util/socket-metadata'
7073import {
7174 parseRequestBody ,
7275 waitForCompletedRequest ,
@@ -93,6 +96,7 @@ type ExtendedRawRequest = (http.IncomingMessage | http2.Http2ServerRequest) & {
9396 body ?: OngoingBody ;
9497 path ?: string ;
9598 destination ?: Destination ;
99+ [ SocketMetadata ] ?: SocketMetadata ;
96100} ;
97101
98102const serverPortCheckMutex = new Mutex ( ) ;
@@ -600,10 +604,13 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
600604 private preprocessRequest ( req : ExtendedRawRequest , type : 'request' | 'websocket' ) : OngoingRequest {
601605 parseRequestBody ( req , { maxSize : this . maxBodySize } ) ;
602606
607+ let rawHeaders = pairFlatRawHeaders ( req . rawHeaders ) ;
608+ let socketMetadata : SocketMetadata | undefined = req . socket [ SocketMetadata ] ;
609+
603610 // Make req.url always absolute, if it isn't already, using the host header.
604611 // It might not be if this is a direct request, or if it's being transparently proxied.
605612 if ( ! isAbsoluteUrl ( req . url ! ) ) {
606- req . protocol = req . headers [ ':scheme' ] as string ||
613+ req . protocol = getHeaderValue ( rawHeaders , ':scheme' ) ||
607614 ( req . socket [ LastHopEncrypted ] ? 'https' : 'http' ) ;
608615 req . path = req . url ;
609616
@@ -617,8 +624,8 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
617624 // If you explicitly tunnel to a hostname, that's the URL's hostname:
618625 const hostname = tunnelUrlHost
619626 // Otherwise, we infer based on headers: HTTP/2 or HTTP/1
620- ?? getHeaderValue ( req . headers , ':authority' )
621- ?? getHeaderValue ( req . headers , 'host' )
627+ ?? getHeaderValue ( rawHeaders , ':authority' )
628+ ?? getHeaderValue ( rawHeaders , 'host' )
622629 ?? req . socket [ LastTunnelAddress ] // Iff we have no hostname available at all
623630 ?? `localhost:${ this . port } ` ; // If you specify literally nothing, it's a direct request
624631
@@ -637,7 +644,7 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
637644
638645 const absoluteUrl = `${ req . protocol } ://${ host } ${ req . path } ` ;
639646
640- if ( ! req . headers [ ':path' ] ) {
647+ if ( ! getHeaderValue ( rawHeaders , ':path' ) ) {
641648 ( req as Mutable < ExtendedRawRequest > ) . url = new url . URL ( absoluteUrl ) . toString ( ) ;
642649 } else {
643650 // Node's HTTP/2 compat logic maps .url to headers[':path']. We want them to
@@ -648,12 +655,27 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
648655 } ) ;
649656 }
650657 } else {
658+ // We have an absolute request. This is effectively a combined tunnel + end-server request,
659+ // so we need to handle both of those, and hide the proxy-specific bits from later logic.
651660 req . protocol = req . url ! . split ( '://' , 1 ) [ 0 ] ;
652661 req . path = getPathFromAbsoluteUrl ( req . url ! ) ;
653662 req . destination = getDestination (
654663 req . protocol ,
655664 req . socket [ LastTunnelAddress ] ?? getHostFromAbsoluteUrl ( req . url ! )
656665 ) ;
666+
667+ const proxyAuthHeader = getHeaderValue ( rawHeaders , 'proxy-authorization' ) ;
668+ if ( proxyAuthHeader ) {
669+ // Use this metadata for this request, but _only_ this request - it's not relevant
670+ // to other requests on the same socket so we don't add it to req.socket.
671+ socketMetadata = getSocketMetadataFromProxyAuth ( req . socket , proxyAuthHeader ) ;
672+ }
673+
674+ rawHeaders = rawHeaders . filter ( ( [ key ] ) => {
675+ const lcKey = key . toLowerCase ( ) ;
676+ return lcKey !== 'proxy-connection' &&
677+ lcKey !== 'proxy-authorization' ;
678+ } )
657679 }
658680
659681 if ( type === 'websocket' ) {
@@ -668,7 +690,8 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
668690 }
669691
670692 const id = uuid ( ) ;
671- const tags : string [ ] = getSocketMetadataTags ( req . socket ) ;
693+
694+ const tags : string [ ] = getSocketMetadataTags ( socketMetadata ) ;
672695
673696 const timingEvents : TimingEvents = {
674697 startTime : Date . now ( ) ,
@@ -679,7 +702,6 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
679702 timingEvents . bodyReceivedTimestamp ||= now ( ) ;
680703 } ) ;
681704
682- const rawHeaders = pairFlatRawHeaders ( req . rawHeaders ) ;
683705 const headers = rawHeadersToObject ( rawHeaders ) ;
684706
685707 // Not writable for HTTP/2:
@@ -1027,7 +1049,7 @@ ${await this.suggestRule(request)}`
10271049 id : uuid ( ) ,
10281050 tags : [
10291051 `client-error:${ error . code || 'UNKNOWN' } ` ,
1030- ...getSocketMetadataTags ( socket )
1052+ ...getSocketMetadataTags ( socket [ SocketMetadata ] )
10311053 ] ,
10321054 timingEvents : { startTime : Date . now ( ) , startTimestamp : now ( ) } as TimingEvents
10331055 } ;
@@ -1120,7 +1142,7 @@ ${await this.suggestRule(request)}`
11201142 tags : [
11211143 `client-error:${ error . code || 'UNKNOWN' } ` ,
11221144 ...( isBadPreface ? [ 'client-error:bad-preface' ] : [ ] ) ,
1123- ...getSocketMetadataTags ( socket )
1145+ ...getSocketMetadataTags ( socket ?. [ SocketMetadata ] )
11241146 ] ,
11251147 httpVersion : '2' ,
11261148
0 commit comments