


















@@ -9,13 +9,15 @@ import net from "node:net";
99import path from "node:path";
1010import { performance } from "node:perf_hooks";
1111import { fileURLToPath, pathToFileURL } from "node:url";
12+import { readBoundedResponseText } from "./lib/bounded-response.mjs";
12131314const DEFAULT_METHODS = ["health", "config.get"];
1415const DEFAULT_ITERATIONS = 10;
1516/** Maximum time to wait for a spawned gateway to become reachable. */
1617export const READY_TIMEOUT_MS = 120_000;
1718/** Per-probe timeout used while polling gateway readiness endpoints. */
1819export const READY_PROBE_TIMEOUT_MS = 1_000;
20+const READY_PROBE_RESPONSE_BODY_MAX_BYTES = 64 * 1024;
1921const GATEWAY_FORCE_KILL_GRACE_MS = 250;
2022const PARENT_TERMINATION_SIGNALS = ["SIGHUP", "SIGINT", "SIGTERM"];
2123const IS_DIRECT_RUN =
@@ -126,21 +128,56 @@ function formatErrorMessage(error) {
126128return String(error);
127129}
128130129-async function readyzReportsReady(response) {
131+async function readyzReportsReady(response, options = {}) {
130132if (!response.ok) {
131133return false;
132134}
133-if (typeof response.json !== "function") {
134-return false;
135-}
136135try {
137-const body = await response.json();
136+const text = await readBoundedResponseText(
137+response,
138+"RPC RTT /readyz",
139+READY_PROBE_RESPONSE_BODY_MAX_BYTES,
140+options,
141+);
142+const body = JSON.parse(text);
138143return body && typeof body === "object" && body.ready === true;
139144} catch {
140145return false;
141146}
142147}
143148149+async function fetchReadinessProbe(fetchImpl, url, timeoutMs) {
150+const controller = new AbortController();
151+const timeoutError = Object.assign(new Error(`${url} timed out after ${timeoutMs}ms`), {
152+code: "ETIMEDOUT",
153+});
154+let timeout;
155+const timeoutPromise = new Promise((_, reject) => {
156+timeout = setTimeout(() => {
157+controller.abort(timeoutError);
158+reject(timeoutError);
159+}, timeoutMs);
160+timeout.unref?.();
161+});
162+try {
163+const response = await Promise.race([
164+fetchImpl(url, {
165+signal: controller.signal,
166+}),
167+timeoutPromise,
168+]);
169+return {
170+clearTimeout: () => clearTimeout(timeout),
171+ response,
172+signal: controller.signal,
173+ timeoutPromise,
174+};
175+} catch (error) {
176+clearTimeout(timeout);
177+throw error;
178+}
179+}
180+144181/**
145182 * Polls readiness endpoints while also failing fast if the child exits.
146183 */
@@ -172,19 +209,33 @@ export async function waitForGatewayReady({
172209);
173210}
174211try {
175-const response = await fetchImpl(`http://127.0.0.1:${port}/readyz`, {
176-signal: AbortSignal.timeout(probeTimeoutMs),
177-});
178-if (await readyzReportsReady(response)) {
179-return;
212+const probe = await fetchReadinessProbe(
213+fetchImpl,
214+`http://127.0.0.1:${port}/readyz`,
215+probeTimeoutMs,
216+);
217+try {
218+if (
219+await readyzReportsReady(probe.response, {
220+signal: probe.signal,
221+timeoutPromise: probe.timeoutPromise,
222+})
223+) {
224+return;
225+}
226+} finally {
227+probe.clearTimeout();
180228}
181229} catch {
182230// The gateway may not have bound the port yet.
183231}
184232try {
185-await fetchImpl(`http://127.0.0.1:${port}/healthz`, {
186-signal: AbortSignal.timeout(probeTimeoutMs),
187-});
233+const probe = await fetchReadinessProbe(
234+fetchImpl,
235+`http://127.0.0.1:${port}/healthz`,
236+probeTimeoutMs,
237+);
238+probe.clearTimeout();
188239} catch {
189240// Liveness is diagnostic only; /readyz is the usable RPC readiness contract.
190241}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。