fastify 3.5.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/docs/ContentTypeParser.md +3 -0
  2. package/docs/Ecosystem.md +7 -2
  3. package/docs/Hooks.md +0 -5
  4. package/docs/Lifecycle.md +2 -2
  5. package/docs/Logging.md +1 -1
  6. package/docs/Recommendations.md +17 -0
  7. package/docs/Reply.md +2 -2
  8. package/docs/Request.md +4 -2
  9. package/docs/Routes.md +21 -3
  10. package/docs/Server.md +51 -3
  11. package/docs/Style-Guide.md +180 -0
  12. package/docs/TypeScript.md +359 -359
  13. package/docs/Validation-and-Serialization.md +11 -5
  14. package/examples/parser.js +1 -1
  15. package/fastify.d.ts +2 -2
  16. package/fastify.js +39 -7
  17. package/lib/contentTypeParser.js +16 -11
  18. package/lib/context.js +5 -19
  19. package/lib/decorate.js +1 -1
  20. package/lib/errors.js +2 -2
  21. package/lib/fourOhFour.js +8 -7
  22. package/lib/handleRequest.js +5 -5
  23. package/lib/hooks.js +4 -4
  24. package/lib/logger.js +7 -7
  25. package/lib/pluginUtils.js +4 -2
  26. package/lib/reply.js +27 -24
  27. package/lib/reqIdGenFactory.js +2 -2
  28. package/lib/request.js +20 -8
  29. package/lib/route.js +12 -10
  30. package/lib/schemas.js +1 -1
  31. package/lib/server.js +5 -5
  32. package/lib/symbols.js +2 -1
  33. package/lib/validation.js +9 -8
  34. package/lib/warnings.js +3 -0
  35. package/lib/wrapThenable.js +2 -2
  36. package/package.json +10 -10
  37. package/test/404s.test.js +15 -15
  38. package/test/async-await.test.js +7 -7
  39. package/test/custom-parser-async.test.js +2 -2
  40. package/test/custom-parser.test.js +38 -8
  41. package/test/emit-warning.test.js +31 -0
  42. package/test/fastify-instance.test.js +33 -1
  43. package/test/helper.js +1 -1
  44. package/test/hooks.test.js +6 -6
  45. package/test/http2/head.test.js +1 -1
  46. package/test/http2/plain.test.js +1 -1
  47. package/test/http2/secure-with-fallback.test.js +1 -1
  48. package/test/http2/secure.test.js +1 -1
  49. package/test/http2/unknown-http-method.test.js +1 -1
  50. package/test/https/https.test.js +2 -1
  51. package/test/inject.test.js +2 -2
  52. package/test/internals/all.test.js +1 -1
  53. package/test/internals/hookRunner.test.js +1 -1
  54. package/test/internals/logger.test.js +1 -1
  55. package/test/internals/reply.test.js +62 -7
  56. package/test/internals/request.test.js +31 -5
  57. package/test/internals/version.test.js +43 -0
  58. package/test/listen.test.js +12 -0
  59. package/test/logger.test.js +11 -11
  60. package/test/nullable-validation.test.js +108 -0
  61. package/test/pretty-print.test.js +9 -14
  62. package/test/reply-error.test.js +2 -2
  63. package/test/request-error.test.js +81 -0
  64. package/test/route-hooks.test.js +10 -10
  65. package/test/stream.test.js +11 -11
  66. package/test/types/fastify.test-d.ts +1 -2
  67. package/test/types/instance.test-d.ts +17 -3
  68. package/test/types/logger.test-d.ts +35 -2
  69. package/test/types/register.test-d.ts +16 -0
  70. package/test/types/request.test-d.ts +1 -1
  71. package/test/types/route.test-d.ts +5 -0
  72. package/test/validation-error-handling.test.js +66 -0
  73. package/test/versioned-routes.test.js +55 -0
  74. package/types/.eslintrc.json +4 -1
  75. package/types/content-type-parser.d.ts +0 -15
  76. package/types/hooks.d.ts +138 -16
  77. package/types/instance.d.ts +86 -14
  78. package/types/logger.d.ts +13 -11
  79. package/types/plugin.d.ts +5 -7
  80. package/types/register.d.ts +8 -7
  81. package/types/request.d.ts +3 -1
  82. package/types/route.d.ts +38 -58
  83. package/types/schema.d.ts +4 -4
  84. package/types/serverFactory.d.ts +9 -9
