type EnvSource = Record; type TimeoutLogger = (message: string) => void; type ReadTimeoutOptions = { allowZero?: boolean; logger?: TimeoutLogger; }; export const DEFAULT_FETCH_TIMEOUT_MS = 600_000; export const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 600_000; export const DEFAULT_FETCH_CONNECT_TIMEOUT_MS = 30_000; export const DEFAULT_FETCH_KEEPALIVE_TIMEOUT_MS = 4_000; export const DEFAULT_API_BRIDGE_PROXY_TIMEOUT_MS = 30_000; export const DEFAULT_API_BRIDGE_SERVER_REQUEST_TIMEOUT_MS = 300_000; export const DEFAULT_API_BRIDGE_SERVER_HEADERS_TIMEOUT_MS = 60_000; export const DEFAULT_API_BRIDGE_SERVER_KEEPALIVE_TIMEOUT_MS = 5_000; export const DEFAULT_API_BRIDGE_SERVER_SOCKET_TIMEOUT_MS = 0; function hasEnvValue(env: EnvSource, name: string): boolean { const raw = env[name]; return raw != null && raw.trim() !== ""; } export type UpstreamTimeoutConfig = { fetchTimeoutMs: number; streamIdleTimeoutMs: number; fetchHeadersTimeoutMs: number; fetchBodyTimeoutMs: number; fetchConnectTimeoutMs: number; fetchKeepAliveTimeoutMs: number; }; export type TlsClientTimeoutConfig = { timeoutMs: number; }; export type ApiBridgeTimeoutConfig = { proxyTimeoutMs: number; serverRequestTimeoutMs: number; serverHeadersTimeoutMs: number; serverKeepAliveTimeoutMs: number; serverSocketTimeoutMs: number; }; function readTimeoutMs( env: EnvSource, name: string, defaultValue: number, options: ReadTimeoutOptions = {} ): number { const raw = env[name]; if (raw == null || raw.trim() === "") return defaultValue; const parsed = Number(raw); const isValid = Number.isFinite(parsed) && (options.allowZero ? parsed >= 0 : parsed > 0); if (!isValid) { options.logger?.(`Invalid ${name}="${raw}". Using default ${defaultValue}ms.`); return defaultValue; } return Math.floor(parsed); } export function getUpstreamTimeoutConfig( env: EnvSource = process.env, logger?: TimeoutLogger ): UpstreamTimeoutConfig { const sharedRequestTimeoutMs = hasEnvValue(env, "REQUEST_TIMEOUT_MS") ? readTimeoutMs(env, "REQUEST_TIMEOUT_MS", DEFAULT_FETCH_TIMEOUT_MS, { allowZero: true, logger, }) : undefined; const fetchTimeoutMs = readTimeoutMs( env, "FETCH_TIMEOUT_MS", sharedRequestTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS, { allowZero: true, logger, } ); const streamIdleTimeoutMs = readTimeoutMs( env, "STREAM_IDLE_TIMEOUT_MS", sharedRequestTimeoutMs ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS, { allowZero: true, logger, } ); return { fetchTimeoutMs, streamIdleTimeoutMs, fetchHeadersTimeoutMs: readTimeoutMs(env, "FETCH_HEADERS_TIMEOUT_MS", fetchTimeoutMs, { allowZero: true, logger, }), fetchBodyTimeoutMs: readTimeoutMs(env, "FETCH_BODY_TIMEOUT_MS", fetchTimeoutMs, { allowZero: true, logger, }), fetchConnectTimeoutMs: readTimeoutMs( env, "FETCH_CONNECT_TIMEOUT_MS", DEFAULT_FETCH_CONNECT_TIMEOUT_MS, { allowZero: true, logger, } ), fetchKeepAliveTimeoutMs: readTimeoutMs( env, "FETCH_KEEPALIVE_TIMEOUT_MS", DEFAULT_FETCH_KEEPALIVE_TIMEOUT_MS, { logger, } ), }; } export function getTlsClientTimeoutConfig( env: EnvSource = process.env, logger?: TimeoutLogger ): TlsClientTimeoutConfig { const upstream = getUpstreamTimeoutConfig(env, logger); return { timeoutMs: readTimeoutMs(env, "TLS_CLIENT_TIMEOUT_MS", upstream.fetchTimeoutMs, { allowZero: true, logger, }), }; } export function getApiBridgeTimeoutConfig( env: EnvSource = process.env, logger?: TimeoutLogger ): ApiBridgeTimeoutConfig { const sharedRequestTimeoutMs = hasEnvValue(env, "REQUEST_TIMEOUT_MS") ? readTimeoutMs(env, "REQUEST_TIMEOUT_MS", DEFAULT_FETCH_TIMEOUT_MS, { allowZero: true, logger, }) : undefined; const proxyTimeoutMs = readTimeoutMs( env, "API_BRIDGE_PROXY_TIMEOUT_MS", sharedRequestTimeoutMs ?? DEFAULT_API_BRIDGE_PROXY_TIMEOUT_MS, { allowZero: true, logger, } ); const derivedRequestTimeoutMs = proxyTimeoutMs > 0 ? Math.max(proxyTimeoutMs, DEFAULT_API_BRIDGE_SERVER_REQUEST_TIMEOUT_MS) : DEFAULT_API_BRIDGE_SERVER_REQUEST_TIMEOUT_MS; const serverKeepAliveTimeoutMs = readTimeoutMs( env, "API_BRIDGE_SERVER_KEEPALIVE_TIMEOUT_MS", DEFAULT_API_BRIDGE_SERVER_KEEPALIVE_TIMEOUT_MS, { allowZero: true, logger, } ); const serverHeadersTimeoutMs = readTimeoutMs( env, "API_BRIDGE_SERVER_HEADERS_TIMEOUT_MS", DEFAULT_API_BRIDGE_SERVER_HEADERS_TIMEOUT_MS, { allowZero: true, logger, } ); return { proxyTimeoutMs, serverRequestTimeoutMs: readTimeoutMs( env, "API_BRIDGE_SERVER_REQUEST_TIMEOUT_MS", sharedRequestTimeoutMs ? Math.max(sharedRequestTimeoutMs, derivedRequestTimeoutMs) : derivedRequestTimeoutMs, { allowZero: true, logger, } ), serverHeadersTimeoutMs: serverHeadersTimeoutMs > 0 && serverKeepAliveTimeoutMs > 0 ? Math.max(serverHeadersTimeoutMs, serverKeepAliveTimeoutMs + 1_000) : serverHeadersTimeoutMs, serverKeepAliveTimeoutMs, serverSocketTimeoutMs: readTimeoutMs( env, "API_BRIDGE_SERVER_SOCKET_TIMEOUT_MS", DEFAULT_API_BRIDGE_SERVER_SOCKET_TIMEOUT_MS, { allowZero: true, logger, } ), }; }