mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-14 07:18:35 +00:00
fix(web_fetch): cap response body before parsing
This commit is contained in:
@@ -86,10 +86,85 @@ export function withTimeout(signal: AbortSignal | undefined, timeoutMs: number):
|
||||
return controller.signal;
|
||||
}
|
||||
|
||||
export async function readResponseText(res: Response): Promise<string> {
|
||||
export type ReadResponseTextResult = {
|
||||
text: string;
|
||||
truncated: boolean;
|
||||
bytesRead: number;
|
||||
};
|
||||
|
||||
export async function readResponseText(
|
||||
res: Response,
|
||||
options?: { maxBytes?: number },
|
||||
): Promise<ReadResponseTextResult> {
|
||||
const maxBytesRaw = options?.maxBytes;
|
||||
const maxBytes =
|
||||
typeof maxBytesRaw === "number" && Number.isFinite(maxBytesRaw) && maxBytesRaw > 0
|
||||
? Math.floor(maxBytesRaw)
|
||||
: undefined;
|
||||
|
||||
const body = (res as unknown as { body?: unknown }).body;
|
||||
if (
|
||||
maxBytes &&
|
||||
body &&
|
||||
typeof body === "object" &&
|
||||
"getReader" in body &&
|
||||
typeof (body as { getReader: () => unknown }).getReader === "function"
|
||||
) {
|
||||
const reader = (body as ReadableStream<Uint8Array>).getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let bytesRead = 0;
|
||||
let truncated = false;
|
||||
const parts: string[] = [];
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
if (!value || value.byteLength === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let chunk = value;
|
||||
if (bytesRead + chunk.byteLength > maxBytes) {
|
||||
const remaining = Math.max(0, maxBytes - bytesRead);
|
||||
if (remaining <= 0) {
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
chunk = chunk.subarray(0, remaining);
|
||||
truncated = true;
|
||||
}
|
||||
|
||||
bytesRead += chunk.byteLength;
|
||||
parts.push(decoder.decode(chunk, { stream: true }));
|
||||
|
||||
if (truncated || bytesRead >= maxBytes) {
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Best-effort: return whatever we decoded so far.
|
||||
} finally {
|
||||
if (truncated) {
|
||||
try {
|
||||
await reader.cancel();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(decoder.decode());
|
||||
return { text: parts.join(""), truncated, bytesRead };
|
||||
}
|
||||
|
||||
try {
|
||||
return await res.text();
|
||||
const text = await res.text();
|
||||
return { text, truncated: false, bytesRead: text.length };
|
||||
} catch {
|
||||
return "";
|
||||
return { text: "", truncated: false, bytesRead: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user