You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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 getProxyForUrl = require('proxy-from-env').getProxyForUrl;
  11. var http = require('http');
  12. var https = require('https');
  13. var PacProxyAgent = require('pac-proxy-agent');
  14. var HttpProxyAgent = require('http-proxy-agent');
  15. var HttpsProxyAgent = require('https-proxy-agent');
  16. var SocksProxyAgent = require('socks-proxy-agent');
  17. /**
  18. * Module exports.
  19. */
  20. exports = module.exports = ProxyAgent;
  21. /**
  22. * Number of `http.Agent` instances to cache.
  23. *
  24. * This value was arbitrarily chosen... a better
  25. * value could be conceived with some benchmarks.
  26. */
  27. var cacheSize = 20;
  28. /**
  29. * Cache for `http.Agent` instances.
  30. */
  31. exports.cache = new LRU(cacheSize);
  32. /**
  33. * Built-in proxy types.
  34. */
  35. exports.proxies = Object.create(null);
  36. exports.proxies.http = httpOrHttpsProxy;
  37. exports.proxies.https = httpOrHttpsProxy;
  38. exports.proxies.socks = SocksProxyAgent;
  39. exports.proxies.socks4 = SocksProxyAgent;
  40. exports.proxies.socks4a = SocksProxyAgent;
  41. exports.proxies.socks5 = SocksProxyAgent;
  42. exports.proxies.socks5h = SocksProxyAgent;
  43. PacProxyAgent.protocols.forEach(function (protocol) {
  44. exports.proxies['pac+' + protocol] = PacProxyAgent;
  45. });
  46. function httpOrHttps(opts, secureEndpoint) {
  47. if (secureEndpoint) {
  48. // HTTPS
  49. return https.globalAgent;
  50. } else {
  51. // HTTP
  52. return http.globalAgent;
  53. }
  54. }
  55. function httpOrHttpsProxy(opts, secureEndpoint) {
  56. if (secureEndpoint) {
  57. // HTTPS
  58. return new HttpsProxyAgent(opts);
  59. } else {
  60. // HTTP
  61. return new HttpProxyAgent(opts);
  62. }
  63. }
  64. function mapOptsToProxy(opts) {
  65. // NO_PROXY case
  66. if (!opts) {
  67. return {
  68. uri: 'no proxy',
  69. fn: httpOrHttps
  70. };
  71. }
  72. if ('string' == typeof opts) opts = url.parse(opts);
  73. var proxies;
  74. if (opts.proxies) {
  75. proxies = Object.assign({}, exports.proxies, opts.proxies);
  76. } else {
  77. proxies = exports.proxies;
  78. }
  79. // get the requested proxy "protocol"
  80. var protocol = opts.protocol;
  81. if (!protocol) {
  82. throw new TypeError('You must specify a "protocol" for the ' +
  83. 'proxy type (' + Object.keys(proxies).join(', ') + ')');
  84. }
  85. // strip the trailing ":" if present
  86. if (':' == protocol[protocol.length - 1]) {
  87. protocol = protocol.substring(0, protocol.length - 1);
  88. }
  89. // get the proxy `http.Agent` creation function
  90. var proxyFn = proxies[protocol];
  91. if ('function' != typeof proxyFn) {
  92. throw new TypeError('unsupported proxy protocol: "' + protocol + '"');
  93. }
  94. // format the proxy info back into a URI, since an opts object
  95. // could have been passed in originally. This generated URI is used
  96. // as part of the "key" for the LRU cache
  97. return {
  98. opts: opts,
  99. uri: url.format({
  100. protocol: protocol + ':',
  101. slashes: true,
  102. auth: opts.auth,
  103. hostname: opts.hostname || opts.host,
  104. port: opts.port
  105. }),
  106. fn: proxyFn,
  107. }
  108. }
  109. /**
  110. * Attempts to get an `http.Agent` instance based off of the given proxy URI
  111. * information, and the `secure` flag.
  112. *
  113. * An LRU cache is used, to prevent unnecessary creation of proxy
  114. * `http.Agent` instances.
  115. *
  116. * @param {String} uri proxy url
  117. * @param {Boolean} secure true if this is for an HTTPS request, false for HTTP
  118. * @return {http.Agent}
  119. * @api public
  120. */
  121. function ProxyAgent (opts) {
  122. if (!(this instanceof ProxyAgent)) return new ProxyAgent(opts);
  123. debug('creating new ProxyAgent instance: %o', opts);
  124. Agent.call(this, connect);
  125. if (opts) {
  126. var proxy = mapOptsToProxy(opts);
  127. this.proxy = proxy.opts;
  128. this.proxyUri = proxy.uri;
  129. this.proxyFn = proxy.fn;
  130. }
  131. }
  132. inherits(ProxyAgent, Agent);
  133. /**
  134. *
  135. */
  136. function connect (req, opts, fn) {
  137. var proxyOpts = this.proxy;
  138. var proxyUri = this.proxyUri;
  139. var proxyFn = this.proxyFn;
  140. // if we did not instantiate with a proxy, set one per request
  141. if (!proxyOpts) {
  142. var urlOpts = getProxyForUrl(opts);
  143. var proxy = mapOptsToProxy(urlOpts, opts);
  144. proxyOpts = proxy.opts;
  145. proxyUri = proxy.uri;
  146. proxyFn = proxy.fn;
  147. }
  148. // create the "key" for the LRU cache
  149. var key = proxyUri;
  150. if (opts.secureEndpoint) key += ' secure';
  151. // attempt to get a cached `http.Agent` instance first
  152. var agent = exports.cache.get(key);
  153. if (!agent) {
  154. // get an `http.Agent` instance from protocol-specific agent function
  155. agent = proxyFn(proxyOpts, opts.secureEndpoint);
  156. if (agent) {
  157. exports.cache.set(key, agent);
  158. }
  159. } else {
  160. debug('cache hit with key: %o', key);
  161. }
  162. if (!proxyOpts) {
  163. agent.addRequest(req, opts);
  164. } else {
  165. // XXX: agent.callback() is an agent-base-ism
  166. agent.callback(req, opts, fn);
  167. }
  168. }