@push.rocks/smartrequest
Version:
A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.
111 lines • 9.23 kB
JavaScript
const lineBreak = '\r\n';
const defaultContentType = 'application/octet-stream';
const textEncoder = new TextEncoder();
const escapeContentDispositionValue = (value) => {
return value
.replace(/\\/g, '\\\\')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A')
.replace(/"/g, '%22');
};
const getFilename = (filename) => {
const normalizedFilename = filename.replace(/\\/g, '/');
return normalizedFilename.slice(normalizedFilename.lastIndexOf('/') + 1);
};
const createBoundary = () => {
const randomBytes = new Uint8Array(12);
globalThis.crypto.getRandomValues(randomBytes);
const randomHex = Array.from(randomBytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
return `--------------------------${randomHex}`;
};
/**
* Minimal multipart/form-data encoder for the values accepted by SmartRequest.
*/
export class MultipartFormData {
constructor(boundary = createBoundary()) {
this.parts = [];
if (boundary.length > 70 ||
!/^[0-9A-Za-z'()+_,./:=?-]+$/.test(boundary)) {
throw new Error('Multipart boundary contains invalid characters.');
}
this.boundary = boundary;
}
append(name, value, options) {
let filename;
let contentType;
if (typeof value !== 'string') {
filename = getFilename(options?.filename || 'file');
contentType = options?.contentType;
this.validateContentType(contentType);
}
this.parts.push({
name,
value: typeof value === 'string' ? textEncoder.encode(value) : value,
filename,
contentType,
});
}
resolveMissingContentTypes(resolver) {
for (const part of this.parts) {
if (part.filename && !part.contentType) {
part.contentType = resolver(part.filename) || defaultContentType;
this.validateContentType(part.contentType);
}
}
}
getHeaders() {
return {
'content-type': `multipart/form-data; boundary=${this.boundary}`,
};
}
getContentLength() {
let length = textEncoder.encode(`--${this.boundary}--${lineBreak}`).byteLength;
for (const part of this.parts) {
length +=
this.getPartHeader(part).byteLength +
part.value.byteLength +
lineBreak.length;
}
return length;
}
toArrayBuffer() {
const buffer = new ArrayBuffer(this.getContentLength());
const body = new Uint8Array(buffer);
let offset = 0;
for (const chunk of this.getChunks()) {
body.set(chunk, offset);
offset += chunk.byteLength;
}
return buffer;
}
*getChunks() {
const lineBreakBuffer = textEncoder.encode(lineBreak);
for (const part of this.parts) {
yield this.getPartHeader(part);
yield part.value;
yield lineBreakBuffer;
}
yield textEncoder.encode(`--${this.boundary}--${lineBreak}`);
}
getPartHeader(part) {
const dispositionParts = [
'form-data',
`name="${escapeContentDispositionValue(part.name)}"`,
];
const headerLines = [`--${this.boundary}`];
if (part.filename) {
dispositionParts.push(`filename="${escapeContentDispositionValue(part.filename)}"`);
}
headerLines.push(`Content-Disposition: ${dispositionParts.join('; ')}`);
if (part.filename) {
headerLines.push(`Content-Type: ${part.contentType || defaultContentType}`);
}
return textEncoder.encode(`${headerLines.join(lineBreak)}${lineBreak}${lineBreak}`);
}
validateContentType(contentType) {
if (contentType && /[\r\n]/.test(contentType)) {
throw new Error('Multipart content type must be a single-line value.');
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXVsdGlwYXJ0Zm9ybWRhdGEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jb3JlX2Jhc2UvbXVsdGlwYXJ0Zm9ybWRhdGEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDO0FBQ3pCLE1BQU0sa0JBQWtCLEdBQUcsMEJBQTBCLENBQUM7QUFDdEQsTUFBTSxXQUFXLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztBQWN0QyxNQUFNLDZCQUE2QixHQUFHLENBQUMsS0FBYSxFQUFVLEVBQUU7SUFDOUQsT0FBTyxLQUFLO1NBQ1QsT0FBTyxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUM7U0FDdEIsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUM7U0FDckIsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUM7U0FDckIsT0FBTyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQztBQUMxQixDQUFDLENBQUM7QUFFRixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQWdCLEVBQVUsRUFBRTtJQUMvQyxNQUFNLGtCQUFrQixHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3hELE9BQU8sa0JBQWtCLENBQUMsS0FBSyxDQUFDLGtCQUFrQixDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUMzRSxDQUFDLENBQUM7QUFFRixNQUFNLGNBQWMsR0FBRyxHQUFXLEVBQUU7SUFDbEMsTUFBTSxXQUFXLEdBQUcsSUFBSSxVQUFVLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDdkMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDL0MsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUNqRCxJQUFJLENBQUMsUUFBUSxD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