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