happy-dom
Version:
Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG DOM and HTML.
180 lines • 6.8 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const File_js_1 = __importDefault(require("../../file/File.cjs"));
var MultipartParserStateEnum;
(function (MultipartParserStateEnum) {
MultipartParserStateEnum[MultipartParserStateEnum["boundary"] = 0] = "boundary";
MultipartParserStateEnum[MultipartParserStateEnum["headerStart"] = 2] = "headerStart";
MultipartParserStateEnum[MultipartParserStateEnum["header"] = 3] = "header";
MultipartParserStateEnum[MultipartParserStateEnum["data"] = 5] = "data";
})(MultipartParserStateEnum || (MultipartParserStateEnum = {}));
const CHARACTER_CODE = {
lf: 10,
cr: 13
};
/**
* Multipart reader.
*
* Based on:
* https://github.com/node-fetch/node-fetch/blob/main/src/utils/multipart-parser.js (MIT)
*/
class MultipartReader {
formData;
boundary;
boundaryIndex = 0;
state = MultipartParserStateEnum.boundary;
data = {
contentDisposition: null,
value: [],
contentType: null,
header: ''
};
/**
* Constructor.
*
* @param window Window.
* @param formData Form data.
* @param boundary Boundary.
*/
constructor(window, boundary) {
const boundaryHeader = `--${boundary}`;
this.boundary = new Uint8Array(boundaryHeader.length);
this.formData = new window.FormData();
for (let i = 0, max = boundaryHeader.length; i < max; i++) {
this.boundary[i] = boundaryHeader.charCodeAt(i);
}
}
/**
* Appends data.
*
* @param data Data.
*/
write(data) {
let char;
let nextChar;
for (let i = 0, max = data.length; i < max; i++) {
char = data[i];
nextChar = data[i + 1];
switch (this.state) {
case MultipartParserStateEnum.boundary:
if (char === this.boundary[this.boundaryIndex]) {
this.boundaryIndex++;
}
else {
this.boundaryIndex = 0;
}
if (this.boundaryIndex === this.boundary.length) {
this.state = MultipartParserStateEnum.headerStart;
this.boundaryIndex = 0;
}
break;
case MultipartParserStateEnum.headerStart:
if (nextChar !== CHARACTER_CODE.cr && nextChar !== CHARACTER_CODE.lf) {
this.data.header = '';
this.state =
data[i - 2] === CHARACTER_CODE.lf
? MultipartParserStateEnum.data
: MultipartParserStateEnum.header;
}
break;
case MultipartParserStateEnum.header:
if (char === CHARACTER_CODE.cr) {
if (this.data.header) {
const headerParts = this.data.header.split(':');
if (headerParts.length > 1) {
const headerName = headerParts[0].toLowerCase();
const headerValue = headerParts[1].trim();
switch (headerName) {
case 'content-disposition':
this.data.contentDisposition = this.getContentDisposition(headerValue);
break;
case 'content-type':
this.data.contentType = headerValue;
break;
}
}
}
this.state = MultipartParserStateEnum.headerStart;
}
else {
this.data.header += String.fromCharCode(char);
}
break;
case MultipartParserStateEnum.data:
if (char === this.boundary[this.boundaryIndex]) {
this.boundaryIndex++;
}
else {
this.boundaryIndex = 0;
}
if (this.boundaryIndex === this.boundary.length) {
this.state = MultipartParserStateEnum.headerStart;
if (this.data.value.length) {
this.appendFormData(this.data.contentDisposition.name, Buffer.from(this.data.value.slice(0, -(this.boundary.length + 1))), this.data.contentDisposition.filename, this.data.contentType);
this.data.value = [];
this.data.contentDisposition = null;
this.data.contentType = null;
}
this.boundaryIndex = 0;
}
else {
this.data.value.push(char);
}
break;
}
}
}
/**
* Ends the stream.
*
* @returns Form data.
*/
end() {
// If we are missing an end boundary, but we have data, we should append it.
if (this.data.contentDisposition && this.data.value.length) {
this.appendFormData(this.data.contentDisposition.name, Buffer.from(this.data.value.slice(0, -2)), this.data.contentDisposition.filename, this.data.contentType);
}
return this.formData;
}
/**
* Appends data.
*
* @param key Key.
* @param value value.
* @param filename Filename.
* @param type Type.
*/
appendFormData(key, value, filename, type) {
if (!value.length) {
return;
}
if (filename) {
this.formData.append(key, new File_js_1.default([value], filename, {
type
}));
}
else {
this.formData.append(key, value.toString());
}
}
/**
* Returns content disposition.
*
* @param headerValue Header value.
* @returns Content disposition.
*/
getContentDisposition(headerValue) {
const regex = /([a-z]+) *= *"([^"]+)"/g;
const contentDisposition = {};
let match;
while ((match = regex.exec(headerValue))) {
contentDisposition[match[1]] = match[2];
}
return contentDisposition;
}
}
exports.default = MultipartReader;
//# sourceMappingURL=MultipartReader.cjs.map
;