(url, dest, redirectsLeft)
| 164 | |
| 165 | // GET with manual redirect following (GitHub release URLs redirect to a CDN). |
| 166 | function download(url, dest, redirectsLeft) { |
| 167 | return new Promise(function (resolve, reject) { |
| 168 | var https = require('https'); |
| 169 | // timeout is an idle/inactivity timeout — it won't kill a slow-but-progressing |
| 170 | // download, only a stalled connection (so a blocked mirror fails fast with |
| 171 | // guidance instead of hanging the user's command forever). |
| 172 | var req = https.get(url, { headers: { 'User-Agent': 'codegraph-npm-shim' }, timeout: 30000 }, function (res) { |
| 173 | var status = res.statusCode; |
| 174 | if (status >= 300 && status < 400 && res.headers.location) { |
| 175 | res.resume(); |
| 176 | if (redirectsLeft <= 0) { reject(new Error('too many redirects')); return; } |
| 177 | download(new URL(res.headers.location, url).toString(), dest, redirectsLeft - 1).then(resolve, reject); |
| 178 | return; |
| 179 | } |
| 180 | if (status !== 200) { res.resume(); reject(new Error('HTTP ' + status)); return; } |
| 181 | var file = fs.createWriteStream(dest); |
| 182 | res.on('error', reject); |
| 183 | res.pipe(file); |
| 184 | file.on('error', reject); |
| 185 | file.on('finish', function () { file.close(function () { resolve(); }); }); |
| 186 | }); |
| 187 | req.on('timeout', function () { req.destroy(new Error('connection timed out')); }); |
| 188 | req.on('error', reject); |
| 189 | }); |
| 190 | } |
| 191 | |
| 192 | // Best-effort integrity check. When the release publishes a SHA256SUMS file, the |
| 193 | // downloaded archive MUST match its listed hash or we abort. When that file is |
no test coverage detected