@@ -69,11 +69,61 @@ function createFakeHttpClientRequest(
6969 getHeader ( name : string ) {
7070 return headers [ name ] ;
7171 } ,
72+ setHeader ( name : string , value : string ) {
73+ headers [ name ] = value ;
74+ } ,
75+ _implicitHeader ( ) {
76+ // Even some internal/non-public properties like this are required by http-proxy-agent:
77+ // https://github.com/TooTallNate/proxy-agents/blob/5555794b6d9e4b0a36fac80a2d3acea876a8f7dc/packages/http-proxy-agent/src/index.ts#L36
78+ } ,
7279 overrideProtocol,
7380 }
7481 ) ;
7582}
7683
84+ export async function connectThroughAgent ( {
85+ dstAddr,
86+ dstPort,
87+ agent,
88+ overrideProtocol,
89+ } : {
90+ dstAddr : string ;
91+ dstPort : number ;
92+ agent : AgentWithInitialize ;
93+ overrideProtocol ?: string | undefined ;
94+ } ) : Promise < Duplex > {
95+ const channel = await new Promise < Duplex | undefined > ( ( resolve , reject ) => {
96+ const req = createFakeHttpClientRequest ( dstAddr , dstPort , overrideProtocol ) ;
97+ req . onSocket = ( sock ) => {
98+ if ( sock ) resolve ( sock ) ;
99+ } ;
100+ agent . createSocket (
101+ req ,
102+ {
103+ host : dstAddr ,
104+ port : dstPort ,
105+ } ,
106+ ( err , sock ) => {
107+ // Ideally, we would always be using this callback for retrieving the `sock`
108+ // instance. However, agent-base does not call the callback at all if
109+ // the agent resolved to another agent (as is the case for e.g. `ProxyAgent`).
110+ if ( err ) reject ( err ) ;
111+ else if ( sock ) resolve ( sock ) ;
112+ else
113+ reject (
114+ new Error (
115+ 'Received neither error object nor socket from agent.createSocket()'
116+ )
117+ ) ;
118+ }
119+ ) ;
120+ } ) ;
121+
122+ if ( ! channel )
123+ throw new Error ( `Could not create channel to ${ dstAddr } :${ dstPort } ` ) ;
124+ return channel ;
125+ }
126+
77127// The original version of this code was largely taken from
78128// https://github.com/mongodb-js/compass/tree/55a5a608713d7316d158dc66febeb6b114d8b40d/packages/ssh-tunnel/src
79129class Socks5Server extends EventEmitter implements Tunnel {
@@ -237,40 +287,12 @@ class Socks5Server extends EventEmitter implements Tunnel {
237287 }
238288
239289 private async forwardOut ( dstAddr : string , dstPort : number ) : Promise < Duplex > {
240- const channel = await new Promise < Duplex > ( ( resolve , reject ) => {
241- const req = createFakeHttpClientRequest (
242- dstAddr ,
243- dstPort ,
244- this . overrideProtocol
245- ) ;
246- req . onSocket = ( sock ) => {
247- if ( sock ) resolve ( sock ) ;
248- } ;
249- this . agent . createSocket (
250- req ,
251- {
252- host : dstAddr ,
253- port : dstPort ,
254- } ,
255- ( err , sock ) => {
256- // Ideally, we would always be using this callback for retrieving the `sock`
257- // instance. However, agent-base does not call the callback at all if
258- // the agent resolved to another agent (as is the case for e.g. `ProxyAgent`).
259- if ( err ) reject ( err ) ;
260- else if ( sock ) resolve ( sock ) ;
261- else
262- reject (
263- new Error (
264- 'Received neither error object nor socket from agent.createSocket()'
265- )
266- ) ;
267- }
268- ) ;
290+ return await connectThroughAgent ( {
291+ dstAddr,
292+ dstPort,
293+ agent : this . agent ,
294+ overrideProtocol : this . overrideProtocol ,
269295 } ) ;
270-
271- if ( ! channel )
272- throw new Error ( `Could not create channel to ${ dstAddr } :${ dstPort } ` ) ;
273- return channel ;
274296 }
275297
276298 private async socks5Request (
@@ -309,9 +331,13 @@ class Socks5Server extends EventEmitter implements Tunnel {
309331 socket . on ( 'error' , forwardingErrorHandler ) ;
310332
311333 socket . once ( 'close' , ( ) => {
334+ if ( ! channel ?. destroyed ) channel . destroy ( ) ;
312335 this . logger . emit ( 'socks5:forwarded-socket-closed' , { ...logMetadata } ) ;
313336 this . connections . delete ( socket as Socket ) ;
314337 } ) ;
338+ channel . once ( 'close' , ( ) => {
339+ if ( ! socket ?. destroyed ) socket ?. destroy ( ) ;
340+ } ) ;
315341
316342 socket . pipe ( channel ) . pipe ( socket ) ;
317343 } catch ( err ) {
@@ -370,7 +396,7 @@ export function createSocks5Tunnel(
370396 return new ExistingTunnel ( socks5OnlyProxyOptions ) ;
371397 }
372398
373- const agent = useOrCreateAgent ( proxyOptions , target ) ;
399+ const agent = useOrCreateAgent ( proxyOptions , target , true ) ;
374400 if ( ! agent ) return undefined ;
375401
376402 let generateCredentials = false ;
0 commit comments