513
514
private async _fetch(
516
>
dest: string,
517
>
token: CancellationToken,
518
>
onBytes?: (receivedBytes: number, totalBytes: number | undefined) => void,
519
>
): Promise<void> {
520
>
// Delegate to IRequestService (corporate proxy, strictSSL, kerberos,
521
>
// retries, redirect follow). `fs.createWriteStream` (not
522
>
// `IFileService.writeFile`) so that cancelling a multi-MB download
523
>
// aborts promptly via destroy(). Manual pipe (not `stream.pipeline`)
524
>
// because the source is a VSBufferReadableStream — not a Node
525
>
// Readable — so node-stream utilities can't introspect it.
526
>
if (token.isCancellationRequested) {
527
throw new CancellationError();
528
}
530
>
url,
531
>
type: 'GET',
532
>
callSite: 'agentSdkDownloader',
533
>
}, token);
534
>
if (token.isCancellationRequested) {
535
context.stream.destroy();
536
throw new CancellationError();
537
}
539
>
const statusCode = context.res.statusCode ?? 0;
540
>
if (statusCode < 200 || statusCode >= 300) {
541
context.stream.destroy();
542
throw new Error(`HTTP ${statusCode} fetching ${url}`);
543
}
545
>
// The CDN sends `Content-Length` for these static tarballs, which lets
546
>
// us report determinate percentage progress. A missing/garbled header
547
>
// degrades gracefully to an indeterminate (byte-count only) report.
548
>
const totalBytes = parseContentLength(context.res.headers['content-length']);
549
>
550
>
await new Promise<void>((resolve, reject) => {
551
>
const out = fs.createWriteStream(dest);
552
>
let settled = false;
553
>
// Throttle progress so a fast link doesn't fire thousands of
554
>
// samples. The first chunk always passes (lastEmit starts at 0)
555
>
// and 'end' forces a final sample, so consumers see a start and a
556
>
// 100% finish regardless of chunk timing.
557
>
let receivedBytes = 0;
558
>
let lastEmitTime = 0;
559
>
const emitBytes = (force: boolean) => {
560
>
if (!onBytes) {
561
return;
562
}
564
>
if (!force && now - lastEmitTime < PROGRESS_EMIT_THROTTLE_MS) {
565
return;
566
}
568
>
onBytes(receivedBytes, totalBytes);
569
>
};
570
>
const settleResolve = () => {
571
if (settled) { return; }
572
settled = true;