fix(deps)!: mime-types@^3.0.0 (#5882)
[express.git] / test / express.json.js
blob859347e1dcb41eda5135a8932abcbee6f68b156d
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.json()', function () {
14 it('should parse JSON', function (done) {
15 request(createApp())
16 .post('/')
17 .set('Content-Type', 'application/json')
18 .send('{"user":"tobi"}')
19 .expect(200, '{"user":"tobi"}', done)
22 it('should handle Content-Length: 0', function (done) {
23 request(createApp())
24 .post('/')
25 .set('Content-Type', 'application/json')
26 .set('Content-Length', '0')
27 .expect(200, '{}', done)
30 it('should handle empty message-body', function (done) {
31 request(createApp())
32 .post('/')
33 .set('Content-Type', 'application/json')
34 .set('Transfer-Encoding', 'chunked')
35 .expect(200, '{}', done)
38 it('should handle no message-body', function (done) {
39 request(createApp())
40 .post('/')
41 .set('Content-Type', 'application/json')
42 .unset('Transfer-Encoding')
43 .expect(200, '{}', done)
46 // The old node error message modification in body parser is catching this
47 it('should 400 when only whitespace', function (done) {
48 request(createApp())
49 .post('/')
50 .set('Content-Type', 'application/json')
51 .send(' \n')
52 .expect(400, '[entity.parse.failed] ' + parseError(' \n'), done)
55 it('should 400 when invalid content-length', function (done) {
56 var app = express()
58 app.use(function (req, res, next) {
59 req.headers['content-length'] = '20' // bad length
60 next()
63 app.use(express.json())
65 app.post('/', function (req, res) {
66 res.json(req.body)
69 request(app)
70 .post('/')
71 .set('Content-Type', 'application/json')
72 .send('{"str":')
73 .expect(400, /content length/, done)
76 it('should handle duplicated middleware', function (done) {
77 var app = express()
79 app.use(express.json())
80 app.use(express.json())
82 app.post('/', function (req, res) {
83 res.json(req.body)
86 request(app)
87 .post('/')
88 .set('Content-Type', 'application/json')
89 .send('{"user":"tobi"}')
90 .expect(200, '{"user":"tobi"}', done)
93 describe('when JSON is invalid', function () {
94 before(function () {
95 this.app = createApp()
98 it('should 400 for bad token', function (done) {
99 request(this.app)
100 .post('/')
101 .set('Content-Type', 'application/json')
102 .send('{:')
103 .expect(400, '[entity.parse.failed] ' + parseError('{:'), done)
106 it('should 400 for incomplete', function (done) {
107 request(this.app)
108 .post('/')
109 .set('Content-Type', 'application/json')
110 .send('{"user"')
111 .expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done)
114 it('should include original body on error object', function (done) {
115 request(this.app)
116 .post('/')
117 .set('Content-Type', 'application/json')
118 .set('X-Error-Property', 'body')
119 .send(' {"user"')
120 .expect(400, ' {"user"', done)
124 describe('with limit option', function () {
125 it('should 413 when over limit with Content-Length', function (done) {
126 var buf = Buffer.alloc(1024, '.')
127 request(createApp({ limit: '1kb' }))
128 .post('/')
129 .set('Content-Type', 'application/json')
130 .set('Content-Length', '1034')
131 .send(JSON.stringify({ str: buf.toString() }))
132 .expect(413, '[entity.too.large] request entity too large', done)
135 it('should 413 when over limit with chunked encoding', function (done) {
136 var app = createApp({ limit: '1kb' })
137 var buf = Buffer.alloc(1024, '.')
138 var test = request(app).post('/')
139 test.set('Content-Type', 'application/json')
140 test.set('Transfer-Encoding', 'chunked')
141 test.write('{"str":')
142 test.write('"' + buf.toString() + '"}')
143 test.expect(413, done)
146 it('should 413 when inflated body over limit', function (done) {
147 var app = createApp({ limit: '1kb' })
148 var test = request(app).post('/')
149 test.set('Content-Encoding', 'gzip')
150 test.set('Content-Type', 'application/json')
151 test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex'))
152 test.expect(413, done)
155 it('should accept number of bytes', function (done) {
156 var buf = Buffer.alloc(1024, '.')
157 request(createApp({ limit: 1024 }))
158 .post('/')
159 .set('Content-Type', 'application/json')
160 .send(JSON.stringify({ str: buf.toString() }))
161 .expect(413, done)
164 it('should not change when options altered', function (done) {
165 var buf = Buffer.alloc(1024, '.')
166 var options = { limit: '1kb' }
167 var app = createApp(options)
169 options.limit = '100kb'
171 request(app)
172 .post('/')
173 .set('Content-Type', 'application/json')
174 .send(JSON.stringify({ str: buf.toString() }))
175 .expect(413, done)
178 it('should not hang response', function (done) {
179 var buf = Buffer.alloc(10240, '.')
180 var app = createApp({ limit: '8kb' })
181 var test = request(app).post('/')
182 test.set('Content-Type', 'application/json')
183 test.write(buf)
184 test.write(buf)
185 test.write(buf)
186 test.expect(413, done)
189 it('should not error when inflating', function (done) {
190 var app = createApp({ limit: '1kb' })
191 var test = request(app).post('/')
192 test.set('Content-Encoding', 'gzip')
193 test.set('Content-Type', 'application/json')
194 test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex'))
195 test.expect(413, done)
199 describe('with inflate option', function () {
200 describe('when false', function () {
201 before(function () {
202 this.app = createApp({ inflate: false })
205 it('should not accept content-encoding', function (done) {
206 var test = request(this.app).post('/')
207 test.set('Content-Encoding', 'gzip')
208 test.set('Content-Type', 'application/json')
209 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
210 test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
214 describe('when true', function () {
215 before(function () {
216 this.app = createApp({ inflate: true })
219 it('should accept content-encoding', function (done) {
220 var test = request(this.app).post('/')
221 test.set('Content-Encoding', 'gzip')
222 test.set('Content-Type', 'application/json')
223 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
224 test.expect(200, '{"name":"论"}', done)
229 describe('with strict option', function () {
230 describe('when undefined', function () {
231 before(function () {
232 this.app = createApp()
235 it('should 400 on primitives', function (done) {
236 request(this.app)
237 .post('/')
238 .set('Content-Type', 'application/json')
239 .send('true')
240 .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
244 describe('when false', function () {
245 before(function () {
246 this.app = createApp({ strict: false })
249 it('should parse primitives', function (done) {
250 request(this.app)
251 .post('/')
252 .set('Content-Type', 'application/json')
253 .send('true')
254 .expect(200, 'true', done)
258 describe('when true', function () {
259 before(function () {
260 this.app = createApp({ strict: true })
263 it('should not parse primitives', function (done) {
264 request(this.app)
265 .post('/')
266 .set('Content-Type', 'application/json')
267 .send('true')
268 .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
271 it('should not parse primitives with leading whitespaces', function (done) {
272 request(this.app)
273 .post('/')
274 .set('Content-Type', 'application/json')
275 .send(' true')
276 .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done)
279 it('should allow leading whitespaces in JSON', function (done) {
280 request(this.app)
281 .post('/')
282 .set('Content-Type', 'application/json')
283 .send(' { "user": "tobi" }')
284 .expect(200, '{"user":"tobi"}', done)
287 it('should include correct message in stack trace', function (done) {
288 request(this.app)
289 .post('/')
290 .set('Content-Type', 'application/json')
291 .set('X-Error-Property', 'stack')
292 .send('true')
293 .expect(400)
294 .expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't')))
295 .end(done)
300 describe('with type option', function () {
301 describe('when "application/vnd.api+json"', function () {
302 before(function () {
303 this.app = createApp({ type: 'application/vnd.api+json' })
306 it('should parse JSON for custom type', function (done) {
307 request(this.app)
308 .post('/')
309 .set('Content-Type', 'application/vnd.api+json')
310 .send('{"user":"tobi"}')
311 .expect(200, '{"user":"tobi"}', done)
314 it('should ignore standard type', function (done) {
315 request(this.app)
316 .post('/')
317 .set('Content-Type', 'application/json')
318 .send('{"user":"tobi"}')
319 .expect(200, '', done)
323 describe('when ["application/json", "application/vnd.api+json"]', function () {
324 before(function () {
325 this.app = createApp({
326 type: ['application/json', 'application/vnd.api+json']
330 it('should parse JSON for "application/json"', function (done) {
331 request(this.app)
332 .post('/')
333 .set('Content-Type', 'application/json')
334 .send('{"user":"tobi"}')
335 .expect(200, '{"user":"tobi"}', done)
338 it('should parse JSON for "application/vnd.api+json"', function (done) {
339 request(this.app)
340 .post('/')
341 .set('Content-Type', 'application/vnd.api+json')
342 .send('{"user":"tobi"}')
343 .expect(200, '{"user":"tobi"}', done)
346 it('should ignore "application/x-json"', function (done) {
347 request(this.app)
348 .post('/')
349 .set('Content-Type', 'application/x-json')
350 .send('{"user":"tobi"}')
351 .expect(200, '', done)
355 describe('when a function', function () {
356 it('should parse when truthy value returned', function (done) {
357 var app = createApp({ type: accept })
359 function accept (req) {
360 return req.headers['content-type'] === 'application/vnd.api+json'
363 request(app)
364 .post('/')
365 .set('Content-Type', 'application/vnd.api+json')
366 .send('{"user":"tobi"}')
367 .expect(200, '{"user":"tobi"}', done)
370 it('should work without content-type', function (done) {
371 var app = createApp({ type: accept })
373 function accept (req) {
374 return true
377 var test = request(app).post('/')
378 test.write('{"user":"tobi"}')
379 test.expect(200, '{"user":"tobi"}', done)
382 it('should not invoke without a body', function (done) {
383 var app = createApp({ type: accept })
385 function accept (req) {
386 throw new Error('oops!')
389 request(app)
390 .get('/')
391 .expect(404, done)
396 describe('with verify option', function () {
397 it('should assert value if function', function () {
398 assert.throws(createApp.bind(null, { verify: 'lol' }),
399 /TypeError: option verify must be function/)
402 it('should error from verify', function (done) {
403 var app = createApp({
404 verify: function (req, res, buf) {
405 if (buf[0] === 0x5b) throw new Error('no arrays')
409 request(app)
410 .post('/')
411 .set('Content-Type', 'application/json')
412 .send('["tobi"]')
413 .expect(403, '[entity.verify.failed] no arrays', done)
416 it('should allow custom codes', function (done) {
417 var app = createApp({
418 verify: function (req, res, buf) {
419 if (buf[0] !== 0x5b) return
420 var err = new Error('no arrays')
421 err.status = 400
422 throw err
426 request(app)
427 .post('/')
428 .set('Content-Type', 'application/json')
429 .send('["tobi"]')
430 .expect(400, '[entity.verify.failed] no arrays', done)
433 it('should allow custom type', function (done) {
434 var app = createApp({
435 verify: function (req, res, buf) {
436 if (buf[0] !== 0x5b) return
437 var err = new Error('no arrays')
438 err.type = 'foo.bar'
439 throw err
443 request(app)
444 .post('/')
445 .set('Content-Type', 'application/json')
446 .send('["tobi"]')
447 .expect(403, '[foo.bar] no arrays', done)
450 it('should include original body on error object', function (done) {
451 var app = createApp({
452 verify: function (req, res, buf) {
453 if (buf[0] === 0x5b) throw new Error('no arrays')
457 request(app)
458 .post('/')
459 .set('Content-Type', 'application/json')
460 .set('X-Error-Property', 'body')
461 .send('["tobi"]')
462 .expect(403, '["tobi"]', done)
465 it('should allow pass-through', function (done) {
466 var app = createApp({
467 verify: function (req, res, buf) {
468 if (buf[0] === 0x5b) throw new Error('no arrays')
472 request(app)
473 .post('/')
474 .set('Content-Type', 'application/json')
475 .send('{"user":"tobi"}')
476 .expect(200, '{"user":"tobi"}', done)
479 it('should work with different charsets', function (done) {
480 var app = createApp({
481 verify: function (req, res, buf) {
482 if (buf[0] === 0x5b) throw new Error('no arrays')
486 var test = request(app).post('/')
487 test.set('Content-Type', 'application/json; charset=utf-16')
488 test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))
489 test.expect(200, '{"name":"论"}', done)
492 it('should 415 on unknown charset prior to verify', function (done) {
493 var app = createApp({
494 verify: function (req, res, buf) {
495 throw new Error('unexpected verify call')
499 var test = request(app).post('/')
500 test.set('Content-Type', 'application/json; charset=x-bogus')
501 test.write(Buffer.from('00000000', 'hex'))
502 test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
506 describeAsyncHooks('async local storage', function () {
507 before(function () {
508 var app = express()
509 var store = { foo: 'bar' }
511 app.use(function (req, res, next) {
512 req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
513 req.asyncLocalStorage.run(store, next)
516 app.use(express.json())
518 app.use(function (req, res, next) {
519 var local = req.asyncLocalStorage.getStore()
521 if (local) {
522 res.setHeader('x-store-foo', String(local.foo))
525 next()
528 app.use(function (err, req, res, next) {
529 var local = req.asyncLocalStorage.getStore()
531 if (local) {
532 res.setHeader('x-store-foo', String(local.foo))
535 res.status(err.status || 500)
536 res.send('[' + err.type + '] ' + err.message)
539 app.post('/', function (req, res) {
540 res.json(req.body)
543 this.app = app
546 it('should presist store', function (done) {
547 request(this.app)
548 .post('/')
549 .set('Content-Type', 'application/json')
550 .send('{"user":"tobi"}')
551 .expect(200)
552 .expect('x-store-foo', 'bar')
553 .expect('{"user":"tobi"}')
554 .end(done)
557 it('should persist store when unmatched content-type', function (done) {
558 request(this.app)
559 .post('/')
560 .set('Content-Type', 'application/fizzbuzz')
561 .send('buzz')
562 .expect(200)
563 .expect('x-store-foo', 'bar')
564 .expect('')
565 .end(done)
568 it('should presist store when inflated', function (done) {
569 var test = request(this.app).post('/')
570 test.set('Content-Encoding', 'gzip')
571 test.set('Content-Type', 'application/json')
572 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
573 test.expect(200)
574 test.expect('x-store-foo', 'bar')
575 test.expect('{"name":"论"}')
576 test.end(done)
579 it('should presist store when inflate error', function (done) {
580 var test = request(this.app).post('/')
581 test.set('Content-Encoding', 'gzip')
582 test.set('Content-Type', 'application/json')
583 test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
584 test.expect(400)
585 test.expect('x-store-foo', 'bar')
586 test.end(done)
589 it('should presist store when parse error', function (done) {
590 request(this.app)
591 .post('/')
592 .set('Content-Type', 'application/json')
593 .send('{"user":')
594 .expect(400)
595 .expect('x-store-foo', 'bar')
596 .end(done)
599 it('should presist store when limit exceeded', function (done) {
600 request(this.app)
601 .post('/')
602 .set('Content-Type', 'application/json')
603 .send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}')
604 .expect(413)
605 .expect('x-store-foo', 'bar')
606 .end(done)
610 describe('charset', function () {
611 before(function () {
612 this.app = createApp()
615 it('should parse utf-8', function (done) {
616 var test = request(this.app).post('/')
617 test.set('Content-Type', 'application/json; charset=utf-8')
618 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
619 test.expect(200, '{"name":"论"}', done)
622 it('should parse utf-16', function (done) {
623 var test = request(this.app).post('/')
624 test.set('Content-Type', 'application/json; charset=utf-16')
625 test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))
626 test.expect(200, '{"name":"论"}', done)
629 it('should parse when content-length != char length', function (done) {
630 var test = request(this.app).post('/')
631 test.set('Content-Type', 'application/json; charset=utf-8')
632 test.set('Content-Length', '13')
633 test.write(Buffer.from('7b2274657374223a22c3a5227d', 'hex'))
634 test.expect(200, '{"test":"å"}', done)
637 it('should default to utf-8', function (done) {
638 var test = request(this.app).post('/')
639 test.set('Content-Type', 'application/json')
640 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
641 test.expect(200, '{"name":"论"}', done)
644 it('should fail on unknown charset', function (done) {
645 var test = request(this.app).post('/')
646 test.set('Content-Type', 'application/json; charset=koi8-r')
647 test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
648 test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
652 describe('encoding', function () {
653 before(function () {
654 this.app = createApp({ limit: '1kb' })
657 it('should parse without encoding', function (done) {
658 var test = request(this.app).post('/')
659 test.set('Content-Type', 'application/json')
660 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
661 test.expect(200, '{"name":"论"}', done)
664 it('should support identity encoding', function (done) {
665 var test = request(this.app).post('/')
666 test.set('Content-Encoding', 'identity')
667 test.set('Content-Type', 'application/json')
668 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
669 test.expect(200, '{"name":"论"}', done)
672 it('should support gzip encoding', function (done) {
673 var test = request(this.app).post('/')
674 test.set('Content-Encoding', 'gzip')
675 test.set('Content-Type', 'application/json')
676 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
677 test.expect(200, '{"name":"论"}', done)
680 it('should support deflate encoding', function (done) {
681 var test = request(this.app).post('/')
682 test.set('Content-Encoding', 'deflate')
683 test.set('Content-Type', 'application/json')
684 test.write(Buffer.from('789cab56ca4bcc4d55b2527ab16e97522d00274505ac', 'hex'))
685 test.expect(200, '{"name":"论"}', done)
688 it('should be case-insensitive', function (done) {
689 var test = request(this.app).post('/')
690 test.set('Content-Encoding', 'GZIP')
691 test.set('Content-Type', 'application/json')
692 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
693 test.expect(200, '{"name":"论"}', done)
696 it('should 415 on unknown encoding', function (done) {
697 var test = request(this.app).post('/')
698 test.set('Content-Encoding', 'nulls')
699 test.set('Content-Type', 'application/json')
700 test.write(Buffer.from('000000000000', 'hex'))
701 test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
704 it('should 400 on malformed encoding', function (done) {
705 var test = request(this.app).post('/')
706 test.set('Content-Encoding', 'gzip')
707 test.set('Content-Type', 'application/json')
708 test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
709 test.expect(400, done)
712 it('should 413 when inflated value exceeds limit', function (done) {
713 // gzip'd data exceeds 1kb, but deflated below 1kb
714 var test = request(this.app).post('/')
715 test.set('Content-Encoding', 'gzip')
716 test.set('Content-Type', 'application/json')
717 test.write(Buffer.from('1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000', 'hex'))
718 test.write(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'))
719 test.write(Buffer.from('0000000000000000004f0625b3b71650c30000', 'hex'))
720 test.expect(413, done)
725 function createApp (options) {
726 var app = express()
728 app.use(express.json(options))
730 app.use(function (err, req, res, next) {
731 // console.log(err)
732 res.status(err.status || 500)
733 res.send(String(req.headers['x-error-property']
734 ? err[req.headers['x-error-property']]
735 : ('[' + err.type + '] ' + err.message)))
738 app.post('/', function (req, res) {
739 res.json(req.body)
742 return app
745 function parseError (str) {
746 try {
747 JSON.parse(str); throw new SyntaxError('strict violation')
748 } catch (e) {
749 return e.message
753 function shouldContainInBody (str) {
754 return function (res) {
755 assert.ok(res.text.indexOf(str) !== -1,
756 'expected \'' + res.text + '\' to contain \'' + str + '\'')
760 function tryRequire (name) {
761 try {
762 return require(name)
763 } catch (e) {
764 return {}