2 var assert = require('assert')
3 var Buffer = require('safe-buffer').Buffer
4 var express = require('..')
5 var request = require('supertest')
7 describe('express.json()', function () {
8 it('should parse JSON', function (done) {
11 .set('Content-Type', 'application/json')
12 .send('{"user":"tobi"}')
13 .expect(200, '{"user":"tobi"}', done)
16 it('should handle Content-Length: 0', function (done) {
19 .set('Content-Type', 'application/json')
20 .set('Content-Length', '0')
21 .expect(200, '{}', done)
24 it('should handle empty message-body', function (done) {
27 .set('Content-Type', 'application/json')
28 .set('Transfer-Encoding', 'chunked')
29 .expect(200, '{}', done)
32 it('should handle no message-body', function (done) {
35 .set('Content-Type', 'application/json')
36 .unset('Transfer-Encoding')
37 .expect(200, '{}', done)
40 it('should 400 when invalid content-length', function (done) {
43 app.use(function (req, res, next) {
44 req.headers['content-length'] = '20' // bad length
48 app.use(express.json())
50 app.post('/', function (req, res) {
56 .set('Content-Type', 'application/json')
58 .expect(400, /content length/, done)
61 it('should handle duplicated middleware', function (done) {
64 app.use(express.json())
65 app.use(express.json())
67 app.post('/', function (req, res) {
73 .set('Content-Type', 'application/json')
74 .send('{"user":"tobi"}')
75 .expect(200, '{"user":"tobi"}', done)
78 describe('when JSON is invalid', function () {
80 this.app = createApp()
83 it('should 400 for bad token', function (done) {
86 .set('Content-Type', 'application/json')
88 .expect(400, parseError('{:'), done)
91 it('should 400 for incomplete', function (done) {
94 .set('Content-Type', 'application/json')
96 .expect(400, parseError('{"user"'), done)
99 it('should error with type = "entity.parse.failed"', function (done) {
102 .set('Content-Type', 'application/json')
103 .set('X-Error-Property', 'type')
105 .expect(400, 'entity.parse.failed', done)
108 it('should include original body on error object', function (done) {
111 .set('Content-Type', 'application/json')
112 .set('X-Error-Property', 'body')
114 .expect(400, ' {"user"', done)
118 describe('with limit option', function () {
119 it('should 413 when over limit with Content-Length', function (done) {
120 var buf = Buffer.alloc(1024, '.')
121 request(createApp({ limit: '1kb' }))
123 .set('Content-Type', 'application/json')
124 .set('Content-Length', '1034')
125 .send(JSON.stringify({ str: buf.toString() }))
129 it('should error with type = "entity.too.large"', function (done) {
130 var buf = Buffer.alloc(1024, '.')
131 request(createApp({ limit: '1kb' }))
133 .set('Content-Type', 'application/json')
134 .set('Content-Length', '1034')
135 .set('X-Error-Property', 'type')
136 .send(JSON.stringify({ str: buf.toString() }))
137 .expect(413, 'entity.too.large', done)
140 it('should 413 when over limit with chunked encoding', function (done) {
141 var buf = Buffer.alloc(1024, '.')
142 var server = createApp({ limit: '1kb' })
143 var test = request(server).post('/')
144 test.set('Content-Type', 'application/json')
145 test.set('Transfer-Encoding', 'chunked')
146 test.write('{"str":')
147 test.write('"' + buf.toString() + '"}')
148 test.expect(413, done)
151 it('should accept number of bytes', function (done) {
152 var buf = Buffer.alloc(1024, '.')
153 request(createApp({ limit: 1024 }))
155 .set('Content-Type', 'application/json')
156 .send(JSON.stringify({ str: buf.toString() }))
160 it('should not change when options altered', function (done) {
161 var buf = Buffer.alloc(1024, '.')
162 var options = { limit: '1kb' }
163 var server = createApp(options)
165 options.limit = '100kb'
169 .set('Content-Type', 'application/json')
170 .send(JSON.stringify({ str: buf.toString() }))
174 it('should not hang response', function (done) {
175 var buf = Buffer.alloc(10240, '.')
176 var server = createApp({ limit: '8kb' })
177 var test = request(server).post('/')
178 test.set('Content-Type', 'application/json')
182 test.expect(413, done)
186 describe('with inflate option', function () {
187 describe('when false', function () {
189 this.app = createApp({ inflate: false })
192 it('should not accept content-encoding', function (done) {
193 var test = request(this.app).post('/')
194 test.set('Content-Encoding', 'gzip')
195 test.set('Content-Type', 'application/json')
196 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
197 test.expect(415, 'content encoding unsupported', done)
201 describe('when true', function () {
203 this.app = createApp({ inflate: true })
206 it('should accept content-encoding', function (done) {
207 var test = request(this.app).post('/')
208 test.set('Content-Encoding', 'gzip')
209 test.set('Content-Type', 'application/json')
210 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
211 test.expect(200, '{"name":"论"}', done)
216 describe('with strict option', function () {
217 describe('when undefined', function () {
219 this.app = createApp()
222 it('should 400 on primitives', function (done) {
225 .set('Content-Type', 'application/json')
227 .expect(400, parseError('#rue').replace('#', 't'), done)
231 describe('when false', function () {
233 this.app = createApp({ strict: false })
236 it('should parse primitives', function (done) {
239 .set('Content-Type', 'application/json')
241 .expect(200, 'true', done)
245 describe('when true', function () {
247 this.app = createApp({ strict: true })
250 it('should not parse primitives', function (done) {
253 .set('Content-Type', 'application/json')
255 .expect(400, parseError('#rue').replace('#', 't'), done)
258 it('should not parse primitives with leading whitespaces', function (done) {
261 .set('Content-Type', 'application/json')
263 .expect(400, parseError(' #rue').replace('#', 't'), done)
266 it('should allow leading whitespaces in JSON', function (done) {
269 .set('Content-Type', 'application/json')
270 .send(' { "user": "tobi" }')
271 .expect(200, '{"user":"tobi"}', done)
274 it('should error with type = "entity.parse.failed"', function (done) {
277 .set('Content-Type', 'application/json')
278 .set('X-Error-Property', 'type')
280 .expect(400, 'entity.parse.failed', done)
283 it('should include correct message in stack trace', function (done) {
286 .set('Content-Type', 'application/json')
287 .set('X-Error-Property', 'stack')
290 .expect(shouldContainInBody(parseError('#rue').replace('#', 't')))
296 describe('with type option', function () {
297 describe('when "application/vnd.api+json"', function () {
299 this.app = createApp({ type: 'application/vnd.api+json' })
302 it('should parse JSON for custom type', function (done) {
305 .set('Content-Type', 'application/vnd.api+json')
306 .send('{"user":"tobi"}')
307 .expect(200, '{"user":"tobi"}', done)
310 it('should ignore standard type', function (done) {
313 .set('Content-Type', 'application/json')
314 .send('{"user":"tobi"}')
315 .expect(200, '{}', done)
319 describe('when ["application/json", "application/vnd.api+json"]', function () {
321 this.app = createApp({
322 type: ['application/json', 'application/vnd.api+json']
326 it('should parse JSON for "application/json"', function (done) {
329 .set('Content-Type', 'application/json')
330 .send('{"user":"tobi"}')
331 .expect(200, '{"user":"tobi"}', done)
334 it('should parse JSON for "application/vnd.api+json"', function (done) {
337 .set('Content-Type', 'application/vnd.api+json')
338 .send('{"user":"tobi"}')
339 .expect(200, '{"user":"tobi"}', done)
342 it('should ignore "application/x-json"', function (done) {
345 .set('Content-Type', 'application/x-json')
346 .send('{"user":"tobi"}')
347 .expect(200, '{}', done)
351 describe('when a function', function () {
352 it('should parse when truthy value returned', function (done) {
353 var app = createApp({ type: accept })
355 function accept (req) {
356 return req.headers['content-type'] === 'application/vnd.api+json'
361 .set('Content-Type', 'application/vnd.api+json')
362 .send('{"user":"tobi"}')
363 .expect(200, '{"user":"tobi"}', done)
366 it('should work without content-type', function (done) {
367 var app = createApp({ type: accept })
369 function accept (req) {
373 var test = request(app).post('/')
374 test.write('{"user":"tobi"}')
375 test.expect(200, '{"user":"tobi"}', done)
378 it('should not invoke without a body', function (done) {
379 var app = createApp({ type: accept })
381 function accept (req) {
382 throw new Error('oops!')
392 describe('with verify option', function () {
393 it('should assert value if function', function () {
394 assert.throws(createApp.bind(null, { verify: 'lol' }),
395 /TypeError: option verify must be function/)
398 it('should error from verify', function (done) {
399 var app = createApp({ verify: function (req, res, buf) {
400 if (buf[0] === 0x5b) throw new Error('no arrays')
405 .set('Content-Type', 'application/json')
407 .expect(403, 'no arrays', done)
410 it('should error with type = "entity.verify.failed"', function (done) {
411 var app = createApp({ verify: function (req, res, buf) {
412 if (buf[0] === 0x5b) throw new Error('no arrays')
417 .set('Content-Type', 'application/json')
418 .set('X-Error-Property', 'type')
420 .expect(403, 'entity.verify.failed', done)
423 it('should allow custom codes', function (done) {
424 var app = createApp({ verify: function (req, res, buf) {
425 if (buf[0] !== 0x5b) return
426 var err = new Error('no arrays')
433 .set('Content-Type', 'application/json')
435 .expect(400, 'no arrays', done)
438 it('should allow custom type', function (done) {
439 var app = createApp({ verify: function (req, res, buf) {
440 if (buf[0] !== 0x5b) return
441 var err = new Error('no arrays')
448 .set('Content-Type', 'application/json')
449 .set('X-Error-Property', 'type')
451 .expect(403, 'foo.bar', done)
454 it('should include original body on error object', function (done) {
455 var app = createApp({ verify: function (req, res, buf) {
456 if (buf[0] === 0x5b) throw new Error('no arrays')
461 .set('Content-Type', 'application/json')
462 .set('X-Error-Property', 'body')
464 .expect(403, '["tobi"]', done)
467 it('should allow pass-through', function (done) {
468 var app = createApp({ verify: function (req, res, buf) {
469 if (buf[0] === 0x5b) throw new Error('no arrays')
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({ verify: function (req, res, buf) {
481 if (buf[0] === 0x5b) throw new Error('no arrays')
484 var test = request(app).post('/')
485 test.set('Content-Type', 'application/json; charset=utf-16')
486 test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))
487 test.expect(200, '{"name":"论"}', done)
490 it('should 415 on unknown charset prior to verify', function (done) {
491 var app = createApp({ verify: function (req, res, buf) {
492 throw new Error('unexpected verify call')
495 var test = request(app).post('/')
496 test.set('Content-Type', 'application/json; charset=x-bogus')
497 test.write(Buffer.from('00000000', 'hex'))
498 test.expect(415, 'unsupported charset "X-BOGUS"', done)
502 describe('charset', function () {
504 this.app = createApp()
507 it('should parse utf-8', function (done) {
508 var test = request(this.app).post('/')
509 test.set('Content-Type', 'application/json; charset=utf-8')
510 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
511 test.expect(200, '{"name":"论"}', done)
514 it('should parse utf-16', function (done) {
515 var test = request(this.app).post('/')
516 test.set('Content-Type', 'application/json; charset=utf-16')
517 test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))
518 test.expect(200, '{"name":"论"}', done)
521 it('should parse when content-length != char length', function (done) {
522 var test = request(this.app).post('/')
523 test.set('Content-Type', 'application/json; charset=utf-8')
524 test.set('Content-Length', '13')
525 test.write(Buffer.from('7b2274657374223a22c3a5227d', 'hex'))
526 test.expect(200, '{"test":"å"}', done)
529 it('should default to utf-8', function (done) {
530 var test = request(this.app).post('/')
531 test.set('Content-Type', 'application/json')
532 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
533 test.expect(200, '{"name":"论"}', done)
536 it('should fail on unknown charset', function (done) {
537 var test = request(this.app).post('/')
538 test.set('Content-Type', 'application/json; charset=koi8-r')
539 test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
540 test.expect(415, 'unsupported charset "KOI8-R"', done)
543 it('should error with type = "charset.unsupported"', function (done) {
544 var test = request(this.app).post('/')
545 test.set('Content-Type', 'application/json; charset=koi8-r')
546 test.set('X-Error-Property', 'type')
547 test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
548 test.expect(415, 'charset.unsupported', done)
552 describe('encoding', function () {
554 this.app = createApp({ limit: '1kb' })
557 it('should parse without encoding', function (done) {
558 var test = request(this.app).post('/')
559 test.set('Content-Type', 'application/json')
560 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
561 test.expect(200, '{"name":"论"}', done)
564 it('should support identity encoding', function (done) {
565 var test = request(this.app).post('/')
566 test.set('Content-Encoding', 'identity')
567 test.set('Content-Type', 'application/json')
568 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
569 test.expect(200, '{"name":"论"}', done)
572 it('should support gzip encoding', function (done) {
573 var test = request(this.app).post('/')
574 test.set('Content-Encoding', 'gzip')
575 test.set('Content-Type', 'application/json')
576 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
577 test.expect(200, '{"name":"论"}', done)
580 it('should support deflate encoding', function (done) {
581 var test = request(this.app).post('/')
582 test.set('Content-Encoding', 'deflate')
583 test.set('Content-Type', 'application/json')
584 test.write(Buffer.from('789cab56ca4bcc4d55b2527ab16e97522d00274505ac', 'hex'))
585 test.expect(200, '{"name":"论"}', done)
588 it('should be case-insensitive', function (done) {
589 var test = request(this.app).post('/')
590 test.set('Content-Encoding', 'GZIP')
591 test.set('Content-Type', 'application/json')
592 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
593 test.expect(200, '{"name":"论"}', done)
596 it('should 415 on unknown encoding', function (done) {
597 var test = request(this.app).post('/')
598 test.set('Content-Encoding', 'nulls')
599 test.set('Content-Type', 'application/json')
600 test.write(Buffer.from('000000000000', 'hex'))
601 test.expect(415, 'unsupported content encoding "nulls"', done)
604 it('should error with type = "encoding.unsupported"', function (done) {
605 var test = request(this.app).post('/')
606 test.set('Content-Encoding', 'nulls')
607 test.set('Content-Type', 'application/json')
608 test.set('X-Error-Property', 'type')
609 test.write(Buffer.from('000000000000', 'hex'))
610 test.expect(415, 'encoding.unsupported', done)
613 it('should 400 on malformed encoding', function (done) {
614 var test = request(this.app).post('/')
615 test.set('Content-Encoding', 'gzip')
616 test.set('Content-Type', 'application/json')
617 test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
618 test.expect(400, done)
621 it('should 413 when inflated value exceeds limit', function (done) {
622 // gzip'd data exceeds 1kb, but deflated below 1kb
623 var test = request(this.app).post('/')
624 test.set('Content-Encoding', 'gzip')
625 test.set('Content-Type', 'application/json')
626 test.write(Buffer.from('1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000', 'hex'))
627 test.write(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'))
628 test.write(Buffer.from('0000000000000000004f0625b3b71650c30000', 'hex'))
629 test.expect(413, done)
634 function createApp (options) {
637 app.use(express.json(options))
639 app.use(function (err, req, res, next) {
640 res.status(err.status || 500)
641 res.send(String(err[req.headers['x-error-property'] || 'message']))
644 app.post('/', function (req, res) {
651 function parseError (str) {
653 JSON.parse(str); throw new SyntaxError('strict violation')
659 function shouldContainInBody (str) {
660 return function (res) {
661 assert.ok(res.text.indexOf(str) !== -1,
662 'expected \'' + res.text + '\' to contain \'' + str + '\'')