deps: body-parser@1.20.0
[express.git] / test / express.raw.js
blob4aa62bb85bca6e494417208537a14f2307a4cccc
1 'use strict'
3 var assert = require('assert')
4 var asyncHooks = tryRequire('async_hooks')
5 var Buffer = require('safe-buffer').Buffer
6 var express = require('..')
7 var request = require('supertest')
9 var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
10   ? describe
11   : describe.skip
13 describe('express.raw()', function () {
14   before(function () {
15     this.app = createApp()
16   })
18   it('should parse application/octet-stream', function (done) {
19     request(this.app)
20       .post('/')
21       .set('Content-Type', 'application/octet-stream')
22       .send('the user is tobi')
23       .expect(200, { buf: '746865207573657220697320746f6269' }, done)
24   })
26   it('should 400 when invalid content-length', function (done) {
27     var app = express()
29     app.use(function (req, res, next) {
30       req.headers['content-length'] = '20' // bad length
31       next()
32     })
34     app.use(express.raw())
36     app.post('/', function (req, res) {
37       if (Buffer.isBuffer(req.body)) {
38         res.json({ buf: req.body.toString('hex') })
39       } else {
40         res.json(req.body)
41       }
42     })
44     request(app)
45       .post('/')
46       .set('Content-Type', 'application/octet-stream')
47       .send('stuff')
48       .expect(400, /content length/, done)
49   })
51   it('should handle Content-Length: 0', function (done) {
52     request(this.app)
53       .post('/')
54       .set('Content-Type', 'application/octet-stream')
55       .set('Content-Length', '0')
56       .expect(200, { buf: '' }, done)
57   })
59   it('should handle empty message-body', function (done) {
60     request(this.app)
61       .post('/')
62       .set('Content-Type', 'application/octet-stream')
63       .set('Transfer-Encoding', 'chunked')
64       .send('')
65       .expect(200, { buf: '' }, done)
66   })
68   it('should 500 if stream not readable', function (done) {
69     var app = express()
71     app.use(function (req, res, next) {
72       req.on('end', next)
73       req.resume()
74     })
76     app.use(express.raw())
78     app.use(function (err, req, res, next) {
79       res.status(err.status || 500)
80       res.send('[' + err.type + '] ' + err.message)
81     })
83     app.post('/', function (req, res) {
84       if (Buffer.isBuffer(req.body)) {
85         res.json({ buf: req.body.toString('hex') })
86       } else {
87         res.json(req.body)
88       }
89     })
91     request(app)
92       .post('/')
93       .set('Content-Type', 'application/octet-stream')
94       .send('the user is tobi')
95       .expect(500, '[stream.not.readable] stream is not readable', done)
96   })
98   it('should handle duplicated middleware', function (done) {
99     var app = express()
101     app.use(express.raw())
102     app.use(express.raw())
104     app.post('/', function (req, res) {
105       if (Buffer.isBuffer(req.body)) {
106         res.json({ buf: req.body.toString('hex') })
107       } else {
108         res.json(req.body)
109       }
110     })
112     request(app)
113       .post('/')
114       .set('Content-Type', 'application/octet-stream')
115       .send('the user is tobi')
116       .expect(200, { buf: '746865207573657220697320746f6269' }, done)
117   })
119   describe('with limit option', function () {
120     it('should 413 when over limit with Content-Length', function (done) {
121       var buf = Buffer.alloc(1028, '.')
122       var app = createApp({ limit: '1kb' })
123       var test = request(app).post('/')
124       test.set('Content-Type', 'application/octet-stream')
125       test.set('Content-Length', '1028')
126       test.write(buf)
127       test.expect(413, done)
128     })
130     it('should 413 when over limit with chunked encoding', function (done) {
131       var buf = Buffer.alloc(1028, '.')
132       var app = createApp({ limit: '1kb' })
133       var test = request(app).post('/')
134       test.set('Content-Type', 'application/octet-stream')
135       test.set('Transfer-Encoding', 'chunked')
136       test.write(buf)
137       test.expect(413, done)
138     })
140     it('should 413 when inflated body over limit', function (done) {
141       var app = createApp({ limit: '1kb' })
142       var test = request(app).post('/')
143       test.set('Content-Encoding', 'gzip')
144       test.set('Content-Type', 'application/octet-stream')
145       test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex'))
146       test.expect(413, done)
147     })
149     it('should accept number of bytes', function (done) {
150       var buf = Buffer.alloc(1028, '.')
151       var app = createApp({ limit: 1024 })
152       var test = request(app).post('/')
153       test.set('Content-Type', 'application/octet-stream')
154       test.write(buf)
155       test.expect(413, done)
156     })
158     it('should not change when options altered', function (done) {
159       var buf = Buffer.alloc(1028, '.')
160       var options = { limit: '1kb' }
161       var app = createApp(options)
163       options.limit = '100kb'
165       var test = request(app).post('/')
166       test.set('Content-Type', 'application/octet-stream')
167       test.write(buf)
168       test.expect(413, done)
169     })
171     it('should not hang response', function (done) {
172       var buf = Buffer.alloc(10240, '.')
173       var app = createApp({ limit: '8kb' })
174       var test = request(app).post('/')
175       test.set('Content-Type', 'application/octet-stream')
176       test.write(buf)
177       test.write(buf)
178       test.write(buf)
179       test.expect(413, done)
180     })
182     it('should not error when inflating', function (done) {
183       var app = createApp({ limit: '1kb' })
184       var test = request(app).post('/')
185       test.set('Content-Encoding', 'gzip')
186       test.set('Content-Type', 'application/octet-stream')
187       test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex'))
188       test.expect(413, done)
189     })
190   })
192   describe('with inflate option', function () {
193     describe('when false', function () {
194       before(function () {
195         this.app = createApp({ inflate: false })
196       })
198       it('should not accept content-encoding', function (done) {
199         var test = request(this.app).post('/')
200         test.set('Content-Encoding', 'gzip')
201         test.set('Content-Type', 'application/octet-stream')
202         test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
203         test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
204       })
205     })
207     describe('when true', function () {
208       before(function () {
209         this.app = createApp({ inflate: true })
210       })
212       it('should accept content-encoding', function (done) {
213         var test = request(this.app).post('/')
214         test.set('Content-Encoding', 'gzip')
215         test.set('Content-Type', 'application/octet-stream')
216         test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
217         test.expect(200, { buf: '6e616d653de8aeba' }, done)
218       })
219     })
220   })
222   describe('with type option', function () {
223     describe('when "application/vnd+octets"', function () {
224       before(function () {
225         this.app = createApp({ type: 'application/vnd+octets' })
226       })
228       it('should parse for custom type', function (done) {
229         var test = request(this.app).post('/')
230         test.set('Content-Type', 'application/vnd+octets')
231         test.write(Buffer.from('000102', 'hex'))
232         test.expect(200, { buf: '000102' }, done)
233       })
235       it('should ignore standard type', function (done) {
236         var test = request(this.app).post('/')
237         test.set('Content-Type', 'application/octet-stream')
238         test.write(Buffer.from('000102', 'hex'))
239         test.expect(200, '{}', done)
240       })
241     })
243     describe('when ["application/octet-stream", "application/vnd+octets"]', function () {
244       before(function () {
245         this.app = createApp({
246           type: ['application/octet-stream', 'application/vnd+octets']
247         })
248       })
250       it('should parse "application/octet-stream"', function (done) {
251         var test = request(this.app).post('/')
252         test.set('Content-Type', 'application/octet-stream')
253         test.write(Buffer.from('000102', 'hex'))
254         test.expect(200, { buf: '000102' }, done)
255       })
257       it('should parse "application/vnd+octets"', function (done) {
258         var test = request(this.app).post('/')
259         test.set('Content-Type', 'application/vnd+octets')
260         test.write(Buffer.from('000102', 'hex'))
261         test.expect(200, { buf: '000102' }, done)
262       })
264       it('should ignore "application/x-foo"', function (done) {
265         var test = request(this.app).post('/')
266         test.set('Content-Type', 'application/x-foo')
267         test.write(Buffer.from('000102', 'hex'))
268         test.expect(200, '{}', done)
269       })
270     })
272     describe('when a function', function () {
273       it('should parse when truthy value returned', function (done) {
274         var app = createApp({ type: accept })
276         function accept (req) {
277           return req.headers['content-type'] === 'application/vnd.octet'
278         }
280         var test = request(app).post('/')
281         test.set('Content-Type', 'application/vnd.octet')
282         test.write(Buffer.from('000102', 'hex'))
283         test.expect(200, { buf: '000102' }, done)
284       })
286       it('should work without content-type', function (done) {
287         var app = createApp({ type: accept })
289         function accept (req) {
290           return true
291         }
293         var test = request(app).post('/')
294         test.write(Buffer.from('000102', 'hex'))
295         test.expect(200, { buf: '000102' }, done)
296       })
298       it('should not invoke without a body', function (done) {
299         var app = createApp({ type: accept })
301         function accept (req) {
302           throw new Error('oops!')
303         }
305         request(app)
306           .get('/')
307           .expect(404, done)
308       })
309     })
310   })
312   describe('with verify option', function () {
313     it('should assert value is function', function () {
314       assert.throws(createApp.bind(null, { verify: 'lol' }),
315         /TypeError: option verify must be function/)
316     })
318     it('should error from verify', function (done) {
319       var app = createApp({
320         verify: function (req, res, buf) {
321           if (buf[0] === 0x00) throw new Error('no leading null')
322         }
323       })
325       var test = request(app).post('/')
326       test.set('Content-Type', 'application/octet-stream')
327       test.write(Buffer.from('000102', 'hex'))
328       test.expect(403, '[entity.verify.failed] no leading null', done)
329     })
331     it('should allow custom codes', function (done) {
332       var app = createApp({
333         verify: function (req, res, buf) {
334           if (buf[0] !== 0x00) return
335           var err = new Error('no leading null')
336           err.status = 400
337           throw err
338         }
339       })
341       var test = request(app).post('/')
342       test.set('Content-Type', 'application/octet-stream')
343       test.write(Buffer.from('000102', 'hex'))
344       test.expect(400, '[entity.verify.failed] no leading null', done)
345     })
347     it('should allow pass-through', function (done) {
348       var app = createApp({
349         verify: function (req, res, buf) {
350           if (buf[0] === 0x00) throw new Error('no leading null')
351         }
352       })
354       var test = request(app).post('/')
355       test.set('Content-Type', 'application/octet-stream')
356       test.write(Buffer.from('0102', 'hex'))
357       test.expect(200, { buf: '0102' }, done)
358     })
359   })
361   describeAsyncHooks('async local storage', function () {
362     before(function () {
363       var app = express()
364       var store = { foo: 'bar' }
366       app.use(function (req, res, next) {
367         req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
368         req.asyncLocalStorage.run(store, next)
369       })
371       app.use(express.raw())
373       app.use(function (req, res, next) {
374         var local = req.asyncLocalStorage.getStore()
376         if (local) {
377           res.setHeader('x-store-foo', String(local.foo))
378         }
380         next()
381       })
383       app.use(function (err, req, res, next) {
384         var local = req.asyncLocalStorage.getStore()
386         if (local) {
387           res.setHeader('x-store-foo', String(local.foo))
388         }
390         res.status(err.status || 500)
391         res.send('[' + err.type + '] ' + err.message)
392       })
394       app.post('/', function (req, res) {
395         if (Buffer.isBuffer(req.body)) {
396           res.json({ buf: req.body.toString('hex') })
397         } else {
398           res.json(req.body)
399         }
400       })
402       this.app = app
403     })
405     it('should presist store', function (done) {
406       request(this.app)
407         .post('/')
408         .set('Content-Type', 'application/octet-stream')
409         .send('the user is tobi')
410         .expect(200)
411         .expect('x-store-foo', 'bar')
412         .expect({ buf: '746865207573657220697320746f6269' })
413         .end(done)
414     })
416     it('should presist store when unmatched content-type', function (done) {
417       request(this.app)
418         .post('/')
419         .set('Content-Type', 'application/fizzbuzz')
420         .send('buzz')
421         .expect(200)
422         .expect('x-store-foo', 'bar')
423         .expect('{}')
424         .end(done)
425     })
427     it('should presist store when inflated', function (done) {
428       var test = request(this.app).post('/')
429       test.set('Content-Encoding', 'gzip')
430       test.set('Content-Type', 'application/octet-stream')
431       test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
432       test.expect(200)
433       test.expect('x-store-foo', 'bar')
434       test.expect({ buf: '6e616d653de8aeba' })
435       test.end(done)
436     })
438     it('should presist store when inflate error', function (done) {
439       var test = request(this.app).post('/')
440       test.set('Content-Encoding', 'gzip')
441       test.set('Content-Type', 'application/octet-stream')
442       test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))
443       test.expect(400)
444       test.expect('x-store-foo', 'bar')
445       test.end(done)
446     })
448     it('should presist store when limit exceeded', function (done) {
449       request(this.app)
450         .post('/')
451         .set('Content-Type', 'application/octet-stream')
452         .send('the user is ' + Buffer.alloc(1024 * 100, '.').toString())
453         .expect(413)
454         .expect('x-store-foo', 'bar')
455         .end(done)
456     })
457   })
459   describe('charset', function () {
460     before(function () {
461       this.app = createApp()
462     })
464     it('should ignore charset', function (done) {
465       var test = request(this.app).post('/')
466       test.set('Content-Type', 'application/octet-stream; charset=utf-8')
467       test.write(Buffer.from('6e616d6520697320e8aeba', 'hex'))
468       test.expect(200, { buf: '6e616d6520697320e8aeba' }, done)
469     })
470   })
472   describe('encoding', function () {
473     before(function () {
474       this.app = createApp({ limit: '10kb' })
475     })
477     it('should parse without encoding', function (done) {
478       var test = request(this.app).post('/')
479       test.set('Content-Type', 'application/octet-stream')
480       test.write(Buffer.from('6e616d653de8aeba', 'hex'))
481       test.expect(200, { buf: '6e616d653de8aeba' }, done)
482     })
484     it('should support identity encoding', function (done) {
485       var test = request(this.app).post('/')
486       test.set('Content-Encoding', 'identity')
487       test.set('Content-Type', 'application/octet-stream')
488       test.write(Buffer.from('6e616d653de8aeba', 'hex'))
489       test.expect(200, { buf: '6e616d653de8aeba' }, done)
490     })
492     it('should support gzip encoding', function (done) {
493       var test = request(this.app).post('/')
494       test.set('Content-Encoding', 'gzip')
495       test.set('Content-Type', 'application/octet-stream')
496       test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
497       test.expect(200, { buf: '6e616d653de8aeba' }, done)
498     })
500     it('should support deflate encoding', function (done) {
501       var test = request(this.app).post('/')
502       test.set('Content-Encoding', 'deflate')
503       test.set('Content-Type', 'application/octet-stream')
504       test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex'))
505       test.expect(200, { buf: '6e616d653de8aeba' }, done)
506     })
508     it('should be case-insensitive', function (done) {
509       var test = request(this.app).post('/')
510       test.set('Content-Encoding', 'GZIP')
511       test.set('Content-Type', 'application/octet-stream')
512       test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
513       test.expect(200, { buf: '6e616d653de8aeba' }, done)
514     })
516     it('should 415 on unknown encoding', function (done) {
517       var test = request(this.app).post('/')
518       test.set('Content-Encoding', 'nulls')
519       test.set('Content-Type', 'application/octet-stream')
520       test.write(Buffer.from('000000000000', 'hex'))
521       test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
522     })
523   })
526 function createApp (options) {
527   var app = express()
529   app.use(express.raw(options))
531   app.use(function (err, req, res, next) {
532     res.status(err.status || 500)
533     res.send(String(req.headers['x-error-property']
534       ? err[req.headers['x-error-property']]
535       : ('[' + err.type + '] ' + err.message)))
536   })
538   app.post('/', function (req, res) {
539     if (Buffer.isBuffer(req.body)) {
540       res.json({ buf: req.body.toString('hex') })
541     } else {
542       res.json(req.body)
543     }
544   })
546   return app
549 function tryRequire (name) {
550   try {
551     return require(name)
552   } catch (e) {
553     return {}
554   }