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() | ||
|  |   } | ||
|  | } |