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