@@ -6,6 +6,7 @@ import path from "node:path";
|
6 | 6 | import process from "node:process"; |
7 | 7 | import { setTimeout as delay } from "node:timers/promises"; |
8 | 8 | import { fileURLToPath } from "node:url"; |
| 9 | +import { readBoundedResponseText } from "../bounded-response-text.mjs"; |
9 | 10 | |
10 | 11 | const TOKEN = "bundled-plugin-runtime-smoke-token"; |
11 | 12 | const RUNTIME_PORT_BASE_ENV = "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE"; |
@@ -31,6 +32,7 @@ const RPC_READY_TIMEOUT_MS = readPositiveIntEnv(
|
31 | 32 | ); |
32 | 33 | const COMMAND_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS", 120000); |
33 | 34 | const HTTP_PROBE_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS", 5000); |
| 35 | +const HTTP_PROBE_BODY_MAX_BYTES = 1024 * 1024; |
34 | 36 | const GATEWAY_TEARDOWN_GRACE_MS = readPositiveIntEnv( |
35 | 37 | "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS", |
36 | 38 | 10000, |
@@ -693,18 +695,37 @@ export async function waitForReady(params) {
|
693 | 695 | async function fetchHttpProbeStatus(port, pathName, options = {}) { |
694 | 696 | const { parseJson = false, timeoutMs = HTTP_PROBE_TIMEOUT_MS } = options; |
695 | 697 | const controller = new AbortController(); |
696 | | -const clearProbeTimer = timeoutMs |
697 | | - ? setTimeout(() => { |
698 | | -controller.abort(); |
699 | | -}, timeoutMs) |
| 698 | +const timeoutError = Object.assign( |
| 699 | +new Error(`${pathName} probe timed out after ${timeoutMs}ms`), |
| 700 | +{ |
| 701 | +code: "ETIMEDOUT", |
| 702 | +}, |
| 703 | +); |
| 704 | +let clearProbeTimer; |
| 705 | +const timeoutPromise = timeoutMs |
| 706 | + ? new Promise((_, reject) => { |
| 707 | +clearProbeTimer = setTimeout(() => { |
| 708 | +controller.abort(timeoutError); |
| 709 | +reject(timeoutError); |
| 710 | +}, timeoutMs); |
| 711 | +clearProbeTimer.unref?.(); |
| 712 | +}) |
700 | 713 | : undefined; |
701 | 714 | try { |
702 | | -const res = await fetch(`http://127.0.0.1:${port}${pathName}`, { |
703 | | -signal: controller.signal, |
704 | | -}); |
| 715 | +const res = await Promise.race([ |
| 716 | +fetch(`http://127.0.0.1:${port}${pathName}`, { |
| 717 | +signal: controller.signal, |
| 718 | +}), |
| 719 | + ...(timeoutPromise ? [timeoutPromise] : []), |
| 720 | +]); |
705 | 721 | const status = { ok: res.ok, status: res.status, body: undefined, bodyText: undefined }; |
706 | 722 | if (parseJson) { |
707 | | -const text = await res.text(); |
| 723 | +const text = await readBoundedResponseText( |
| 724 | +res, |
| 725 | +`${pathName} probe`, |
| 726 | +HTTP_PROBE_BODY_MAX_BYTES, |
| 727 | +timeoutPromise, |
| 728 | +); |
708 | 729 | status.bodyText = text; |
709 | 730 | if (text.trim()) { |
710 | 731 | try { |
|