You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					206 lines
				
				4.2 KiB
			
		
		
			
		
	
	
					206 lines
				
				4.2 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * body-parser
							 | 
						||
| 
								 | 
							
								 * Copyright(c) 2014-2015 Douglas Christopher Wilson
							 | 
						||
| 
								 | 
							
								 * MIT Licensed
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								'use strict'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |