1
1
'use strict'
2
2
3
- const { kProxy, kClose, kDestroy } = require ( '../core/symbols' )
3
+ const { kProxy, kClose, kDestroy, kDispatch , kConnector } = require ( '../core/symbols' )
4
4
const { URL } = require ( 'node:url' )
5
5
const Agent = require ( './agent' )
6
6
const Pool = require ( './pool' )
7
7
const DispatcherBase = require ( './dispatcher-base' )
8
8
const { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = require ( '../core/errors' )
9
9
const buildConnector = require ( '../core/connect' )
10
+ const Client = require ( './client' )
10
11
11
12
const kAgent = Symbol ( 'proxy agent' )
12
13
const kClient = Symbol ( 'proxy client' )
@@ -26,6 +27,61 @@ function defaultFactory (origin, opts) {
26
27
27
28
const noop = ( ) => { }
28
29
30
+ class ProxyClient extends DispatcherBase {
31
+ #client = null
32
+ constructor ( origin , opts ) {
33
+ if ( typeof origin === 'string' ) {
34
+ origin = new URL ( origin )
35
+ }
36
+
37
+ if ( origin . protocol !== 'http:' && origin . protocol !== 'https:' ) {
38
+ throw new InvalidArgumentError ( 'ProxyClient only supports http and https protocols' )
39
+ }
40
+
41
+ super ( )
42
+
43
+ this . #client = new Client ( origin , opts )
44
+ }
45
+
46
+ async [ kClose ] ( ) {
47
+ await this . #client. close ( )
48
+ }
49
+
50
+ async [ kDestroy ] ( ) {
51
+ await this . #client. destroy ( )
52
+ }
53
+
54
+ async [ kDispatch ] ( opts , handler ) {
55
+ const { method, origin } = opts
56
+ if ( method === 'CONNECT' ) {
57
+ this . #client[ kConnector ] ( {
58
+ origin,
59
+ port : opts . port || defaultProtocolPort ( opts . protocol ) ,
60
+ path : opts . host ,
61
+ signal : opts . signal ,
62
+ headers : {
63
+ ...this [ kProxyHeaders ] ,
64
+ host : opts . host
65
+ } ,
66
+ servername : this [ kProxyTls ] ?. servername || opts . servername
67
+ } ,
68
+ ( err , socket ) => {
69
+ if ( err ) {
70
+ handler . callback ( err )
71
+ } else {
72
+ handler . callback ( null , { socket, statusCode : 200 } )
73
+ }
74
+ }
75
+ )
76
+ return
77
+ }
78
+ if ( typeof origin === 'string' ) {
79
+ opts . origin = new URL ( origin )
80
+ }
81
+
82
+ return this . #client. dispatch ( opts , handler )
83
+ }
84
+ }
29
85
class ProxyAgent extends DispatcherBase {
30
86
constructor ( opts ) {
31
87
if ( ! opts || ( typeof opts === 'object' && ! ( opts instanceof URL ) && ! opts . uri ) ) {
@@ -60,9 +116,18 @@ class ProxyAgent extends DispatcherBase {
60
116
this [ kProxyHeaders ] [ 'proxy-authorization' ] = `Basic ${ Buffer . from ( `${ decodeURIComponent ( username ) } :${ decodeURIComponent ( password ) } ` ) . toString ( 'base64' ) } `
61
117
}
62
118
119
+ const factory = ( ! tunnelProxy && protocol === 'http:' )
120
+ ? ( origin , options ) => {
121
+ if ( origin . protocol === 'http:' ) {
122
+ return new ProxyClient ( origin , options )
123
+ }
124
+ return new Client ( origin , options )
125
+ }
126
+ : undefined
127
+
63
128
const connect = buildConnector ( { ...opts . proxyTls } )
64
129
this [ kConnectEndpoint ] = buildConnector ( { ...opts . requestTls } )
65
- this [ kClient ] = clientFactory ( url , { connect } )
130
+ this [ kClient ] = clientFactory ( url , { connect, factory } )
66
131
this [ kTunnelProxy ] = tunnelProxy
67
132
this [ kAgent ] = new Agent ( {
68
133
...opts ,
@@ -71,7 +136,6 @@ class ProxyAgent extends DispatcherBase {
71
136
if ( ! opts . port ) {
72
137
requestedPath += `:${ defaultProtocolPort ( opts . protocol ) } `
73
138
}
74
- const shouldConnect = this [ kTunnelProxy ] || protocol !== 'http:' || opts . protocol !== 'http:'
75
139
try {
76
140
const { socket, statusCode } = await this [ kClient ] . connect ( {
77
141
origin,
@@ -82,7 +146,6 @@ class ProxyAgent extends DispatcherBase {
82
146
...this [ kProxyHeaders ] ,
83
147
host : opts . host
84
148
} ,
85
- rawSocket : ! shouldConnect ,
86
149
servername : this [ kProxyTls ] ?. servername || proxyHostname
87
150
} )
88
151
if ( statusCode !== 200 ) {
@@ -121,10 +184,8 @@ class ProxyAgent extends DispatcherBase {
121
184
headers . host = host
122
185
}
123
186
124
- // If we have a non-secure, non-tunneling proxy, the server URL is prepended to the
125
- // path to ensure that the Proxy can handle the request appropriately.
126
- if ( ! this [ kTunnelProxy ] && opts . path && opts . origin ) {
127
- opts . path = `${ opts . origin } ${ opts . path } `
187
+ if ( ! this . #shouldConnect( new URL ( opts . origin ) ) ) {
188
+ opts . path = opts . origin + opts . path
128
189
}
129
190
130
191
return this [ kAgent ] . dispatch (
@@ -159,6 +220,19 @@ class ProxyAgent extends DispatcherBase {
159
220
await this [ kAgent ] . destroy ( )
160
221
await this [ kClient ] . destroy ( )
161
222
}
223
+
224
+ #shouldConnect ( uri ) {
225
+ if ( typeof uri === 'string' ) {
226
+ uri = new URL ( uri )
227
+ }
228
+ if ( this [ kTunnelProxy ] ) {
229
+ return true
230
+ }
231
+ if ( uri . protocol !== 'http:' || this [ kProxy ] . protocol !== 'http:' ) {
232
+ return true
233
+ }
234
+ return false
235
+ }
162
236
}
163
237
164
238
/**
0 commit comments