|
| 1 | +import { URL } from 'url'; |
| 2 | + |
| 3 | +// logic from https://github.com/Rob--W/proxy-from-env |
| 4 | + |
| 5 | +export function getProxyForUrl(url: string): URL { |
| 6 | + const parsedUrl = new URL(url); |
| 7 | + let protocol = parsedUrl.protocol; |
| 8 | + let hostname = parsedUrl.host; |
| 9 | + if (typeof hostname !== 'string' || !hostname || typeof protocol !== 'string') { |
| 10 | + return null; // Don't proxy URLs without a valid scheme or host. |
| 11 | + } |
| 12 | + |
| 13 | + setProxyVars(); |
| 14 | + protocol = protocol.split(':', 1)[0]; |
| 15 | + // Stripping ports in this way instead of using parsedUrl.hostname to make |
| 16 | + // sure that the brackets around IPv6 addresses are kept. |
| 17 | + hostname = hostname.replace(/:\d*$/, ''); |
| 18 | + let parsedPort = parseInt(parsedUrl.port, 10) || 0; |
| 19 | + if (!parsedPort) parsedPort = protocol === 'https:' ? 443 : 80; |
| 20 | + if (!shouldProxy(hostname, parsedPort)) { |
| 21 | + return null; // Don't proxy URLs that match NO_PROXY. |
| 22 | + } |
| 23 | + |
| 24 | + let proxy = |
| 25 | + getEnv(`npm_config_${protocol}_proxy`) || |
| 26 | + getEnv(`${protocol}_proxy`) || |
| 27 | + getEnv('npm_config_proxy') || |
| 28 | + getEnv('all_proxy'); |
| 29 | + if (proxy && proxy.indexOf('://') === -1) { |
| 30 | + // Missing scheme in proxy, default to the requested URL's scheme. |
| 31 | + proxy = `${protocol}://${proxy}`; |
| 32 | + } |
| 33 | + if (!proxy) return null; |
| 34 | + return new URL(proxy); |
| 35 | +} |
| 36 | + |
| 37 | +function shouldProxy(hostname: string, port: number): boolean { |
| 38 | + const NO_PROXY = (getEnv('npm_config_no_proxy') || getEnv('no_proxy')).toLowerCase(); |
| 39 | + if (!NO_PROXY) { |
| 40 | + return true; // Always proxy if NO_PROXY is not set. |
| 41 | + } |
| 42 | + if (NO_PROXY === '*') { |
| 43 | + return false; // Never proxy if wildcard is set. |
| 44 | + } |
| 45 | + |
| 46 | + for (const proxy of NO_PROXY.split(/[,\s]/)) { |
| 47 | + if (!proxy) continue; |
| 48 | + const parsedProxy = proxy.match(/^(.+):(\d+)$/); |
| 49 | + let parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy; |
| 50 | + const parsedProxyPort = parsedProxy ? parseInt(parsedProxy[2], 10) : 0; |
| 51 | + if (parsedProxyPort && parsedProxyPort !== port) { |
| 52 | + continue; |
| 53 | + } |
| 54 | + |
| 55 | + if (!parsedProxyHostname.startsWith('.') && !parsedProxyHostname.startsWith('*')) { |
| 56 | + // No wildcards, so stop proxying if there is an exact match. |
| 57 | + if (hostname === parsedProxyHostname) { |
| 58 | + return false; |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + if (parsedProxyHostname.startsWith('*')) { |
| 63 | + // Remove leading wildcard. |
| 64 | + parsedProxyHostname = parsedProxyHostname.slice(1); |
| 65 | + } |
| 66 | + // Stop proxying if the hostname ends with the no_proxy host. |
| 67 | + if (!hostname.endsWith(parsedProxyHostname)) { |
| 68 | + return false; |
| 69 | + } |
| 70 | + } |
| 71 | + return true; |
| 72 | +} |
| 73 | + |
| 74 | +function getEnv(key: string): string { |
| 75 | + return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || ''; |
| 76 | +} |
| 77 | + |
| 78 | +function setProxyVars(): void { |
| 79 | + // Override current environment proxy settings with npm configuration, if any. |
| 80 | + const NPM_HTTPS_PROXY = process.env.npm_config_https_proxy || process.env.npm_config_proxy; |
| 81 | + const NPM_HTTP_PROXY = process.env.npm_config_http_proxy || process.env.npm_config_proxy; |
| 82 | + const NPM_NO_PROXY = process.env.npm_config_no_proxy; |
| 83 | + |
| 84 | + if (NPM_HTTPS_PROXY) process.env.HTTPS_PROXY = NPM_HTTPS_PROXY; |
| 85 | + if (NPM_HTTP_PROXY) process.env.HTTP_PROXY = NPM_HTTP_PROXY; |
| 86 | + if (NPM_NO_PROXY) process.env.NO_PROXY = NPM_NO_PROXY; |
| 87 | +} |
0 commit comments