UNPKG

http-micro

Version:

Micro-framework on top of node's http module

179 lines (178 loc) 6.77 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const rawBody = require("raw-body"); const qs = require("querystring"); const typeis = require("type-is"); const contentType = require("content-type"); const mimeTypes = require("mime-types"); const error_utils_1 = require("./error-utils"); const defaultLimit = 1024 * 1024 / 2; // 512Kb function rawBodyParserFactory() { return function rawParser(req, callback, opts) { if (handleRequestBodyAbsence(req, callback)) return; let limit, contentLength, encoding; if (opts) { limit = opts.limit; contentLength = opts.length; encoding = opts.encoding; } limit = limit || defaultLimit; contentLength = contentLength || Number(req.headers["content-length"]); if (encoding === undefined) { let contentTypeHeader = req.headers["content-type"]; // Ensure that further attempts are skipped, as contentType // will throw on invalid header. Since rawParser could // potentially be passed on it's own to get a buffer back. if (contentTypeHeader) { try { let ct = contentType.parse(contentTypeHeader); encoding = ct.parameters["charset"]; if (!encoding) { // No valid encoding was found, but content-type // header is valid. So, pick up the default // encoding for the mime. let mimeCharset = mimeTypes.charset(ct.type); encoding = mimeCharset ? mimeCharset : undefined; } } catch (err) { throw error_utils_1.intoHttpError(err, 400); } } } if (encoding === undefined && opts) encoding = opts.defaultEncoding; rawBody(req, { limit: limit, length: contentLength, encoding, }, callback); }; } exports.rawBodyParserFactory = rawBodyParserFactory; function jsonBodyParserFactory(opts, baseParser) { let rawParser = baseParser || rawBodyParserFactory(); let reviver = opts ? opts.reviver : undefined; return function jsonParser(req, callback, baseParserOpts) { rawParser(req, function (err, body) { if (err) { return callback(err); } let res; try { if (typeof body !== "string") throw new Error("buffered raw body is not a string to parse as json"); res = JSON.parse(body, reviver); } catch (e) { return callback(e); } callback(null, res); }, baseParserOpts); }; } exports.jsonBodyParserFactory = jsonBodyParserFactory; function formBodyParserFactory(opts, baseParser) { let rawParser = baseParser || rawBodyParserFactory(); let qsParse, sep, eq, options; if (opts) { qsParse = opts.parser; sep = opts.sep; eq = opts.eq; options = opts.options; } qsParse = qsParse || qs.parse; return function formBodyParser(req, callback, baseParserOpts) { let baseOpts = baseParserOpts; // TODO: Not very happy with the implementation here, for passing override opts to // the raw parser. It works, however, could be better designed. if (baseOpts == null || baseOpts.defaultEncoding === undefined) { // It's important to pass in the default encoding as true (translates to 'utf-8'), // since, mime-types don't resolve the default charset for // `application/x-www-form-urlencoded` // TODO: Default charset Latin-1? baseOpts = Object.assign({}, baseOpts, { defaultEncoding: true }); } rawParser(req, function (err, body) { if (err) { return callback(err); } let res; try { if (typeof body !== "string") throw new Error("buffered raw body is not a string to parse as url encoded form"); // TODO: How to handle charset? res = qsParse(body, sep, eq, options); } catch (e) { return callback(e); } callback(null, res); }, baseOpts); }; } exports.formBodyParserFactory = formBodyParserFactory; function anyBodyParserFactory(opts, baseParser) { let rawParser = baseParser || rawBodyParserFactory(); let jsonParser = jsonBodyParserFactory(opts, rawParser); let formParser = formBodyParserFactory(opts, rawParser); let types = ["json", "urlencoded"]; return function anyBodyParser(req, callback, baseParserOpts) { if (handleRequestBodyAbsence(req, callback)) return; let t = typeis(req, types); switch (t) { case types[0]: { jsonParser(req, callback, baseParserOpts); break; } case types[1]: { formParser(req, callback, baseParserOpts); break; } default: { rawParser(req, callback, baseParserOpts); } } }; } exports.anyBodyParserFactory = anyBodyParserFactory; function createAsyncParser(parser) { return function parse(req, opts) { return new Promise((resolve, reject) => { parser(req, (err, body) => { if (err) { reject(error_utils_1.intoHttpError(err, 400)); } else { resolve(body); } }, opts); }); }; } exports.createAsyncParser = createAsyncParser; function parseBody(req, opts, parser = anyBodyParserFactory()) { let finalParser = createAsyncParser(parser); return finalParser(req, opts); } exports.parseBody = parseBody; function handleRequestBodyAbsence(req, callback) { if (!hasBody(req)) { callback(null, null); return true; } return false; } exports.handleRequestBodyAbsence = handleRequestBodyAbsence; function hasBody(req) { let headers = req.headers; if (headers["transfer-encoding"] !== undefined) return true; let contentLength = headers["content-length"]; if (contentLength && Number(contentLength) > 0) return true; return false; } exports.hasBody = hasBody;