thoughts/node_modules/body-parser/lib/read.js
node_modules/body-parser/lib/read.jsRaw
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
14var createError = require('http-errors')
15var getBody = require('raw-body')
16var iconv = require('iconv-lite')
17var onFinished = require('on-finished')
18var zlib = require('zlib')
19
20/**
21 * Module exports.
22 */
23
24module.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
38function 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
143function 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
183function setErrorStatus (error, status) {
184 if (!error.status && !error.statusCode) {
185 error.status = status
186 error.statusCode = status
187 }
188}