agent.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. 'use strict'
  2. const LRU = require('lru-cache')
  3. const url = require('url')
  4. let AGENT_CACHE = new LRU({ max: 50 })
  5. let HttpsAgent
  6. let HttpAgent
  7. module.exports = getAgent
  8. function getAgent (uri, opts) {
  9. const parsedUri = url.parse(typeof uri === 'string' ? uri : uri.url)
  10. const isHttps = parsedUri.protocol === 'https:'
  11. const pxuri = getProxyUri(uri, opts)
  12. const key = [
  13. `https:${isHttps}`,
  14. pxuri
  15. ? `proxy:${pxuri.protocol}//${pxuri.host}:${pxuri.port}`
  16. : '>no-proxy<',
  17. `local-address:${opts.localAddress || '>no-local-address<'}`,
  18. `strict-ssl:${isHttps ? !!opts.strictSSL : '>no-strict-ssl<'}`,
  19. `ca:${(isHttps && opts.ca) || '>no-ca<'}`,
  20. `cert:${(isHttps && opts.cert) || '>no-cert<'}`,
  21. `key:${(isHttps && opts.key) || '>no-key<'}`
  22. ].join(':')
  23. if (opts.agent != null) { // `agent: false` has special behavior!
  24. return opts.agent
  25. }
  26. if (AGENT_CACHE.peek(key)) {
  27. return AGENT_CACHE.get(key)
  28. }
  29. if (pxuri) {
  30. const proxy = getProxy(pxuri, opts, isHttps)
  31. AGENT_CACHE.set(key, proxy)
  32. return proxy
  33. }
  34. if (isHttps && !HttpsAgent) {
  35. HttpsAgent = require('agentkeepalive').HttpsAgent
  36. } else if (!isHttps && !HttpAgent) {
  37. HttpAgent = require('agentkeepalive')
  38. }
  39. // If opts.timeout is zero, set the agentTimeout to zero as well. A timeout
  40. // of zero disables the timeout behavior (OS limits still apply). Else, if
  41. // opts.timeout is a non-zero value, set it to timeout + 1, to ensure that
  42. // the node-fetch-npm timeout will always fire first, giving us more
  43. // consistent errors.
  44. const agentTimeout = opts.timeout === 0 ? 0 : opts.timeout + 1
  45. const agent = isHttps ? new HttpsAgent({
  46. maxSockets: opts.maxSockets || 15,
  47. ca: opts.ca,
  48. cert: opts.cert,
  49. key: opts.key,
  50. localAddress: opts.localAddress,
  51. rejectUnauthorized: opts.strictSSL,
  52. timeout: agentTimeout
  53. }) : new HttpAgent({
  54. maxSockets: opts.maxSockets || 15,
  55. localAddress: opts.localAddress,
  56. timeout: agentTimeout
  57. })
  58. AGENT_CACHE.set(key, agent)
  59. return agent
  60. }
  61. function checkNoProxy (uri, opts) {
  62. const host = url.parse(uri).hostname.split('.').reverse()
  63. let noproxy = (opts.noProxy || getProcessEnv('no_proxy'))
  64. if (typeof noproxy === 'string') {
  65. noproxy = noproxy.split(/\s*,\s*/g)
  66. }
  67. return noproxy && noproxy.some(no => {
  68. const noParts = no.split('.').filter(x => x).reverse()
  69. if (!noParts.length) { return false }
  70. for (let i = 0; i < noParts.length; i++) {
  71. if (host[i] !== noParts[i]) {
  72. return false
  73. }
  74. }
  75. return true
  76. })
  77. }
  78. module.exports.getProcessEnv = getProcessEnv
  79. function getProcessEnv (env) {
  80. if (!env) { return }
  81. let value
  82. if (Array.isArray(env)) {
  83. for (let e of env) {
  84. value = process.env[e] ||
  85. process.env[e.toUpperCase()] ||
  86. process.env[e.toLowerCase()]
  87. if (typeof value !== 'undefined') { break }
  88. }
  89. }
  90. if (typeof env === 'string') {
  91. value = process.env[env] ||
  92. process.env[env.toUpperCase()] ||
  93. process.env[env.toLowerCase()]
  94. }
  95. return value
  96. }
  97. function getProxyUri (uri, opts) {
  98. const protocol = url.parse(uri).protocol
  99. const proxy = opts.proxy || (
  100. protocol === 'https:' && getProcessEnv('https_proxy')
  101. ) || (
  102. protocol === 'http:' && getProcessEnv(['https_proxy', 'http_proxy', 'proxy'])
  103. )
  104. if (!proxy) { return null }
  105. const parsedProxy = (typeof proxy === 'string') ? url.parse(proxy) : proxy
  106. return !checkNoProxy(uri, opts) && parsedProxy
  107. }
  108. let HttpProxyAgent
  109. let HttpsProxyAgent
  110. let SocksProxyAgent
  111. function getProxy (proxyUrl, opts, isHttps) {
  112. let popts = {
  113. host: proxyUrl.hostname,
  114. port: proxyUrl.port,
  115. protocol: proxyUrl.protocol,
  116. path: proxyUrl.path,
  117. auth: proxyUrl.auth,
  118. ca: opts.ca,
  119. cert: opts.cert,
  120. key: opts.key,
  121. timeout: opts.timeout === 0 ? 0 : opts.timeout + 1,
  122. localAddress: opts.localAddress,
  123. maxSockets: opts.maxSockets || 15,
  124. rejectUnauthorized: opts.strictSSL
  125. }
  126. if (proxyUrl.protocol === 'http:' || proxyUrl.protocol === 'https:') {
  127. if (!isHttps) {
  128. if (!HttpProxyAgent) {
  129. HttpProxyAgent = require('http-proxy-agent')
  130. }
  131. return new HttpProxyAgent(popts)
  132. } else {
  133. if (!HttpsProxyAgent) {
  134. HttpsProxyAgent = require('https-proxy-agent')
  135. }
  136. return new HttpsProxyAgent(popts)
  137. }
  138. }
  139. if (proxyUrl.protocol.startsWith('socks')) {
  140. if (!SocksProxyAgent) {
  141. SocksProxyAgent = require('socks-proxy-agent')
  142. }
  143. return new SocksProxyAgent(popts)
  144. }
  145. }