@@ -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" ;
1213
1314const 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) {
126128 return String ( error ) ;
127129}
128130
129- async function readyzReportsReady ( response ) {
131+ async function readyzReportsReady ( response , options = { } ) {
130132 if ( ! response . ok ) {
131133 return false ;
132134 }
133- if ( typeof response . json !== "function" ) {
134- return false ;
135- }
136135 try {
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 ) ;
138143 return body && typeof body === "object" && body . ready === true ;
139144 } catch {
140145 return false ;
141146 }
142147}
143148
149+ 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 }
174211 try {
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 }
184232 try {
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 }
0 commit comments