source: trip-planner-front/node_modules/raw-body/index.js@ eed0bf8

Last change on this file since eed0bf8 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 5.9 KB
RevLine 
[6a3a178]1/*!
2 * raw-body
3 * Copyright(c) 2013-2014 Jonathan Ong
4 * Copyright(c) 2014-2015 Douglas Christopher Wilson
5 * MIT Licensed
6 */
7
8'use strict'
9
10/**
11 * Module dependencies.
12 * @private
13 */
14
15var bytes = require('bytes')
16var createError = require('http-errors')
17var iconv = require('iconv-lite')
18var unpipe = require('unpipe')
19
20/**
21 * Module exports.
22 * @public
23 */
24
25module.exports = getRawBody
26
27/**
28 * Module variables.
29 * @private
30 */
31
32var ICONV_ENCODING_MESSAGE_REGEXP = /^Encoding not recognized: /
33
34/**
35 * Get the decoder for a given encoding.
36 *
37 * @param {string} encoding
38 * @private
39 */
40
41function getDecoder (encoding) {
42 if (!encoding) return null
43
44 try {
45 return iconv.getDecoder(encoding)
46 } catch (e) {
47 // error getting decoder
48 if (!ICONV_ENCODING_MESSAGE_REGEXP.test(e.message)) throw e
49
50 // the encoding was not found
51 throw createError(415, 'specified encoding unsupported', {
52 encoding: encoding,
53 type: 'encoding.unsupported'
54 })
55 }
56}
57
58/**
59 * Get the raw body of a stream (typically HTTP).
60 *
61 * @param {object} stream
62 * @param {object|string|function} [options]
63 * @param {function} [callback]
64 * @public
65 */
66
67function getRawBody (stream, options, callback) {
68 var done = callback
69 var opts = options || {}
70
71 if (options === true || typeof options === 'string') {
72 // short cut for encoding
73 opts = {
74 encoding: options
75 }
76 }
77
78 if (typeof options === 'function') {
79 done = options
80 opts = {}
81 }
82
83 // validate callback is a function, if provided
84 if (done !== undefined && typeof done !== 'function') {
85 throw new TypeError('argument callback must be a function')
86 }
87
88 // require the callback without promises
89 if (!done && !global.Promise) {
90 throw new TypeError('argument callback is required')
91 }
92
93 // get encoding
94 var encoding = opts.encoding !== true
95 ? opts.encoding
96 : 'utf-8'
97
98 // convert the limit to an integer
99 var limit = bytes.parse(opts.limit)
100
101 // convert the expected length to an integer
102 var length = opts.length != null && !isNaN(opts.length)
103 ? parseInt(opts.length, 10)
104 : null
105
106 if (done) {
107 // classic callback style
108 return readStream(stream, encoding, length, limit, done)
109 }
110
111 return new Promise(function executor (resolve, reject) {
112 readStream(stream, encoding, length, limit, function onRead (err, buf) {
113 if (err) return reject(err)
114 resolve(buf)
115 })
116 })
117}
118
119/**
120 * Halt a stream.
121 *
122 * @param {Object} stream
123 * @private
124 */
125
126function halt (stream) {
127 // unpipe everything from the stream
128 unpipe(stream)
129
130 // pause stream
131 if (typeof stream.pause === 'function') {
132 stream.pause()
133 }
134}
135
136/**
137 * Read the data from the stream.
138 *
139 * @param {object} stream
140 * @param {string} encoding
141 * @param {number} length
142 * @param {number} limit
143 * @param {function} callback
144 * @public
145 */
146
147function readStream (stream, encoding, length, limit, callback) {
148 var complete = false
149 var sync = true
150
151 // check the length and limit options.
152 // note: we intentionally leave the stream paused,
153 // so users should handle the stream themselves.
154 if (limit !== null && length !== null && length > limit) {
155 return done(createError(413, 'request entity too large', {
156 expected: length,
157 length: length,
158 limit: limit,
159 type: 'entity.too.large'
160 }))
161 }
162
163 // streams1: assert request encoding is buffer.
164 // streams2+: assert the stream encoding is buffer.
165 // stream._decoder: streams1
166 // state.encoding: streams2
167 // state.decoder: streams2, specifically < 0.10.6
168 var state = stream._readableState
169 if (stream._decoder || (state && (state.encoding || state.decoder))) {
170 // developer error
171 return done(createError(500, 'stream encoding should not be set', {
172 type: 'stream.encoding.set'
173 }))
174 }
175
176 var received = 0
177 var decoder
178
179 try {
180 decoder = getDecoder(encoding)
181 } catch (err) {
182 return done(err)
183 }
184
185 var buffer = decoder
186 ? ''
187 : []
188
189 // attach listeners
190 stream.on('aborted', onAborted)
191 stream.on('close', cleanup)
192 stream.on('data', onData)
193 stream.on('end', onEnd)
194 stream.on('error', onEnd)
195
196 // mark sync section complete
197 sync = false
198
199 function done () {
200 var args = new Array(arguments.length)
201
202 // copy arguments
203 for (var i = 0; i < args.length; i++) {
204 args[i] = arguments[i]
205 }
206
207 // mark complete
208 complete = true
209
210 if (sync) {
211 process.nextTick(invokeCallback)
212 } else {
213 invokeCallback()
214 }
215
216 function invokeCallback () {
217 cleanup()
218
219 if (args[0]) {
220 // halt the stream on error
221 halt(stream)
222 }
223
224 callback.apply(null, args)
225 }
226 }
227
228 function onAborted () {
229 if (complete) return
230
231 done(createError(400, 'request aborted', {
232 code: 'ECONNABORTED',
233 expected: length,
234 length: length,
235 received: received,
236 type: 'request.aborted'
237 }))
238 }
239
240 function onData (chunk) {
241 if (complete) return
242
243 received += chunk.length
244
245 if (limit !== null && received > limit) {
246 done(createError(413, 'request entity too large', {
247 limit: limit,
248 received: received,
249 type: 'entity.too.large'
250 }))
251 } else if (decoder) {
252 buffer += decoder.write(chunk)
253 } else {
254 buffer.push(chunk)
255 }
256 }
257
258 function onEnd (err) {
259 if (complete) return
260 if (err) return done(err)
261
262 if (length !== null && received !== length) {
263 done(createError(400, 'request size did not match content length', {
264 expected: length,
265 length: length,
266 received: received,
267 type: 'request.size.invalid'
268 }))
269 } else {
270 var string = decoder
271 ? buffer + (decoder.end() || '')
272 : Buffer.concat(buffer)
273 done(null, string)
274 }
275 }
276
277 function cleanup () {
278 buffer = null
279
280 stream.removeListener('aborted', onAborted)
281 stream.removeListener('data', onData)
282 stream.removeListener('end', onEnd)
283 stream.removeListener('error', onEnd)
284 stream.removeListener('close', cleanup)
285 }
286}
Note: See TracBrowser for help on using the repository browser.