index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. 'use strict';
  2. /**
  3. * Module dependencies.
  4. */
  5. var url = require('url');
  6. var LRU = require('lru-cache');
  7. var Agent = require('agent-base');
  8. var inherits = require('util').inherits;
  9. var debug = require('debug')('proxy-agent');
  10. var PacProxyAgent = require('pac-proxy-agent');
  11. var HttpProxyAgent = require('http-proxy-agent');
  12. var HttpsProxyAgent = require('https-proxy-agent');
  13. var SocksProxyAgent = require('socks-proxy-agent');
  14. /**
  15. * Module exports.
  16. */
  17. exports = module.exports = ProxyAgent;
  18. /**
  19. * Number of `http.Agent` instances to cache.
  20. *
  21. * This value was arbitrarily chosen... a better
  22. * value could be conceived with some benchmarks.
  23. */
  24. var cacheSize = 20;
  25. /**
  26. * Cache for `http.Agent` instances.
  27. */
  28. exports.cache = new LRU(cacheSize);
  29. /**
  30. * Built-in proxy types.
  31. */
  32. exports.proxies = Object.create(null);
  33. exports.proxies.http = httpOrHttpsProxy;
  34. exports.proxies.https = httpOrHttpsProxy;
  35. exports.proxies.socks = SocksProxyAgent;
  36. exports.proxies.socks4 = SocksProxyAgent;
  37. exports.proxies.socks4a = SocksProxyAgent;
  38. exports.proxies.socks5 = SocksProxyAgent;
  39. exports.proxies.socks5h = SocksProxyAgent;
  40. PacProxyAgent.protocols.forEach(function (protocol) {
  41. exports.proxies['pac+' + protocol] = PacProxyAgent;
  42. });
  43. function httpOrHttpsProxy (opts, secureEndpoint) {
  44. if (secureEndpoint) {
  45. // HTTPS
  46. return new HttpsProxyAgent(opts);
  47. } else {
  48. // HTTP
  49. return new HttpProxyAgent(opts);
  50. }
  51. }
  52. /**
  53. * Attempts to get an `http.Agent` instance based off of the given proxy URI
  54. * information, and the `secure` flag.
  55. *
  56. * An LRU cache is used, to prevent unnecessary creation of proxy
  57. * `http.Agent` instances.
  58. *
  59. * @param {String} uri proxy url
  60. * @param {Boolean} secure true if this is for an HTTPS request, false for HTTP
  61. * @return {http.Agent}
  62. * @api public
  63. */
  64. function ProxyAgent (opts) {
  65. if (!(this instanceof ProxyAgent)) return new ProxyAgent(opts);
  66. if ('string' == typeof opts) opts = url.parse(opts);
  67. if (!opts) throw new TypeError('an HTTP(S) proxy server `host` and `protocol` must be specified!');
  68. debug('creating new ProxyAgent instance: %o', opts);
  69. Agent.call(this, connect);
  70. var proxies;
  71. if (opts.proxies) {
  72. proxies = Object.assign({}, exports.proxies, opts.proxies);
  73. } else {
  74. proxies = exports.proxies;
  75. }
  76. // get the requested proxy "protocol"
  77. var protocol = opts.protocol;
  78. if (!protocol) {
  79. throw new TypeError('You must specify a "protocol" for the ' +
  80. 'proxy type (' + Object.keys(proxies).join(', ') + ')');
  81. }
  82. // strip the trailing ":" if present
  83. if (':' == protocol[protocol.length - 1]) {
  84. protocol = protocol.substring(0, protocol.length - 1);
  85. }
  86. // get the proxy `http.Agent` creation function
  87. var proxyFn = proxies[protocol];
  88. if ('function' != typeof proxyFn) {
  89. throw new TypeError('unsupported proxy protocol: "' + protocol + '"');
  90. }
  91. this.proxy = opts;
  92. // format the proxy info back into a URI, since an opts object
  93. // could have been passed in originally. This generated URI is
  94. // used as part of the "key" for the LRU cache
  95. this.proxyUri = url.format({
  96. protocol: protocol + ':',
  97. slashes: true,
  98. auth:opts.auth,
  99. hostname: opts.hostname || opts.host,
  100. port: opts.port
  101. });
  102. this.proxyFn = proxyFn;
  103. }
  104. inherits(ProxyAgent, Agent);
  105. /**
  106. *
  107. */
  108. function connect (req, opts) {
  109. // create the "key" for the LRU cache
  110. var key = this.proxyUri;
  111. if (opts.secureEndpoint) key += ' secure';
  112. // attempt to get a cached `http.Agent` instance first
  113. var agent = exports.cache.get(key);
  114. if (!agent) {
  115. // get an `http.Agent` instance from protocol-specific agent function
  116. agent = this.proxyFn(this.proxy, opts.secureEndpoint);
  117. if (agent) {
  118. exports.cache.set(key, agent);
  119. }
  120. } else {
  121. debug('cache hit with key: %o', key);
  122. }
  123. return agent;
  124. }