"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _http = _interopRequireDefault(require("http"));

var _https = _interopRequireDefault(require("https"));

var _boolean = require("boolean");

var _semver = _interopRequireDefault(require("semver"));

var _Logger = _interopRequireDefault(require("../Logger"));

var _classes = require("../classes");

var _errors = require("../errors");

var _utilities = require("../utilities");

var _createProxyController = _interopRequireDefault(require("./createProxyController"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const httpGet = _http.default.get;
const httpRequest = _http.default.request;
const httpsGet = _https.default.get;
const httpsRequest = _https.default.request;

const log = _Logger.default.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 => {
  // 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' ? (0, _boolean.boolean)(environment.GLOBAL_AGENT_FORCE_GLOBAL_AGENT) : true,
    socketConnectionTimeout: typeof environment.GLOBAL_AGENT_SOCKET_CONNECTION_TIMEOUT === 'string' ? parseInt(environment.GLOBAL_AGENT_SOCKET_CONNECTION_TIMEOUT, 10) : defaultConfigurationInput.socketConnectionTimeout
  }; // $FlowFixMe

  return { ...defaultConfiguration,
    ...omitUndefined(configurationInput)
  };
};

const createGlobalProxyAgent = (configurationInput = defaultConfigurationInput) => {
  const configuration = createConfiguration(configurationInput);
  const proxyController = (0, _createProxyController.default)(); // 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 !(0, _utilities.isUrlMatchingNoProxy)(url, proxyController.NO_PROXY);
    };
  };

  const getUrlProxy = getProxy => {
    return () => {
      const proxy = getProxy();

      if (!proxy) {
        throw new _errors.UnexpectedStateError('HTTP(S) proxy must be configured.');
      }

      return (0, _utilities.parseProxyUrl)(proxy);
    };
  };

  const getHttpProxy = () => {
    return proxyController.HTTP_PROXY;
  };

  const BoundHttpProxyAgent = class extends _classes.HttpProxyAgent {
    constructor() {
      super(() => {
        return getHttpProxy();
      }, mustUrlUseProxy(getHttpProxy), getUrlProxy(getHttpProxy), _http.default.globalAgent, configuration.socketConnectionTimeout);
    }

  };
  const httpAgent = new BoundHttpProxyAgent();

  const getHttpsProxy = () => {
    return proxyController.HTTPS_PROXY || proxyController.HTTP_PROXY;
  };

  const BoundHttpsProxyAgent = class extends _classes.HttpsProxyAgent {
    constructor() {
      super(() => {
        return getHttpsProxy();
      }, mustUrlUseProxy(getHttpsProxy), getUrlProxy(getHttpsProxy), _https.default.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.default.gte(process.version, 'v11.7.0')) {
    // @see https://github.com/facebook/flow/issues/7670
    // $FlowFixMe
    _http.default.globalAgent = httpAgent; // $FlowFixMe

    _https.default.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.default.gte(process.version, 'v10.0.0')) {
    // $FlowFixMe
    _http.default.get = (0, _utilities.bindHttpMethod)(httpGet, httpAgent, configuration.forceGlobalAgent); // $FlowFixMe

    _http.default.request = (0, _utilities.bindHttpMethod)(httpRequest, httpAgent, configuration.forceGlobalAgent); // $FlowFixMe

    _https.default.get = (0, _utilities.bindHttpMethod)(httpsGet, httpsAgent, configuration.forceGlobalAgent); // $FlowFixMe

    _https.default.request = (0, _utilities.bindHttpMethod)(httpsRequest, httpsAgent, configuration.forceGlobalAgent);
  } else {
    log.warn('attempt to initialize global-agent in unsupported Node.js version was ignored');
  }

  return proxyController;
};

var _default = createGlobalProxyAgent;
exports.default = _default;
//# sourceMappingURL=createGlobalProxyAgent.js.map