37 lines
1.1 KiB
TypeScript
37 lines
1.1 KiB
TypeScript
export const MAX_FETCH_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|||
|
|
|
||
|
|
export async function readBoundedResponseBytes(response: Response, maxBytes: number) {
|
||
|
|
const contentLength = Number(response.headers.get("content-length"));
|
||
|
|
if (Number.isFinite(contentLength) && contentLength > maxBytes) {
|
||
|
|
await response.body?.cancel();
|
||
|
|
throw new Error(`URL response exceeds the ${Math.floor(maxBytes / (1024 * 1024))} MiB download limit.`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const reader = response.body?.getReader();
|
||
|
|
if (!reader) return new Uint8Array();
|
||
|
|
const chunks: Uint8Array[] = [];
|
||
|
|
let totalBytes = 0;
|
||
|
|
try {
|
||
|
|
while (true) {
|
||
|
|
const { done, value } = await reader.read();
|
||
|
|
if (done) break;
|
||
|
|
totalBytes += value.byteLength;
|
||
|
|
if (totalBytes > maxBytes) {
|
||
|
|
await reader.cancel();
|
||
|
|
throw new Error(`URL response exceeds the ${Math.floor(maxBytes / (1024 * 1024))} MiB download limit.`);
|
||
|
|
}
|
||
|
|
chunks.push(value);
|
||
|
|
}
|
||
|
|
} finally {
|
||
|
|
reader.releaseLock();
|
||
|
|
}
|
||
|
|
|
||
|
|
const bytes = new Uint8Array(totalBytes);
|
||
|
|
let offset = 0;
|
||
|
|
for (const chunk of chunks) {
|
||
|
|
bytes.set(chunk, offset);
|
||
|
|
offset += chunk.byteLength;
|
||
|
|
}
|
||
|
|
return bytes;
|
||
|
|
}
|