1- import { SocksClient , SocksProxy , SocksClientOptions } from 'socks' ;
1+ import {
2+ SocksClient ,
3+ SocksProxy ,
4+ SocksClientOptions ,
5+ SocksClientChainOptions ,
6+ } from 'socks' ;
27import { Agent , AgentConnectOpts } from 'agent-base' ;
38import createDebug from 'debug' ;
49import * as dns from 'dns' ;
@@ -87,21 +92,38 @@ export class SocksProxyAgent extends Agent {
8792 'socks5h' ,
8893 ] as const ;
8994
90- readonly shouldLookup : boolean ;
91- readonly proxy : SocksProxy ;
95+ readonly shouldLookup ! : boolean ;
96+ readonly proxies : SocksProxy [ ] ;
9297 timeout : number | null ;
9398
94- constructor ( uri : string | URL , opts ?: SocksProxyAgentOptions ) {
99+ constructor (
100+ uri : string | URL | string [ ] | URL [ ] ,
101+ opts ?: SocksProxyAgentOptions
102+ ) {
95103 super ( opts ) ;
96104
97- const url = typeof uri === 'string' ? new URL ( uri ) : uri ;
98- const { proxy, lookup } = parseSocksURL ( url ) ;
105+ const uriList = Array . isArray ( uri ) ? uri : [ uri ] ;
106+
107+ if ( uriList . length === 0 ) {
108+ throw new Error ( 'At least one proxy server URI must be specified.' ) ;
109+ }
110+
111+ this . proxies = [ ] ;
112+ for ( const [ i , uri ] of uriList . entries ( ) ) {
113+ const { proxy, lookup } = parseSocksURL ( new URL ( uri . toString ( ) ) ) ;
114+ this . proxies . push ( proxy ) ;
115+ if ( i === 0 ) {
116+ this . shouldLookup = lookup ;
117+ }
118+ }
99119
100- this . shouldLookup = lookup ;
101- this . proxy = proxy ;
102120 this . timeout = opts ?. timeout ?? null ;
103121 }
104122
123+ get proxy ( ) : SocksProxy {
124+ return this . proxies [ 0 ] ;
125+ }
126+
105127 /**
106128 * Initiates a SOCKS connection to the specified SOCKS proxy server,
107129 * which in turn connects to the specified remote host and port.
@@ -110,7 +132,7 @@ export class SocksProxyAgent extends Agent {
110132 req : http . ClientRequest ,
111133 opts : AgentConnectOpts
112134 ) : Promise < net . Socket > {
113- const { shouldLookup, proxy , timeout } = this ;
135+ const { shouldLookup, proxies , timeout } = this ;
114136
115137 if ( ! opts . host ) {
116138 throw new Error ( 'No `host` defined!' ) ;
@@ -133,25 +155,46 @@ export class SocksProxyAgent extends Agent {
133155 } ) ;
134156 }
135157
136- const socksOpts : SocksClientOptions = {
137- proxy,
138- destination : {
139- host,
140- port : typeof port === 'number' ? port : parseInt ( port , 10 ) ,
141- } ,
142- command : 'connect' ,
143- timeout : timeout ?? undefined ,
144- } ;
145-
158+ let socket : net . Socket ;
146159 const cleanup = ( tlsSocket ?: tls . TLSSocket ) => {
147160 req . destroy ( ) ;
148161 socket . destroy ( ) ;
149162 if ( tlsSocket ) tlsSocket . destroy ( ) ;
150163 } ;
151164
152- debug ( 'Creating socks proxy connection: %o' , socksOpts ) ;
153- const { socket } = await SocksClient . createConnection ( socksOpts ) ;
154- debug ( 'Successfully created socks proxy connection' ) ;
165+ if ( proxies . length === 1 ) {
166+ const socksOpts : SocksClientOptions = {
167+ proxy : proxies [ 0 ] ,
168+ destination : {
169+ host,
170+ port : typeof port === 'number' ? port : parseInt ( port , 10 ) ,
171+ } ,
172+ command : 'connect' ,
173+ timeout : timeout ?? undefined ,
174+ } ;
175+
176+ debug ( 'Creating socks proxy connection: %o' , socksOpts ) ;
177+ const connection = await SocksClient . createConnection ( socksOpts ) ;
178+ socket = connection . socket ;
179+ debug ( 'Successfully created socks proxy connection' ) ;
180+ } else {
181+ const socksOpts : SocksClientChainOptions = {
182+ proxies : proxies ,
183+ destination : {
184+ host,
185+ port : typeof port === 'number' ? port : parseInt ( port , 10 ) ,
186+ } ,
187+ command : 'connect' ,
188+ timeout : timeout ?? undefined ,
189+ } ;
190+
191+ debug ( 'Creating chained socks proxy connection: %o' , socksOpts ) ;
192+ const connection = await SocksClient . createConnectionChain (
193+ socksOpts
194+ ) ;
195+ socket = connection . socket ;
196+ debug ( 'Successfully created chained socks proxy connection' ) ;
197+ }
155198
156199 if ( timeout !== null ) {
157200 socket . setTimeout ( timeout ) ;
0 commit comments