electron/lib/browser/api/net-fetch.ts
John Kleinschmidt b2e73d28e2
build: update to yarn v4 (#48994)
* build: update to yarn v4

(cherry picked from commit 6adec744f3411240a63fd194a319b510872eca93)

* chore: fixup types after yarn v4 migration

* chore: update nan yarn.lock patch

* build: automatically install git for dugite
2025-11-19 17:32:30 -05:00

128 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { allowAnyProtocol } from '@electron/internal/common/api/net-client-request';
import { ClientRequestConstructorOptions, ClientRequest, IncomingMessage, Session as SessionT } from 'electron/main';
import { Readable, Writable, isReadable } from 'stream';
function createDeferredPromise<T, E extends Error = Error> (): { promise: Promise<T>; resolve: (x: T) => void; reject: (e: E) => void; } {
let res: (x: T) => void;
let rej: (e: E) => void;
const promise = new Promise<T>((resolve, reject) => {
res = resolve;
rej = reject;
});
return { promise, resolve: res!, reject: rej! };
}
export function fetchWithSession (input: RequestInfo, init: (RequestInit & {bypassCustomProtocolHandlers?: boolean}) | undefined, session: SessionT | undefined,
request: (options: ClientRequestConstructorOptions | string) => ClientRequest) {
const p = createDeferredPromise<Response>();
let req: Request;
try {
req = new Request(input, init);
} catch (e: any) {
p.reject(e);
return p.promise;
}
if (req.signal.aborted) {
// 1. Abort the fetch() call with p, request, null, and
// requestObjects signals abort reason.
const error = (req.signal as any).reason ?? new DOMException('The operation was aborted.', 'AbortError');
p.reject(error);
if (req.body != null && isReadable(req.body as unknown as NodeJS.ReadableStream)) {
req.body.cancel(error).catch((err) => {
if (err.code === 'ERR_INVALID_STATE') {
// Node bug?
return;
}
throw err;
});
}
// 2. Return p.
return p.promise;
}
let locallyAborted = false;
req.signal.addEventListener(
'abort',
() => {
// 1. Set locallyAborted to true.
locallyAborted = true;
// 2. Abort the fetch() call with p, request, responseObject,
// and requestObjects signals abort reason.
const error = (req.signal as any).reason ?? new DOMException('The operation was aborted.', 'AbortError');
p.reject(error);
if (req.body != null && isReadable(req.body as unknown as NodeJS.ReadableStream)) {
req.body.cancel(error).catch((err) => {
if (err.code === 'ERR_INVALID_STATE') {
// Node bug?
return;
}
throw err;
});
}
r.abort();
},
{ once: true }
);
const origin = req.headers.get('origin') ?? undefined;
// We can't set credentials to same-origin unless there's an origin set.
const credentials = req.credentials === 'same-origin' && !origin ? 'include' : req.credentials;
const r = request(allowAnyProtocol({
session,
method: req.method,
url: req.url,
origin,
credentials,
cache: req.cache,
referrerPolicy: req.referrerPolicy,
redirect: req.redirect
}));
(r as any)._urlLoaderOptions.bypassCustomProtocolHandlers = !!init?.bypassCustomProtocolHandlers;
// cors is the default mode, but we can't set mode=cors without an origin.
if (req.mode && (req.mode !== 'cors' || origin)) {
r.setHeader('Sec-Fetch-Mode', req.mode);
}
for (const [k, v] of req.headers) {
r.setHeader(k, v);
}
r.on('response', (resp: IncomingMessage) => {
if (locallyAborted) return;
const headers = new Headers();
for (const [k, v] of Object.entries(resp.headers)) {
headers.set(k, Array.isArray(v) ? v.join(', ') : v);
}
const nullBodyStatus = [101, 204, 205, 304];
const body = nullBodyStatus.includes(resp.statusCode) || req.method === 'HEAD' ? null : Readable.toWeb(resp as unknown as Readable) as ReadableStream;
const rResp = new Response(body, {
headers,
status: resp.statusCode,
statusText: resp.statusMessage
});
(rResp as any).__original_resp = resp;
p.resolve(rResp);
});
r.on('error', (err) => {
p.reject(err);
});
// pipeTo expects a WritableStream<Uint8Array>. Node.js' Writable.toWeb returns WritableStream<any>,
// which causes a TS structural mismatch.
const writable = Writable.toWeb(r as unknown as Writable) as unknown as WritableStream<Uint8Array>;
if (!req.body?.pipeTo(writable).then(() => r.end())) { r.end(); }
return p.promise;
}