| /*! |
| * 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 |
| } |