@@ -33,10 +33,11 @@ import {
3333 TlsMetadata ,
3434 TlsSetupCompleted ,
3535 getAddressAndPort ,
36- resetOrDestroy
36+ resetOrDestroy ,
37+ SocketMetadata
3738} from '../util/socket-util' ;
3839import { MockttpHttpsOptions } from '../mockttp' ;
39- import { buildSocksServer , SocksTcpAddress } from './socks-server' ;
40+ import { buildSocksServer , SocksServerOptions , SocksTcpAddress } from './socks-server' ;
4041
4142// Hardcore monkey-patching: force TLSSocket to link servername & remoteAddress to
4243// sockets as soon as they're available, without waiting for the handshake to fully
@@ -145,7 +146,7 @@ export interface ComboServerOptions {
145146 debug : boolean ;
146147 https : MockttpHttpsOptions | undefined ;
147148 http2 : boolean | 'fallback' ;
148- socks : boolean ;
149+ socks : boolean | SocksServerOptions ;
149150 passthroughUnknownProtocols : boolean ;
150151
151152 requestListener : ( req : http . IncomingMessage , res : http . ServerResponse ) => void ;
@@ -225,7 +226,7 @@ export async function createComboServer(options: ComboServerOptions): Promise<De
225226 }
226227
227228 if ( options . socks ) {
228- socksServer = buildSocksServer ( ) ;
229+ socksServer = buildSocksServer ( options . socks === true ? { } : options . socks ) ;
229230 socksServer . on ( 'socks-tcp-connect' , ( socket : net . Socket , address : SocksTcpAddress ) => {
230231 const addressString =
231232 address . type === 'ipv4'
@@ -291,8 +292,7 @@ export async function createComboServer(options: ComboServerOptions): Promise<De
291292 if ( parentSocket ) {
292293 // Sometimes wrapper TLS sockets created by the HTTP/2 server don't include the
293294 // underlying socket details, so it's better to make sure we copy them up.
294- copyAddressDetails ( parentSocket , socket ) ;
295- copyTimingDetails ( parentSocket , socket ) ;
295+ inheritSocketDetails ( parentSocket , socket ) ;
296296 // With TLS metadata, we only propagate directly from parent sockets, not through
297297 // CONNECT etc - we only want it if the final hop is TLS, previous values don't matter.
298298 socket [ TlsMetadata ] ??= parentSocket [ TlsMetadata ] ;
@@ -371,8 +371,7 @@ export async function createComboServer(options: ComboServerOptions): Promise<De
371371
372372 // Send a 200 OK response, and start the tunnel:
373373 res . writeHead ( 200 , { } ) ;
374- copyAddressDetails ( res . socket , res . stream ) ;
375- copyTimingDetails ( res . socket , res . stream ) ;
374+ inheritSocketDetails ( res . socket , res . stream ) ;
376375 res . stream [ LastTunnelAddress ] = connectUrl ;
377376
378377 // When layering HTTP/2 on JS streams, we have to make sure the JS stream won't autoclose
@@ -390,39 +389,37 @@ export async function createComboServer(options: ComboServerOptions): Promise<De
390389}
391390
392391
393- const SOCKET_ADDRESS_METADATA_FIELDS = [
392+ const SOCKET_METADATA = [
394393 'localAddress' ,
395394 'localPort' ,
396395 'remoteAddress' ,
397396 'remotePort' ,
397+ SocketTimingInfo ,
398+ SocketMetadata ,
398399 LastTunnelAddress
399400] as const ;
400401
401- // Update the target socket(-ish) with the address details from the source socket,
402- // iff the target has no details of its own.
403- function copyAddressDetails (
404- source : SocketIsh < typeof SOCKET_ADDRESS_METADATA_FIELDS [ number ] > ,
405- target : SocketIsh < typeof SOCKET_ADDRESS_METADATA_FIELDS [ number ] >
402+ function inheritSocketDetails (
403+ source : SocketIsh < typeof SOCKET_METADATA [ number ] > ,
404+ target : SocketIsh < typeof SOCKET_METADATA [ number ] >
406405) {
406+ // Update the target socket(-ish) with the assorted metadata from the source socket,
407+ // iff the target has no details of its own.
408+
409+ // Make sure all properties are writable - HTTP/2 streams notably try to block this.
407410 Object . defineProperties ( target , _ . zipObject (
408- SOCKET_ADDRESS_METADATA_FIELDS ,
409- _ . range ( SOCKET_ADDRESS_METADATA_FIELDS . length ) . map ( ( ) => ( { writable : true } ) )
411+ SOCKET_METADATA ,
412+ _ . range ( SOCKET_METADATA . length ) . map ( ( ) => ( { writable : true } ) )
410413 ) as PropertyDescriptorMap ) ;
411414
412- SOCKET_ADDRESS_METADATA_FIELDS . forEach ( ( fieldName ) => {
415+ for ( let fieldName of SOCKET_METADATA ) {
413416 if ( target [ fieldName ] === undefined ) {
414- ( target as any ) [ fieldName ] = source [ fieldName ] ;
417+ if ( typeof source [ fieldName ] === 'object' ) {
418+ ( target as any ) [ fieldName ] = _ . cloneDeep ( source [ fieldName ] ) ;
419+ } else {
420+ ( target as any ) [ fieldName ] = source [ fieldName ] ;
421+ }
415422 }
416- } ) ;
417- }
418-
419- function copyTimingDetails < T extends SocketIsh < typeof SocketTimingInfo > > (
420- source : SocketIsh < typeof SocketTimingInfo > ,
421- target : T
422- ) : asserts target is T & { [ SocketTimingInfo ] : Required < net . Socket > [ typeof SocketTimingInfo ] } {
423- if ( ! target [ SocketTimingInfo ] ) {
424- // Clone timing info, don't copy it - child sockets get their own independent timing stats
425- target [ SocketTimingInfo ] = Object . assign ( { } , source [ SocketTimingInfo ] ) ;
426423 }
427424}
428425
0 commit comments