@@ -3,6 +3,7 @@
3
3
  const sget = require('simple-get').concat
4
4
  const { test } = require('tap')
5
5
  const Fastify = require('..')
6
+ const semver = require('semver')
6
7
 
7
8
  process.removeAllListeners('warning')
8
9
 
@@ -129,3 +130,33 @@ test('Should emit a warning when using payload less preParsing hook', t => {
129
130
  })
130
131
  })
131
132
  })
133
+
134
+ if (semver.gte(process.versions.node, '13.0.0')) {
135
+ test('Should emit a warning when accessing request.connection instead of request.socket on Node process greater than 13.0.0', t => {
136
+ t.plan(4)
137
+
138
+ process.on('warning', onWarning)
139
+ function onWarning (warning) {
140
+ t.strictEqual(warning.name, 'FastifyDeprecation')
141
+ t.strictEqual(warning.code, 'FSTDEP005')
142
+ t.strictEqual(warning.message, 'You are accessing the deprecated "request.connection" property. Use "request.socket" instead.')
143
+
144
+ // removed listener before light-my-request emit second warning
145
+ process.removeListener('warning', onWarning)
146
+ }
147
+
148
+ const fastify = Fastify()
149
+
150
+ fastify.get('/', (request, reply) => {
151
+ reply.send(request.connection)
152
+ })
153
+
154
+ fastify.inject({
155
+ method: 'GET',
156
+ path: '/'
157
+ }, (err, res) => {
158
+ t.error(err)
159
+ process.removeListener('warning', onWarning)
160
+ })
161
+ })
162
+ }
@@ -4,7 +4,8 @@ const t = require('tap')
4
4
  const test = t.test
5
5
  const Fastify = require('..')
6
6
  const {
7
- kOptions
7
+ kOptions,
8
+ kErrorHandler
8
9
  } = require('../lib/symbols')
9
10
 
10
11
  test('root fastify instance is an object', t => {
@@ -65,3 +66,34 @@ test('fastify instance get invalid ajv options.plugins', t => {
65
66
  }
66
67
  }))
67
68
  })
69
+
70
+ test('fastify instance should contain default errorHandler', t => {
71
+ t.plan(3)
72
+ const fastify = Fastify()
73
+ t.ok(fastify[kErrorHandler] instanceof Function)
74
+ t.same(fastify.errorHandler, fastify[kErrorHandler])
75
+ t.same(Object.getOwnPropertyDescriptor(fastify, 'errorHandler').set, undefined)
76
+ })
77
+
78
+ test('errorHandler in plugin should be separate from the external one', async t => {
79
+ t.plan(4)
80
+ const fastify = Fastify()
81
+
82
+ fastify.register((instance, opts, next) => {
83
+ const inPluginErrHandler = (_, __, reply) => {
84
+ reply.send({ plugin: 'error-object' })
85
+ }
86
+
87
+ instance.setErrorHandler(inPluginErrHandler)
88
+
89
+ t.notSame(instance.errorHandler, fastify.errorHandler)
90
+ t.equal(instance.errorHandler.name, 'bound inPluginErrHandler')
91
+
92
+ next()
93
+ })
94
+
95
+ await fastify.ready()
96
+
97
+ t.ok(fastify[kErrorHandler] instanceof Function)
98
+ t.same(fastify.errorHandler, fastify[kErrorHandler])
99
+ })
package/test/helper.js CHANGED
@@ -282,7 +282,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
282
282
 
283
283
  // Node errors for OPTIONS requests with a stream body and no Content-Length header
