second
[josuexyz/.git] / node_modules / body-parser / lib / read.js
1 /*!
2  * body-parser
3  * Copyright(c) 2014-2015 Douglas Christopher Wilson
4  * MIT Licensed
5  */
6
7 'use strict'
8
9 /**
10  * Module dependencies.
11  * @private
12  */
13
14 var createError = require('http-errors')
15 var getBody = require('raw-body')
16 var iconv = require('iconv-lite')
17 var onFinished = require('on-finished')
18 var zlib = require('zlib')
19
20 /**
21  * Module exports.
22  */
23
24 module.exports = read
25
26 /**
27  * Read a request into a buffer and parse.
28  *
29  * @param {object} req
30  * @param {object} res
31  * @param {function} next
32  * @param {function} parse
33  * @param {function} debug
34  * @param {object} options
35  * @private
36  */
37
38 function read (req, res, next, parse, debug, options) {
39   var length
40   var opts = options
41   var stream
42
43   // flag as parsed
44   req._body = true
45
46   // read options
47   var encoding = opts.encoding !== null
48     ? opts.encoding
49     : null
50   var verify = opts.verify
51
52   try {
53     // get the content stream
54     stream = contentstream(req, debug, opts.inflate)
55     length = stream.length
56     stream.length = undefined
57   } catch (err) {
58     return next(err)
59   }
60
61   // set raw-body options
62   opts.length = length
63   opts.encoding = verify
64     ? null
65     : encoding
66
67   // assert charset is supported
68   if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
69     return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
70       charset: encoding.toLowerCase(),
71       type: 'charset.unsupported'
72     }))
73   }
74
75   // read body
76   debug('read body')
77   getBody(stream, opts, function (error, body) {
78     if (error) {
79       var _error
80
81       if (error.type === 'encoding.unsupported') {
82         // echo back charset
83         _error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
84           charset: encoding.toLowerCase(),
85           type: 'charset.unsupported'
86         })
87       } else {
88         // set status code on error
89         _error = createError(400, error)
90       }
91
92       // read off entire request
93       stream.resume()
94       onFinished(req, function onfinished () {
95         next(createError(400, _error))
96       })
97       return
98     }
99
100     // verify
101     if (verify) {
102       try {
103         debug('verify body')
104         verify(req, res, body, encoding)
105       } catch (err) {
106         next(createError(403, err, {
107           body: body,
108           type: err.type || 'entity.verify.failed'
109         }))
110         return
111       }
112     }
113
114     // parse
115     var str = body
116     try {
117       debug('parse body')
118       str = typeof body !== 'string' && encoding !== null
119         ? iconv.decode(body, encoding)
120         : body
121       req.body = parse(str)
122     } catch (err) {
123       next(createError(400, err, {
124         body: str,
125         type: err.type || 'entity.parse.failed'
126       }))
127       return
128     }
129
130     next()
131   })
132 }
133
134 /**
135  * Get the content stream of the request.
136  *
137  * @param {object} req
138  * @param {function} debug
139  * @param {boolean} [inflate=true]
140  * @return {object}
141  * @api private
142  */
143
144 function contentstream (req, debug, inflate) {
145   var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
146   var length = req.headers['content-length']
147   var stream
148
149   debug('content-encoding "%s"', encoding)
150
151   if (inflate === false && encoding !== 'identity') {
152     throw createError(415, 'content encoding unsupported', {
153       encoding: encoding,
154       type: 'encoding.unsupported'
155     })
156   }
157
158   switch (encoding) {
159     case 'deflate':
160       stream = zlib.createInflate()
161       debug('inflate body')
162       req.pipe(stream)
163       break
164     case 'gzip':
165       stream = zlib.createGunzip()
166       debug('gunzip body')
167       req.pipe(stream)
168       break
169     case 'identity':
170       stream = req
171       stream.length = length
172       break
173     default:
174       throw createError(415, 'unsupported content encoding "' + encoding + '"', {
175         encoding: encoding,
176         type: 'encoding.unsupported'
177       })
178   }
179
180   return stream
181 }