fastify 4.19.1 → 4.20.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 (56) hide show
  1. package/.c8rc.json +8 -0
  2. package/.taprc +3 -2
  3. package/SECURITY.md +9 -0
  4. package/docs/Guides/Prototype-Poisoning.md +2 -2
  5. package/docs/Reference/Errors.md +39 -17
  6. package/docs/Reference/Logging.md +1 -1
  7. package/docs/Reference/Plugins.md +4 -0
  8. package/docs/Reference/Routes.md +8 -0
  9. package/docs/Reference/Server.md +38 -0
  10. package/fastify.d.ts +3 -2
  11. package/fastify.js +51 -24
  12. package/lib/context.js +6 -0
  13. package/lib/errors.js +50 -19
  14. package/lib/fourOhFour.js +5 -9
  15. package/lib/handleRequest.js +3 -5
  16. package/lib/hooks.js +91 -25
  17. package/lib/logger.js +40 -3
  18. package/lib/reply.js +3 -9
  19. package/lib/reqIdGenFactory.js +18 -3
  20. package/lib/route.js +14 -61
  21. package/lib/schema-controller.js +2 -0
  22. package/lib/server.js +23 -8
  23. package/lib/symbols.js +1 -0
  24. package/package.json +8 -7
  25. package/test/500s.test.js +22 -0
  26. package/test/childLoggerFactory.test.js +91 -0
  27. package/test/custom-http-server.test.js +42 -0
  28. package/test/encapsulated-child-logger-factory.test.js +69 -0
  29. package/test/fastify-instance.test.js +43 -10
  30. package/test/inject.test.js +1 -2
  31. package/test/internals/errors.test.js +843 -0
  32. package/test/internals/hookRunner.test.js +22 -8
  33. package/test/internals/initialConfig.test.js +9 -2
  34. package/test/internals/reply.test.js +49 -43
  35. package/test/internals/reqIdGenFactory.test.js +129 -0
  36. package/test/internals/request-validate.test.js +40 -1
  37. package/test/internals/request.test.js +14 -4
  38. package/test/reply-error.test.js +25 -0
  39. package/test/request-id.test.js +131 -0
  40. package/test/route.test.js +135 -0
  41. package/test/server.test.js +64 -2
  42. package/test/types/errors.test-d.ts +82 -0
  43. package/test/types/fastify.test-d.ts +4 -0
  44. package/test/types/hooks.test-d.ts +65 -64
  45. package/test/types/instance.test-d.ts +139 -0
  46. package/test/types/reply.test-d.ts +9 -8
  47. package/test/types/request.test-d.ts +17 -18
  48. package/test/types/route.test-d.ts +10 -7
  49. package/test/types/type-provider.test-d.ts +4 -0
  50. package/types/context.d.ts +3 -3
  51. package/types/errors.d.ts +29 -23
  52. package/types/instance.d.ts +39 -7
  53. package/types/logger.d.ts +25 -0
  54. package/types/reply.d.ts +10 -5
  55. package/types/request.d.ts +5 -5
  56. package/types/route.d.ts +8 -7
package/lib/route.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const FindMyWay = require('find-my-way')
4
4
  const Context = require('./context')
5
5
  const handleRequest = require('./handleRequest')
6
- const { hookRunner, hookIterator, onRequestAbortHookRunner, lifecycleHooks } = require('./hooks')
6
+ const { onRequestAbortHookRunner, lifecycleHooks, preParsingHookRunner, onTimeoutHookRunner, onRequestHookRunner } = require('./hooks')
7
7
  const { supportedMethods } = require('./httpMethods')
8
8
  const { normalizeSchema } = require('./schemas')
9
9
  const { parseHeadOnSendHandlers } = require('./headRoute')
