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