mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 15:30:39 +00:00
Telegram: preserve proxy-aware global dispatcher
This commit is contained in:
committed by
Peter Steinberger
parent
b3990ad58a
commit
666a4763ee
@@ -5,6 +5,8 @@ import { resetTelegramFetchStateForTests, resolveTelegramFetch } from "./fetch.j
|
||||
const setDefaultAutoSelectFamily = vi.hoisted(() => vi.fn());
|
||||
const setDefaultResultOrder = vi.hoisted(() => vi.fn());
|
||||
const setGlobalDispatcher = vi.hoisted(() => vi.fn());
|
||||
const getGlobalDispatcherState = vi.hoisted(() => ({ value: undefined as unknown }));
|
||||
const getGlobalDispatcher = vi.hoisted(() => vi.fn(() => getGlobalDispatcherState.value));
|
||||
const EnvHttpProxyAgentCtor = vi.hoisted(() =>
|
||||
vi.fn(function MockEnvHttpProxyAgent(this: { options: unknown }, options: unknown) {
|
||||
this.options = options;
|
||||
@@ -29,6 +31,7 @@ vi.mock("node:dns", async () => {
|
||||
|
||||
vi.mock("undici", () => ({
|
||||
EnvHttpProxyAgent: EnvHttpProxyAgentCtor,
|
||||
getGlobalDispatcher,
|
||||
setGlobalDispatcher,
|
||||
}));
|
||||
|
||||
@@ -39,6 +42,8 @@ afterEach(() => {
|
||||
setDefaultAutoSelectFamily.mockReset();
|
||||
setDefaultResultOrder.mockReset();
|
||||
setGlobalDispatcher.mockReset();
|
||||
getGlobalDispatcher.mockClear();
|
||||
getGlobalDispatcherState.value = undefined;
|
||||
EnvHttpProxyAgentCtor.mockClear();
|
||||
vi.unstubAllEnvs();
|
||||
vi.clearAllMocks();
|
||||
@@ -160,6 +165,31 @@ describe("resolveTelegramFetch", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps an existing proxy-like global dispatcher", async () => {
|
||||
getGlobalDispatcherState.value = {
|
||||
constructor: { name: "ProxyAgent" },
|
||||
};
|
||||
globalThis.fetch = vi.fn(async () => ({})) as unknown as typeof fetch;
|
||||
|
||||
resolveTelegramFetch(undefined, { network: { autoSelectFamily: true } });
|
||||
|
||||
expect(setGlobalDispatcher).not.toHaveBeenCalled();
|
||||
expect(EnvHttpProxyAgentCtor).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("updates proxy-like dispatcher when proxy env is configured", async () => {
|
||||
vi.stubEnv("HTTPS_PROXY", "http://127.0.0.1:7890");
|
||||
getGlobalDispatcherState.value = {
|
||||
constructor: { name: "ProxyAgent" },
|
||||
};
|
||||
globalThis.fetch = vi.fn(async () => ({})) as unknown as typeof fetch;
|
||||
|
||||
resolveTelegramFetch(undefined, { network: { autoSelectFamily: true } });
|
||||
|
||||
expect(setGlobalDispatcher).toHaveBeenCalledTimes(1);
|
||||
expect(EnvHttpProxyAgentCtor).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("sets global dispatcher only once across repeated equal decisions", async () => {
|
||||
globalThis.fetch = vi.fn(async () => ({})) as unknown as typeof fetch;
|
||||
resolveTelegramFetch(undefined, { network: { autoSelectFamily: true } });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as dns from "node:dns";
|
||||
import * as net from "node:net";
|
||||
import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
|
||||
import { EnvHttpProxyAgent, getGlobalDispatcher, setGlobalDispatcher } from "undici";
|
||||
import type { TelegramNetworkConfig } from "../config/types.telegram.js";
|
||||
import { resolveFetch } from "../infra/fetch.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
@@ -13,6 +13,29 @@ let appliedAutoSelectFamily: boolean | null = null;
|
||||
let appliedDnsResultOrder: string | null = null;
|
||||
let appliedGlobalDispatcherAutoSelectFamily: boolean | null = null;
|
||||
const log = createSubsystemLogger("telegram/network");
|
||||
const PROXY_ENV_KEYS = [
|
||||
"HTTPS_PROXY",
|
||||
"HTTP_PROXY",
|
||||
"ALL_PROXY",
|
||||
"https_proxy",
|
||||
"http_proxy",
|
||||
"all_proxy",
|
||||
] as const;
|
||||
|
||||
function hasProxyEnvConfigured(): boolean {
|
||||
for (const key of PROXY_ENV_KEYS) {
|
||||
const value = process.env[key];
|
||||
if (typeof value === "string" && value.trim().length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isProxyLikeDispatcher(dispatcher: unknown): boolean {
|
||||
const ctorName = (dispatcher as { constructor?: { name?: string } })?.constructor?.name;
|
||||
return typeof ctorName === "string" && ctorName.includes("ProxyAgent");
|
||||
}
|
||||
|
||||
// Node 22 workaround: enable autoSelectFamily to allow IPv4 fallback on broken IPv6 networks.
|
||||
// Many networks have IPv6 configured but not routed, causing "Network is unreachable" errors.
|
||||
@@ -44,19 +67,24 @@ function applyTelegramNetworkWorkarounds(network?: TelegramNetworkConfig): void
|
||||
autoSelectDecision.value !== null &&
|
||||
autoSelectDecision.value !== appliedGlobalDispatcherAutoSelectFamily
|
||||
) {
|
||||
try {
|
||||
setGlobalDispatcher(
|
||||
new EnvHttpProxyAgent({
|
||||
connect: {
|
||||
autoSelectFamily: autoSelectDecision.value,
|
||||
autoSelectFamilyAttemptTimeout: 300,
|
||||
},
|
||||
}),
|
||||
);
|
||||
appliedGlobalDispatcherAutoSelectFamily = autoSelectDecision.value;
|
||||
log.info(`global undici dispatcher autoSelectFamily=${autoSelectDecision.value}`);
|
||||
} catch {
|
||||
// ignore if setGlobalDispatcher is unavailable
|
||||
const existingGlobalDispatcher = getGlobalDispatcher();
|
||||
const shouldPreserveExistingProxy =
|
||||
isProxyLikeDispatcher(existingGlobalDispatcher) && !hasProxyEnvConfigured();
|
||||
if (!shouldPreserveExistingProxy) {
|
||||
try {
|
||||
setGlobalDispatcher(
|
||||
new EnvHttpProxyAgent({
|
||||
connect: {
|
||||
autoSelectFamily: autoSelectDecision.value,
|
||||
autoSelectFamilyAttemptTimeout: 300,
|
||||
},
|
||||
}),
|
||||
);
|
||||
appliedGlobalDispatcherAutoSelectFamily = autoSelectDecision.value;
|
||||
log.info(`global undici dispatcher autoSelectFamily=${autoSelectDecision.value}`);
|
||||
} catch {
|
||||
// ignore if setGlobalDispatcher is unavailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user