@uploadx/core
Version:
Node.js resumable upload middleware
158 lines (157 loc) • 5.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.typeis = void 0;
exports.readBody = readBody;
exports.getJsonBody = getJsonBody;
exports.getHeader = getHeader;
exports.appendHeader = appendHeader;
exports.setHeaders = setHeaders;
exports.getBaseUrl = getBaseUrl;
exports.extractHost = extractHost;
exports.extractProto = extractProto;
exports.responseToTuple = responseToTuple;
exports.tupleToResponse = tupleToResponse;
exports.normalizeHookResponse = normalizeHookResponse;
exports.normalizeOnErrorResponse = normalizeOnErrorResponse;
const primitives_1 = require("./primitives");
const typeis = (req, types) => {
const contentType = req.headers['content-type'] || '';
return exports.typeis.is(contentType, types);
};
exports.typeis = typeis;
exports.typeis.is = (mime, types = ['/']) => {
const re = new RegExp(types.map(str => str.replace(/[*+]/g, '')).join('|'));
return mime.replace(/[*+]/g, '').search(re) !== -1 ? mime : false;
};
exports.typeis.hasBody = (req) => {
const bodySize = Number(req.headers['content-length']);
return !isNaN(bodySize) && bodySize;
};
/**
* Reads the body from the request.
* @param req - request object
* @param encoding - encoding to use
* @param limit - optional limit on the size of the body
*/
function readBody(req, encoding = 'utf8', limit = Infinity) {
return new Promise((resolve, reject) => {
let body = '';
req.setEncoding(encoding);
req.on('data', chunk => {
body += chunk;
if (Buffer.byteLength(body) > limit)
return reject('Content length mismatch');
});
req.once('end', () => resolve(body));
});
}
/**
* Reads the body of the incoming metadata request and parses it as JSON.
* @param req - incoming metadata request
* @param limit - optional limit on the size of the body
*/
async function getJsonBody(req, limit = 16777216) {
if ((0, exports.typeis)(req, ['json'])) {
if (req.body)
return { ...req.body };
const contentLength = exports.typeis.hasBody(req);
if (contentLength) {
if (contentLength > limit)
return Promise.reject('Content length limit exceeded');
const raw = await readBody(req, 'utf8', contentLength);
return { ...JSON.parse(raw) };
}
}
return {};
}
/**
* Retrieve the value of a specific header of an HTTP request.
* @param req - request object
* @param name - name of the header
* @param all - if true, returns all values of the header, comma-separated, otherwise returns the last value.
*/
function getHeader(req, name, all = false) {
const raw = req.headers?.[name.toLowerCase()];
if (!raw || raw.length === 0)
return '';
return all ? raw.toString().trim() : (0, primitives_1.getLastOne)(Array.isArray(raw) ? raw : raw.split(',')).trim();
}
/**
* Appends value to the end of the multi-value header
*/
function appendHeader(res, name, value) {
const s = [res.getHeader(name), value].flat().filter(Boolean).toString();
res.setHeader(name, s);
}
/**
* Sets the value of a specific header of an HTTP response.
*/
function setHeaders(res, headers = {}) {
const keys = Object.keys(headers);
keys.length && appendHeader(res, 'Access-Control-Expose-Headers', keys);
for (const key of keys) {
['location', 'link'].includes(key.toLowerCase())
? res.setHeader(key, encodeURI(headers[key].toString()))
: res.setHeader(key, headers[key]);
}
}
/**
* Try build a protocol:hostname:port string from a request object.
*/
function getBaseUrl(req) {
const host = extractHost(req);
if (!host)
return '';
const proto = extractProto(req);
if (!proto)
return `//${host}`;
return `${proto}://${host}`;
}
/**
* Extracts host with port from a http or https request.
*/
function extractHost(req) {
return getHeader(req, 'host');
// return req.host || req.hostname || getHeader(req, 'host'); // for express v5 / fastify
}
/**
* Extracts protocol from a http or https request.
*/
function extractProto(req) {
return getHeader(req, 'x-forwarded-proto').toLowerCase();
}
function responseToTuple(response) {
if (Array.isArray(response))
return response;
const { statusCode = 200, headers, ...rest } = response;
const body = response.body ? response.body : rest;
return [statusCode, body, headers || {}];
}
function tupleToResponse(response) {
if (!Array.isArray(response))
return response;
const [statusCode, body, headers] = response;
return { statusCode, body, headers };
}
function normalizeHookResponse(fn) {
return async (file) => {
const response = await fn(file);
if ((0, primitives_1.isRecord)(response)) {
const { statusCode, headers, body, ...rest } = response;
return { statusCode, headers, body: body ?? rest };
}
return { body: response ?? '' };
};
}
/*
@internal
*/
function normalizeOnErrorResponse(fn) {
return (error) => {
if ((0, primitives_1.isRecord)(error)) {
const { statusCode, headers, body, ...rest } = error;
return fn({ statusCode, headers, body: body ?? rest });
}
return fn({ body: error ?? 'unknown error', statusCode: 500 });
};
}