@coolio/http
Version:
HTTP networking client
231 lines • 8.14 kB
JavaScript
import * as mime from 'mime';
import CombinedStream from 'combined-stream';
import { Readable, Writable } from 'stream';
export const SUPPORTS_BROWSER_FORM_DATA = (() => {
try {
return !({} instanceof FormData);
}
catch (_a) {
return false;
}
})();
export const isBrowserFormData = (value) => SUPPORTS_BROWSER_FORM_DATA && value instanceof FormData;
export const isBlob = (value) => {
try {
return value instanceof Blob;
}
catch (_a) {
return false;
}
};
export const readBlob = (blob) => {
const reader = new FileReader();
return new Promise((resolve, reject) => {
const onEnd = (e) => {
reader.removeEventListener('loadend', onEnd);
if (e.error) {
reject(e.error);
}
else {
resolve(Buffer.from(reader.result));
}
};
reader.addEventListener('loadend', onEnd);
reader.readAsArrayBuffer(blob);
});
};
export const getFileMeta = (value, existingMeta) => {
const anyValue = value;
const isHttpRequest = anyValue.readable && anyValue.hasOwnProperty('httpVersion');
const path = ((existingMeta === null || existingMeta === void 0 ? void 0 : existingMeta.filename) || (existingMeta === null || existingMeta === void 0 ? void 0 : existingMeta.filepath)
|| anyValue.name
|| anyValue.path
|| (isHttpRequest && anyValue.client._httpMessage.path)
|| '');
const filename = path.substring(Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')) + 1);
const contentType = anyValue.type
|| mime.getType(path)
|| (isHttpRequest && anyValue.headers['content-type'])
|| CFormData.DEFAULT_CONTENT_TYPE;
return Object.assign(Object.assign({ contentType, knownLength: anyValue.size }, existingMeta), { filename });
};
export class CFormData {
constructor(data) {
this.data = new Map();
if (!data) {
return;
}
if (CFormData.isFormData(data)) {
data.forEach((value, key) => this.append(key, value));
return;
}
for (const key in data) {
if (data.hasOwnProperty(key)) {
this.append(key, data[key]);
}
}
}
static from(data, { forceImplementation } = {}) {
if ((isBrowserFormData(data) && forceImplementation !== 'custom')
|| (data instanceof CFormData && forceImplementation !== 'native')) {
return data;
}
const formData = new CFormData(data);
if (!SUPPORTS_BROWSER_FORM_DATA || forceImplementation === 'custom') {
return formData;
}
const browserFormData = new FormData();
formData.forEach((value, name) => browserFormData.append(name, value));
return browserFormData;
}
static isFormData(data) {
return isBrowserFormData(data) || data instanceof CFormData;
}
append(name, value, meta) {
this.insert(name, value, true, meta);
}
delete(name) {
this.data.delete(name);
}
getAll(name) {
var _a;
return ((_a = this.data.get(name)) === null || _a === void 0 ? void 0 : _a.map(e => e.value)) || [];
}
set(name, value, meta) {
this.insert(name, value, false, meta);
}
get(name) {
var _a, _b, _c;
return (_c = (_b = (_a = this.data.get(name)) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : null;
}
has(name) {
return this.data.has(name);
}
forEach(callbackfn) {
this.data.forEach((value, key) => value.forEach(entry => callbackfn(entry.value, key, this)));
}
getBoundary() {
let boundary = this.boundary;
if (!boundary) {
boundary = '--------------------------';
for (let i = 0; i < 24; i++) {
boundary += Math.floor(Math.random() * 10).toString(16);
}
this.boundary = boundary;
}
return boundary;
}
;
getHeaders() {
return {
'content-type': 'multipart/form-data; boundary=' + this.getBoundary(),
};
}
pipe(writable) {
const stream = CombinedStream.create();
this.data.forEach((entries, name) => {
for (const entry of entries) {
stream.append(this.multiPartHeader(name, entry === null || entry === void 0 ? void 0 : entry.meta));
const value = entry.value;
if (value instanceof Readable) {
stream.append(writable);
}
else if (isBlob(value)) {
stream.append(next => readBlob(value).then(next));
}
else {
stream.append(value);
}
stream.append(CFormData.LINE_BREAK);
}
});
stream.append(this.lastBoundary());
stream.pipe(writable);
}
getBuffer() {
return new Promise((resolve, reject) => {
let buffer = Buffer.alloc(0);
const writable = new Writable({
write: (chunk, encoding, callback) => {
buffer = Buffer.concat([buffer, Buffer.from(chunk, encoding)]);
callback();
}
});
writable.on('error', reject);
writable.on('finish', () => resolve(buffer));
this.pipe(writable);
});
}
toString() {
return '[object FormData]';
}
insert(name, value, append, meta) {
if (value === undefined) {
return;
}
if (value instanceof Date) {
value = value.toISOString();
}
if (Array.isArray(value)) {
for (let index = 0; index < value.length; index++) {
this.insert(`${name}[${index}]`, value[index], append);
}
return;
}
if (typeof value === 'object' && !isBlob(value)) {
for (const objectKey in value) {
if (!value.hasOwnProperty(objectKey) || value[objectKey] === undefined) {
continue;
}
this.append(`${name}[${objectKey}]`, value[objectKey]);
}
return;
}
if (isBlob(value)) {
meta = getFileMeta(value, meta);
}
if (!append) {
this.data.set(name, [{ value, meta }]);
return;
}
const existingEntries = this.data.get(name);
const newEntry = { value, meta };
this.data.set(name, existingEntries ? [...existingEntries, newEntry] : [newEntry]);
}
multiPartHeader(name, meta = {}) {
if (typeof meta.header === 'string') {
return meta.header;
}
const contentDisposition = meta.filename ? `filename="${meta.filename}"` : '';
const contentType = meta.contentType;
let contents = '';
const headers = {
'Content-Disposition': ['form-data', `name="${name}"`, contentDisposition],
'Content-Type': [contentType],
};
if (typeof meta.header === 'object') {
Object.assign(headers, meta.header);
}
for (const prop in headers) {
let header = headers[prop];
if (!headers.hasOwnProperty(prop) || !header) {
continue;
}
if (!Array.isArray(header)) {
header = [header];
}
header = header.filter(Boolean);
if (header.length) {
contents += prop + ': ' + header.join('; ') + CFormData.LINE_BREAK;
}
}
return '--' + this.getBoundary() + CFormData.LINE_BREAK + contents + CFormData.LINE_BREAK;
}
;
lastBoundary() {
return '--' + this.getBoundary() + '--' + CFormData.LINE_BREAK;
}
}
CFormData.LINE_BREAK = '\r\n';
CFormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream';
//# sourceMappingURL=formData.js.map