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.
Files changed (95) 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 +32 -0
  7. package/.github/workflows/main.yaml +31 -0
  8. package/.gitignore +14 -0
  9. package/LICENSE +2 -2
  10. package/README.md +67 -8
  11. package/package.json +48 -56
  12. package/src/{Logger.js → Logger.ts} +3 -3
  13. package/{dist/classes/Agent.js.flow → src/classes/Agent.ts} +121 -39
  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} +36 -50
  19. package/{dist/factories/createProxyController.js.flow → src/factories/createProxyController.ts} +8 -5
  20. package/src/factories/index.ts +6 -0
  21. package/src/index.ts +6 -0
  22. package/{dist/routines/bootstrap.js.flow → src/routines/bootstrap.ts} +6 -5
  23. package/src/routines/index.ts +3 -0
  24. package/src/types.ts +66 -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.map +0 -1
  59. package/dist/factories/index.js +0 -24
  60. package/dist/factories/index.js.flow +0 -4
  61. package/dist/factories/index.js.map +0 -1
  62. package/dist/index.js +0 -22
  63. package/dist/index.js.flow +0 -4
  64. package/dist/index.js.map +0 -1
  65. package/dist/routines/bootstrap.js +0 -30
  66. package/dist/routines/bootstrap.js.map +0 -1
  67. package/dist/routines/index.js +0 -16
  68. package/dist/routines/index.js.flow +0 -3
  69. package/dist/routines/index.js.map +0 -1
  70. package/dist/types.js +0 -10
  71. package/dist/types.js.flow +0 -66
  72. package/dist/types.js.map +0 -1
  73. package/dist/utilities/bindHttpMethod.js +0 -62
  74. package/dist/utilities/bindHttpMethod.js.flow +0 -54
  75. package/dist/utilities/bindHttpMethod.js.map +0 -1
  76. package/dist/utilities/index.js +0 -32
  77. package/dist/utilities/index.js.flow +0 -5
  78. package/dist/utilities/index.js.map +0 -1
  79. package/dist/utilities/isUrlMatchingNoProxy.js +0 -43
  80. package/dist/utilities/isUrlMatchingNoProxy.js.map +0 -1
  81. package/dist/utilities/parseProxyUrl.js +0 -42
  82. package/dist/utilities/parseProxyUrl.js.flow +0 -36
  83. package/dist/utilities/parseProxyUrl.js.map +0 -1
  84. package/src/classes/Agent.js +0 -212
  85. package/src/classes/HttpProxyAgent.js +0 -30
  86. package/src/classes/HttpsProxyAgent.js +0 -54
  87. package/src/classes/index.js +0 -5
  88. package/src/factories/createProxyController.js +0 -46
  89. package/src/factories/index.js +0 -4
  90. package/src/index.js +0 -4
  91. package/src/routines/bootstrap.js +0 -25
  92. package/src/routines/index.js +0 -3
  93. package/src/types.js +0 -66
  94. package/src/utilities/index.js +0 -5
  95. package/src/utilities/isUrlMatchingNoProxy.js +0 -37
@@ -1,14 +1,14 @@
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
- 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
- class Agent {
25
- defaultPort: number;
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
- protocol: ProtocolType;
38
+ type RequestOptions = HttpRequestOptions | HttpsRequestOptions;
28
39
 
29
- fallbackAgent: AgentType;
40
+ abstract class Agent {
41
+ public defaultPort: number;
30
42
 
31
- isProxyConfigured: IsProxyConfiguredMethodType;
43
+ public protocol: ProtocolType;
32
44
 
33
- mustUrlUseProxy: MustUrlUseProxyMethodType;
45
+ public fallbackAgent: AgentType;
34
46
 
35
- getUrlProxy: GetUrlProxyMethodType;
47
+ public isProxyConfigured: IsProxyConfiguredMethodType;
36
48
 
37
- socketConnectionTimeout: number;
49
+ public mustUrlUseProxy: MustUrlUseProxyMethodType;
38
50
 
39
- constructor (
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
- addRequest (request: *, configuration: *) {
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://') || request.path.startsWith('https://')) {
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 || configuration.host) + (configuration.port === 80 || configuration.port === 443 ? '' : ':' + configuration.port) + request.path;
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
- // $FlowFixMe It appears that Flow is missing the method description.
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
- // $FlowFixMe It appears that Flow is missing the method description.
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 || configuration.host,
124
- port: configuration.port || 80,
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 (this.protocol === 'https:') {
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: configuration.servername || connectionConfiguration.host,
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;
@@ -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,11 +1,9 @@
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';
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 type {
23
- ProxyAgentConfigurationInputType,
24
- ProxyAgentConfigurationType,
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: 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;
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'] || null;
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'] || null;
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'] || null;
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 || proxyController.HTTP_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 (semver.gte(process.version, 'v11.7.0')) {
149
+ if (semverGte(process.version, 'v11.7.0')) {
164
150
  // @see https://github.com/facebook/flow/issues/7670
165
- // $FlowFixMe
151
+ // @ts-expect-error Node.js version compatibility
166
152
  http.globalAgent = httpAgent;
167
153
 
168
- // $FlowFixMe
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 (semver.gte(process.version, 'v10.0.0')) {
181
- // $FlowFixMe
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
- // $FlowFixMe
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
- // $FlowFixMe
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
- // $FlowFixMe
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');
@@ -1,12 +1,10 @@
1
- // @flow
2
-
3
1
  import Logger from '../Logger';
4
2
 
5
- type ProxyControllerType = {|
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 (): ProxyControllerType => {
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({
@@ -0,0 +1,6 @@
1
+ export {
2
+ default as createGlobalProxyAgent,
3
+ } from './createGlobalProxyAgent';
4
+ export {
5
+ default as createProxyController,
6
+ } from './createProxyController';
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export {
2
+ bootstrap,
3
+ } from './routines';
4
+ export {
5
+ createGlobalProxyAgent,
6
+ } from './factories';