global-agent 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +23 -0
- package/.editorconfig +9 -0
- package/.eslintignore +1 -0
- package/.eslintrc +27 -0
- package/.github/FUNDING.yml +2 -0
- package/.github/workflows/feature.yaml +32 -0
- package/.github/workflows/main.yaml +31 -0
- package/.gitignore +14 -0
- package/LICENSE +2 -2
- package/README.md +67 -8
- package/package.json +48 -56
- package/src/{Logger.js → Logger.ts} +3 -3
- package/{dist/classes/Agent.js.flow → src/classes/Agent.ts} +121 -39
- package/src/classes/HttpProxyAgent.ts +45 -0
- package/src/classes/HttpsProxyAgent.ts +83 -0
- package/src/classes/index.ts +9 -0
- package/src/{errors.js → errors.ts} +2 -6
- package/src/factories/{createGlobalProxyAgent.js → createGlobalProxyAgent.ts} +36 -50
- package/{dist/factories/createProxyController.js.flow → src/factories/createProxyController.ts} +8 -5
- package/src/factories/index.ts +6 -0
- package/src/index.ts +6 -0
- package/{dist/routines/bootstrap.js.flow → src/routines/bootstrap.ts} +6 -5
- package/src/routines/index.ts +3 -0
- package/src/types.ts +66 -0
- package/src/utilities/{bindHttpMethod.js → bindHttpMethod.ts} +6 -7
- package/src/utilities/index.ts +9 -0
- package/{dist/utilities/isUrlMatchingNoProxy.js.flow → src/utilities/isUrlMatchingNoProxy.ts} +1 -6
- package/src/utilities/parseBoolean.ts +17 -0
- package/src/utilities/{parseProxyUrl.js → parseProxyUrl.ts} +12 -9
- package/test/.eslintrc +10 -0
- package/test/global-agent/factories/createGlobalProxyAgent.ts +760 -0
- package/test/global-agent/factories/createProxyController.ts +37 -0
- package/test/global-agent/utilities/isUrlMatchingNoProxy.ts +62 -0
- package/test/global-agent/utilities/parseProxyUrl.ts +38 -0
- package/tsconfig.json +25 -0
- package/.flowconfig +0 -3
- package/dist/Logger.js +0 -18
- package/dist/Logger.js.flow +0 -10
- package/dist/Logger.js.map +0 -1
- package/dist/classes/Agent.js +0 -174
- package/dist/classes/Agent.js.map +0 -1
- package/dist/classes/HttpProxyAgent.js +0 -33
- package/dist/classes/HttpProxyAgent.js.flow +0 -30
- package/dist/classes/HttpProxyAgent.js.map +0 -1
- package/dist/classes/HttpsProxyAgent.js +0 -53
- package/dist/classes/HttpsProxyAgent.js.flow +0 -54
- package/dist/classes/HttpsProxyAgent.js.map +0 -1
- package/dist/classes/index.js +0 -32
- package/dist/classes/index.js.flow +0 -5
- package/dist/classes/index.js.map +0 -1
- package/dist/errors.js +0 -22
- package/dist/errors.js.flow +0 -15
- package/dist/errors.js.map +0 -1
- package/dist/factories/createGlobalProxyAgent.js +0 -175
- package/dist/factories/createGlobalProxyAgent.js.flow +0 -197
- package/dist/factories/createGlobalProxyAgent.js.map +0 -1
- package/dist/factories/createProxyController.js +0 -45
- package/dist/factories/createProxyController.js.map +0 -1
- package/dist/factories/index.js +0 -24
- package/dist/factories/index.js.flow +0 -4
- package/dist/factories/index.js.map +0 -1
- package/dist/index.js +0 -22
- package/dist/index.js.flow +0 -4
- package/dist/index.js.map +0 -1
- package/dist/routines/bootstrap.js +0 -30
- package/dist/routines/bootstrap.js.map +0 -1
- package/dist/routines/index.js +0 -16
- package/dist/routines/index.js.flow +0 -3
- package/dist/routines/index.js.map +0 -1
- package/dist/types.js +0 -10
- package/dist/types.js.flow +0 -66
- package/dist/types.js.map +0 -1
- package/dist/utilities/bindHttpMethod.js +0 -62
- package/dist/utilities/bindHttpMethod.js.flow +0 -54
- package/dist/utilities/bindHttpMethod.js.map +0 -1
- package/dist/utilities/index.js +0 -32
- package/dist/utilities/index.js.flow +0 -5
- package/dist/utilities/index.js.map +0 -1
- package/dist/utilities/isUrlMatchingNoProxy.js +0 -43
- package/dist/utilities/isUrlMatchingNoProxy.js.map +0 -1
- package/dist/utilities/parseProxyUrl.js +0 -42
- package/dist/utilities/parseProxyUrl.js.flow +0 -36
- package/dist/utilities/parseProxyUrl.js.map +0 -1
- package/src/classes/Agent.js +0 -212
- package/src/classes/HttpProxyAgent.js +0 -30
- package/src/classes/HttpsProxyAgent.js +0 -54
- package/src/classes/index.js +0 -5
- package/src/factories/createProxyController.js +0 -46
- package/src/factories/index.js +0 -4
- package/src/index.js +0 -4
- package/src/routines/bootstrap.js +0 -25
- package/src/routines/index.js +0 -3
- package/src/types.js +0 -66
- package/src/utilities/index.js +0 -5
- package/src/utilities/isUrlMatchingNoProxy.js +0 -37
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type * as http from 'http';
|
|
2
|
+
import type * as https from 'https';
|
|
3
|
+
import net from 'net';
|
|
3
4
|
import {
|
|
4
5
|
serializeError,
|
|
5
6
|
} from 'serialize-error';
|
|
6
|
-
import {
|
|
7
|
-
boolean,
|
|
8
|
-
} from 'boolean';
|
|
9
7
|
import Logger from '../Logger';
|
|
10
8
|
import type {
|
|
11
9
|
AgentType,
|
|
10
|
+
ConnectionCallbackType,
|
|
11
|
+
ConnectionConfigurationType,
|
|
12
12
|
GetUrlProxyMethodType,
|
|
13
13
|
IsProxyConfiguredMethodType,
|
|
14
14
|
MustUrlUseProxyMethodType,
|
|
@@ -21,46 +21,134 @@ const log = Logger.child({
|
|
|
21
21
|
|
|
22
22
|
let requestId = 0;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
type AgentRequestOptions = {
|
|
25
|
+
host?: string,
|
|
26
|
+
path?: string,
|
|
27
|
+
port: number,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type HttpRequestOptions = AgentRequestOptions & Omit<http.RequestOptions, keyof AgentRequestOptions> & {
|
|
31
|
+
secureEndpoint: false,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type HttpsRequestOptions = AgentRequestOptions & Omit<https.RequestOptions, keyof AgentRequestOptions> & {
|
|
35
|
+
secureEndpoint: true,
|
|
36
|
+
};
|
|
26
37
|
|
|
27
|
-
|
|
38
|
+
type RequestOptions = HttpRequestOptions | HttpsRequestOptions;
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
abstract class Agent {
|
|
41
|
+
public defaultPort: number;
|
|
30
42
|
|
|
31
|
-
|
|
43
|
+
public protocol: ProtocolType;
|
|
32
44
|
|
|
33
|
-
|
|
45
|
+
public fallbackAgent: AgentType;
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
public isProxyConfigured: IsProxyConfiguredMethodType;
|
|
36
48
|
|
|
37
|
-
|
|
49
|
+
public mustUrlUseProxy: MustUrlUseProxyMethodType;
|
|
38
50
|
|
|
39
|
-
|
|
51
|
+
public getUrlProxy: GetUrlProxyMethodType;
|
|
52
|
+
|
|
53
|
+
public socketConnectionTimeout: number;
|
|
54
|
+
|
|
55
|
+
public ca: string[] | string | undefined;
|
|
56
|
+
|
|
57
|
+
public constructor (
|
|
40
58
|
isProxyConfigured: IsProxyConfiguredMethodType,
|
|
41
59
|
mustUrlUseProxy: MustUrlUseProxyMethodType,
|
|
42
60
|
getUrlProxy: GetUrlProxyMethodType,
|
|
43
61
|
fallbackAgent: AgentType,
|
|
44
62
|
socketConnectionTimeout: number,
|
|
63
|
+
ca: string[] | string | undefined,
|
|
45
64
|
) {
|
|
46
65
|
this.fallbackAgent = fallbackAgent;
|
|
47
66
|
this.isProxyConfigured = isProxyConfigured;
|
|
48
67
|
this.mustUrlUseProxy = mustUrlUseProxy;
|
|
49
68
|
this.getUrlProxy = getUrlProxy;
|
|
50
69
|
this.socketConnectionTimeout = socketConnectionTimeout;
|
|
70
|
+
this.ca = ca;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* This method can be used to append new ca certificates to existing ca certificates
|
|
75
|
+
*
|
|
76
|
+
* @param {string[] | string} ca a ca certificate or an array of ca certificates
|
|
77
|
+
*/
|
|
78
|
+
public addCACertificates (ca: string[] | string) {
|
|
79
|
+
if (!ca) {
|
|
80
|
+
log.error('Invalid input ca certificate');
|
|
81
|
+
} else if (this.ca) {
|
|
82
|
+
if (typeof ca === typeof this.ca) {
|
|
83
|
+
// concat valid ca certificates with the existing certificates,
|
|
84
|
+
if (typeof this.ca === 'string') {
|
|
85
|
+
this.ca = this.ca.concat(ca as string);
|
|
86
|
+
} else {
|
|
87
|
+
this.ca = this.ca.concat(ca as string[]);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
log.error('Input ca certificate type mismatched with existing ca certificate type');
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
this.ca = ca;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* This method clears existing CA Certificates.
|
|
99
|
+
* It sets ca to undefined
|
|
100
|
+
*/
|
|
101
|
+
public clearCACertificates () {
|
|
102
|
+
this.ca = undefined;
|
|
51
103
|
}
|
|
52
104
|
|
|
53
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Evaluate value for tls reject unauthorized variable
|
|
107
|
+
*/
|
|
108
|
+
public getRejectUnauthorized () {
|
|
109
|
+
// eslint-disable-next-line node/no-process-env
|
|
110
|
+
const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
111
|
+
let returnValue = true;
|
|
112
|
+
if (typeof rejectUnauthorized === 'boolean') {
|
|
113
|
+
returnValue = rejectUnauthorized;
|
|
114
|
+
} else if (typeof rejectUnauthorized === 'number') {
|
|
115
|
+
returnValue = rejectUnauthorized === 1;
|
|
116
|
+
} else if (typeof rejectUnauthorized === 'string') {
|
|
117
|
+
returnValue = ['true', 't', 'yes', 'y', 'on', '1'].includes(rejectUnauthorized.trim().toLowerCase());
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return returnValue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public abstract createConnection (configuration: ConnectionConfigurationType, callback: ConnectionCallbackType): void;
|
|
124
|
+
|
|
125
|
+
public addRequest (request: http.ClientRequest, configuration: RequestOptions) {
|
|
54
126
|
let requestUrl;
|
|
55
127
|
|
|
56
128
|
// It is possible that addRequest was constructed for a proxied request already, e.g.
|
|
57
129
|
// "request" package does this when it detects that a proxy should be used
|
|
58
130
|
// https://github.com/request/request/blob/212570b6971a732b8dd9f3c73354bcdda158a737/request.js#L402
|
|
59
131
|
// https://gist.github.com/gajus/e2074cd3b747864ffeaabbd530d30218
|
|
60
|
-
if (request.path.startsWith('http://')
|
|
132
|
+
if (request.path.startsWith('http://') ?? request.path.startsWith('https://')) {
|
|
61
133
|
requestUrl = request.path;
|
|
134
|
+
} else if (request.method === 'CONNECT') {
|
|
135
|
+
requestUrl = 'https://' + request.path;
|
|
62
136
|
} else {
|
|
63
|
-
requestUrl = this.protocol + '//' + (configuration.hostname
|
|
137
|
+
requestUrl = this.protocol + '//' + (configuration.hostname ?? configuration.host) + (configuration.port === 80 ?? configuration.port === 443 ? '' : ':' + configuration.port) + request.path;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// If a request should go to a local socket, proxying it through an HTTP
|
|
141
|
+
// server does not make sense as the information about the target socket
|
|
142
|
+
// will be lost and the proxy won't be able to correctly handle the request.
|
|
143
|
+
if (configuration.socketPath) {
|
|
144
|
+
log.trace({
|
|
145
|
+
destination: configuration.socketPath,
|
|
146
|
+
}, 'not proxying request; destination is a socket');
|
|
147
|
+
|
|
148
|
+
// @ts-expect-error seems like we are using wrong type for fallbackAgent.
|
|
149
|
+
this.fallbackAgent.addRequest(request, configuration);
|
|
150
|
+
|
|
151
|
+
return;
|
|
64
152
|
}
|
|
65
153
|
|
|
66
154
|
if (!this.isProxyConfigured()) {
|
|
@@ -68,7 +156,7 @@ class Agent {
|
|
|
68
156
|
destination: requestUrl,
|
|
69
157
|
}, 'not proxying request; GLOBAL_AGENT.HTTP_PROXY is not configured');
|
|
70
158
|
|
|
71
|
-
//
|
|
159
|
+
// @ts-expect-error seems like we are using wrong type for fallbackAgent.
|
|
72
160
|
this.fallbackAgent.addRequest(request, configuration);
|
|
73
161
|
|
|
74
162
|
return;
|
|
@@ -79,7 +167,7 @@ class Agent {
|
|
|
79
167
|
destination: requestUrl,
|
|
80
168
|
}, 'not proxying request; url matches GLOBAL_AGENT.NO_PROXY');
|
|
81
169
|
|
|
82
|
-
//
|
|
170
|
+
// @ts-expect-error seems like we are using wrong type for fallbackAgent.
|
|
83
171
|
this.fallbackAgent.addRequest(request, configuration);
|
|
84
172
|
|
|
85
173
|
return;
|
|
@@ -103,13 +191,13 @@ class Agent {
|
|
|
103
191
|
requestId: currentRequestId,
|
|
104
192
|
}, 'proxying request');
|
|
105
193
|
|
|
106
|
-
request.on('error', (error) => {
|
|
194
|
+
request.on('error', (error: Error) => {
|
|
107
195
|
log.error({
|
|
108
196
|
error: serializeError(error),
|
|
109
197
|
}, 'request error');
|
|
110
198
|
});
|
|
111
199
|
|
|
112
|
-
request.once('response', (response) => {
|
|
200
|
+
request.once('response', (response: http.IncomingMessage) => {
|
|
113
201
|
log.trace({
|
|
114
202
|
headers: response.headers,
|
|
115
203
|
requestId: currentRequestId,
|
|
@@ -120,8 +208,8 @@ class Agent {
|
|
|
120
208
|
request.shouldKeepAlive = false;
|
|
121
209
|
|
|
122
210
|
const connectionConfiguration = {
|
|
123
|
-
host: configuration.hostname
|
|
124
|
-
port: configuration.port
|
|
211
|
+
host: configuration.hostname ?? configuration.host ?? '',
|
|
212
|
+
port: configuration.port ?? 80,
|
|
125
213
|
proxy,
|
|
126
214
|
tls: {},
|
|
127
215
|
};
|
|
@@ -133,9 +221,13 @@ class Agent {
|
|
|
133
221
|
// > are also accepted:
|
|
134
222
|
// > ca, cert, ciphers, clientCertEngine, crl, dhparam, ecdhCurve, honorCipherOrder,
|
|
135
223
|
// > key, passphrase, pfx, rejectUnauthorized, secureOptions, secureProtocol, servername, sessionIdContext.
|
|
136
|
-
if (
|
|
224
|
+
if (configuration.secureEndpoint) {
|
|
225
|
+
// Determine servername - Node.js doesn't allow IP addresses as servername
|
|
226
|
+
const host = configuration.servername ?? connectionConfiguration.host;
|
|
227
|
+
const servername = net.isIP(host) ? undefined : host;
|
|
228
|
+
|
|
137
229
|
connectionConfiguration.tls = {
|
|
138
|
-
ca: configuration.ca,
|
|
230
|
+
ca: configuration.ca ?? this.ca,
|
|
139
231
|
cert: configuration.cert,
|
|
140
232
|
ciphers: configuration.ciphers,
|
|
141
233
|
clientCertEngine: configuration.clientCertEngine,
|
|
@@ -146,24 +238,14 @@ class Agent {
|
|
|
146
238
|
key: configuration.key,
|
|
147
239
|
passphrase: configuration.passphrase,
|
|
148
240
|
pfx: configuration.pfx,
|
|
149
|
-
rejectUnauthorized: configuration.rejectUnauthorized,
|
|
241
|
+
rejectUnauthorized: configuration.rejectUnauthorized ?? this.getRejectUnauthorized(),
|
|
150
242
|
secureOptions: configuration.secureOptions,
|
|
151
243
|
secureProtocol: configuration.secureProtocol,
|
|
152
|
-
servername
|
|
244
|
+
servername,
|
|
153
245
|
sessionIdContext: configuration.sessionIdContext,
|
|
154
246
|
};
|
|
155
|
-
|
|
156
|
-
// This is not ideal because there is no way to override this setting using `tls` configuration if `NODE_TLS_REJECT_UNAUTHORIZED=0`.
|
|
157
|
-
// However, popular HTTP clients (such as https://github.com/sindresorhus/got) come with pre-configured value for `rejectUnauthorized`,
|
|
158
|
-
// which makes it impossible to override that value globally and respect `rejectUnauthorized` for specific requests only.
|
|
159
|
-
//
|
|
160
|
-
// eslint-disable-next-line no-process-env
|
|
161
|
-
if (typeof process.env.NODE_TLS_REJECT_UNAUTHORIZED === 'string' && boolean(process.env.NODE_TLS_REJECT_UNAUTHORIZED) === false) {
|
|
162
|
-
connectionConfiguration.tls.rejectUnauthorized = false;
|
|
163
|
-
}
|
|
164
247
|
}
|
|
165
248
|
|
|
166
|
-
// $FlowFixMe It appears that Flow is missing the method description.
|
|
167
249
|
this.createConnection(connectionConfiguration, (error, socket) => {
|
|
168
250
|
log.trace({
|
|
169
251
|
target: connectionConfiguration,
|
|
@@ -194,10 +276,10 @@ class Agent {
|
|
|
194
276
|
|
|
195
277
|
if (error) {
|
|
196
278
|
request.emit('error', error);
|
|
197
|
-
} else {
|
|
279
|
+
} else if (socket) {
|
|
198
280
|
log.debug('created socket');
|
|
199
281
|
|
|
200
|
-
socket.on('error', (socketError) => {
|
|
282
|
+
socket.on('error', (socketError: Error) => {
|
|
201
283
|
log.error({
|
|
202
284
|
error: serializeError(socketError),
|
|
203
285
|
}, 'socket error');
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import net from 'net';
|
|
2
|
+
import type {
|
|
3
|
+
AgentType,
|
|
4
|
+
ConnectionCallbackType,
|
|
5
|
+
ConnectionConfigurationType,
|
|
6
|
+
GetUrlProxyMethodType,
|
|
7
|
+
IsProxyConfiguredMethodType,
|
|
8
|
+
MustUrlUseProxyMethodType,
|
|
9
|
+
} from '../types';
|
|
10
|
+
import Agent from './Agent';
|
|
11
|
+
|
|
12
|
+
class HttpProxyAgent extends Agent {
|
|
13
|
+
// @see https://github.com/sindresorhus/eslint-plugin-unicorn/issues/169#issuecomment-486980290
|
|
14
|
+
public constructor (
|
|
15
|
+
isProxyConfigured: IsProxyConfiguredMethodType,
|
|
16
|
+
mustUrlUseProxy: MustUrlUseProxyMethodType,
|
|
17
|
+
getUrlProxy: GetUrlProxyMethodType,
|
|
18
|
+
fallbackAgent: AgentType,
|
|
19
|
+
socketConnectionTimeout: number,
|
|
20
|
+
ca: string[] | string | undefined,
|
|
21
|
+
) {
|
|
22
|
+
super(
|
|
23
|
+
isProxyConfigured,
|
|
24
|
+
mustUrlUseProxy,
|
|
25
|
+
getUrlProxy,
|
|
26
|
+
fallbackAgent,
|
|
27
|
+
socketConnectionTimeout,
|
|
28
|
+
ca,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
this.protocol = 'http:';
|
|
32
|
+
this.defaultPort = 80;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public createConnection (configuration: ConnectionConfigurationType, callback: ConnectionCallbackType) {
|
|
36
|
+
const socket = net.connect(
|
|
37
|
+
configuration.proxy.port,
|
|
38
|
+
configuration.proxy.hostname,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
callback(null, socket);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default HttpProxyAgent;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import net from 'net';
|
|
2
|
+
import tls from 'tls';
|
|
3
|
+
import type {
|
|
4
|
+
AgentType,
|
|
5
|
+
ConnectionCallbackType,
|
|
6
|
+
ConnectionConfigurationType,
|
|
7
|
+
GetUrlProxyMethodType,
|
|
8
|
+
IsProxyConfiguredMethodType,
|
|
9
|
+
MustUrlUseProxyMethodType,
|
|
10
|
+
} from '../types';
|
|
11
|
+
import Agent from './Agent';
|
|
12
|
+
|
|
13
|
+
class HttpsProxyAgent extends Agent {
|
|
14
|
+
public constructor (
|
|
15
|
+
isProxyConfigured: IsProxyConfiguredMethodType,
|
|
16
|
+
mustUrlUseProxy: MustUrlUseProxyMethodType,
|
|
17
|
+
getUrlProxy: GetUrlProxyMethodType,
|
|
18
|
+
fallbackAgent: AgentType,
|
|
19
|
+
socketConnectionTimeout: number,
|
|
20
|
+
ca: string[] | string | undefined,
|
|
21
|
+
) {
|
|
22
|
+
super(
|
|
23
|
+
isProxyConfigured,
|
|
24
|
+
mustUrlUseProxy,
|
|
25
|
+
getUrlProxy,
|
|
26
|
+
fallbackAgent,
|
|
27
|
+
socketConnectionTimeout,
|
|
28
|
+
ca,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
this.protocol = 'https:';
|
|
32
|
+
this.defaultPort = 443;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public createConnection (configuration: ConnectionConfigurationType, callback: ConnectionCallbackType) {
|
|
36
|
+
const socket = net.connect(
|
|
37
|
+
configuration.proxy.port,
|
|
38
|
+
configuration.proxy.hostname,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
socket.on('error', (error) => {
|
|
42
|
+
callback(error);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
socket.once('data', (data) => {
|
|
46
|
+
// Proxies with HTTPS as protocal are not allowed by parseProxyUrl(), so it should be safe to assume that the response is plain text
|
|
47
|
+
const statusLine = data.toString().split('\r\n')[0];
|
|
48
|
+
const statusLineExp = /^HTTP\/(\d)\.(\d) (\d{3}) ?(.*)$/;
|
|
49
|
+
|
|
50
|
+
const statusCode = statusLineExp.exec(statusLine)?.[3];
|
|
51
|
+
|
|
52
|
+
if (typeof statusCode === 'string' && Number(statusCode) >= 400) {
|
|
53
|
+
const error = new Error(`Proxy server refused connecting to '${configuration.host}:${configuration.port}' (${statusLine})`);
|
|
54
|
+
socket.destroy();
|
|
55
|
+
callback(error);
|
|
56
|
+
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const secureSocket = tls.connect({
|
|
61
|
+
...configuration.tls,
|
|
62
|
+
socket,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
callback(null, secureSocket);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
let connectMessage = '';
|
|
69
|
+
|
|
70
|
+
connectMessage += 'CONNECT ' + configuration.host + ':' + configuration.port + ' HTTP/1.1\r\n';
|
|
71
|
+
connectMessage += 'Host: ' + configuration.host + ':' + configuration.port + '\r\n';
|
|
72
|
+
|
|
73
|
+
if (configuration.proxy.authorization) {
|
|
74
|
+
connectMessage += 'Proxy-Authorization: Basic ' + Buffer.from(configuration.proxy.authorization).toString('base64') + '\r\n';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
connectMessage += '\r\n';
|
|
78
|
+
|
|
79
|
+
socket.write(connectMessage);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default HttpsProxyAgent;
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
/* eslint-disable fp/no-class, fp/no-this */
|
|
4
|
-
|
|
5
1
|
import ExtendableError from 'es6-error';
|
|
6
2
|
|
|
7
3
|
export class UnexpectedStateError extends ExtendableError {
|
|
8
|
-
code: string;
|
|
4
|
+
public code: string;
|
|
9
5
|
|
|
10
|
-
constructor (message: string, code: string = 'UNEXPECTED_STATE_ERROR') {
|
|
6
|
+
public constructor (message: string, code: string = 'UNEXPECTED_STATE_ERROR') {
|
|
11
7
|
super(message);
|
|
12
8
|
|
|
13
9
|
this.code = code;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
1
|
import http from 'http';
|
|
4
2
|
import https from 'https';
|
|
5
3
|
import {
|
|
6
|
-
|
|
7
|
-
} from '
|
|
8
|
-
import
|
|
4
|
+
omitUndefined,
|
|
5
|
+
} from 'omit-undefined';
|
|
6
|
+
import semverGte from 'semver/functions/gte';
|
|
9
7
|
import Logger from '../Logger';
|
|
10
8
|
import {
|
|
11
9
|
HttpProxyAgent,
|
|
@@ -14,15 +12,18 @@ import {
|
|
|
14
12
|
import {
|
|
15
13
|
UnexpectedStateError,
|
|
16
14
|
} from '../errors';
|
|
15
|
+
import type {
|
|
16
|
+
ProxyAgentConfigurationInputType,
|
|
17
|
+
ProxyAgentConfigurationType,
|
|
18
|
+
} from '../types';
|
|
17
19
|
import {
|
|
18
20
|
bindHttpMethod,
|
|
19
21
|
isUrlMatchingNoProxy,
|
|
20
22
|
parseProxyUrl,
|
|
21
23
|
} from '../utilities';
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from '../types';
|
|
24
|
+
import {
|
|
25
|
+
parseBoolean,
|
|
26
|
+
} from '../utilities/parseBoolean';
|
|
26
27
|
import createProxyController from './createProxyController';
|
|
27
28
|
|
|
28
29
|
const httpGet = http.get;
|
|
@@ -37,27 +38,11 @@ const log = Logger.child({
|
|
|
37
38
|
const defaultConfigurationInput = {
|
|
38
39
|
environmentVariableNamespace: undefined,
|
|
39
40
|
forceGlobalAgent: undefined,
|
|
40
|
-
socketConnectionTimeout:
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const omitUndefined = (subject) => {
|
|
44
|
-
const keys = Object.keys(subject);
|
|
45
|
-
|
|
46
|
-
const result = {};
|
|
47
|
-
|
|
48
|
-
for (const key of keys) {
|
|
49
|
-
const value = subject[key];
|
|
50
|
-
|
|
51
|
-
if (value !== undefined) {
|
|
52
|
-
result[key] = value;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return result;
|
|
41
|
+
socketConnectionTimeout: 60_000,
|
|
57
42
|
};
|
|
58
43
|
|
|
59
44
|
const createConfiguration = (configurationInput: ProxyAgentConfigurationInputType): ProxyAgentConfigurationType => {
|
|
60
|
-
// eslint-disable-next-line no-process-env
|
|
45
|
+
// eslint-disable-next-line node/no-process-env
|
|
61
46
|
const environment = process.env;
|
|
62
47
|
|
|
63
48
|
const defaultConfiguration = {
|
|
@@ -66,7 +51,6 @@ const createConfiguration = (configurationInput: ProxyAgentConfigurationInputTyp
|
|
|
66
51
|
socketConnectionTimeout: typeof environment.GLOBAL_AGENT_SOCKET_CONNECTION_TIMEOUT === 'string' ? Number.parseInt(environment.GLOBAL_AGENT_SOCKET_CONNECTION_TIMEOUT, 10) : defaultConfigurationInput.socketConnectionTimeout,
|
|
67
52
|
};
|
|
68
53
|
|
|
69
|
-
// $FlowFixMe
|
|
70
54
|
return {
|
|
71
55
|
...defaultConfiguration,
|
|
72
56
|
...omitUndefined(configurationInput),
|
|
@@ -78,22 +62,22 @@ export default (configurationInput: ProxyAgentConfigurationInputType = defaultCo
|
|
|
78
62
|
|
|
79
63
|
const proxyController = createProxyController();
|
|
80
64
|
|
|
81
|
-
// eslint-disable-next-line no-process-env
|
|
82
|
-
proxyController.HTTP_PROXY = process.env[configuration.environmentVariableNamespace + 'HTTP_PROXY']
|
|
65
|
+
// eslint-disable-next-line node/no-process-env
|
|
66
|
+
proxyController.HTTP_PROXY = process.env[configuration.environmentVariableNamespace + 'HTTP_PROXY'] ?? null;
|
|
83
67
|
|
|
84
|
-
// eslint-disable-next-line no-process-env
|
|
85
|
-
proxyController.HTTPS_PROXY = process.env[configuration.environmentVariableNamespace + 'HTTPS_PROXY']
|
|
68
|
+
// eslint-disable-next-line node/no-process-env
|
|
69
|
+
proxyController.HTTPS_PROXY = process.env[configuration.environmentVariableNamespace + 'HTTPS_PROXY'] ?? null;
|
|
86
70
|
|
|
87
|
-
// eslint-disable-next-line no-process-env
|
|
88
|
-
proxyController.NO_PROXY = process.env[configuration.environmentVariableNamespace + 'NO_PROXY']
|
|
71
|
+
// eslint-disable-next-line node/no-process-env
|
|
72
|
+
proxyController.NO_PROXY = process.env[configuration.environmentVariableNamespace + 'NO_PROXY'] ?? null;
|
|
89
73
|
|
|
90
74
|
log.info({
|
|
91
75
|
configuration,
|
|
92
76
|
state: proxyController,
|
|
93
77
|
}, 'global agent has been initialized');
|
|
94
78
|
|
|
95
|
-
const mustUrlUseProxy = (getProxy) => {
|
|
96
|
-
return (url) => {
|
|
79
|
+
const mustUrlUseProxy = (getProxy: () => string | null) => {
|
|
80
|
+
return (url: string): boolean => {
|
|
97
81
|
if (!getProxy()) {
|
|
98
82
|
return false;
|
|
99
83
|
}
|
|
@@ -106,7 +90,7 @@ export default (configurationInput: ProxyAgentConfigurationInputType = defaultCo
|
|
|
106
90
|
};
|
|
107
91
|
};
|
|
108
92
|
|
|
109
|
-
const getUrlProxy = (getProxy) => {
|
|
93
|
+
const getUrlProxy = (getProxy: () => string | null) => {
|
|
110
94
|
return () => {
|
|
111
95
|
const proxy = getProxy();
|
|
112
96
|
|
|
@@ -123,15 +107,16 @@ export default (configurationInput: ProxyAgentConfigurationInputType = defaultCo
|
|
|
123
107
|
};
|
|
124
108
|
|
|
125
109
|
const BoundHttpProxyAgent = class extends HttpProxyAgent {
|
|
126
|
-
constructor () {
|
|
110
|
+
public constructor () {
|
|
127
111
|
super(
|
|
128
112
|
() => {
|
|
129
|
-
return getHttpProxy();
|
|
113
|
+
return Boolean(getHttpProxy());
|
|
130
114
|
},
|
|
131
115
|
mustUrlUseProxy(getHttpProxy),
|
|
132
116
|
getUrlProxy(getHttpProxy),
|
|
133
117
|
http.globalAgent,
|
|
134
118
|
configuration.socketConnectionTimeout,
|
|
119
|
+
configuration.ca,
|
|
135
120
|
);
|
|
136
121
|
}
|
|
137
122
|
};
|
|
@@ -139,19 +124,20 @@ export default (configurationInput: ProxyAgentConfigurationInputType = defaultCo
|
|
|
139
124
|
const httpAgent = new BoundHttpProxyAgent();
|
|
140
125
|
|
|
141
126
|
const getHttpsProxy = () => {
|
|
142
|
-
return proxyController.HTTPS_PROXY
|
|
127
|
+
return proxyController.HTTPS_PROXY ?? proxyController.HTTP_PROXY;
|
|
143
128
|
};
|
|
144
129
|
|
|
145
130
|
const BoundHttpsProxyAgent = class extends HttpsProxyAgent {
|
|
146
|
-
constructor () {
|
|
131
|
+
public constructor () {
|
|
147
132
|
super(
|
|
148
133
|
() => {
|
|
149
|
-
return getHttpsProxy();
|
|
134
|
+
return Boolean(getHttpsProxy());
|
|
150
135
|
},
|
|
151
136
|
mustUrlUseProxy(getHttpsProxy),
|
|
152
137
|
getUrlProxy(getHttpsProxy),
|
|
153
138
|
https.globalAgent,
|
|
154
139
|
configuration.socketConnectionTimeout,
|
|
140
|
+
configuration.ca,
|
|
155
141
|
);
|
|
156
142
|
}
|
|
157
143
|
};
|
|
@@ -160,12 +146,12 @@ export default (configurationInput: ProxyAgentConfigurationInputType = defaultCo
|
|
|
160
146
|
|
|
161
147
|
// Overriding globalAgent was added in v11.7.
|
|
162
148
|
// @see https://nodejs.org/uk/blog/release/v11.7.0/
|
|
163
|
-
if (
|
|
149
|
+
if (semverGte(process.version, 'v11.7.0')) {
|
|
164
150
|
// @see https://github.com/facebook/flow/issues/7670
|
|
165
|
-
//
|
|
151
|
+
// @ts-expect-error Node.js version compatibility
|
|
166
152
|
http.globalAgent = httpAgent;
|
|
167
153
|
|
|
168
|
-
//
|
|
154
|
+
// @ts-expect-error Node.js version compatibility
|
|
169
155
|
https.globalAgent = httpsAgent;
|
|
170
156
|
}
|
|
171
157
|
|
|
@@ -177,17 +163,17 @@ export default (configurationInput: ProxyAgentConfigurationInputType = defaultCo
|
|
|
177
163
|
//
|
|
178
164
|
// We still want to override http(s).globalAgent when possible to enable logic
|
|
179
165
|
// in `bindHttpMethod`.
|
|
180
|
-
if (
|
|
181
|
-
//
|
|
166
|
+
if (semverGte(process.version, 'v10.0.0')) {
|
|
167
|
+
// @ts-expect-error seems like we are using wrong type for httpAgent
|
|
182
168
|
http.get = bindHttpMethod(httpGet, httpAgent, configuration.forceGlobalAgent);
|
|
183
169
|
|
|
184
|
-
//
|
|
170
|
+
// @ts-expect-error seems like we are using wrong type for httpAgent
|
|
185
171
|
http.request = bindHttpMethod(httpRequest, httpAgent, configuration.forceGlobalAgent);
|
|
186
172
|
|
|
187
|
-
//
|
|
173
|
+
// @ts-expect-error seems like we are using wrong type for httpAgent
|
|
188
174
|
https.get = bindHttpMethod(httpsGet, httpsAgent, configuration.forceGlobalAgent);
|
|
189
175
|
|
|
190
|
-
//
|
|
176
|
+
// @ts-expect-error seems like we are using wrong type for httpAgent
|
|
191
177
|
https.request = bindHttpMethod(httpsRequest, httpsAgent, configuration.forceGlobalAgent);
|
|
192
178
|
} else {
|
|
193
179
|
log.warn('attempt to initialize global-agent in unsupported Node.js version was ignored');
|
package/{dist/factories/createProxyController.js.flow → src/factories/createProxyController.ts}
RENAMED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
1
|
import Logger from '../Logger';
|
|
4
2
|
|
|
5
|
-
type
|
|
3
|
+
type ProxyController = {
|
|
6
4
|
HTTP_PROXY: string | null,
|
|
7
5
|
HTTPS_PROXY: string | null,
|
|
8
6
|
NO_PROXY: string | null,
|
|
9
|
-
|
|
7
|
+
};
|
|
10
8
|
|
|
11
9
|
const log = Logger.child({
|
|
12
10
|
namespace: 'createProxyController',
|
|
@@ -18,7 +16,7 @@ const KNOWN_PROPERTY_NAMES = [
|
|
|
18
16
|
'NO_PROXY',
|
|
19
17
|
];
|
|
20
18
|
|
|
21
|
-
export default ():
|
|
19
|
+
export default (): ProxyController => {
|
|
22
20
|
// eslint-disable-next-line fp/no-proxy
|
|
23
21
|
return new Proxy({
|
|
24
22
|
HTTP_PROXY: null,
|
|
@@ -26,10 +24,15 @@ export default (): ProxyControllerType => {
|
|
|
26
24
|
NO_PROXY: null,
|
|
27
25
|
}, {
|
|
28
26
|
set: (subject, name, value) => {
|
|
27
|
+
if (typeof name !== 'string') {
|
|
28
|
+
throw new TypeError('Unexpected object member.');
|
|
29
|
+
}
|
|
30
|
+
|
|
29
31
|
if (!KNOWN_PROPERTY_NAMES.includes(name)) {
|
|
30
32
|
throw new Error('Cannot set an unmapped property "' + name + '".');
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
// @ts-expect-error string cannot be used to index an object
|
|
33
36
|
subject[name] = value;
|
|
34
37
|
|
|
35
38
|
log.info({
|