284
284
  if (upMethod !== 'OPTIONS') {
285
- var chunk = Buffer.alloc(1024 * 1024 + 1, 0)
285
+ let chunk = Buffer.alloc(1024 * 1024 + 1, 0)
286
286
  const largeStream = new stream.Readable({
287
287
  read () {
288
288
  this.push(chunk)
@@ -197,7 +197,7 @@ test('onRequest hook should support encapsulation / 1', t => {
197
197
  test('onRequest hook should support encapsulation / 2', t => {
198
198
  t.plan(3)
199
199
  const fastify = Fastify()
200
- var pluginInstance
200
+ let pluginInstance
201
201
 
202
202
  fastify.addHook('onRequest', () => {})
203
203
 
@@ -813,7 +813,7 @@ test('onResponse hook should support encapsulation / 1', t => {
813
813
  test('onResponse hook should support encapsulation / 2', t => {
814
814
  t.plan(3)
815
815
  const fastify = Fastify()
816
- var pluginInstance
816
+ let pluginInstance
817
817
 
818
818
  fastify.addHook('onResponse', () => {})
819
819
 
@@ -890,7 +890,7 @@ test('onResponse hook should support encapsulation / 3', t => {
890
890
  test('onSend hook should support encapsulation / 1', t => {
891
891
  t.plan(3)
892
892
  const fastify = Fastify()
893
- var pluginInstance
893
+ let pluginInstance
894
894
 
895
895
  fastify.addHook('onSend', () => {})
896
896
 
@@ -1015,7 +1015,7 @@ test('onSend hook is called after payload is serialized and headers are set', t
1015
1015
  })
1016
1016
 
1017
1017
  fastify.register((instance, opts, done) => {
1018
- var chunk = 'stream payload'
1018
+ let chunk = 'stream payload'
1019
1019
  const thePayload = new stream.Readable({
1020
1020
  read () {
1021
1021
  this.push(chunk)
@@ -2163,7 +2163,7 @@ test('preValidation hook should support encapsulation / 1', t => {
2163
2163
  test('preValidation hook should support encapsulation / 2', t => {
2164
2164
  t.plan(3)
2165
2165
  const fastify = Fastify()
2166
- var pluginInstance
2166
+ let pluginInstance
2167
2167
 
2168
2168
  fastify.addHook('preValidation', () => {})
2169
2169
 
@@ -2582,7 +2582,7 @@ test('preParsing hook should support encapsulation / 1', t => {
2582
2582
  test('preParsing hook should support encapsulation / 2', t => {
2583
2583
  t.plan(3)
2584
2584
  const fastify = Fastify()
2585
- var pluginInstance
2585
+ let pluginInstance
2586
2586
 
2587
2587
  fastify.addHook('preParsing', function a () {})
2588
2588
 
@@ -6,7 +6,7 @@ const Fastify = require('../..')
6
6
  const h2url = require('h2url')
7
7
  const msg = { hello: 'world' }
8
8
 
9
- var fastify
9
+ let fastify
10
10
  try {
11
11
  fastify = Fastify({
12
12
  http2: true
@@ -6,7 +6,7 @@ const Fastify = require('../..')
6
6
  const h2url = require('h2url')
7
7
  const msg = { hello: 'world' }
8
8
 
9
- var fastify
9
+ let fastify
10
10
  try {
11
11
  fastify = Fastify({
12
12
  http2: true
@@ -9,7 +9,7 @@ const h2url = require('h2url')
9
9
  const sget = require('simple-get').concat
10
10
  const msg = { hello: 'world' }
11
11
 
12
- var fastify
12
+ let fastify
13
13
  try {
14
14
  fastify = Fastify({
15
15
  http2: true,
@@ -8,7 +8,7 @@ const Fastify = require('../..')
8
8
  const h2url = require('h2url')
9
9
  const msg = { hello: 'world' }
10
10
 
11
- var fastify
11
+ let fastify
12
12
  try {
13
13
  fastify = Fastify({
14
14
  http2: true,
@@ -6,7 +6,7 @@ const Fastify = require('../..')
6
6
  const h2url = require('h2url')
7
7
  const msg = { hello: 'world' }
8
8
 
9
- var fastify
9
+ let fastify
10
10
  try {
11
11
  fastify = Fastify({
12
12
  http2: true
@@ -7,8 +7,9 @@ const fs = require('fs')
7
7
  const path = require('path')
8
8
  const Fastify = require('../..')
9
9
 
10
+ let fastify
10
11
  try {
11
- var fastify = Fastify({
12
+ fastify = Fastify({
12
13
  https: {
13
14
  key: fs.readFileSync(path.join(__dirname, 'fastify.key')),
14
15
  cert: fs.readFileSync(path.join(__dirname, 'fastify.cert'))
@@ -329,7 +329,7 @@ test('inject a multipart request using form-body', t => {
329
329
  const fastify = Fastify()
330
330
 
331
331
  fastify.addContentTypeParser('*', function (req, payload, done) {
332
- var body = ''
332
+ let body = ''
333
333
  payload.on('data', d => {
334
334
  body += d
335
335
  })
@@ -362,7 +362,7 @@ function getStream () {
362
362
  }
363
363
  util.inherits(Read, Stream.Readable)
364
364
  const word = '{"hello":"world"}'
365
- var i = 0
365
+ let i = 0
366
366
 
367
367
  Read.prototype._read = function (size) {
368
368
  this.push(word[i] ? word[i++] : null)
@@ -28,7 +28,7 @@ test('fastify.all should add all the methods to the same url', t => {
28
28
 
29
29
  fastify.inject(options, (err, res) => {
30
30
  t.error(err)
31
- var payload = JSON.parse(res.payload)
31
+ const payload = JSON.parse(res.payload)
32
32
  t.deepEqual(payload, { method: method })
33
33
  })
34
34
  }
@@ -139,7 +139,7 @@ test('hookRunner - In case of error should skip to done (with promises)', t => {
139
139
  test('hookRunner - Be able to exit before its natural end', t => {
140
140
  t.plan(4)
141
141
 
142
- var shouldStop = false
142
+ let shouldStop = false
143
143
  hookRunner([fn1, fn2, fn3], iterator, 'a', 'b', done)
144
144
 
145
145
  function iterator (fn, a, b, next) {
@@ -23,7 +23,7 @@ test('The logger should add a unique id for every request', t => {
23
23
  fastify.listen(0, err => {
24
24
  t.error(err)
25
25
  const queue = new Queue()
26
- for (var i = 0; i < 10; i++) {
26
+ for (let i = 0; i < 10; i++) {
27
27
  queue.add(checkId)
28
28
  }
29
29
  queue.add(() => {
@@ -79,7 +79,7 @@ test('reply.send throw with circular JSON', t => {
79
79
  }
80
80
  const reply = new Reply(response, { context: { onSend: [] } })
81
81
  t.throws(() => {
82
- var obj = {}
82
+ const obj = {}
83
83
  obj.obj = obj
84
84
  reply.send(JSON.stringify(obj))
85
85
  }, 'Converting circular structure to JSON')
@@ -430,9 +430,9 @@ test('stream with content type should not send application/octet-stream', t => {
430
430
  const fs = require('fs')
431
431
  const path = require('path')
432
432
 
433
- var streamPath = path.join(__dirname, '..', '..', 'package.json')
434
- var stream = fs.createReadStream(streamPath)
435
- var buf = fs.readFileSync(streamPath)
433
+ const streamPath = path.join(__dirname, '..', '..', 'package.json')
434
+ const stream = fs.createReadStream(streamPath)
435
+ const buf = fs.readFileSync(streamPath)
436
436
 
437
437
  fastify.get('/', function (req, reply) {
438
438
  reply.header('Content-Type', 'text/plain').send(stream)
@@ -459,9 +459,9 @@ test('stream using reply.raw.writeHead should return customize headers', t => {
459
459
  const fs = require('fs')
460
460
  const path = require('path')
461
461
 
462
- var streamPath = path.join(__dirname, '..', '..', 'package.json')
463
- var stream = fs.createReadStream(streamPath)
464
- var buf = fs.readFileSync(streamPath)
462
+ const streamPath = path.join(__dirname, '..', '..', 'package.json')
463
+ const stream = fs.createReadStream(streamPath)
464
+ const buf = fs.readFileSync(streamPath)
465
465
 
466
466
  fastify.get('/', function (req, reply) {
467
467
  reply.log.warn = function mockWarn (message) {
@@ -781,6 +781,61 @@ test('undefined payload should be sent as-is', t => {
781
781
  })
782
782
  })
783
783
 
784
+ test('for HEAD method, no body should be sent but content-length should be', t => {
785
+ t.plan(11)
786
+
787
+ const fastify = require('../..')()
788
+ const contentType = 'application/json; charset=utf-8'
789
+ const bodySize = JSON.stringify({ foo: 'bar' }).length
790
+
791
+ fastify.head('/', {
792
+ onSend: function (request, reply, payload, next) {
793
+ t.strictEqual(payload, undefined)
794
+ next()
795
+ }
796
+ }, function (req, reply) {
797
+ reply.header('content-length', bodySize)
798
+ reply.header('content-type', contentType)
799
+ reply.code(200).send()
800
+ })
801
+
802
+ fastify.head('/with/null', {
803
+ onSend: function (request, reply, payload, next) {
804
+ t.strictEqual(payload, 'null')
805
+ next()
806
+ }
807
+ }, function (req, reply) {
808
+ reply.header('content-length', bodySize)
809
+ reply.header('content-type', contentType)
810
+ reply.code(200).send(null)
811
+ })
812
+
813
+ fastify.listen(0, err => {
814
+ t.error(err)
815
+ fastify.server.unref()
816
+
817
+ sget({
818
+ method: 'HEAD',
819
+ url: `http://localhost:${fastify.server.address().port}`
820
+ }, (err, response, body) => {
821
+ t.error(err)
822
+ t.strictEqual(response.headers['content-type'], contentType)
823
+ t.strictEqual(response.headers['content-length'], bodySize.toString())
824
+ t.strictEqual(body.length, 0)
825
+ })
826
+
827
+ sget({
828
+ method: 'HEAD',
829
+ url: `http://localhost:${fastify.server.address().port}/with/null`
830
+ }, (err, response, body) => {
831
+ t.error(err)
832
+ t.strictEqual(response.headers['content-type'], contentType)
833
+ t.strictEqual(response.headers['content-length'], bodySize.toString())
834
+ t.strictEqual(body.length, 0)
835
+ })
836
+ })
837
+ })
838
+
784
839
  test('reply.send(new NotFound()) should not invoke the 404 handler', t => {
785
840
  t.plan(9)
786
841
 
@@ -12,7 +12,7 @@ test('Regular request', t => {
12
12
  const req = {
13
13
  method: 'GET',
14
14
  url: '/',
15
- connection: { remoteAddress: 'ip' },
15
+ socket: { remoteAddress: 'ip' },
16
16
  headers
17
17
  }
18
18
  const request = new Request('id', 'params', req, 'query', 'log')
@@ -29,7 +29,7 @@ test('Regular request', t => {
29
29
  t.strictEqual(request.body, null)
30
30
  t.strictEqual(request.method, 'GET')
31
31
  t.strictEqual(request.url, '/')
32
- t.deepEqual(request.connection, req.connection)
32
+ t.deepEqual(request.socket, req.socket)
33
33
  })
34
34
 
35
35
  test('Regular request - hostname from authority', t => {
@@ -40,7 +40,7 @@ test('Regular request - hostname from authority', t => {
40
40
  const req = {
41
41
  method: 'GET',
42
42
  url: '/',
43
- connection: { remoteAddress: 'ip' },
43
+ socket: { remoteAddress: 'ip' },
44
44
  headers
45
45
  }
46
46
 
@@ -58,7 +58,7 @@ test('Regular request - host header has precedence over authority', t => {
58
58
  const req = {
59
59
  method: 'GET',
60
60
  url: '/',
61
- connection: { remoteAddress: 'ip' },
61
+ socket: { remoteAddress: 'ip' },
62
62
  headers
63
63
  }
64
64
  const request = new Request('id', 'params', req, 'query', 'log')
@@ -75,6 +75,9 @@ test('Request with trust proxy', t => {
75
75
  const req = {
76
76
  method: 'GET',
77
77
  url: '/',
78
+ // Some dependencies (proxy-addr, forwarded) still depend on the deprecated
79
+ // .connection property, we use .socket. Include both to satisfy everyone.
80
+ socket: { remoteAddress: 'ip' },
78
81
  connection: { remoteAddress: 'ip' },
79
82
  headers
80
83
  }
@@ -94,7 +97,7 @@ test('Request with trust proxy', t => {
94
97
  t.strictEqual(request.body, null)
95
98
  t.strictEqual(request.method, 'GET')
96
99
  t.strictEqual(request.url, '/')
97
- t.deepEqual(request.connection, req.connection)
100
+ t.deepEqual(request.socket, req.socket)
98
101
  })
99
102
 
100
103
  test('Request with trust proxy - no x-forwarded-host header', t => {
@@ -154,3 +157,26 @@ test('Request with trust proxy - x-forwarded-host header has precedence over hos
154
157
  t.type(request, TpRequest)
155
158
  t.strictEqual(request.hostname, 'example.com')
156
159
  })
160
+
161
+ test('Request with trust proxy - handles multiple entries in x-forwarded-host/proto', t => {
162
+ t.plan(3)
163
+ const headers = {
164
+ 'x-forwarded-host': 'example2.com, example.com',
165
+ 'x-forwarded-proto': 'http, https'
166
+ }
167
+ const req = {
168
+ method: 'GET',
169
+ url: '/',
170
+ // Some dependencies (proxy-addr, forwarded) still depend on the deprecated
171
+ // .connection property, we use .socket. Include both to satisfy everyone.
172
+ socket: { remoteAddress: 'ip' },
173
+ connection: { remoteAddress: 'ip' },
174
+ headers
175
+ }
176
+
177
+ const TpRequest = Request.buildRequest(Request, true)
178
+ const request = new TpRequest('id', 'params', req, 'query', 'log')
179
+ t.type(request, TpRequest)
180
+ t.strictEqual(request.hostname, 'example.com')
181
+ t.strictEqual(request.protocol, 'https')
182
+ })
@@ -0,0 +1,43 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const proxyquire = require('proxyquire')
6
+
7
+ test('should output an undefined version in case of package.json not available', t => {
8
+ const Fastify = proxyquire('../..', { fs: { accessSync: () => { throw Error('error') } } })
9
+ t.plan(1)
10
+ const srv = Fastify()
11
+ t.is(srv.version, undefined)
12
+ })
13
+
14
+ test('should output an undefined version in case of package.json is not the fastify one', t => {
15
+ const Fastify = proxyquire('../..', { fs: { accessSync: () => { }, readFileSync: () => JSON.stringify({ name: 'foo', version: '6.6.6' }) } })
16
+ t.plan(1)
17
+ const srv = Fastify()
18
+ t.is(srv.version, undefined)
19
+ })
20
+
21
+ test('should skip the version check if the version is undefined', t => {
22
+ const Fastify = proxyquire('../..', { fs: { accessSync: () => { }, readFileSync: () => JSON.stringify({ name: 'foo', version: '6.6.6' }) } })
23
+ t.plan(3)
24
+ const srv = Fastify()
25
+ t.is(srv.version, undefined)
26
+
27
+ plugin[Symbol.for('skip-override')] = false
28
+ plugin[Symbol.for('plugin-meta')] = {
29
+ name: 'plugin',
30
+ fastify: '>=99.0.0'
31
+ }
32
+
33
+ srv.register(plugin)
34
+
35
+ srv.ready((err) => {
36
+ t.error(err)
37
+ t.pass('everything right')
38
+ })
39
+
40
+ function plugin (instance, opts, next) {
41
+ next()
42
+ }
43
+ })
@@ -62,6 +62,18 @@ test('listen accepts options and a callback', t => {
62
62
  })
63
63
  })
64
64
 
65
+ test('listen accepts options, backlog and a callback', t => {
66
+ t.plan(1)
67
+ const fastify = Fastify()
68
+ t.tearDown(fastify.close.bind(fastify))
69
+ fastify.listen({
70
+ port: 0,
71
+ host: 'localhost'
72
+ }, 511, (err) => {
73
+ t.error(err)
74
+ })
75
+ })
76
+
65
77
  test('listen accepts a port, address and a callback with (err, address)', t => {
66
78
  t.plan(2)
67
79
  const fastify = Fastify()
@@ -32,8 +32,8 @@ tearDown(() => {
32
32
 
33
33
  test('defaults to info level', t => {
34
34
  t.plan(13)
35
- var fastify = null
36
- var stream = split(JSON.parse)
35
+ let fastify = null
36
+ const stream = split(JSON.parse)
37
37
  try {
38
38
  fastify = Fastify({
39
39
  logger: {
@@ -80,8 +80,8 @@ test('defaults to info level', t => {
80
80
 
81
81
  test('test log stream', t => {
82
82
  t.plan(12)
83
- var fastify = null
84
- var stream = split(JSON.parse)
83
+ let fastify = null
84
+ const stream = split(JSON.parse)
85
85
  try {
86
86
  fastify = Fastify({
87
87
  logger: {
@@ -127,8 +127,8 @@ test('test log stream', t => {
127
127
 
128
128
  test('test error log stream', t => {
129
129
  t.plan(11)
130
- var fastify = null
131
- var stream = split(JSON.parse)
130
+ let fastify = null
131
+ const stream = split(JSON.parse)
132
132
  try {
133
133
  fastify = Fastify({
134
134
  logger: {
@@ -248,8 +248,8 @@ test('can use external logger instance with custom serializer', t => {
248
248
 
249
249
  test('expose the logger', t => {
250
250
  t.plan(2)
251
- var fastify = null
252
- var stream = split(JSON.parse)
251
+ let fastify = null
252
+ const stream = split(JSON.parse)
253
253
  try {
254
254
  fastify = Fastify({
255
255
  logger: {
@@ -1190,8 +1190,8 @@ test('Do not wrap IPv4 address', t => {
1190
1190
 
1191
1191
  test('file option', t => {
1192
1192
  t.plan(13)
1193
- var fastify = null
1194
- var dest = file()
1193
+ let fastify = null
1194
+ const dest = file()
1195
1195
 
1196
1196
  fastify = Fastify({
1197
1197
  logger: {
@@ -1423,7 +1423,7 @@ test('should redact the authorization header if so specified', t => {
1423
1423
  headers: req.headers,
1424
1424
  hostname: req.hostname,
1425
1425
  remoteAddress: req.ip,
1426
- remotePort: req.connection.remotePort
1426
+ remotePort: req.socket.remotePort
1427
1427
  }
1428
1428
  }
1429
1429
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  const t = require('tap')
4
4
  const test = t.test
5
+ const sget = require('simple-get').concat
5
6
  const Fastify = require('..')
6
7
 
7
8
  test('nullable string', t => {
@@ -50,3 +51,110 @@ test('nullable string', t => {
50
51
  t.same(res.payload.hello, null)
51
52
  })
52
53
  })
54
+
55
+ test('object or null body', t => {
56
+ t.plan(5)
57
+
58
+ const fastify = Fastify()
59
+
60
+ fastify.route({
61
+ method: 'POST',
62
+ url: '/',
63
+ handler: (req, reply) => {
64
+ t.strictEqual(req.body, null)
65
+ reply.code(200).send({ requestBody: req.body })
66
+ },
67
+ schema: {
68
+ body: {
69
+ type: ['object', 'null'],
70
+ properties: {
71
+ hello: {
72
+ type: 'string',
73
+ format: 'email'
74
+ }
75
+ }
76
+ },
77
+ response: {
78
+ 200: {
79
+ type: 'object',
80
+ nullable: true,
81
+ properties: {
82
+ requestBody: {
83
+ type: 'string',
84
+ format: 'email',
85
+ nullable: true
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ })
92
+
93
+ fastify.listen(0, (err) => {
94
+ fastify.server.unref()
95
+ t.error(err)
96
+
97
+ sget({
98
+ method: 'POST',
99
+ url: 'http://localhost:' + fastify.server.address().port
100
+ }, (err, response, body) => {
101
+ t.error(err)
102
+ t.strictEqual(response.statusCode, 200)
103
+ t.deepEqual(JSON.parse(body), { requestBody: null })
104
+ })
105
+ })
106
+ })
107
+
108
+ test('nullable body', t => {
109
+ t.plan(5)
110
+
111
+ const fastify = Fastify()
112
+
113
+ fastify.route({
114
+ method: 'POST',
115
+ url: '/',
116
+ handler: (req, reply) => {
117
+ t.strictEqual(req.body, null)
118
+ reply.code(200).send({ requestBody: req.body })
119
+ },
120
+ schema: {
121
+ body: {
122
+ type: 'object',
123
+ nullable: true,
124
+ properties: {
125
+ hello: {
126
+ type: 'string',
127
+ format: 'email'
128
+ }
129
+ }
130
+ },
131
+ response: {
132
+ 200: {
133
+ type: 'object',
134
+ nullable: true,
135
+ properties: {
136
+ requestBody: {
137
+ type: 'string',
138
+ format: 'email',
139
+ nullable: true
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ })
146
+
147
+ fastify.listen(0, (err) => {
148
+ fastify.server.unref()
149
+ t.error(err)
150
+
151
+ sget({
152
+ method: 'POST',
153
+ url: 'http://localhost:' + fastify.server.address().port
154
+ }, (err, response, body) => {
155
+ t.error(err)
156
+ t.strictEqual(response.statusCode, 200)
157
+ t.deepEqual(JSON.parse(body), { requestBody: null })
158
+ })
159
+ })
160
+ })