74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
import * as undici from "undici";
|
|
|
|
export const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
|
|
|
|
export const HTTP_IDLE_TIMEOUT_CHOICES = [
|
|
{ label: "30 sec", timeoutMs: 30_000 },
|
|
{ label: "1 min", timeoutMs: 60_000 },
|
|
{ label: "2 min", timeoutMs: 120_000 },
|
|
{ label: "5 min", timeoutMs: 300_000 },
|
|
{ label: "disabled", timeoutMs: 0 },
|
|
] as const;
|
|
|
|
const originalGlobalFetch = globalThis.fetch;
|
|
let installedGlobalFetch: typeof globalThis.fetch | undefined;
|
|
|
|
export function parseHttpIdleTimeoutMs(value: unknown): number | undefined {
|
|
if (typeof value === "string") {
|
|
const trimmed = value.trim();
|
|
if (trimmed.toLowerCase() === "disabled") {
|
|
return 0;
|
|
}
|
|
if (trimmed.length === 0) {
|
|
return undefined;
|
|
}
|
|
return parseHttpIdleTimeoutMs(Number(trimmed));
|
|
}
|
|
|
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
return undefined;
|
|
}
|
|
return Math.floor(value);
|
|
}
|
|
|
|
export function formatHttpIdleTimeoutMs(timeoutMs: number): string {
|
|
const choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs);
|
|
if (choice) {
|
|
return choice.label;
|
|
}
|
|
return `${timeoutMs / 1000} sec`;
|
|
}
|
|
|
|
export function applyHttpProxySettings(httpProxy: string | undefined): void {
|
|
const proxy = httpProxy?.trim();
|
|
if (!proxy) return;
|
|
process.env.HTTP_PROXY ??= proxy;
|
|
process.env.HTTPS_PROXY ??= proxy;
|
|
}
|
|
|
|
export function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {
|
|
const normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);
|
|
if (normalizedTimeoutMs === undefined) {
|
|
throw new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);
|
|
}
|
|
undici.setGlobalDispatcher(
|
|
new undici.EnvHttpProxyAgent({
|
|
allowH2: false,
|
|
bodyTimeout: normalizedTimeoutMs,
|
|
headersTimeout: normalizedTimeoutMs,
|
|
}),
|
|
);
|
|
// Keep fetch and the dispatcher on the same undici implementation. Node 26.0's
|
|
// bundled fetch can otherwise consume compressed responses through npm undici's
|
|
// dispatcher without decompressing them, causing response.json() failures.
|
|
// If a caller replaced fetch after module load, preserve that deliberate override.
|
|
const shouldInstallGlobals =
|
|
installedGlobalFetch === undefined
|
|
? globalThis.fetch === originalGlobalFetch
|
|
: globalThis.fetch === installedGlobalFetch;
|
|
if (shouldInstallGlobals) {
|
|
undici.install?.();
|
|
installedGlobalFetch = globalThis.fetch;
|
|
}
|
|
}
|