@@ -20,7 +20,6 @@ const {
20
20
  FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
21
21
  FST_ERR_DUPLICATED_ROUTE,
22
22
  FST_ERR_INVALID_URL,
23
- FST_ERR_SEND_UNDEFINED_ERR,
24
23
  FST_ERR_HOOK_INVALID_HANDLER,
25
24
  FST_ERR_ROUTE_OPTIONS_NOT_OBJ,
26
25
  FST_ERR_ROUTE_DUPLICATED_HANDLER,
@@ -51,13 +50,13 @@ const {
51
50
  kRouteContext
52
51
  } = require('./symbols.js')
53
52
  const { buildErrorHandler } = require('./error-handler')
53
+ const { createChildLogger } = require('./logger')
54
54
 
55
55
  function buildRouting (options) {
56
56
  const router = FindMyWay(options.config)
57
57
 
58
58
  let avvio
59
59
  let fourOhFour
60
- let requestIdLogLabel
61
60
  let logger
62
61
  let hasLogger
63
62
  let setupResponseListeners
@@ -74,6 +73,10 @@ function buildRouting (options) {
74
73
  let closing = false
75
74
 
76
75
  return {
76
+ /**
77
+ * @param {import('../fastify').FastifyServerOptions} options
78
+ * @param {*} fastifyArgs
79
+ */
77
80
  setup (options, fastifyArgs) {
78
81
  avvio = fastifyArgs.avvio
79
82
  fourOhFour = fastifyArgs.fourOhFour
@@ -84,7 +87,6 @@ function buildRouting (options) {
84
87
  validateHTTPVersion = fastifyArgs.validateHTTPVersion
85
88
 
86
89
  globalExposeHeadRoutes = options.exposeHeadRoutes
87
- requestIdLogLabel = options.requestIdLogLabel
88
90
  genReqId = options.genReqId
89
91
  disableRequestLogging = options.disableRequestLogging
90
92
  ignoreTrailingSlash = options.ignoreTrailingSlash
@@ -168,7 +170,10 @@ function buildRouting (options) {
168
170
  ) !== null
169
171
  }
170
172
 
171
- // Route management
173
+ /**
174
+ * Route management
175
+ * @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
176
+ */
172
177
  function route ({ options, isFastify }) {
173
178
  // Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
174
179
  const opts = { ...options }
@@ -282,6 +287,7 @@ function buildRouting (options) {
282
287
  handler: opts.handler.bind(this),
283
288
  config,
284
289
  errorHandler: opts.errorHandler,
290
+ childLoggerFactory: opts.childLoggerFactory,
285
291
  bodyLimit: opts.bodyLimit,
286
292
  logLevel: opts.logLevel,
287
293
  logSerializers: opts.logSerializers,
@@ -399,10 +405,6 @@ function buildRouting (options) {
399
405
  function routeHandler (req, res, params, context, query) {
400
406
  const id = genReqId(req)
401
407
 
402
- const loggerBinding = {
403
- [requestIdLogLabel]: id
404
- }
405
-
406
408
  const loggerOpts = {
407
409
  level: context.logLevel
408
410
  }
@@ -410,7 +412,7 @@ function buildRouting (options) {
410
412
  if (context.logSerializers) {
411
413
  loggerOpts.serializers = context.logSerializers
412
414
  }
413
- const childLogger = logger.child(loggerBinding, loggerOpts)
415
+ const childLogger = createChildLogger(context, logger, req, id, loggerOpts)
414
416
  childLogger[kDisableRequestLogging] = disableRequestLogging
415
417
 
416
418
  // TODO: The check here should be removed once https://github.com/nodejs/node/issues/43115 resolve in core.
@@ -477,9 +479,8 @@ function buildRouting (options) {
477
479
  }
478
480
 
479
481
  if (context.onRequest !== null) {
480
- hookRunner(
482
+ onRequestHookRunner(
481
483
  context.onRequest,
482
- hookIterator,
483
484
  request,
484
485
  reply,
485
486
  runPreParsing
@@ -494,7 +495,6 @@ function buildRouting (options) {
494
495
  if (req.aborted) {
495
496
  onRequestAbortHookRunner(
496
497
  context.onRequestAbort,
497
- hookIterator,
498
498
  request,
499
499
  handleOnRequestAbortHooksErrors.bind(null, reply)
500
500
  )
@@ -519,9 +519,8 @@ function handleOnRequestAbortHooksErrors (reply, err) {
519
519
 
520
520
  function handleTimeout () {
521
521
  const { context, request, reply } = this._meta
522
- hookRunner(
522
+ onTimeoutHookRunner(
523
523
  context.onTimeout,
524
- hookIterator,
525
524
  request,
526
525
  reply,
527
526
  noop
@@ -570,52 +569,6 @@ function runPreParsing (err, request, reply) {
570
569
  }
571
570
  }
572
571
 
573
- function preParsingHookRunner (functions, request, reply, cb) {
574
- let i = 0
575
-
576
- function next (err, stream) {
577
- if (reply.sent) {
578
- return
579
- }
580
-
581
- if (typeof stream !== 'undefined') {
582
- request[kRequestPayloadStream] = stream
583
- }
584
-
585
- if (err || i === functions.length) {
586
- cb(err, request, reply)
587
- return
588
- }
589
-
590
- const fn = functions[i++]
591
- let result
592
- try {
593
- result = fn(request, reply, request[kRequestPayloadStream], next)
594
- } catch (error) {
595
- next(error)
596
- return
597
- }
598
-
599
- if (result && typeof result.then === 'function') {
600
- result.then(handleResolve, handleReject)
601
- }
602
- }
603
-
604
- function handleResolve (stream) {
605
- next(null, stream)
606
- }
607
-
608
- function handleReject (err) {
609
- if (!err) {
610
- err = new FST_ERR_SEND_UNDEFINED_ERR()
611
- }
612
-
613
- next(err)
614
- }
615
-
616
- next(null, request[kRequestPayloadStream])
617
- }
618
-
619
572
  /**
620
573
  * Used within the route handler as a `net.Socket.close` event handler.
621
574
  * The purpose is to remove a socket from the tracked sockets collection when
@@ -50,6 +50,8 @@ class SchemaController {
50
50
  this.schemaBucket = this.opts.bucket(parent.getSchemas())
51
51
  this.validatorCompiler = parent.getValidatorCompiler()
52
52
  this.serializerCompiler = parent.getSerializerCompiler()
53
+ this.isCustomValidatorCompiler = parent.isCustomValidatorCompiler
54
+ this.isCustomSerializerCompiler = parent.isCustomSerializerCompiler
53
55
  this.parent = parent
54
56
  } else {
55
57
  this.schemaBucket = this.opts.bucket()
package/lib/server.js CHANGED
@@ -9,7 +9,8 @@ const { kState, kOptions, kServerBindings } = require('./symbols')
9
9
  const {
10
10
  FST_ERR_HTTP2_INVALID_VERSION,
11
11
  FST_ERR_REOPENED_CLOSE_SERVER,
12
- FST_ERR_REOPENED_SERVER
12
+ FST_ERR_REOPENED_SERVER,
13
+ FST_ERR_LISTEN_OPTIONS_INVALID
13
14
  } = require('./errors')
14
15
 
15
16
  module.exports.createServer = createServer
@@ -22,8 +23,6 @@ function defaultResolveServerListeningText (address) {
22
23
  function createServer (options, httpHandler) {
23
24
  const server = getServerInstance(options, httpHandler)
24
25
 
25
- return { server, listen }
26
-
27
26
  // `this` is the Fastify object
28
27
  function listen (listenOptions, ...args) {
29
28
  let cb = args.slice(-1).pop()
@@ -48,6 +47,20 @@ function createServer (options, httpHandler) {
48
47
  } else {
49
48
  listenOptions.cb = cb
50
49
  }
50
+ if (listenOptions.signal) {
51
+ if (typeof listenOptions.signal.on !== 'function' && typeof listenOptions.signal.addEventListener !== 'function') {
52
+ throw new FST_ERR_LISTEN_OPTIONS_INVALID('Invalid options.signal')
53
+ }
54
+
55
+ if (listenOptions.signal.aborted) {
56
+ this.close()
57
+ } else {
58
+ const onAborted = () => {
59
+ this.close()
60
+ }
61
+ listenOptions.signal.addEventListener('abort', onAborted, { once: true })
62
+ }
63
+ }
51
64
 
52
65
  // If we have a path specified, don't default host to 'localhost' so we don't end up listening
53
66
  // on both path and host
@@ -99,6 +112,8 @@ function createServer (options, httpHandler) {
99
112
 
100
113
  this.ready(listenCallback.call(this, server, listenOptions))
101
114
  }
115
+
116
+ return { server, listen }
102
117
  }
103
118
 
104
119
  function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, onListen) {
@@ -138,17 +153,16 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
138
153
  // pace through the `onClose` hook.
139
154
  // It also allows them to handle possible connections already
140
155
  // attached to them if any.
156
+ /* c8 ignore next 20 */
141
157
  this.onClose((instance, done) => {
142
158
  if (instance[kState].listening) {
143
159
  // No new TCP connections are accepted
144
160
  // We swallow any error from the secondary
145
161
  // server
146
162
  secondaryServer.close(() => done())
147
- /* istanbul ignore next: Cannot test this without Node.js core support */
148
163
  if (serverOpts.forceCloseConnections === 'idle') {
149
164
  // Not needed in Node 19
150
165
  secondaryServer.closeIdleConnections()
151
- /* istanbul ignore next: Cannot test this without Node.js core support */
152
166
  } else if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections) {
153
167
  secondaryServer.closeAllConnections()
154
168
  }
@@ -216,9 +230,10 @@ function listenCallback (server, listenOptions) {
216
230
  }
217
231
 
218
232
  server.once('error', wrap)
219
- server.listen(listenOptions, wrap)
220
-
221
- this[kState].listening = true
233
+ if (!this[kState].closing) {
234
+ server.listen(listenOptions, wrap)
235
+ this[kState].listening = true
236
+ }
222
237
  }
223
238
  }
224
239
 
package/lib/symbols.js CHANGED
@@ -55,6 +55,7 @@ const keys = {
55
55
  // This symbol is only meant to be used for fastify tests and should not be used for any other purpose
56
56
  kTestInternals: Symbol('fastify.testInternals'),
57
57
  kErrorHandler: Symbol('fastify.errorHandler'),
58
+ kChildLoggerFactory: Symbol('fastify.childLoggerFactory'),
58
59
  kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
59
60
  kKeepAliveConnections: Symbol('fastify.keepAliveConnections'),
60
61
  kRouteByFastify: Symbol('fastify.routeByFastify')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.19.1",
3
+ "version": "4.20.0",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -10,9 +10,9 @@
10
10
  "benchmark": "npx concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"npx autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
11
11
  "benchmark:parser": "npx concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"npx autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
12
12
  "build:validation": "node build/build-error-serializer.js && node build/build-validation.js",
13
- "coverage": "cross-env NODE_OPTIONS=no-network-family-autoselection npm run unit -- --cov --coverage-report=html",
14
- "coverage:ci": "npm run unit -- --cov --coverage-report=html --no-browser --no-check-coverage",
15
- "coverage:ci-check-coverage": "nyc check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
13
+ "coverage": "npm run unit -- --coverage-report=html",
14
+ "coverage:ci": "npm run unit -- --coverage-report=html --no-browser --no-check-coverage",
15
+ "coverage:ci-check-coverage": "c8 check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
16
16
  "license-checker": "license-checker --production --onlyAllow=\"MIT;ISC;BSD-3-Clause;BSD-2-Clause\"",
17
17
  "lint": "npm run lint:standard && npm run lint:typescript && npm run lint:markdown",
18
18
  "lint:fix": "standard --fix",
@@ -20,13 +20,13 @@
20
20
  "lint:standard": "standard | snazzy",
21
21
  "lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
22
22
  "prepublishOnly": "cross-env PREPUBLISH=true tap --no-check-coverage test/build/**.test.js && npm run test:validator:integrity",
23
- "test": "npm run lint && cross-env NODE_OPTIONS=no-network-family-autoselection npm run unit && npm run test:typescript",
23
+ "test": "npm run lint && npm run unit && npm run test:typescript",
24
24
  "test:ci": "npm run unit -- --cov --coverage-report=lcovonly && npm run test:typescript",
25
25
  "test:report": "npm run lint && npm run unit:report && npm run test:typescript",
26
26
  "test:validator:integrity": "npm run build:validation && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/error-serializer.js && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/configValidator.js",
27
27
  "test:typescript": "tsc test/types/import.ts && tsd",
28
28
  "test:watch": "npm run unit -- -w --no-coverage-report -R terse",
29
- "unit": "tap",
29
+ "unit": "c8 tap",
30
30
  "unit:junit": "tap-mocha-reporter xunit < out.tap > test/junit-testresults.xml",
31
31
  "unit:report": "tap --cov --coverage-report=html --coverage-report=cobertura | tee out.tap"
32
32
  },
@@ -134,7 +134,7 @@
134
134
  "homepage": "https://www.fastify.io/",
135
135
  "devDependencies": {
136
136
  "@fastify/pre-commit": "^2.0.2",
137
- "@sinclair/typebox": "^0.28.9",
137
+ "@sinclair/typebox": "^0.29.1",
138
138
  "@sinonjs/fake-timers": "^11.0.0",
139
139
  "@types/node": "^20.1.0",
140
140
  "@typescript-eslint/eslint-plugin": "^5.59.2",
@@ -145,6 +145,7 @@
145
145
  "ajv-i18n": "^4.2.0",
146
146
  "ajv-merge-patch": "^5.0.1",
147
147
  "branch-comparer": "^1.1.0",
148
+ "c8": "^8.0.0",
148
149
  "cross-env": "^7.0.3",
149
150
  "eslint": "^8.39.0",
150
151
  "eslint-config-standard": "^17.0.0",
package/test/500s.test.js CHANGED
@@ -9,6 +9,7 @@ test('default 500', t => {
9
9
  t.plan(4)
10
10
 
11
11
  const fastify = Fastify()
12
+ t.teardown(fastify.close.bind(fastify))
12
13
 
13
14
  fastify.get('/', function (req, reply) {
14
15
  reply.send(new Error('kaboom'))
@@ -33,6 +34,7 @@ test('custom 500', t => {
33
34
  t.plan(6)
34
35
 
35
36
  const fastify = Fastify()
37
+ t.teardown(fastify.close.bind(fastify))
36
38
 
37
39
  fastify.get('/', function (req, reply) {
38
40
  reply.send(new Error('kaboom'))
@@ -62,6 +64,7 @@ test('encapsulated 500', t => {
62
64
  t.plan(10)
63
65
 
64
66
  const fastify = Fastify()
67
+ t.teardown(fastify.close.bind(fastify))
65
68
 
66
69
  fastify.get('/', function (req, reply) {
67
70
  reply.send(new Error('kaboom'))
@@ -113,6 +116,7 @@ test('custom 500 with hooks', t => {
113
116
  t.plan(7)
114
117
 
115
118
  const fastify = Fastify()
119
+ t.teardown(fastify.close.bind(fastify))
116
120
 
117
121
  fastify.get('/', function (req, reply) {
118
122
  reply.send(new Error('kaboom'))
@@ -166,3 +170,21 @@ test('cannot set errorHandler after binding', t => {
166
170
  }
167
171
  })
168
172
  })
173
+
174
+ test('cannot set childLoggerFactory after binding', t => {
175
+ t.plan(2)
176
+
177
+ const fastify = Fastify()
178
+ t.teardown(fastify.close.bind(fastify))
179
+
180
+ fastify.listen({ port: 0 }, err => {
181
+ t.error(err)
182
+
183
+ try {
184
+ fastify.setChildLoggerFactory(() => { })
185
+ t.fail()
186
+ } catch (e) {
187
+ t.pass()
188
+ }
189
+ })
190
+ })
@@ -0,0 +1,91 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const Fastify = require('..')
5
+
6
+ test('Should accept a custom childLoggerFactory function', t => {
7
+ t.plan(4)
8
+
9
+ const fastify = Fastify()
10
+ fastify.setChildLoggerFactory(function (logger, bindings, opts) {
11
+ t.ok(bindings.reqId)
12
+ t.ok(opts)
13
+ this.log.debug(bindings, 'created child logger')
14
+ return logger.child(bindings, opts)
15
+ })
16
+
17
+ fastify.get('/', (req, reply) => {
18
+ req.log.info('log message')
19
+ reply.send()
20
+ })
21
+
22
+ fastify.listen({ port: 0 }, err => {
23
+ t.error(err)
24
+ fastify.inject({
25
+ method: 'GET',
26
+ url: 'http://localhost:' + fastify.server.address().port
27
+ }, (err, res) => {
28
+ t.error(err)
29
+ fastify.close()
30
+ })
31
+ })
32
+ })
33
+
34
+ test('req.log should be the instance returned by the factory', t => {
35
+ t.plan(3)
36
+
37
+ const fastify = Fastify()
38
+ fastify.setChildLoggerFactory(function (logger, bindings, opts) {
39
+ this.log.debug('using root logger')
40
+ return this.log
41
+ })
42
+
43
+ fastify.get('/', (req, reply) => {
44
+ t.equal(req.log, fastify.log)
45
+ req.log.info('log message')
46
+ reply.send()
47
+ })
48
+
49
+ fastify.listen({ port: 0 }, err => {
50
+ t.error(err)
51
+ fastify.inject({
52
+ method: 'GET',
53
+ url: 'http://localhost:' + fastify.server.address().port
54
+ }, (err, res) => {
55
+ t.error(err)
56
+ fastify.close()
57
+ })
58
+ })
59
+ })
60
+
61
+ test('should throw error if invalid logger is returned', t => {
62
+ t.plan(2)
63
+
64
+ const fastify = Fastify()
65
+ fastify.setChildLoggerFactory(function () {
66
+ this.log.debug('returning an invalid logger, expect error')
67
+ return undefined
68
+ })
69
+
70
+ fastify.get('/', (req, reply) => {
71
+ reply.send()
72
+ })
73
+
74
+ fastify.listen({ port: 0 }, err => {
75
+ t.error(err)
76
+ t.throws(() => {
77
+ try {
78
+ fastify.inject({
79
+ method: 'GET',
80
+ url: 'http://localhost:' + fastify.server.address().port
81
+ }, (err) => {
82
+ t.fail('request should have failed but did not')
83
+ t.error(err)
84
+ fastify.close()
85
+ })
86
+ } finally {
87
+ fastify.close()
88
+ }
89
+ }, { code: 'FST_ERR_LOG_INVALID_LOGGER' })
90
+ })
91
+ })
@@ -82,3 +82,45 @@ test('Should accept user defined serverFactory and ignore secondary server creat
82
82
  await app.listen({ port: 0 })
83
83
  })
84
84
  })
85
+
86
+ test('Should not call close on the server if it has not created it', async t => {
87
+ const server = http.createServer()
88
+
89
+ const serverFactory = (handler, opts) => {
90
+ server.on('request', handler)
91
+ return server
92
+ }
93
+
94
+ const fastify = Fastify({ serverFactory })
95
+
96
+ fastify.get('/', (req, reply) => {
97
+ reply.send({ hello: 'world' })
98
+ })
99
+
100
+ await fastify.ready()
101
+
102
+ await new Promise((resolve, reject) => {
103
+ server.listen(0)
104
+ server.on('listening', resolve)
105
+ server.on('error', reject)
106
+ })
107
+
108
+ const address = server.address()
109
+ t.equal(server.listening, true)
110
+ await fastify.close()
111
+
112
+ t.equal(server.listening, true)
113
+ t.same(server.address(), address)
114
+ t.same(fastify.addresses(), [address])
115
+
116
+ await new Promise((resolve, reject) => {
117
+ server.close((err) => {
118
+ if (err) {
119
+ return reject(err)
120
+ }
121
+ resolve()
122
+ })
123
+ })
124
+ t.equal(server.listening, false)
125
+ t.same(server.address(), null)
126
+ })
@@ -0,0 +1,69 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const Fastify = require('..')
5
+ const fp = require('fastify-plugin')
6
+
7
+ test('encapsulates an child logger factory', async t => {
8
+ t.plan(4)
9
+
10
+ const fastify = Fastify()
11
+ fastify.register(async function (fastify) {
12
+ fastify.setChildLoggerFactory(function pluginFactory (logger, bindings, opts) {
13
+ const child = logger.child(bindings, opts)
14
+ child.customLog = function (message) {
15
+ t.equal(message, 'custom')
16
+ }
17
+ return child
18
+ })
19
+ fastify.get('/encapsulated', async (req) => {
20
+ req.log.customLog('custom')
21
+ })
22
+ })
23
+
24
+ fastify.setChildLoggerFactory(function globalFactory (logger, bindings, opts) {
25
+ const child = logger.child(bindings, opts)
26
+ child.globalLog = function (message) {
27
+ t.equal(message, 'global')
28
+ }
29
+ return child
30
+ })
31
+ fastify.get('/not-encapsulated', async (req) => {
32
+ req.log.globalLog('global')
33
+ })
34
+
35
+ const res1 = await fastify.inject('/encapsulated')
36
+ t.equal(res1.statusCode, 200)
37
+
38
+ const res2 = await fastify.inject('/not-encapsulated')
39
+ t.equal(res2.statusCode, 200)
40
+ })
41
+
42
+ test('child logger factory set on root scope when using fastify-plugin', async t => {
43
+ t.plan(4)
44
+
45
+ const fastify = Fastify()
46
+ fastify.register(fp(async function (fastify) {
47
+ // Using fastify-plugin, the factory should be set on the root scope
48
+ fastify.setChildLoggerFactory(function pluginFactory (logger, bindings, opts) {
49
+ const child = logger.child(bindings, opts)
50
+ child.customLog = function (message) {
51
+ t.equal(message, 'custom')
52
+ }
53
+ return child
54
+ })
55
+ fastify.get('/not-encapsulated-1', async (req) => {
56
+ req.log.customLog('custom')
57
+ })
58
+ }))
59
+
60
+ fastify.get('/not-encapsulated-2', async (req) => {
61
+ req.log.customLog('custom')
62
+ })
63
+
64
+ const res1 = await fastify.inject('/not-encapsulated-1')
65
+ t.equal(res1.statusCode, 200)
66
+
67
+ const res2 = await fastify.inject('/not-encapsulated-2')
68
+ t.equal(res2.statusCode, 200)
69
+ })
@@ -7,7 +7,8 @@ const os = require('os')
7
7
 
8
8
  const {
9
9
  kOptions,
10
- kErrorHandler
10
+ kErrorHandler,
11
+ kChildLoggerFactory
11
12
  } = require('../lib/symbols')
12
13
 
13
14
  test('root fastify instance is an object', t => {
@@ -100,6 +101,37 @@ test('errorHandler in plugin should be separate from the external one', async t
100
101
  t.same(fastify.errorHandler, fastify[kErrorHandler].func)
101
102
  })
102
103
 
104
+ test('fastify instance should contain default childLoggerFactory', t => {
105
+ t.plan(3)
106
+ const fastify = Fastify()
107
+ t.ok(fastify[kChildLoggerFactory] instanceof Function)
108
+ t.same(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
109
+ t.same(Object.getOwnPropertyDescriptor(fastify, 'childLoggerFactory').set, undefined)
110
+ })
111
+
112
+ test('childLoggerFactory in plugin should be separate from the external one', async t => {
113
+ t.plan(4)
114
+ const fastify = Fastify()
115
+
116
+ fastify.register((instance, opts, done) => {
117
+ const inPluginLoggerFactory = function (logger, bindings, opts) {
118
+ return logger.child(bindings, opts)
119
+ }
120
+
121
+ instance.setChildLoggerFactory(inPluginLoggerFactory)
122
+
123
+ t.notSame(instance.childLoggerFactory, fastify.childLoggerFactory)
124
+ t.equal(instance.childLoggerFactory.name, 'inPluginLoggerFactory')
125
+
126
+ done()
127
+ })
128
+
129
+ await fastify.ready()
130
+
131
+ t.ok(fastify[kChildLoggerFactory] instanceof Function)
132
+ t.same(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
133
+ })
134
+
103
135
  test('fastify instance should contains listeningOrigin property (with port and host)', async t => {
104
136
  t.plan(1)
105
137
  const port = 3000
@@ -120,15 +152,6 @@ test('fastify instance should contains listeningOrigin property (with port and h
120
152
  await fastify.close()
121
153
  })
122
154
 
123
- test('fastify instance should contains listeningOrigin property (no options)', async t => {
124
- t.plan(1)
125
- const fastify = Fastify()
126
- await fastify.listen()
127
- const address = fastify.server.address()
128
- t.same(fastify.listeningOrigin, `http://${address.address}:${address.port}`)
129
- await fastify.close()
130
- })
131
-
132
155
  test('fastify instance should contains listeningOrigin property (unix socket)', { skip: os.platform() === 'win32' }, async t => {
133
156
  const fastify = Fastify()
134
157
  const path = `fastify.${Date.now()}.sock`
@@ -136,3 +159,13 @@ test('fastify instance should contains listeningOrigin property (unix socket)',
136
159
  t.same(fastify.listeningOrigin, path)
137
160
  await fastify.close()
138
161
  })
162
+
163
+ test('fastify instance should contains listeningOrigin property (IPv6)', async t => {
164
+ t.plan(1)
165
+ const port = 3000
166
+ const host = '::1'
167
+ const fastify = Fastify()
168
+ await fastify.listen({ port, host })
169
+ t.same(fastify.listeningOrigin, `http://[::1]:${port}`)
170
+ await fastify.close()
171
+ })
@@ -458,14 +458,13 @@ test('should handle errors in builder-style injection correctly', async (t) => {
458
458
  })
459
459
 
460
460
  test('Should not throw on access to routeConfig frameworkErrors handler - FST_ERR_BAD_URL', t => {
461
- t.plan(6)
461
+ t.plan(5)
462
462
 
463
463
  const fastify = Fastify({
464
464
  frameworkErrors: function (err, req, res) {
465
465
  t.ok(typeof req.id === 'string')
466
466
  t.ok(req.raw instanceof Readable)
467
467
  t.same(req.routerPath, undefined)
468
- t.same(req.routeConfig, undefined)
469
468
  res.send(`${err.message} - ${err.code}`)
470
469
  }
471
470
  })