mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-22 23:41:07 +00:00
96 lines
2.4 KiB
TypeScript
96 lines
2.4 KiB
TypeScript
async function readChunkWithIdleTimeout(
|
|
reader: ReadableStreamDefaultReader<Uint8Array>,
|
|
chunkTimeoutMs: number,
|
|
): Promise<Awaited<ReturnType<typeof reader.read>>> {
|
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
let timedOut = false;
|
|
|
|
return await new Promise((resolve, reject) => {
|
|
const clear = () => {
|
|
if (timeoutId !== undefined) {
|
|
clearTimeout(timeoutId);
|
|
timeoutId = undefined;
|
|
}
|
|
};
|
|
|
|
timeoutId = setTimeout(() => {
|
|
timedOut = true;
|
|
clear();
|
|
void reader.cancel().catch(() => undefined);
|
|
reject(new Error(`Media download stalled: no data received for ${chunkTimeoutMs}ms`));
|
|
}, chunkTimeoutMs);
|
|
|
|
void reader.read().then(
|
|
(result) => {
|
|
clear();
|
|
if (!timedOut) {
|
|
resolve(result);
|
|
}
|
|
},
|
|
(err) => {
|
|
clear();
|
|
if (!timedOut) {
|
|
reject(err);
|
|
}
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
export async function readResponseWithLimit(
|
|
res: Response,
|
|
maxBytes: number,
|
|
opts?: {
|
|
onOverflow?: (params: { size: number; maxBytes: number; res: Response }) => Error;
|
|
chunkTimeoutMs?: number;
|
|
},
|
|
): Promise<Buffer> {
|
|
const onOverflow =
|
|
opts?.onOverflow ??
|
|
((params: { size: number; maxBytes: number }) =>
|
|
new Error(`Content too large: ${params.size} bytes (limit: ${params.maxBytes} bytes)`));
|
|
const chunkTimeoutMs = opts?.chunkTimeoutMs;
|
|
|
|
const body = res.body;
|
|
if (!body || typeof body.getReader !== "function") {
|
|
const fallback = Buffer.from(await res.arrayBuffer());
|
|
if (fallback.length > maxBytes) {
|
|
throw onOverflow({ size: fallback.length, maxBytes, res });
|
|
}
|
|
return fallback;
|
|
}
|
|
|
|
const reader = body.getReader();
|
|
const chunks: Uint8Array[] = [];
|
|
let total = 0;
|
|
try {
|
|
while (true) {
|
|
const { done, value } = chunkTimeoutMs
|
|
? await readChunkWithIdleTimeout(reader, chunkTimeoutMs)
|
|
: await reader.read();
|
|
if (done) {
|
|
break;
|
|
}
|
|
if (value?.length) {
|
|
total += value.length;
|
|
if (total > maxBytes) {
|
|
try {
|
|
await reader.cancel();
|
|
} catch {}
|
|
throw onOverflow({ size: total, maxBytes, res });
|
|
}
|
|
chunks.push(value);
|
|
}
|
|
}
|
|
} finally {
|
|
try {
|
|
reader.releaseLock();
|
|
} catch {}
|
|
}
|
|
|
|
return Buffer.concat(
|
|
chunks.map((chunk) => Buffer.from(chunk)),
|
|
total,
|
|
);
|
|
}
|