Path: blob/main/src/vs/base/parts/request/common/requestImpl.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { bufferToStream, VSBuffer } from '../../../common/buffer.js';6import { CancellationToken } from '../../../common/cancellation.js';7import { canceled } from '../../../common/errors.js';8import { IHeaders, IRequestContext, IRequestOptions, OfflineError } from './request.js';910export async function request(options: IRequestOptions, token: CancellationToken, isOnline?: () => boolean): Promise<IRequestContext> {11if (token.isCancellationRequested) {12throw canceled();13}1415const cancellation = new AbortController();16const disposable = token.onCancellationRequested(() => cancellation.abort());17const signal = options.timeout ? AbortSignal.any([18cancellation.signal,19AbortSignal.timeout(options.timeout),20]) : cancellation.signal;2122try {23const fetchInit: RequestInit = {24method: options.type || 'GET',25headers: getRequestHeaders(options),26body: options.data,27signal28};29if (options.disableCache) {30fetchInit.cache = 'no-store';31}32const res = await fetch(options.url || '', fetchInit);33return {34res: {35statusCode: res.status,36headers: getResponseHeaders(res),37},38stream: bufferToStream(VSBuffer.wrap(new Uint8Array(await res.arrayBuffer()))),39};40} catch (err) {41if (isOnline && !isOnline()) {42throw new OfflineError();43}44if (err?.name === 'AbortError') {45throw canceled();46}47if (err?.name === 'TimeoutError') {48throw new Error(`Fetch timeout: ${options.timeout}ms`);49}50throw err;51} finally {52disposable.dispose();53}54}5556function getRequestHeaders(options: IRequestOptions) {57if (options.headers || options.user || options.password || options.proxyAuthorization) {58const headers = new Headers();59outer: for (const k in options.headers) {60switch (k.toLowerCase()) {61case 'user-agent':62case 'accept-encoding':63case 'content-length':64// unsafe headers65continue outer;66}67const header = options.headers[k];68if (typeof header === 'string') {69headers.set(k, header);70} else if (Array.isArray(header)) {71for (const h of header) {72headers.append(k, h);73}74}75}76if (options.user || options.password) {77headers.set('Authorization', 'Basic ' + btoa(`${options.user || ''}:${options.password || ''}`));78}79if (options.proxyAuthorization) {80headers.set('Proxy-Authorization', options.proxyAuthorization);81}82return headers;83}84return undefined;85}8687function getResponseHeaders(res: Response): IHeaders {88const headers: IHeaders = Object.create(null);89res.headers.forEach((value, key) => {90if (headers[key]) {91if (Array.isArray(headers[key])) {92headers[key].push(value);93} else {94headers[key] = [headers[key], value];95}96} else {97headers[key] = value;98}99});100return headers;101}102103104