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'
13 describe('express.raw()', function () {
15 this.app = createApp()
18 it('should parse application/octet-stream', function (done) {
21 .set('Content-Type', 'application/octet-stream')
22 .send('the user is tobi')
23 .expect(200, { buf: '746865207573657220697320746f6269' }, done)
26 it('should 400 when invalid content-length', function (done) {
29 app.use(function (req, res, next) {
30 req.headers['content-length'] = '20' // bad length
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') })
46 .set('Content-Type', 'application/octet-stream')
48 .expect(400, /content length/, done)
51 it('should handle Content-Length: 0', function (done) {
54 .set('Content-Type', 'application/octet-stream')
55 .set('Content-Length', '0')
56 .expect(200, { buf: '' }, done)
59 it('should handle empty message-body', function (done) {
62 .set('Content-Type', 'application/octet-stream')
63 .set('Transfer-Encoding', 'chunked')
65 .expect(200, { buf: '' }, done)
68 it('should 500 if stream not readable', function (done) {
71 app.use(function (req, res, next) {
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)
83 app.post('/', function (req, res) {
84 if (Buffer.isBuffer(req.body)) {
85 res.json({ buf: req.body.toString('hex') })
93 .set('Content-Type', 'application/octet-stream')
94 .send('the user is tobi')
95 .expect(500, '[stream.not.readable] stream is not readable', done)
98 it('should handle duplicated middleware', function (done) {
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') })
114 .set('Content-Type', 'application/octet-stream')
115 .send('the user is tobi')
116 .expect(200, { buf: '746865207573657220697320746f6269' }, done)
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')
127 test.expect(413, done)
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')
137 test.expect(413, done)
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)
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')
155 test.expect(413, done)
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')
168 test.expect(413, done)
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')
179 test.expect(413, done)
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)
192 describe('with inflate option', function () {
193 describe('when false', function () {
195 this.app = createApp({ inflate: false })
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)
207 describe('when true', function () {
209 this.app = createApp({ inflate: true })
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)
222 describe('with type option', function () {
223 describe('when "application/vnd+octets"', function () {
225 this.app = createApp({ type: 'application/vnd+octets' })
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)
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)
243 describe('when ["application/octet-stream", "application/vnd+octets"]', function () {
245 this.app = createApp({
246 type: ['application/octet-stream', 'application/vnd+octets']
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)
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)
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)
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'
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)
286 it('should work without content-type', function (done) {
287 var app = createApp({ type: accept })
289 function accept (req) {
293 var test = request(app).post('/')
294 test.write(Buffer.from('000102', 'hex'))
295 test.expect(200, { buf: '000102' }, done)
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!')
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/)
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')
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)
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')
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)
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')
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)
361 describeAsyncHooks('async local storage', function () {
364 var store = { foo: 'bar' }
366 app.use(function (req, res, next) {
367 req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
368 req.asyncLocalStorage.run(store, next)
371 app.use(express.raw())
373 app.use(function (req, res, next) {
374 var local = req.asyncLocalStorage.getStore()
377 res.setHeader('x-store-foo', String(local.foo))
383 app.use(function (err, req, res, next) {
384 var local = req.asyncLocalStorage.getStore()
387 res.setHeader('x-store-foo', String(local.foo))
390 res.status(err.status || 500)
391 res.send('[' + err.type + '] ' + err.message)
394 app.post('/', function (req, res) {
395 if (Buffer.isBuffer(req.body)) {
396 res.json({ buf: req.body.toString('hex') })
405 it('should presist store', function (done) {
408 .set('Content-Type', 'application/octet-stream')
409 .send('the user is tobi')
411 .expect('x-store-foo', 'bar')
412 .expect({ buf: '746865207573657220697320746f6269' })
416 it('should presist store when unmatched content-type', function (done) {
419 .set('Content-Type', 'application/fizzbuzz')
422 .expect('x-store-foo', 'bar')
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'))
433 test.expect('x-store-foo', 'bar')
434 test.expect({ buf: '6e616d653de8aeba' })
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'))
444 test.expect('x-store-foo', 'bar')
448 it('should presist store when limit exceeded', function (done) {
451 .set('Content-Type', 'application/octet-stream')
452 .send('the user is ' + Buffer.alloc(1024 * 100, '.').toString())
454 .expect('x-store-foo', 'bar')
459 describe('charset', function () {
461 this.app = createApp()
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)
472 describe('encoding', function () {
474 this.app = createApp({ limit: '10kb' })
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)
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)
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)
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)
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)
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)
526 function createApp (options) {
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)))
538 app.post('/', function (req, res) {
539 if (Buffer.isBuffer(req.body)) {
540 res.json({ buf: req.body.toString('hex') })
549 function tryRequire (name) {