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