cleanup: remove unnecessary require for global Buffer
[express.git] / test / express.json.js
bloba46cc16fb892e1309895d2aad1a29fa24cd7e266
1 'use strict'
3 var assert = require('assert')
4 var asyncHooks = tryRequire('async_hooks')
5 var express = require('..')
6 var request = require('supertest')
8 var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
9 ? describe
10 : describe.skip
12 describe('express.json()', function () {
13 it('should parse JSON', function (done) {
14 request(createApp())
15 .post('/')
16 .set('Content-Type', 'application/json')
17 .send('{"user":"tobi"}')
18 .expect(200, '{"user":"tobi"}', done)
21 it('should handle Content-Length: 0', function (done) {
22 request(createApp())
23 .post('/')
24 .set('Content-Type', 'application/json')
25 .set('Content-Length', '0')
26 .expect(200, '{}', done)
29 it('should handle empty message-body', function (done) {
30 request(createApp())
31 .post('/')
32 .set('Content-Type', 'application/json')
33 .set('Transfer-Encoding', 'chunked')
34 .expect(200, '{}', done)
37 it('should handle no message-body', function (done) {
38 request(createApp())
39 .post('/')
40 .set('Content-Type', 'application/json')
41 .unset('Transfer-Encoding')
42 .expect(200, '{}', done)
45 // The old node error message modification in body parser is catching this
46 it('should 400 when only whitespace', function (done) {
47 request(createApp())
48 .post('/')
49 .set('Content-Type', 'application/json')
50 .send(' \n')
51 .expect(400, '[entity.parse.failed] ' + parseError(' \n'), done)
54 it('should 400 when invalid content-length', function (done) {
55 var app = express()
57 app.use(function (req, res, next) {
58 req.headers['content-length'] = '20' // bad length
59 next()
62 app.use(express.json())
64 app.post('/', function (req, res) {
65 res.json(req.body)
68 request(app)
69 .post('/')
70 .set('Content-Type', 'application/json')
71 .send('{"str":')
72 .expect(400, /content length/, done)
75 it('should handle duplicated middleware', function (done) {
76 var app = express()
78 app.use(express.json())
79 app.use(express.json())
81 app.post('/', function (req, res) {
82 res.json(req.body)
85 request(app)
86 .post('/')
87 .set('Content-Type', 'application/json')
88 .send('{"user":"tobi"}')
89 .expect(200, '{"user":"tobi"}', done)
92 describe('when JSON is invalid', function () {
93 before(function () {
94 this.app = createApp()
97 it('should 400 for bad token', function (done) {
98 request(this.app)
99 .post('/')
100 .set('Content-Type', 'application/json')
101 .send('{:')
102 .expect(400, '[entity.parse.failed] ' + parseError('{:'), done)
105 it('should 400 for incomplete', function (done) {
106 request(this.app)
107 .post('/')
108 .set('Content-Type', 'application/json')
109 .send('{"user"')
110 .expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done)
113 it('should include original body on error object', function (done) {
114 request(this.app)
115 .post('/')
116 .set('Content-Type', 'application/json')
117 .set('X-Error-Property', 'body')
118 .send(' {"user"')
119 .expect(400, ' {"user"', done)
123 describe('with limit option', function () {
124 it('should 413 when over limit with Content-Length', function (done) {
125 var buf = Buffer.alloc(1024, '.')
126 request(createApp({ limit: '1kb' }))
127 .post('/')
128 .set('Content-Type', 'application/json')
129 .set('Content-Length', '1034')
130 .send(JSON.stringify({ str: buf.toString() }))
131 .expect(413, '[entity.too.large] request entity too large', done)
134 it('should 413 when over limit with chunked encoding', function (done) {
135 var app = createApp({ limit: '1kb' })
136 var buf = Buffer.alloc(1024, '.')
137 var test = request(app).post('/')
138 test.set('Content-Type', 'application/json')
139 test.set('Transfer-Encoding', 'chunked')
140 test.write('{"str":')
141 test.write('"' + buf.toString() + '"}')
142 test.expect(413, done)
145 it('should 413 when inflated body over limit', function (done) {
146 var app = createApp({ limit: '1kb' })
147 var test = request(app).post('/')
148 test.set('Content-Encoding', 'gzip')
149 test.set('Content-Type', 'application/json')
150 test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex'))
151 test.expect(413, done)
154 it('should accept number of bytes', function (done) {
155 var buf = Buffer.alloc(1024, '.')
156 request(createApp({ limit: 1024 }))
157 .post('/')
158 .set('Content-Type', 'application/json')
159 .send(JSON.stringify({ str: buf.toString() }))
160 .expect(413, done)
163 it('should not change when options altered', function (done) {
164 var buf = Buffer.alloc(1024, '.')
165 var options = { limit: '1kb' }
166 var app = createApp(options)
168 options.limit = '100kb'
170 request(app)
171 .post('/')
172 .set('Content-Type', 'application/json')
173 .send(JSON.stringify({ str: buf.toString() }))
174 .expect(413, done)
177 it('should not hang response', function (done) {
178 var buf = Buffer.alloc(10240, '.')
179 var app = createApp({ limit: '8kb' })
180 var test = request(app).post('/')
181 test.set('Content-Type', 'application/json')
182 test.write(buf)
183 test.write(buf)
184 test.write(buf)
185 test.expect(413, done)
188 it('should not error when inflating', function (done) {
189 var app = createApp({ limit: '1kb' })
190 var test = request(app).post('/')
191 test.set('Content-Encoding', 'gzip')
192 test.set('Content-Type', 'application/json')
193 test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex'))
194 test.expect(413, done)
198 describe('with inflate option', function () {
199 describe('when false', function () {
200 before(function () {
201 this.app = createApp({ inflate: false })
204 it('should not accept content-encoding', function (done) {
205 var test = request(this.app).post('/')
206 test.set('Content-Encoding', 'gzip')
207 test.set('Content-Type', 'application/json')
208 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
209 test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
213 describe('when true', function () {
214 before(function () {
215 this.app = createApp({ inflate: true })
218 it('should accept content-encoding', function (done) {
219 var test = request(this.app).post('/')
220 test.set('Content-Encoding', 'gzip')
221 test.set('Content-Type', 'application/json')
222 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
223 test.expect(200, '{"name":"论"}', done)
228 describe('with strict option', function () {
229 describe('when undefined', function () {
230 before(function () {
231 this.app = createApp()
234 it('should 400 on primitives', function (done) {
235 request(this.app)
236 .post('/')
237 .set('Content-Type', 'application/json')
238 .send('true')
239 .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
243 describe('when false', function () {
244 before(function () {
245 this.app = createApp({ strict: false })
248 it('should parse primitives', function (done) {
249 request(this.app)
250 .post('/')
251 .set('Content-Type', 'application/json')
252 .send('true')
253 .expect(200, 'true', done)
257 describe('when true', function () {
258 before(function () {
259 this.app = createApp({ strict: true })
262 it('should not parse primitives', function (done) {
263 request(this.app)
264 .post('/')
265 .set('Content-Type', 'application/json')
266 .send('true')
267 .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)
270 it('should not parse primitives with leading whitespaces', function (done) {
271 request(this.app)
272 .post('/')
273 .set('Content-Type', 'application/json')
274 .send(' true')
275 .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done)
278 it('should allow leading whitespaces in JSON', function (done) {
279 request(this.app)
280 .post('/')
281 .set('Content-Type', 'application/json')
282 .send(' { "user": "tobi" }')
283 .expect(200, '{"user":"tobi"}', done)
286 it('should include correct message in stack trace', function (done) {
287 request(this.app)
288 .post('/')
289 .set('Content-Type', 'application/json')
290 .set('X-Error-Property', 'stack')
291 .send('true')
292 .expect(400)
293 .expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't')))
294 .end(done)
299 describe('with type option', function () {
300 describe('when "application/vnd.api+json"', function () {
301 before(function () {
302 this.app = createApp({ type: 'application/vnd.api+json' })
305 it('should parse JSON for custom type', function (done) {
306 request(this.app)
307 .post('/')
308 .set('Content-Type', 'application/vnd.api+json')
309 .send('{"user":"tobi"}')
310 .expect(200, '{"user":"tobi"}', done)
313 it('should ignore standard type', function (done) {
314 request(this.app)
315 .post('/')
316 .set('Content-Type', 'application/json')
317 .send('{"user":"tobi"}')
318 .expect(200, '', done)
322 describe('when ["application/json", "application/vnd.api+json"]', function () {
323 before(function () {
324 this.app = createApp({
325 type: ['application/json', 'application/vnd.api+json']
329 it('should parse JSON for "application/json"', function (done) {
330 request(this.app)
331 .post('/')
332 .set('Content-Type', 'application/json')
333 .send('{"user":"tobi"}')
334 .expect(200, '{"user":"tobi"}', done)
337 it('should parse JSON for "application/vnd.api+json"', function (done) {
338 request(this.app)
339 .post('/')
340 .set('Content-Type', 'application/vnd.api+json')
341 .send('{"user":"tobi"}')
342 .expect(200, '{"user":"tobi"}', done)
345 it('should ignore "application/x-json"', function (done) {
346 request(this.app)
347 .post('/')
348 .set('Content-Type', 'application/x-json')
349 .send('{"user":"tobi"}')
350 .expect(200, '', done)
354 describe('when a function', function () {
355 it('should parse when truthy value returned', function (done) {
356 var app = createApp({ type: accept })
358 function accept (req) {
359 return req.headers['content-type'] === 'application/vnd.api+json'
362 request(app)
363 .post('/')
364 .set('Content-Type', 'application/vnd.api+json')
365 .send('{"user":"tobi"}')
366 .expect(200, '{"user":"tobi"}', done)
369 it('should work without content-type', function (done) {
370 var app = createApp({ type: accept })
372 function accept (req) {
373 return true
376 var test = request(app).post('/')
377 test.write('{"user":"tobi"}')
378 test.expect(200, '{"user":"tobi"}', done)
381 it('should not invoke without a body', function (done) {
382 var app = createApp({ type: accept })
384 function accept (req) {
385 throw new Error('oops!')
388 request(app)
389 .get('/')
390 .expect(404, done)
395 describe('with verify option', function () {
396 it('should assert value if function', function () {
397 assert.throws(createApp.bind(null, { verify: 'lol' }),
398 /TypeError: option verify must be function/)
401 it('should error from verify', function (done) {
402 var app = createApp({
403 verify: function (req, res, buf) {
404 if (buf[0] === 0x5b) throw new Error('no arrays')
408 request(app)
409 .post('/')
410 .set('Content-Type', 'application/json')
411 .send('["tobi"]')
412 .expect(403, '[entity.verify.failed] no arrays', done)
415 it('should allow custom codes', function (done) {
416 var app = createApp({
417 verify: function (req, res, buf) {
418 if (buf[0] !== 0x5b) return
419 var err = new Error('no arrays')
420 err.status = 400
421 throw err
425 request(app)
426 .post('/')
427 .set('Content-Type', 'application/json')
428 .send('["tobi"]')
429 .expect(400, '[entity.verify.failed] no arrays', done)
432 it('should allow custom type', function (done) {
433 var app = createApp({
434 verify: function (req, res, buf) {
435 if (buf[0] !== 0x5b) return
436 var err = new Error('no arrays')
437 err.type = 'foo.bar'
438 throw err
442 request(app)
443 .post('/')
444 .set('Content-Type', 'application/json')
445 .send('["tobi"]')
446 .expect(403, '[foo.bar] no arrays', done)
449 it('should include original body on error object', function (done) {
450 var app = createApp({
451 verify: function (req, res, buf) {
452 if (buf[0] === 0x5b) throw new Error('no arrays')
456 request(app)
457 .post('/')
458 .set('Content-Type', 'application/json')
459 .set('X-Error-Property', 'body')
460 .send('["tobi"]')
461 .expect(403, '["tobi"]', done)
464 it('should allow pass-through', function (done) {
465 var app = createApp({
466 verify: function (req, res, buf) {
467 if (buf[0] === 0x5b) throw new Error('no arrays')
471 request(app)
472 .post('/')
473 .set('Content-Type', 'application/json')
474 .send('{"user":"tobi"}')
475 .expect(200, '{"user":"tobi"}', done)
478 it('should work with different charsets', function (done) {
479 var app = createApp({
480 verify: function (req, res, buf) {
481 if (buf[0] === 0x5b) throw new Error('no arrays')
485 var test = request(app).post('/')
486 test.set('Content-Type', 'application/json; charset=utf-16')
487 test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))
488 test.expect(200, '{"name":"论"}', done)
491 it('should 415 on unknown charset prior to verify', function (done) {
492 var app = createApp({
493 verify: function (req, res, buf) {
494 throw new Error('unexpected verify call')
498 var test = request(app).post('/')
499 test.set('Content-Type', 'application/json; charset=x-bogus')
500 test.write(Buffer.from('00000000', 'hex'))
501 test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
505 describeAsyncHooks('async local storage', function () {
506 before(function () {
507 var app = express()
508 var store = { foo: 'bar' }
510 app.use(function (req, res, next) {
511 req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
512 req.asyncLocalStorage.run(store, next)
515 app.use(express.json())
517 app.use(function (req, res, next) {
518 var local = req.asyncLocalStorage.getStore()
520 if (local) {
521 res.setHeader('x-store-foo', String(local.foo))
524 next()
527 app.use(function (err, req, res, next) {
528 var local = req.asyncLocalStorage.getStore()
530 if (local) {
531 res.setHeader('x-store-foo', String(local.foo))
534 res.status(err.status || 500)
535 res.send('[' + err.type + '] ' + err.message)
538 app.post('/', function (req, res) {
539 res.json(req.body)
542 this.app = app
545 it('should presist store', function (done) {
546 request(this.app)
547 .post('/')
548 .set('Content-Type', 'application/json')
549 .send('{"user":"tobi"}')
550 .expect(200)
551 .expect('x-store-foo', 'bar')
552 .expect('{"user":"tobi"}')
553 .end(done)
556 it('should persist store when unmatched content-type', function (done) {
557 request(this.app)
558 .post('/')
559 .set('Content-Type', 'application/fizzbuzz')
560 .send('buzz')
561 .expect(200)
562 .expect('x-store-foo', 'bar')
563 .expect('')
564 .end(done)
567 it('should presist store when inflated', function (done) {
568 var test = request(this.app).post('/')
569 test.set('Content-Encoding', 'gzip')
570 test.set('Content-Type', 'application/json')
571 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
572 test.expect(200)
573 test.expect('x-store-foo', 'bar')
574 test.expect('{"name":"论"}')
575 test.end(done)
578 it('should presist store when inflate error', function (done) {
579 var test = request(this.app).post('/')
580 test.set('Content-Encoding', 'gzip')
581 test.set('Content-Type', 'application/json')
582 test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
583 test.expect(400)
584 test.expect('x-store-foo', 'bar')
585 test.end(done)
588 it('should presist store when parse error', function (done) {
589 request(this.app)
590 .post('/')
591 .set('Content-Type', 'application/json')
592 .send('{"user":')
593 .expect(400)
594 .expect('x-store-foo', 'bar')
595 .end(done)
598 it('should presist store when limit exceeded', function (done) {
599 request(this.app)
600 .post('/')
601 .set('Content-Type', 'application/json')
602 .send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}')
603 .expect(413)
604 .expect('x-store-foo', 'bar')
605 .end(done)
609 describe('charset', function () {
610 before(function () {
611 this.app = createApp()
614 it('should parse utf-8', function (done) {
615 var test = request(this.app).post('/')
616 test.set('Content-Type', 'application/json; charset=utf-8')
617 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
618 test.expect(200, '{"name":"论"}', done)
621 it('should parse utf-16', function (done) {
622 var test = request(this.app).post('/')
623 test.set('Content-Type', 'application/json; charset=utf-16')
624 test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))
625 test.expect(200, '{"name":"论"}', done)
628 it('should parse when content-length != char length', function (done) {
629 var test = request(this.app).post('/')
630 test.set('Content-Type', 'application/json; charset=utf-8')
631 test.set('Content-Length', '13')
632 test.write(Buffer.from('7b2274657374223a22c3a5227d', 'hex'))
633 test.expect(200, '{"test":"å"}', done)
636 it('should default to utf-8', function (done) {
637 var test = request(this.app).post('/')
638 test.set('Content-Type', 'application/json')
639 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
640 test.expect(200, '{"name":"论"}', done)
643 it('should fail on unknown charset', function (done) {
644 var test = request(this.app).post('/')
645 test.set('Content-Type', 'application/json; charset=koi8-r')
646 test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))
647 test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
651 describe('encoding', function () {
652 before(function () {
653 this.app = createApp({ limit: '1kb' })
656 it('should parse without encoding', function (done) {
657 var test = request(this.app).post('/')
658 test.set('Content-Type', 'application/json')
659 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
660 test.expect(200, '{"name":"论"}', done)
663 it('should support identity encoding', function (done) {
664 var test = request(this.app).post('/')
665 test.set('Content-Encoding', 'identity')
666 test.set('Content-Type', 'application/json')
667 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))
668 test.expect(200, '{"name":"论"}', done)
671 it('should support gzip encoding', function (done) {
672 var test = request(this.app).post('/')
673 test.set('Content-Encoding', 'gzip')
674 test.set('Content-Type', 'application/json')
675 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
676 test.expect(200, '{"name":"论"}', done)
679 it('should support deflate encoding', function (done) {
680 var test = request(this.app).post('/')
681 test.set('Content-Encoding', 'deflate')
682 test.set('Content-Type', 'application/json')
683 test.write(Buffer.from('789cab56ca4bcc4d55b2527ab16e97522d00274505ac', 'hex'))
684 test.expect(200, '{"name":"论"}', done)
687 it('should be case-insensitive', function (done) {
688 var test = request(this.app).post('/')
689 test.set('Content-Encoding', 'GZIP')
690 test.set('Content-Type', 'application/json')
691 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
692 test.expect(200, '{"name":"论"}', done)
695 it('should 415 on unknown encoding', function (done) {
696 var test = request(this.app).post('/')
697 test.set('Content-Encoding', 'nulls')
698 test.set('Content-Type', 'application/json')
699 test.write(Buffer.from('000000000000', 'hex'))
700 test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
703 it('should 400 on malformed encoding', function (done) {
704 var test = request(this.app).post('/')
705 test.set('Content-Encoding', 'gzip')
706 test.set('Content-Type', 'application/json')
707 test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))
708 test.expect(400, done)
711 it('should 413 when inflated value exceeds limit', function (done) {
712 // gzip'd data exceeds 1kb, but deflated below 1kb
713 var test = request(this.app).post('/')
714 test.set('Content-Encoding', 'gzip')
715 test.set('Content-Type', 'application/json')
716 test.write(Buffer.from('1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000', 'hex'))
717 test.write(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'))
718 test.write(Buffer.from('0000000000000000004f0625b3b71650c30000', 'hex'))
719 test.expect(413, done)
724 function createApp (options) {
725 var app = express()
727 app.use(express.json(options))
729 app.use(function (err, req, res, next) {
730 // console.log(err)
731 res.status(err.status || 500)
732 res.send(String(req.headers['x-error-property']
733 ? err[req.headers['x-error-property']]
734 : ('[' + err.type + '] ' + err.message)))
737 app.post('/', function (req, res) {
738 res.json(req.body)
741 return app
744 function parseError (str) {
745 try {
746 JSON.parse(str); throw new SyntaxError('strict violation')
747 } catch (e) {
748 return e.message
752 function shouldContainInBody (str) {
753 return function (res) {
754 assert.ok(res.text.indexOf(str) !== -1,
755 'expected \'' + res.text + '\' to contain \'' + str + '\'')
759 function tryRequire (name) {
760 try {
761 return require(name)
762 } catch (e) {
763 return {}