kequapp
Version:
A minimal, zero-magic Node web framework built on native APIs
70 lines (69 loc) • 2.19 kB
JavaScript
import Ex from "../../built-in/tools/ex.js";
import headerAttributes from "../../util/header-attributes.js";
const CR = 0x0d;
const LF = 0x0a;
export default function splitMultipart(body) {
const contentType = body.headers['content-type'];
if (!contentType?.startsWith('multipart/')) {
throw Ex.BadRequest('Unable to process request', {
contentType,
});
}
const boundary = extractBoundary(contentType);
const buffer = body.data;
const result = [];
let headers = {};
let i = findNextLine(buffer, buffer.indexOf(boundary, 0));
function addHeader(nextLine) {
const line = buffer.slice(i, nextLine).toString();
const parts = line.split(':');
const key = parts[0].trim().toLowerCase();
const value = parts[1]?.trim();
if (key && value)
headers[key] = value;
}
function addPart(nextBoundary) {
const dataEnd = nextBoundary - (buffer[nextBoundary - 2] === CR ? 2 : 1);
result.push({
headers,
data: buffer.slice(i, dataEnd),
});
headers = {};
}
while (i > -1) {
// until two new lines
while (i > -1 && buffer[i] !== CR && buffer[i] !== LF) {
const nextLine = findNextLine(buffer, i);
if (nextLine > -1)
addHeader(nextLine);
i = nextLine;
}
if (i < 0)
break;
i += buffer[i] === CR ? 2 : 1;
if (i >= buffer.length)
break;
// data start
const nextBoundary = buffer.indexOf(boundary, i);
if (nextBoundary < 0)
break;
addPart(nextBoundary);
i = findNextLine(buffer, nextBoundary);
}
return result;
}
function extractBoundary(contentType) {
const boundary = headerAttributes(contentType).boundary;
if (!boundary) {
throw Ex.BadRequest('Multipart request requires boundary attribute', {
contentType,
});
}
return `--${boundary}`;
}
function findNextLine(buffer, from) {
const i = buffer.indexOf(LF, from) + 1;
if (i < 1 || i >= buffer.length)
return -1;
return i;
}