1'use strict'
2
3const { HttpProxyAgent } = require('http-proxy-agent')
4const { HttpsProxyAgent } = require('https-proxy-agent')
5const { SocksProxyAgent } = require('socks-proxy-agent')
6const { LRUCache } = require('lru-cache')
7const { InvalidProxyProtocolError } = require('./errors.js')
8
9const PROXY_CACHE = new LRUCache({ max: 20 })
10
11const SOCKS_PROTOCOLS = new Set(SocksProxyAgent.protocols)
12
13const PROXY_ENV_KEYS = new Set(['https_proxy', 'http_proxy', 'proxy', 'no_proxy'])
14
15const PROXY_ENV = Object.entries(process.env).reduce((acc, [key, value]) => {
16  key = key.toLowerCase()
17  if (PROXY_ENV_KEYS.has(key)) {
18    acc[key] = value
19  }
20  return acc
21}, {})
22
23const getProxyAgent = (url) => {
24  url = new URL(url)
25
26  const protocol = url.protocol.slice(0, -1)
27  if (SOCKS_PROTOCOLS.has(protocol)) {
28    return SocksProxyAgent
29  }
30  if (protocol === 'https' || protocol === 'http') {
31    return [HttpProxyAgent, HttpsProxyAgent]
32  }
33
34  throw new InvalidProxyProtocolError(url)
35}
36
37const isNoProxy = (url, noProxy) => {
38  if (typeof noProxy === 'string') {
39    noProxy = noProxy.split(',').map((p) => p.trim()).filter(Boolean)
40  }
41
42  if (!noProxy || !noProxy.length) {
43    return false
44  }
45
46  const hostSegments = url.hostname.split('.').reverse()
47
48  return noProxy.some((no) => {
49    const noSegments = no.split('.').filter(Boolean).reverse()
50    if (!noSegments.length) {
51      return false
52    }
53
54    for (let i = 0; i < noSegments.length; i++) {
55      if (hostSegments[i] !== noSegments[i]) {
56        return false
57      }
58    }
59
60    return true
61  })
62}
63
64const getProxy = (url, { proxy, noProxy }) => {
65  url = new URL(url)
66
67  if (!proxy) {
68    proxy = url.protocol === 'https:'
69      ? PROXY_ENV.https_proxy
70      : PROXY_ENV.https_proxy || PROXY_ENV.http_proxy || PROXY_ENV.proxy
71  }
72
73  if (!noProxy) {
74    noProxy = PROXY_ENV.no_proxy
75  }
76
77  if (!proxy || isNoProxy(url, noProxy)) {
78    return null
79  }
80
81  return new URL(proxy)
82}
83
84module.exports = {
85  getProxyAgent,
86  getProxy,
87  proxyCache: PROXY_CACHE,
88}
89