cleanup: remove unnecessary require for global Buffer
[express.git] / test / express.urlencoded.js
blob8503e0079aff7f3cd2708c2450bb385446f79edd
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.urlencoded()', function () {
13 before(function () {
14 this.app = createApp()
17 it('should parse x-www-form-urlencoded', function (done) {
18 request(this.app)
19 .post('/')
20 .set('Content-Type', 'application/x-www-form-urlencoded')
21 .send('user=tobi')
22 .expect(200, '{"user":"tobi"}', done)
25 it('should 400 when invalid content-length', function (done) {
26 var app = express()
28 app.use(function (req, res, next) {
29 req.headers['content-length'] = '20' // bad length
30 next()
33 app.use(express.urlencoded())
35 app.post('/', function (req, res) {
36 res.json(req.body)
39 request(app)
40 .post('/')
41 .set('Content-Type', 'application/x-www-form-urlencoded')
42 .send('str=')
43 .expect(400, /content length/, done)
46 it('should handle Content-Length: 0', function (done) {
47 request(this.app)
48 .post('/')
49 .set('Content-Type', 'application/x-www-form-urlencoded')
50 .set('Content-Length', '0')
51 .send('')
52 .expect(200, '{}', done)
55 it('should handle empty message-body', function (done) {
56 request(createApp({ limit: '1kb' }))
57 .post('/')
58 .set('Content-Type', 'application/x-www-form-urlencoded')
59 .set('Transfer-Encoding', 'chunked')
60 .send('')
61 .expect(200, '{}', done)
64 it('should handle duplicated middleware', function (done) {
65 var app = express()
67 app.use(express.urlencoded())
68 app.use(express.urlencoded())
70 app.post('/', function (req, res) {
71 res.json(req.body)
74 request(app)
75 .post('/')
76 .set('Content-Type', 'application/x-www-form-urlencoded')
77 .send('user=tobi')
78 .expect(200, '{"user":"tobi"}', done)
81 it('should not parse extended syntax', function (done) {
82 request(this.app)
83 .post('/')
84 .set('Content-Type', 'application/x-www-form-urlencoded')
85 .send('user[name][first]=Tobi')
86 .expect(200, '{"user[name][first]":"Tobi"}', done)
89 describe('with extended option', function () {
90 describe('when false', function () {
91 before(function () {
92 this.app = createApp({ extended: false })
95 it('should not parse extended syntax', function (done) {
96 request(this.app)
97 .post('/')
98 .set('Content-Type', 'application/x-www-form-urlencoded')
99 .send('user[name][first]=Tobi')
100 .expect(200, '{"user[name][first]":"Tobi"}', done)
103 it('should parse multiple key instances', function (done) {
104 request(this.app)
105 .post('/')
106 .set('Content-Type', 'application/x-www-form-urlencoded')
107 .send('user=Tobi&user=Loki')
108 .expect(200, '{"user":["Tobi","Loki"]}', done)
112 describe('when true', function () {
113 before(function () {
114 this.app = createApp({ extended: true })
117 it('should parse multiple key instances', function (done) {
118 request(this.app)
119 .post('/')
120 .set('Content-Type', 'application/x-www-form-urlencoded')
121 .send('user=Tobi&user=Loki')
122 .expect(200, '{"user":["Tobi","Loki"]}', done)
125 it('should parse extended syntax', function (done) {
126 request(this.app)
127 .post('/')
128 .set('Content-Type', 'application/x-www-form-urlencoded')
129 .send('user[name][first]=Tobi')
130 .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done)
133 it('should parse parameters with dots', function (done) {
134 request(this.app)
135 .post('/')
136 .set('Content-Type', 'application/x-www-form-urlencoded')
137 .send('user.name=Tobi')
138 .expect(200, '{"user.name":"Tobi"}', done)
141 it('should parse fully-encoded extended syntax', function (done) {
142 request(this.app)
143 .post('/')
144 .set('Content-Type', 'application/x-www-form-urlencoded')
145 .send('user%5Bname%5D%5Bfirst%5D=Tobi')
146 .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done)
149 it('should parse array index notation', function (done) {
150 request(this.app)
151 .post('/')
152 .set('Content-Type', 'application/x-www-form-urlencoded')
153 .send('foo[0]=bar&foo[1]=baz')
154 .expect(200, '{"foo":["bar","baz"]}', done)
157 it('should parse array index notation with large array', function (done) {
158 var str = 'f[0]=0'
160 for (var i = 1; i < 500; i++) {
161 str += '&f[' + i + ']=' + i.toString(16)
164 request(this.app)
165 .post('/')
166 .set('Content-Type', 'application/x-www-form-urlencoded')
167 .send(str)
168 .expect(function (res) {
169 var obj = JSON.parse(res.text)
170 assert.strictEqual(Object.keys(obj).length, 1)
171 assert.strictEqual(Array.isArray(obj.f), true)
172 assert.strictEqual(obj.f.length, 500)
174 .expect(200, done)
177 it('should parse array of objects syntax', function (done) {
178 request(this.app)
179 .post('/')
180 .set('Content-Type', 'application/x-www-form-urlencoded')
181 .send('foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!')
182 .expect(200, '{"foo":[{"bar":"baz","fizz":"buzz"},"done!"]}', done)
185 it('should parse deep object', function (done) {
186 var str = 'foo'
188 for (var i = 0; i < 32; i++) {
189 str += '[p]'
192 str += '=bar'
194 request(this.app)
195 .post('/')
196 .set('Content-Type', 'application/x-www-form-urlencoded')
197 .send(str)
198 .expect(function (res) {
199 var obj = JSON.parse(res.text)
200 assert.strictEqual(Object.keys(obj).length, 1)
201 assert.strictEqual(typeof obj.foo, 'object')
203 var depth = 0
204 var ref = obj.foo
205 while ((ref = ref.p)) { depth++ }
206 assert.strictEqual(depth, 32)
208 .expect(200, done)
213 describe('with inflate option', function () {
214 describe('when false', function () {
215 before(function () {
216 this.app = createApp({ inflate: false })
219 it('should not accept content-encoding', function (done) {
220 var test = request(this.app).post('/')
221 test.set('Content-Encoding', 'gzip')
222 test.set('Content-Type', 'application/x-www-form-urlencoded')
223 test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
224 test.expect(415, '[encoding.unsupported] content encoding unsupported', done)
228 describe('when true', function () {
229 before(function () {
230 this.app = createApp({ inflate: true })
233 it('should accept content-encoding', function (done) {
234 var test = request(this.app).post('/')
235 test.set('Content-Encoding', 'gzip')
236 test.set('Content-Type', 'application/x-www-form-urlencoded')
237 test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
238 test.expect(200, '{"name":"论"}', done)
243 describe('with limit option', function () {
244 it('should 413 when over limit with Content-Length', function (done) {
245 var buf = Buffer.alloc(1024, '.')
246 request(createApp({ limit: '1kb' }))
247 .post('/')
248 .set('Content-Type', 'application/x-www-form-urlencoded')
249 .set('Content-Length', '1028')
250 .send('str=' + buf.toString())
251 .expect(413, done)
254 it('should 413 when over limit with chunked encoding', function (done) {
255 var app = createApp({ limit: '1kb' })
256 var buf = Buffer.alloc(1024, '.')
257 var test = request(app).post('/')
258 test.set('Content-Type', 'application/x-www-form-urlencoded')
259 test.set('Transfer-Encoding', 'chunked')
260 test.write('str=')
261 test.write(buf.toString())
262 test.expect(413, done)
265 it('should 413 when inflated body over limit', function (done) {
266 var app = createApp({ limit: '1kb' })
267 var test = request(app).post('/')
268 test.set('Content-Encoding', 'gzip')
269 test.set('Content-Type', 'application/x-www-form-urlencoded')
270 test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex'))
271 test.expect(413, done)
274 it('should accept number of bytes', function (done) {
275 var buf = Buffer.alloc(1024, '.')
276 request(createApp({ limit: 1024 }))
277 .post('/')
278 .set('Content-Type', 'application/x-www-form-urlencoded')
279 .send('str=' + buf.toString())
280 .expect(413, done)
283 it('should not change when options altered', function (done) {
284 var buf = Buffer.alloc(1024, '.')
285 var options = { limit: '1kb' }
286 var app = createApp(options)
288 options.limit = '100kb'
290 request(app)
291 .post('/')
292 .set('Content-Type', 'application/x-www-form-urlencoded')
293 .send('str=' + buf.toString())
294 .expect(413, done)
297 it('should not hang response', function (done) {
298 var app = createApp({ limit: '8kb' })
299 var buf = Buffer.alloc(10240, '.')
300 var test = request(app).post('/')
301 test.set('Content-Type', 'application/x-www-form-urlencoded')
302 test.write(buf)
303 test.write(buf)
304 test.write(buf)
305 test.expect(413, done)
308 it('should not error when inflating', function (done) {
309 var app = createApp({ limit: '1kb' })
310 var test = request(app).post('/')
311 test.set('Content-Encoding', 'gzip')
312 test.set('Content-Type', 'application/x-www-form-urlencoded')
313 test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex'))
314 test.expect(413, done)
318 describe('with parameterLimit option', function () {
319 describe('with extended: false', function () {
320 it('should reject 0', function () {
321 assert.throws(createApp.bind(null, { extended: false, parameterLimit: 0 }),
322 /TypeError: option parameterLimit must be a positive number/)
325 it('should reject string', function () {
326 assert.throws(createApp.bind(null, { extended: false, parameterLimit: 'beep' }),
327 /TypeError: option parameterLimit must be a positive number/)
330 it('should 413 if over limit', function (done) {
331 request(createApp({ extended: false, parameterLimit: 10 }))
332 .post('/')
333 .set('Content-Type', 'application/x-www-form-urlencoded')
334 .send(createManyParams(11))
335 .expect(413, '[parameters.too.many] too many parameters', done)
338 it('should work when at the limit', function (done) {
339 request(createApp({ extended: false, parameterLimit: 10 }))
340 .post('/')
341 .set('Content-Type', 'application/x-www-form-urlencoded')
342 .send(createManyParams(10))
343 .expect(expectKeyCount(10))
344 .expect(200, done)
347 it('should work if number is floating point', function (done) {
348 request(createApp({ extended: false, parameterLimit: 10.1 }))
349 .post('/')
350 .set('Content-Type', 'application/x-www-form-urlencoded')
351 .send(createManyParams(11))
352 .expect(413, /too many parameters/, done)
355 it('should work with large limit', function (done) {
356 request(createApp({ extended: false, parameterLimit: 5000 }))
357 .post('/')
358 .set('Content-Type', 'application/x-www-form-urlencoded')
359 .send(createManyParams(5000))
360 .expect(expectKeyCount(5000))
361 .expect(200, done)
364 it('should work with Infinity limit', function (done) {
365 request(createApp({ extended: false, parameterLimit: Infinity }))
366 .post('/')
367 .set('Content-Type', 'application/x-www-form-urlencoded')
368 .send(createManyParams(10000))
369 .expect(expectKeyCount(10000))
370 .expect(200, done)
374 describe('with extended: true', function () {
375 it('should reject 0', function () {
376 assert.throws(createApp.bind(null, { extended: true, parameterLimit: 0 }),
377 /TypeError: option parameterLimit must be a positive number/)
380 it('should reject string', function () {
381 assert.throws(createApp.bind(null, { extended: true, parameterLimit: 'beep' }),
382 /TypeError: option parameterLimit must be a positive number/)
385 it('should 413 if over limit', function (done) {
386 request(createApp({ extended: true, parameterLimit: 10 }))
387 .post('/')
388 .set('Content-Type', 'application/x-www-form-urlencoded')
389 .send(createManyParams(11))
390 .expect(413, '[parameters.too.many] too many parameters', done)
393 it('should work when at the limit', function (done) {
394 request(createApp({ extended: true, parameterLimit: 10 }))
395 .post('/')
396 .set('Content-Type', 'application/x-www-form-urlencoded')
397 .send(createManyParams(10))
398 .expect(expectKeyCount(10))
399 .expect(200, done)
402 it('should work if number is floating point', function (done) {
403 request(createApp({ extended: true, parameterLimit: 10.1 }))
404 .post('/')
405 .set('Content-Type', 'application/x-www-form-urlencoded')
406 .send(createManyParams(11))
407 .expect(413, /too many parameters/, done)
410 it('should work with large limit', function (done) {
411 request(createApp({ extended: true, parameterLimit: 5000 }))
412 .post('/')
413 .set('Content-Type', 'application/x-www-form-urlencoded')
414 .send(createManyParams(5000))
415 .expect(expectKeyCount(5000))
416 .expect(200, done)
419 it('should work with Infinity limit', function (done) {
420 request(createApp({ extended: true, parameterLimit: Infinity }))
421 .post('/')
422 .set('Content-Type', 'application/x-www-form-urlencoded')
423 .send(createManyParams(10000))
424 .expect(expectKeyCount(10000))
425 .expect(200, done)
430 describe('with type option', function () {
431 describe('when "application/vnd.x-www-form-urlencoded"', function () {
432 before(function () {
433 this.app = createApp({ type: 'application/vnd.x-www-form-urlencoded' })
436 it('should parse for custom type', function (done) {
437 request(this.app)
438 .post('/')
439 .set('Content-Type', 'application/vnd.x-www-form-urlencoded')
440 .send('user=tobi')
441 .expect(200, '{"user":"tobi"}', done)
444 it('should ignore standard type', function (done) {
445 request(this.app)
446 .post('/')
447 .set('Content-Type', 'application/x-www-form-urlencoded')
448 .send('user=tobi')
449 .expect(200, '', done)
453 describe('when ["urlencoded", "application/x-pairs"]', function () {
454 before(function () {
455 this.app = createApp({
456 type: ['urlencoded', 'application/x-pairs']
460 it('should parse "application/x-www-form-urlencoded"', function (done) {
461 request(this.app)
462 .post('/')
463 .set('Content-Type', 'application/x-www-form-urlencoded')
464 .send('user=tobi')
465 .expect(200, '{"user":"tobi"}', done)
468 it('should parse "application/x-pairs"', function (done) {
469 request(this.app)
470 .post('/')
471 .set('Content-Type', 'application/x-pairs')
472 .send('user=tobi')
473 .expect(200, '{"user":"tobi"}', done)
476 it('should ignore application/x-foo', function (done) {
477 request(this.app)
478 .post('/')
479 .set('Content-Type', 'application/x-foo')
480 .send('user=tobi')
481 .expect(200, '', done)
485 describe('when a function', function () {
486 it('should parse when truthy value returned', function (done) {
487 var app = createApp({ type: accept })
489 function accept (req) {
490 return req.headers['content-type'] === 'application/vnd.something'
493 request(app)
494 .post('/')
495 .set('Content-Type', 'application/vnd.something')
496 .send('user=tobi')
497 .expect(200, '{"user":"tobi"}', done)
500 it('should work without content-type', function (done) {
501 var app = createApp({ type: accept })
503 function accept (req) {
504 return true
507 var test = request(app).post('/')
508 test.write('user=tobi')
509 test.expect(200, '{"user":"tobi"}', done)
512 it('should not invoke without a body', function (done) {
513 var app = createApp({ type: accept })
515 function accept (req) {
516 throw new Error('oops!')
519 request(app)
520 .get('/')
521 .expect(404, done)
526 describe('with verify option', function () {
527 it('should assert value if function', function () {
528 assert.throws(createApp.bind(null, { verify: 'lol' }),
529 /TypeError: option verify must be function/)
532 it('should error from verify', function (done) {
533 var app = createApp({
534 verify: function (req, res, buf) {
535 if (buf[0] === 0x20) throw new Error('no leading space')
539 request(app)
540 .post('/')
541 .set('Content-Type', 'application/x-www-form-urlencoded')
542 .send(' user=tobi')
543 .expect(403, '[entity.verify.failed] no leading space', done)
546 it('should allow custom codes', function (done) {
547 var app = createApp({
548 verify: function (req, res, buf) {
549 if (buf[0] !== 0x20) return
550 var err = new Error('no leading space')
551 err.status = 400
552 throw err
556 request(app)
557 .post('/')
558 .set('Content-Type', 'application/x-www-form-urlencoded')
559 .send(' user=tobi')
560 .expect(400, '[entity.verify.failed] no leading space', done)
563 it('should allow custom type', function (done) {
564 var app = createApp({
565 verify: function (req, res, buf) {
566 if (buf[0] !== 0x20) return
567 var err = new Error('no leading space')
568 err.type = 'foo.bar'
569 throw err
573 request(app)
574 .post('/')
575 .set('Content-Type', 'application/x-www-form-urlencoded')
576 .send(' user=tobi')
577 .expect(403, '[foo.bar] no leading space', done)
580 it('should allow pass-through', function (done) {
581 var app = createApp({
582 verify: function (req, res, buf) {
583 if (buf[0] === 0x5b) throw new Error('no arrays')
587 request(app)
588 .post('/')
589 .set('Content-Type', 'application/x-www-form-urlencoded')
590 .send('user=tobi')
591 .expect(200, '{"user":"tobi"}', done)
594 it('should 415 on unknown charset prior to verify', function (done) {
595 var app = createApp({
596 verify: function (req, res, buf) {
597 throw new Error('unexpected verify call')
601 var test = request(app).post('/')
602 test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus')
603 test.write(Buffer.from('00000000', 'hex'))
604 test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)
608 describeAsyncHooks('async local storage', function () {
609 before(function () {
610 var app = express()
611 var store = { foo: 'bar' }
613 app.use(function (req, res, next) {
614 req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
615 req.asyncLocalStorage.run(store, next)
618 app.use(express.urlencoded())
620 app.use(function (req, res, next) {
621 var local = req.asyncLocalStorage.getStore()
623 if (local) {
624 res.setHeader('x-store-foo', String(local.foo))
627 next()
630 app.use(function (err, req, res, next) {
631 var local = req.asyncLocalStorage.getStore()
633 if (local) {
634 res.setHeader('x-store-foo', String(local.foo))
637 res.status(err.status || 500)
638 res.send('[' + err.type + '] ' + err.message)
641 app.post('/', function (req, res) {
642 res.json(req.body)
645 this.app = app
648 it('should presist store', function (done) {
649 request(this.app)
650 .post('/')
651 .set('Content-Type', 'application/x-www-form-urlencoded')
652 .send('user=tobi')
653 .expect(200)
654 .expect('x-store-foo', 'bar')
655 .expect('{"user":"tobi"}')
656 .end(done)
659 it('should presist store when unmatched content-type', function (done) {
660 request(this.app)
661 .post('/')
662 .set('Content-Type', 'application/fizzbuzz')
663 .send('buzz')
664 .expect(200)
665 .expect('x-store-foo', 'bar')
666 .end(done)
669 it('should presist store when inflated', function (done) {
670 var test = request(this.app).post('/')
671 test.set('Content-Encoding', 'gzip')
672 test.set('Content-Type', 'application/x-www-form-urlencoded')
673 test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
674 test.expect(200)
675 test.expect('x-store-foo', 'bar')
676 test.expect('{"name":"论"}')
677 test.end(done)
680 it('should presist store when inflate error', function (done) {
681 var test = request(this.app).post('/')
682 test.set('Content-Encoding', 'gzip')
683 test.set('Content-Type', 'application/x-www-form-urlencoded')
684 test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex'))
685 test.expect(400)
686 test.expect('x-store-foo', 'bar')
687 test.end(done)
690 it('should presist store when limit exceeded', function (done) {
691 request(this.app)
692 .post('/')
693 .set('Content-Type', 'application/x-www-form-urlencoded')
694 .send('user=' + Buffer.alloc(1024 * 100, '.').toString())
695 .expect(413)
696 .expect('x-store-foo', 'bar')
697 .end(done)
701 describe('charset', function () {
702 before(function () {
703 this.app = createApp()
706 it('should parse utf-8', function (done) {
707 var test = request(this.app).post('/')
708 test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
709 test.write(Buffer.from('6e616d653de8aeba', 'hex'))
710 test.expect(200, '{"name":"论"}', done)
713 it('should parse when content-length != char length', function (done) {
714 var test = request(this.app).post('/')
715 test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
716 test.set('Content-Length', '7')
717 test.write(Buffer.from('746573743dc3a5', 'hex'))
718 test.expect(200, '{"test":"å"}', done)
721 it('should default to utf-8', function (done) {
722 var test = request(this.app).post('/')
723 test.set('Content-Type', 'application/x-www-form-urlencoded')
724 test.write(Buffer.from('6e616d653de8aeba', 'hex'))
725 test.expect(200, '{"name":"论"}', done)
728 it('should fail on unknown charset', function (done) {
729 var test = request(this.app).post('/')
730 test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r')
731 test.write(Buffer.from('6e616d653dcec5d4', 'hex'))
732 test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)
736 describe('encoding', function () {
737 before(function () {
738 this.app = createApp({ limit: '10kb' })
741 it('should parse without encoding', function (done) {
742 var test = request(this.app).post('/')
743 test.set('Content-Type', 'application/x-www-form-urlencoded')
744 test.write(Buffer.from('6e616d653de8aeba', 'hex'))
745 test.expect(200, '{"name":"论"}', done)
748 it('should support identity encoding', function (done) {
749 var test = request(this.app).post('/')
750 test.set('Content-Encoding', 'identity')
751 test.set('Content-Type', 'application/x-www-form-urlencoded')
752 test.write(Buffer.from('6e616d653de8aeba', 'hex'))
753 test.expect(200, '{"name":"论"}', done)
756 it('should support gzip encoding', function (done) {
757 var test = request(this.app).post('/')
758 test.set('Content-Encoding', 'gzip')
759 test.set('Content-Type', 'application/x-www-form-urlencoded')
760 test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
761 test.expect(200, '{"name":"论"}', done)
764 it('should support deflate encoding', function (done) {
765 var test = request(this.app).post('/')
766 test.set('Content-Encoding', 'deflate')
767 test.set('Content-Type', 'application/x-www-form-urlencoded')
768 test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex'))
769 test.expect(200, '{"name":"论"}', done)
772 it('should be case-insensitive', function (done) {
773 var test = request(this.app).post('/')
774 test.set('Content-Encoding', 'GZIP')
775 test.set('Content-Type', 'application/x-www-form-urlencoded')
776 test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex'))
777 test.expect(200, '{"name":"论"}', done)
780 it('should 415 on unknown encoding', function (done) {
781 var test = request(this.app).post('/')
782 test.set('Content-Encoding', 'nulls')
783 test.set('Content-Type', 'application/x-www-form-urlencoded')
784 test.write(Buffer.from('000000000000', 'hex'))
785 test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)
790 function createManyParams (count) {
791 var str = ''
793 if (count === 0) {
794 return str
797 str += '0=0'
799 for (var i = 1; i < count; i++) {
800 var n = i.toString(36)
801 str += '&' + n + '=' + n
804 return str
807 function createApp (options) {
808 var app = express()
810 app.use(express.urlencoded(options))
812 app.use(function (err, req, res, next) {
813 res.status(err.status || 500)
814 res.send(String(req.headers['x-error-property']
815 ? err[req.headers['x-error-property']]
816 : ('[' + err.type + '] ' + err.message)))
819 app.post('/', function (req, res) {
820 res.json(req.body)
823 return app
826 function expectKeyCount (count) {
827 return function (res) {
828 assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count)
832 function tryRequire (name) {
833 try {
834 return require(name)
835 } catch (e) {
836 return {}