Spaces:
Sleeping
Sleeping
/*! | |
* body-parser | |
* Copyright(c) 2014-2015 Douglas Christopher Wilson | |
* MIT Licensed | |
*/ | |
/** | |
* Module dependencies. | |
* @private | |
*/ | |
var createError = require('http-errors') | |
var destroy = require('destroy') | |
var getBody = require('raw-body') | |
var iconv = require('iconv-lite') | |
var onFinished = require('on-finished') | |
var unpipe = require('unpipe') | |
var zlib = require('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 | |
// flag as parsed | |
req._body = true | |
// 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) { | |
unpipe(req) | |
destroy(stream, true) | |
} | |
// 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) | |
} 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'] | |
var stream | |
debug('content-encoding "%s"', encoding) | |
if (inflate === false && encoding !== 'identity') { | |
throw createError(415, 'content encoding unsupported', { | |
encoding: encoding, | |
type: 'encoding.unsupported' | |
}) | |
} | |
switch (encoding) { | |
case 'deflate': | |
stream = zlib.createInflate() | |
debug('inflate body') | |
req.pipe(stream) | |
break | |
case 'gzip': | |
stream = zlib.createGunzip() | |
debug('gunzip body') | |
req.pipe(stream) | |
break | |
case 'identity': | |
stream = req | |
stream.length = length | |
break | |
default: | |
throw createError(415, 'unsupported content encoding "' + encoding + '"', { | |
encoding: encoding, | |
type: 'encoding.unsupported' | |
}) | |
} | |
return stream | |
} | |
/** | |
* 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() | |
} | |
} | |