sc-site-sync/lib/common/cappedBody.js
2026-10-19 01:25:16 -05:00

50 lines
1.6 KiB
JavaScript

// Byte-capped body readers: never buffer more than a caller-set limit from a
// peer or remote API.
// A fetch Response body as a Buffer, refusing more than maxBytes: by declared
// Content-Length first, then by running count, so a lying or chunked response is
// cut off (leaving the for-await cancels the stream) rather than buffered.
const readCappedResponse = async (res, maxBytes) => {
const declared = Number(res.headers.get("content-length"));
if (Number.isFinite(declared) && declared > maxBytes) {
throw new Error(`response too large (${declared} bytes exceeds ${maxBytes})`);
}
if (!res.body) {
return Buffer.alloc(0);
}
const chunks = [];
let total = 0;
for await (const chunk of res.body) {
total += chunk.length;
if (total > maxBytes) {
throw new Error(`response too large (exceeds ${maxBytes} bytes)`);
}
chunks.push(Buffer.from(chunk));
}
return Buffer.concat(chunks);
};
// An incoming request stream as a Buffer, or null once it exceeds maxBytes.
// Leaving the for-await early destroys the stream, so an oversized upload is cut
// off rather than buffered.
const readCappedRequest = async (req, maxBytes) => {
const chunks = [];
let total = 0;
for await (const chunk of req) {
const buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
total += buf.length;
if (total > maxBytes) {
return null;
}
chunks.push(buf);
}
return Buffer.concat(chunks);
};
module.exports = {
readCappedResponse,
readCappedRequest
};