feat(core): use experiment flags for default fetch timeouts (#24261)

This commit is contained in:
Yuna Seol
2026-04-07 18:35:04 -04:00
committed by GitHub
parent 986293bd38
commit 3c5b5db034
10 changed files with 164 additions and 20 deletions

View File

@@ -4,21 +4,37 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { updateGlobalFetchTimeouts } from './fetch.js';
import { describe, it, expect, vi, beforeEach, afterAll } from 'vitest';
import {
isPrivateIp,
isPrivateIpAsync,
isAddressPrivate,
fetchWithTimeout,
} from './fetch.js';
import * as dnsPromises from 'node:dns/promises';
import type { LookupAddress, LookupAllOptions } from 'node:dns';
import ipaddr from 'ipaddr.js';
const { setGlobalDispatcher, Agent, ProxyAgent } = vi.hoisted(() => ({
setGlobalDispatcher: vi.fn(),
Agent: vi.fn(),
ProxyAgent: vi.fn(),
}));
vi.mock('undici', () => ({
setGlobalDispatcher,
Agent,
ProxyAgent,
}));
vi.mock('node:dns/promises', () => ({
lookup: vi.fn(),
}));
// Import after mocks are established
const {
isPrivateIp,
isPrivateIpAsync,
isAddressPrivate,
fetchWithTimeout,
setGlobalProxy,
} = await import('./fetch.js');
// Mock global fetch
const originalFetch = global.fetch;
global.fetch = vi.fn();
@@ -183,4 +199,19 @@ describe('fetch utils', () => {
);
});
});
describe('setGlobalProxy', () => {
it('should configure ProxyAgent with experiment flag timeout', () => {
const proxyUrl = 'http://proxy.example.com';
updateGlobalFetchTimeouts(45773134);
setGlobalProxy(proxyUrl);
expect(ProxyAgent).toHaveBeenCalledWith({
uri: proxyUrl,
headersTimeout: 45773134,
bodyTimeout: 45773134,
});
expect(setGlobalDispatcher).toHaveBeenCalled();
});
});
});

View File

@@ -10,9 +10,6 @@ import { Agent, ProxyAgent, setGlobalDispatcher } from 'undici';
import ipaddr from 'ipaddr.js';
import { lookup } from 'node:dns/promises';
const DEFAULT_HEADERS_TIMEOUT = 300000; // 5 minutes
const DEFAULT_BODY_TIMEOUT = 300000; // 5 minutes
export class FetchError extends Error {
constructor(
message: string,
@@ -31,14 +28,36 @@ export class PrivateIpError extends Error {
}
}
let defaultTimeout = 300000; // 5 minutes
let currentProxy: string | undefined = undefined;
// Configure default global dispatcher with higher timeouts
setGlobalDispatcher(
new Agent({
headersTimeout: DEFAULT_HEADERS_TIMEOUT,
bodyTimeout: DEFAULT_BODY_TIMEOUT,
headersTimeout: defaultTimeout,
bodyTimeout: defaultTimeout,
}),
);
export function updateGlobalFetchTimeouts(timeoutMs: number) {
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
throw new RangeError(
`Invalid timeout value: ${timeoutMs}. Must be a positive finite number.`,
);
}
defaultTimeout = timeoutMs;
if (currentProxy) {
setGlobalProxy(currentProxy);
} else {
setGlobalDispatcher(
new Agent({
headersTimeout: defaultTimeout,
bodyTimeout: defaultTimeout,
}),
);
}
}
/**
* Sanitizes a hostname by stripping IPv6 brackets if present.
*/
@@ -191,11 +210,12 @@ export async function fetchWithTimeout(
}
export function setGlobalProxy(proxy: string) {
currentProxy = proxy;
setGlobalDispatcher(
new ProxyAgent({
uri: proxy,
headersTimeout: DEFAULT_HEADERS_TIMEOUT,
bodyTimeout: DEFAULT_BODY_TIMEOUT,
headersTimeout: defaultTimeout,
bodyTimeout: defaultTimeout,
}),
);
}