60 lines
1.5 KiB
JavaScript
60 lines
1.5 KiB
JavaScript
export async function readRequestBody(req, limitBytes) {
|
|
const chunks = [];
|
|
let size = 0;
|
|
|
|
for await (const chunk of req) {
|
|
size += chunk.length;
|
|
if (size > limitBytes) {
|
|
const error = new Error('Request body is too large');
|
|
error.status = 413;
|
|
throw error;
|
|
}
|
|
chunks.push(chunk);
|
|
}
|
|
|
|
return Buffer.concat(chunks);
|
|
}
|
|
|
|
export function parseMultipart(buffer, contentType) {
|
|
const match = /boundary=(?:"([^"]+)"|([^;]+))/i.exec(contentType || '');
|
|
if (!match) {
|
|
const error = new Error('Missing multipart boundary');
|
|
error.status = 400;
|
|
throw error;
|
|
}
|
|
|
|
const boundary = `--${match[1] || match[2]}`;
|
|
const binary = buffer.toString('binary');
|
|
const parts = [];
|
|
|
|
for (const raw of binary.split(boundary)) {
|
|
if (!raw || raw === '--\r\n' || raw === '--') {
|
|
continue;
|
|
}
|
|
|
|
const trimmed = raw.replace(/^\r\n/, '').replace(/\r\n--$/, '');
|
|
const separator = trimmed.indexOf('\r\n\r\n');
|
|
if (separator === -1) {
|
|
continue;
|
|
}
|
|
|
|
const headerText = trimmed.slice(0, separator);
|
|
let bodyText = trimmed.slice(separator + 4);
|
|
if (bodyText.endsWith('\r\n')) {
|
|
bodyText = bodyText.slice(0, -2);
|
|
}
|
|
|
|
const disposition = /content-disposition:\s*form-data;\s*([^\r\n]+)/i.exec(headerText)?.[1] || '';
|
|
const name = /name="([^"]+)"/i.exec(disposition)?.[1] || '';
|
|
const filename = /filename="([^"]*)"/i.exec(disposition)?.[1] || '';
|
|
|
|
parts.push({
|
|
name,
|
|
filename,
|
|
data: Buffer.from(bodyText, 'binary')
|
|
});
|
|
}
|
|
|
|
return parts;
|
|
}
|