28b1f3896c
The message-size checks ran after the body had been read, so a request without a content-length was buffered and parsed in full before being refused. An ingress cap on the agent's paths now counts the bytes as they arrive, and the chat proxy reads its body with a ceiling instead of reading it whole first.
36 lines
1.0 KiB
TypeScript
36 lines
1.0 KiB
TypeScript
/**
|
|
* Reading a request body with a ceiling. `request.text()` buffers the whole body before the
|
|
* caller can look at its size, so a route that only checks afterwards has already paid for it.
|
|
*/
|
|
export type BoundedBody = { ok: true; text: string } | { ok: false; reason: "too_large" };
|
|
|
|
/** Stops at the first chunk that crosses `maxBytes` and cancels the stream. */
|
|
export async function readBoundedBodyText(
|
|
request: Request,
|
|
maxBytes: number
|
|
): Promise<BoundedBody> {
|
|
if (!request.body) return { ok: true, text: "" };
|
|
|
|
const reader = request.body.getReader();
|
|
const chunks: Uint8Array[] = [];
|
|
let received = 0;
|
|
|
|
try {
|
|
for (;;) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
if (!value) continue;
|
|
received += value.byteLength;
|
|
if (received > maxBytes) {
|
|
await reader.cancel();
|
|
return { ok: false, reason: "too_large" };
|
|
}
|
|
chunks.push(value);
|
|
}
|
|
} finally {
|
|
reader.releaseLock();
|
|
}
|
|
|
|
return { ok: true, text: Buffer.concat(chunks).toString("utf8") };
|
|
}
|