|
5 | 5 | */ |
6 | 6 | const fs = require('fs'); |
7 | 7 | const path = require('path'); |
| 8 | +const http = require('http'); |
8 | 9 | const https = require('https'); |
| 10 | +const { URL } = require('url'); |
| 11 | +// Respect proxy settings from environment (HTTP(S)_PROXY, NO_PROXY) and npm configs |
| 12 | +let getProxyForUrl; |
| 13 | +let HttpsProxyAgent; |
| 14 | +let HttpProxyAgent; |
| 15 | +try { |
| 16 | + ({ getProxyForUrl } = require('proxy-from-env')); |
| 17 | + ({ HttpsProxyAgent } = require('https-proxy-agent')); |
| 18 | + ({ HttpProxyAgent } = require('http-proxy-agent')); |
| 19 | +} catch (_) { |
| 20 | + // Dependencies may not resolve if a user vendors this; fall back to no-proxy mode. |
| 21 | +} |
9 | 22 |
|
10 | 23 | const REPO = 'zxch3n/loctok'; |
11 | 24 |
|
@@ -41,13 +54,53 @@ function ensureDir(p) { |
41 | 54 | fs.mkdirSync(p, { recursive: true }); |
42 | 55 | } |
43 | 56 |
|
| 57 | +function chooseAgent(u) { |
| 58 | + try { |
| 59 | + if (!getProxyForUrl || (!HttpsProxyAgent && !HttpProxyAgent)) return undefined; |
| 60 | + const proxy = getProxyForUrl(u) || |
| 61 | + // fallback to npm-specific envs if set |
| 62 | + (new URL(u).protocol === 'https:' |
| 63 | + ? (process.env.npm_config_https_proxy || process.env.npm_config_proxy) |
| 64 | + : (process.env.npm_config_http_proxy || process.env.npm_config_proxy)); |
| 65 | + if (!proxy) return undefined; |
| 66 | + const isHttps = new URL(u).protocol === 'https:'; |
| 67 | + if (isHttps && HttpsProxyAgent) return new HttpsProxyAgent(proxy); |
| 68 | + if (!isHttps && HttpProxyAgent) return new HttpProxyAgent(proxy); |
| 69 | + } catch (_) { |
| 70 | + return undefined; |
| 71 | + } |
| 72 | + return undefined; |
| 73 | +} |
| 74 | + |
| 75 | +function getLib(u) { |
| 76 | + const proto = new URL(u).protocol; |
| 77 | + return proto === 'http:' ? http : https; |
| 78 | +} |
| 79 | + |
| 80 | +function toAbsoluteLocation(currentUrl, location) { |
| 81 | + try { |
| 82 | + return new URL(location, currentUrl).toString(); |
| 83 | + } catch (_) { |
| 84 | + return location; // best effort |
| 85 | + } |
| 86 | +} |
| 87 | + |
44 | 88 | function download(url, dest) { |
45 | 89 | return new Promise((resolve, reject) => { |
46 | 90 | const doReq = (u, redirectsLeft = 5) => { |
47 | | - const req = https.get(u, { headers: { 'User-Agent': 'loctok-installer' } }, (res) => { |
| 91 | + const lib = getLib(u); |
| 92 | + const opts = { |
| 93 | + headers: { 'User-Agent': 'loctok-installer' }, |
| 94 | + }; |
| 95 | + const agent = chooseAgent(u); |
| 96 | + if (agent) opts.agent = agent; |
| 97 | + |
| 98 | + const req = lib.get(u, opts, (res) => { |
48 | 99 | if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { |
49 | 100 | if (redirectsLeft === 0) return reject(new Error('Too many redirects')); |
50 | | - return doReq(res.headers.location, redirectsLeft - 1); |
| 101 | + const next = toAbsoluteLocation(u, res.headers.location); |
| 102 | + res.resume(); // drain |
| 103 | + return doReq(next, redirectsLeft - 1); |
51 | 104 | } |
52 | 105 | if (res.statusCode !== 200) { |
53 | 106 | return reject(new Error(`HTTP ${res.statusCode} for ${u}`)); |
|
0 commit comments