|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- // @flow
-
- import http from 'http';
- import https from 'https';
- import {
- boolean as parseBoolean,
- } from 'boolean';
- import semver from 'semver';
- import Logger from '../Logger';
- import {
- HttpProxyAgent,
- HttpsProxyAgent,
- } from '../classes';
- import {
- UnexpectedStateError,
- } from '../errors';
- import {
- bindHttpMethod,
- isUrlMatchingNoProxy,
- parseProxyUrl,
- } from '../utilities';
- import type {
- ProxyAgentConfigurationInputType,
- ProxyAgentConfigurationType,
- } from '../types';
- import createProxyController from './createProxyController';
-
- const httpGet = http.get;
- const httpRequest = http.request;
- const httpsGet = https.get;
- const httpsRequest = https.request;
-
- const log = Logger.child({
- namespace: 'createGlobalProxyAgent',
- });
-
- const defaultConfigurationInput = {
- environmentVariableNamespace: undefined,
- forceGlobalAgent: undefined,
- socketConnectionTimeout: 60000,
- };
-
- const omitUndefined = (subject) => {
- const keys = Object.keys(subject);
-
- const result = {};
-
- for (const key of keys) {
- const value = subject[key];
-
- if (value !== undefined) {
- result[key] = value;
- }
- }
-
- return result;
- };
-
- const createConfiguration = (configurationInput: ProxyAgentConfigurationInputType): ProxyAgentConfigurationType => {
- // eslint-disable-next-line no-process-env
- const environment = process.env;
-
- const defaultConfiguration = {
- environmentVariableNamespace: typeof environment.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE === 'string' ? environment.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE : 'GLOBAL_AGENT_',
- forceGlobalAgent: typeof environment.GLOBAL_AGENT_FORCE_GLOBAL_AGENT === 'string' ? parseBoolean(environment.GLOBAL_AGENT_FORCE_GLOBAL_AGENT) : true,
- socketConnectionTimeout: typeof environment.GLOBAL_AGENT_SOCKET_CONNECTION_TIMEOUT === 'string' ? Number.parseInt(environment.GLOBAL_AGENT_SOCKET_CONNECTION_TIMEOUT, 10) : defaultConfigurationInput.socketConnectionTimeout,
- };
-
- // $FlowFixMe
- return {
- ...defaultConfiguration,
- ...omitUndefined(configurationInput),
- };
- };
-
- export default (configurationInput: ProxyAgentConfigurationInputType = defaultConfigurationInput) => {
- const configuration = createConfiguration(configurationInput);
-
- const proxyController = createProxyController();
-
- // eslint-disable-next-line no-process-env
- proxyController.HTTP_PROXY = process.env[configuration.environmentVariableNamespace + 'HTTP_PROXY'] || null;
-
- // eslint-disable-next-line no-process-env
- proxyController.HTTPS_PROXY = process.env[configuration.environmentVariableNamespace + 'HTTPS_PROXY'] || null;
-
- // eslint-disable-next-line no-process-env
- proxyController.NO_PROXY = process.env[configuration.environmentVariableNamespace + 'NO_PROXY'] || null;
-
- log.info({
- configuration,
- state: proxyController,
- }, 'global agent has been initialized');
-
- const mustUrlUseProxy = (getProxy) => {
- return (url) => {
- if (!getProxy()) {
- return false;
- }
-
- if (!proxyController.NO_PROXY) {
- return true;
- }
-
- return !isUrlMatchingNoProxy(url, proxyController.NO_PROXY);
- };
- };
-
- const getUrlProxy = (getProxy) => {
- return () => {
- const proxy = getProxy();
-
- if (!proxy) {
- throw new UnexpectedStateError('HTTP(S) proxy must be configured.');
- }
-
- return parseProxyUrl(proxy);
- };
- };
-
- const getHttpProxy = () => {
- return proxyController.HTTP_PROXY;
- };
-
- const BoundHttpProxyAgent = class extends HttpProxyAgent {
- constructor () {
- super(
- () => {
- return getHttpProxy();
- },
- mustUrlUseProxy(getHttpProxy),
- getUrlProxy(getHttpProxy),
- http.globalAgent,
- configuration.socketConnectionTimeout,
- );
- }
- };
-
- const httpAgent = new BoundHttpProxyAgent();
-
- const getHttpsProxy = () => {
- return proxyController.HTTPS_PROXY || proxyController.HTTP_PROXY;
- };
-
- const BoundHttpsProxyAgent = class extends HttpsProxyAgent {
- constructor () {
- super(
- () => {
- return getHttpsProxy();
- },
- mustUrlUseProxy(getHttpsProxy),
- getUrlProxy(getHttpsProxy),
- https.globalAgent,
- configuration.socketConnectionTimeout,
- );
- }
- };
-
- const httpsAgent = new BoundHttpsProxyAgent();
-
- // Overriding globalAgent was added in v11.7.
- // @see https://nodejs.org/uk/blog/release/v11.7.0/
- if (semver.gte(process.version, 'v11.7.0')) {
- // @see https://github.com/facebook/flow/issues/7670
- // $FlowFixMe
- http.globalAgent = httpAgent;
-
- // $FlowFixMe
- https.globalAgent = httpsAgent;
- }
-
- // The reason this logic is used in addition to overriding http(s).globalAgent
- // is because there is no guarantee that we set http(s).globalAgent variable
- // before an instance of http(s).Agent has been already constructed by someone,
- // e.g. Stripe SDK creates instances of http(s).Agent at the top-level.
- // @see https://github.com/gajus/global-agent/pull/13
- //
- // We still want to override http(s).globalAgent when possible to enable logic
- // in `bindHttpMethod`.
- if (semver.gte(process.version, 'v10.0.0')) {
- // $FlowFixMe
- http.get = bindHttpMethod(httpGet, httpAgent, configuration.forceGlobalAgent);
-
- // $FlowFixMe
- http.request = bindHttpMethod(httpRequest, httpAgent, configuration.forceGlobalAgent);
-
- // $FlowFixMe
- https.get = bindHttpMethod(httpsGet, httpsAgent, configuration.forceGlobalAgent);
-
- // $FlowFixMe
- https.request = bindHttpMethod(httpsRequest, httpsAgent, configuration.forceGlobalAgent);
- } else {
- log.warn('attempt to initialize global-agent in unsupported Node.js version was ignored');
- }
-
- return proxyController;
- };
|