body-parser
Version:
Node.js body parsing middleware
191 lines (152 loc) • 3.87 kB
JavaScript
var bytes = require('bytes');
var getBody = require('raw-body');
var typeis = require('type-is');
var http = require('http');
var qs = require('qs');
var firstcharRegExp = /^\s*(.)/
exports = module.exports = bodyParser;
exports.json = json;
exports.urlencoded = urlencoded;
function bodyParser(options){
var opts = {}
options = options || {}
// exclude type option
for (var prop in options) {
if ('type' !== prop) {
opts[prop] = options[prop]
}
}
var _urlencoded = urlencoded(opts)
var _json = json(opts)
return function bodyParser(req, res, next) {
_json(req, res, function(err){
if (err) return next(err);
_urlencoded(req, res, next);
});
}
}
function json(options){
options = options || {};
var limit = typeof options.limit !== 'number'
? bytes(options.limit || '100kb')
: options.limit;
var reviver = options.reviver
var strict = options.strict !== false;
var type = options.type || 'json';
var verify = options.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
function parse(str) {
if (0 === str.length) {
throw new Error('invalid json, empty body')
}
if (strict) {
var first = firstchar(str)
if (first !== '{' && first !== '[') {
throw new Error('invalid json')
}
}
return JSON.parse(str, reviver)
}
return function jsonParser(req, res, next) {
if (req._body) return next();
req.body = req.body || {}
if (!typeis(req, type)) return next();
// read
read(req, res, next, parse, {
limit: limit,
verify: verify
})
}
}
function urlencoded(options){
options = options || {};
var limit = typeof options.limit !== 'number'
? bytes(options.limit || '100kb')
: options.limit;
var type = options.type || 'urlencoded';
var verify = options.verify || false;
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
function parse(str) {
return str.length
? qs.parse(str)
: {}
}
return function urlencodedParser(req, res, next) {
if (req._body) return next();
req.body = req.body || {}
if (!typeis(req, type)) return next();
// read
read(req, res, next, parse, {
limit: limit,
verify: verify
})
}
}
function firstchar(str) {
if (!str) return ''
var match = firstcharRegExp.exec(str)
return match ? match[1] : ''
}
function read(req, res, next, parse, options) {
var length = req.headers['content-length']
var waitend = true
// flag as parsed
req._body = true
options = options || {}
options.length = length
var encoding = options.encoding || 'utf-8'
var verify = options.verify
options.encoding = verify
? null
: encoding
req.on('aborted', cleanup)
req.on('end', cleanup)
req.on('error', cleanup)
// read body
getBody(req, options, function (err, body) {
if (err && waitend && req.readable) {
// read off entire request
req.resume()
req.once('end', function onEnd() {
next(err)
})
return
}
if (err) {
next(err)
return
}
var str
// verify
if (verify) {
try {
verify(req, res, body, encoding)
} catch (err) {
if (!err.status) err.status = 403
return next(err)
}
}
// parse
try {
str = typeof body !== 'string'
? body.toString(encoding)
: body
req.body = parse(str)
} catch (err){
err.body = str
err.status = 400
return next(err)
}
next()
})
function cleanup() {
waitend = false
req.removeListener('aborted', cleanup)
req.removeListener('end', cleanup)
req.removeListener('error', cleanup)
}
}