From c9c66d7a1d5ca71c1c1b0329f1df61542bcdcade Mon Sep 17 00:00:00 2001 From: Shakker Date: Wed, 6 May 2026 03:17:57 +0100 Subject: [PATCH] fix: restore no-proxy dispatcher boundary --- .../net/undici-global-dispatcher.test.ts | 34 +++++-------------- src/infra/net/undici-global-dispatcher.ts | 33 ++++++++---------- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/src/infra/net/undici-global-dispatcher.test.ts b/src/infra/net/undici-global-dispatcher.test.ts index cecc34a1ce6..01db4cc5f00 100644 --- a/src/infra/net/undici-global-dispatcher.test.ts +++ b/src/infra/net/undici-global-dispatcher.test.ts @@ -106,32 +106,24 @@ describe("ensureGlobalUndiciStreamTimeouts", () => { vi.mocked(resolveEnvHttpProxyAgentOptions).mockReturnValue(undefined); }); - it("replaces direct Agent dispatcher with extended stream timeouts when no env proxy is configured", () => { + it("records timeout bridge without importing undici when no env proxy is configured", () => { getDefaultAutoSelectFamily.mockReturnValue(true); ensureGlobalUndiciStreamTimeouts(); - expect(loadUndiciGlobalDispatcherDeps).toHaveBeenCalledTimes(1); - expect(setGlobalDispatcher).toHaveBeenCalledTimes(1); - const next = getCurrentDispatcher() as { options?: Record }; - expect(next).toBeInstanceOf(Agent); - expect(next.options?.bodyTimeout).toBe(DEFAULT_UNDICI_STREAM_TIMEOUT_MS); - expect(next.options?.headersTimeout).toBe(DEFAULT_UNDICI_STREAM_TIMEOUT_MS); - expect(next.options?.connect).toEqual({ - autoSelectFamily: true, - autoSelectFamilyAttemptTimeout: 300, - }); + expect(loadUndiciGlobalDispatcherDeps).not.toHaveBeenCalled(); + expect(setGlobalDispatcher).not.toHaveBeenCalled(); expect(undiciGlobalDispatcherModule._globalUndiciStreamTimeoutMs).toBe( DEFAULT_UNDICI_STREAM_TIMEOUT_MS, ); }); - it("does not initialize the undici global dispatcher during no-proxy bootstrap", () => { + it("does not initialize the undici global dispatcher in a no-proxy subprocess", () => { const moduleUrl = pathToFileURL(path.resolve("src/infra/net/undici-global-dispatcher.ts")).href; const source = ` const dispatcherKey = Symbol.for("undici.globalDispatcher.1"); const mod = await import(${JSON.stringify(moduleUrl)}); - mod.ensureGlobalUndiciEnvProxyDispatcher(); + mod.ensureGlobalUndiciStreamTimeouts({ timeoutMs: 1_900_000 }); if (globalThis[dispatcherKey] !== undefined) { throw new Error("undici global dispatcher was initialized"); } @@ -222,12 +214,8 @@ describe("ensureGlobalUndiciStreamTimeouts", () => { it("does not lower global stream timeouts below the default floor", () => { ensureGlobalUndiciStreamTimeouts({ timeoutMs: 15_000 }); - expect(loadUndiciGlobalDispatcherDeps).toHaveBeenCalledTimes(1); - expect(setGlobalDispatcher).toHaveBeenCalledTimes(1); - const next = getCurrentDispatcher() as { options?: Record }; - expect(next).toBeInstanceOf(Agent); - expect(next.options?.bodyTimeout).toBe(DEFAULT_UNDICI_STREAM_TIMEOUT_MS); - expect(next.options?.headersTimeout).toBe(DEFAULT_UNDICI_STREAM_TIMEOUT_MS); + expect(loadUndiciGlobalDispatcherDeps).not.toHaveBeenCalled(); + expect(setGlobalDispatcher).not.toHaveBeenCalled(); expect(undiciGlobalDispatcherModule._globalUndiciStreamTimeoutMs).toBe( DEFAULT_UNDICI_STREAM_TIMEOUT_MS, ); @@ -238,12 +226,8 @@ describe("ensureGlobalUndiciStreamTimeouts", () => { ensureGlobalUndiciStreamTimeouts({ timeoutMs }); - expect(loadUndiciGlobalDispatcherDeps).toHaveBeenCalledTimes(1); - expect(setGlobalDispatcher).toHaveBeenCalledTimes(1); - const next = getCurrentDispatcher() as { options?: Record }; - expect(next).toBeInstanceOf(Agent); - expect(next.options?.bodyTimeout).toBe(timeoutMs); - expect(next.options?.headersTimeout).toBe(timeoutMs); + expect(loadUndiciGlobalDispatcherDeps).not.toHaveBeenCalled(); + expect(setGlobalDispatcher).not.toHaveBeenCalled(); expect(undiciGlobalDispatcherModule._globalUndiciStreamTimeoutMs).toBe(timeoutMs); }); diff --git a/src/infra/net/undici-global-dispatcher.ts b/src/infra/net/undici-global-dispatcher.ts index f59de617858..47660ebfde9 100644 --- a/src/infra/net/undici-global-dispatcher.ts +++ b/src/infra/net/undici-global-dispatcher.ts @@ -99,12 +99,19 @@ export function ensureGlobalUndiciStreamTimeouts(opts?: { timeoutMs?: number }): } const timeoutMs = Math.max(DEFAULT_UNDICI_STREAM_TIMEOUT_MS, Math.floor(timeoutMsRaw)); _globalUndiciStreamTimeoutMs = timeoutMs; + if (!hasEnvHttpProxyAgentConfigured()) { + lastAppliedTimeoutKey = null; + return; + } const runtime = loadUndiciGlobalDispatcherDeps(); - const { Agent, EnvHttpProxyAgent, setGlobalDispatcher } = runtime; + const { EnvHttpProxyAgent, setGlobalDispatcher } = runtime; const kind = resolveCurrentDispatcherKind(runtime); if (kind === null) { return; } + if (kind !== "env-proxy") { + return; + } const autoSelectFamily = resolveUndiciAutoSelectFamily(); const nextKey = resolveDispatcherKey({ kind, timeoutMs, autoSelectFamily }); @@ -114,23 +121,13 @@ export function ensureGlobalUndiciStreamTimeouts(opts?: { timeoutMs?: number }): const connect = createUndiciAutoSelectFamilyConnectOptions(autoSelectFamily); try { - if (kind === "env-proxy") { - const proxyOptions = { - ...resolveEnvHttpProxyAgentOptions(), - bodyTimeout: timeoutMs, - headersTimeout: timeoutMs, - ...(connect ? { connect } : {}), - } as ConstructorParameters[0]; - setGlobalDispatcher(new EnvHttpProxyAgent(proxyOptions)); - } else { - setGlobalDispatcher( - new Agent({ - bodyTimeout: timeoutMs, - headersTimeout: timeoutMs, - ...(connect ? { connect } : {}), - }), - ); - } + const proxyOptions = { + ...resolveEnvHttpProxyAgentOptions(), + bodyTimeout: timeoutMs, + headersTimeout: timeoutMs, + ...(connect ? { connect } : {}), + } as ConstructorParameters[0]; + setGlobalDispatcher(new EnvHttpProxyAgent(proxyOptions)); lastAppliedTimeoutKey = nextKey; } catch { // Best-effort hardening only.