UNPKG

3.89 kBJavaScriptView Raw
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 * @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
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
144function 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}