Skip to content

Commit c560c4e

Browse files
committed
feat: complete SOCKS5 ProxyAgent integration (Phase 2)
Integrate SOCKS5 proxy support into the existing ProxyAgent class: - Add SOCKS5 protocol detection (socks5: and socks: schemes) - Use Socks5ProxyWrapper for SOCKS5 connections instead of HTTP CONNECT - Properly handle SOCKS5 proxy lifecycle (no proxy client needed) - Pass through authentication credentials to SOCKS5 wrapper - Disable CONNECT tunneling for SOCKS5 proxies This completes Phase 2 of the SOCKS5 implementation. Note: Current implementation has architectural limitation requiring Pool dispatcher instead of Client for proper connection lifecycle management. Resolves: #4260
1 parent 9c8dd9c commit c560c4e

1 file changed

Lines changed: 30 additions & 1 deletion

File tree

lib/dispatcher/proxy-agent.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError }
88
const buildConnector = require('../core/connect')
99
const Client = require('./client')
1010
const { channels } = require('../core/diagnostics')
11+
const Socks5ProxyWrapper = require('./socks5-proxy-wrapper')
1112

1213
const kAgent = Symbol('proxy agent')
1314
const kClient = Symbol('proxy client')
@@ -132,6 +133,19 @@ class ProxyAgent extends DispatcherBase {
132133
const agentFactory = opts.factory || defaultAgentFactory
133134
const factory = (origin, options) => {
134135
const { protocol } = new URL(origin)
136+
137+
// Handle SOCKS5 proxy
138+
if (this[kProxy].protocol === 'socks5:' || this[kProxy].protocol === 'socks:') {
139+
return new Socks5ProxyWrapper(this[kProxy].uri, {
140+
headers: this[kProxyHeaders],
141+
connect,
142+
factory: agentFactory,
143+
username: opts.username || username,
144+
password: opts.password || password,
145+
proxyTls: opts.proxyTls
146+
})
147+
}
148+
135149
if (!this[kTunnelProxy] && protocol === 'http:' && this[kProxy].protocol === 'http:') {
136150
return new Http1ProxyWrapper(this[kProxy].uri, {
137151
headers: this[kProxyHeaders],
@@ -141,11 +155,26 @@ class ProxyAgent extends DispatcherBase {
141155
}
142156
return agentFactory(origin, options)
143157
}
144-
this[kClient] = clientFactory(url, { connect })
158+
159+
// For SOCKS5 proxies, we don't need a client to the proxy itself
160+
// The SOCKS5 connection is handled within the Socks5ProxyWrapper
161+
if (protocol === 'socks5:' || protocol === 'socks:') {
162+
this[kClient] = null
163+
} else {
164+
this[kClient] = clientFactory(url, { connect })
165+
}
166+
145167
this[kAgent] = new Agent({
146168
...opts,
147169
factory,
148170
connect: async (opts, callback) => {
171+
// For SOCKS5 proxies, the connection is handled by the Socks5ProxyWrapper
172+
// This connect function is only needed for HTTP CONNECT tunneling
173+
if (this[kProxy].protocol === 'socks5:' || this[kProxy].protocol === 'socks:') {
174+
callback(new InvalidArgumentError('SOCKS5 proxy does not use CONNECT tunneling'))
175+
return
176+
}
177+
149178
let requestedPath = opts.host
150179
if (!opts.port) {
151180
requestedPath += `:${defaultProtocolPort(opts.protocol)}`

0 commit comments

Comments
 (0)