Initial commit
[yaffs-website] / 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  * @api 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 || 'utf-8'
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     }))
72   }
73
74   // read body
75   debug('read body')
76   getBody(stream, opts, function (err, body) {
77     if (err) {
78       // default to 400
79       setErrorStatus(err, 400)
80
81       // echo back charset
82       if (err.type === 'encoding.unsupported') {
83         err = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
84           charset: encoding.toLowerCase()
85         })
86       }
87
88       // read off entire request
89       stream.resume()
90       onFinished(req, function onfinished() {
91         next(err)
92       })
93       return
94     }
95
96     // verify
97     if (verify) {
98       try {
99         debug('verify body')
100         verify(req, res, body, encoding)
101       } catch (err) {
102         // default to 403
103         setErrorStatus(err, 403)
104         next(err)
105         return
106       }
107     }
108
109     // parse
110     var str
111     try {
112       debug('parse body')
113       str = typeof body !== 'string' && encoding !== null
114         ? iconv.decode(body, encoding)
115         : body
116       req.body = parse(str)
117     } catch (err) {
118       err.body = str === undefined
119         ? body
120         : str
121
122       // default to 400
123       setErrorStatus(err, 400)
124
125       next(err)
126       return
127     }
128
129     next()
130   })
131 }
132
133 /**
134  * Get the content stream of the request.
135  *
136  * @param {object} req
137  * @param {function} debug
138  * @param {boolean} [inflate=true]
139  * @return {object}
140  * @api private
141  */
142
143 function contentstream(req, debug, inflate) {
144   var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
145   var length = req.headers['content-length']
146   var stream
147
148   debug('content-encoding "%s"', encoding)
149
150   if (inflate === false && encoding !== 'identity') {
151     throw createError(415, 'content encoding unsupported')
152   }
153
154   switch (encoding) {
155     case 'deflate':
156       stream = zlib.createInflate()
157       debug('inflate body')
158       req.pipe(stream)
159       break
160     case 'gzip':
161       stream = zlib.createGunzip()
162       debug('gunzip body')
163       req.pipe(stream)
164       break
165     case 'identity':
166       stream = req
167       stream.length = length
168       break
169     default:
170       throw createError(415, 'unsupported content encoding "' + encoding + '"', {
171         encoding: encoding
172       })
173   }
174
175   return stream
176 }
177
178 /**
179  * Set a status on an error object, if ones does not exist
180  * @private
181  */
182
183 function setErrorStatus(error, status) {
184   if (!error.status && !error.statusCode) {
185     error.status = status
186     error.statusCode = status
187   }
188 }