* Common logic for downloading and verifying a binary. * Includes stall detection (aborts if no bytes for 60s) and retry logic.
(
binaryUrl: string,
expectedChecksum: string,
binaryPath: string,
requestConfig: Record<string, unknown> = {},
)
| 291 | * Includes stall detection (aborts if no bytes for 60s) and retry logic. |
| 292 | */ |
| 293 | async function downloadAndVerifyBinary( |
| 294 | binaryUrl: string, |
| 295 | expectedChecksum: string, |
| 296 | binaryPath: string, |
| 297 | requestConfig: Record<string, unknown> = {}, |
| 298 | ) { |
| 299 | let lastError: Error | undefined |
| 300 | |
| 301 | for (let attempt = 1; attempt <= MAX_DOWNLOAD_RETRIES; attempt++) { |
| 302 | const controller = new AbortController() |
| 303 | let stallTimer: ReturnType<typeof setTimeout> | undefined |
| 304 | |
| 305 | const clearStallTimer = () => { |
| 306 | if (stallTimer) { |
| 307 | clearTimeout(stallTimer) |
| 308 | stallTimer = undefined |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | const resetStallTimer = () => { |
| 313 | clearStallTimer() |
| 314 | stallTimer = setTimeout(c => c.abort(), getStallTimeoutMs(), controller) |
| 315 | } |
| 316 | |
| 317 | try { |
| 318 | // Start the stall timer before the request |
| 319 | resetStallTimer() |
| 320 | |
| 321 | const response = await axios.get(binaryUrl, { |
| 322 | timeout: 5 * 60000, // 5 minute total timeout |
| 323 | responseType: 'arraybuffer', |
| 324 | signal: controller.signal, |
| 325 | onDownloadProgress: () => { |
| 326 | // Reset stall timer on each chunk of data received |
| 327 | resetStallTimer() |
| 328 | }, |
| 329 | ...requestConfig, |
| 330 | }) |
| 331 | |
| 332 | clearStallTimer() |
| 333 | |
| 334 | // Verify checksum |
| 335 | const hash = createHash('sha256') |
| 336 | hash.update(response.data) |
| 337 | const actualChecksum = hash.digest('hex') |
| 338 | |
| 339 | if (actualChecksum !== expectedChecksum) { |
| 340 | throw new Error( |
| 341 | `Checksum mismatch: expected ${expectedChecksum}, got ${actualChecksum}`, |
| 342 | ) |
| 343 | } |
| 344 | |
| 345 | // Write binary to disk |
| 346 | await writeFile(binaryPath, Buffer.from(response.data)) |
| 347 | await chmod(binaryPath, 0o755) |
| 348 | |
| 349 | // Success - return early |
| 350 | return |
no test coverage detected