|
|
/*! * 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('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) }
// read off entire request
stream.resume() onFinished(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 }
|