2025-04-02 06:50:39 -04:00

226 lines
4.6 KiB
JavaScript

/*!
* body-parser
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var createError = require('http-errors');
var getBody = require('raw-body');
var iconv = require('iconv-lite');
var onFinished = require('on-finished');
var zlib = require('node:zlib');
/**
* Module exports.
*/
module.exports = read;
/**
* Read a request into a buffer and parse.
*
* @param {object} req
* @param {object} res
* @param {function} next
* @param {function} parse
* @param {function} debug
* @param {object} options
* @private
*/
function read(req, res, next, parse, debug, options) {
var length;
var opts = options;
var stream;
// read options
var encoding = opts.encoding !== null ? opts.encoding : null;
var verify = opts.verify;
try {
// get the content stream
stream = contentstream(req, debug, opts.inflate);
length = stream.length;
stream.length = undefined;
} catch (err) {
return next(err);
}
// set raw-body options
opts.length = length;
opts.encoding = verify ? null : encoding;
// assert charset is supported
if (
opts.encoding === null &&
encoding !== null &&
!iconv.encodingExists(encoding)
) {
return next(
createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
charset: encoding.toLowerCase(),
type: 'charset.unsupported',
})
);
}
// read body
debug('read body');
getBody(stream, opts, function (error, body) {
if (error) {
var _error;
if (error.type === 'encoding.unsupported') {
// echo back charset
_error = createError(
415,
'unsupported charset "' + encoding.toUpperCase() + '"',
{
charset: encoding.toLowerCase(),
type: 'charset.unsupported',
}
);
} else {
// set status code on error
_error = createError(400, error);
}
// unpipe from stream and destroy
if (stream !== req) {
req.unpipe();
stream.destroy();
}
// read off entire request
dump(req, function onfinished() {
next(createError(400, _error));
});
return;
}
// verify
if (verify) {
try {
debug('verify body');
verify(req, res, body, encoding);
} catch (err) {
next(
createError(403, err, {
body: body,
type: err.type || 'entity.verify.failed',
})
);
return;
}
}
// parse
var str = body;
try {
debug('parse body');
str =
typeof body !== 'string' && encoding !== null ?
iconv.decode(body, encoding)
: body;
req.body = parse(str, encoding);
} catch (err) {
next(
createError(400, err, {
body: str,
type: err.type || 'entity.parse.failed',
})
);
return;
}
next();
});
}
/**
* Get the content stream of the request.
*
* @param {object} req
* @param {function} debug
* @param {boolean} [inflate=true]
* @return {object}
* @api private
*/
function contentstream(req, debug, inflate) {
var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase();
var length = req.headers['content-length'];
debug('content-encoding "%s"', encoding);
if (inflate === false && encoding !== 'identity') {
throw createError(415, 'content encoding unsupported', {
encoding: encoding,
type: 'encoding.unsupported',
});
}
if (encoding === 'identity') {
req.length = length;
return req;
}
var stream = createDecompressionStream(encoding, debug);
req.pipe(stream);
return stream;
}
/**
* Create a decompression stream for the given encoding.
* @param {string} encoding
* @param {function} debug
* @return {object}
* @api private
*/
function createDecompressionStream(encoding, debug) {
switch (encoding) {
case 'deflate':
debug('inflate body');
return zlib.createInflate();
case 'gzip':
debug('gunzip body');
return zlib.createGunzip();
case 'br':
debug('brotli decompress body');
return zlib.createBrotliDecompress();
default:
throw createError(
415,
'unsupported content encoding "' + encoding + '"',
{
encoding: encoding,
type: 'encoding.unsupported',
}
);
}
}
/**
* Dump the contents of a request.
*
* @param {object} req
* @param {function} callback
* @api private
*/
function dump(req, callback) {
if (onFinished.isFinished(req)) {
callback(null);
} else {
onFinished(req, callback);
req.resume();
}
}