fastify 5.3.2 → 5.4.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 (103) hide show
  1. package/README.md +2 -0
  2. package/build/build-validation.js +2 -1
  3. package/docs/Guides/Delay-Accepting-Requests.md +3 -3
  4. package/docs/Guides/Ecosystem.md +16 -7
  5. package/docs/Guides/Serverless.md +28 -69
  6. package/docs/Reference/ContentTypeParser.md +1 -1
  7. package/docs/Reference/Errors.md +2 -4
  8. package/docs/Reference/Hooks.md +14 -14
  9. package/docs/Reference/Logging.md +3 -3
  10. package/docs/Reference/Middleware.md +1 -1
  11. package/docs/Reference/Reply.md +8 -8
  12. package/docs/Reference/Request.md +1 -1
  13. package/docs/Reference/Routes.md +3 -3
  14. package/docs/Reference/Server.md +40 -10
  15. package/docs/Reference/Validation-and-Serialization.md +1 -1
  16. package/eslint.config.js +17 -9
  17. package/fastify.d.ts +2 -1
  18. package/fastify.js +20 -4
  19. package/lib/configValidator.js +1 -1
  20. package/lib/decorate.js +2 -2
  21. package/lib/errors.js +6 -8
  22. package/lib/logger-factory.js +1 -1
  23. package/lib/logger-pino.js +2 -2
  24. package/lib/pluginOverride.js +3 -1
  25. package/lib/reply.js +9 -13
  26. package/lib/request.js +4 -11
  27. package/lib/server.js +30 -51
  28. package/lib/symbols.js +1 -0
  29. package/lib/warnings.js +8 -0
  30. package/package.json +11 -7
  31. package/test/404s.test.js +226 -325
  32. package/test/allow-unsafe-regex.test.js +19 -48
  33. package/test/als.test.js +28 -40
  34. package/test/async-await.test.js +11 -2
  35. package/test/body-limit.test.js +41 -65
  36. package/test/build-certificate.js +1 -1
  37. package/test/close-pipelining.test.js +5 -4
  38. package/test/custom-parser-async.test.js +17 -22
  39. package/test/decorator-namespace.test._js_ +3 -4
  40. package/test/decorator.test.js +422 -341
  41. package/test/diagnostics-channel/async-delay-request.test.js +7 -16
  42. package/test/diagnostics-channel/sync-delay-request.test.js +7 -16
  43. package/test/helper.js +108 -70
  44. package/test/hooks-async.test.js +248 -218
  45. package/test/hooks.on-listen.test.js +255 -239
  46. package/test/hooks.on-ready.test.js +110 -92
  47. package/test/hooks.test.js +910 -769
  48. package/test/http-methods/lock.test.js +31 -31
  49. package/test/http-methods/mkcol.test.js +5 -9
  50. package/test/http-methods/proppatch.test.js +23 -29
  51. package/test/http-methods/report.test.js +44 -69
  52. package/test/http-methods/search.test.js +67 -82
  53. package/test/http2/closing.test.js +38 -20
  54. package/test/http2/secure-with-fallback.test.js +28 -27
  55. package/test/https/https.test.js +56 -53
  56. package/test/inject.test.js +114 -97
  57. package/test/input-validation.js +63 -53
  58. package/test/internals/errors.test.js +0 -10
  59. package/test/internals/handle-request.test.js +49 -66
  60. package/test/internals/hooks.test.js +17 -0
  61. package/test/issue-4959.test.js +14 -5
  62. package/test/listen.4.test.js +31 -43
  63. package/test/logger/response.test.js +19 -20
  64. package/test/nullable-validation.test.js +33 -46
  65. package/test/options.error-handler.test.js +1 -1
  66. package/test/options.test.js +1 -1
  67. package/test/output-validation.test.js +49 -72
  68. package/test/patch.error-handler.test.js +1 -1
  69. package/test/patch.test.js +1 -1
  70. package/test/plugin.1.test.js +71 -60
  71. package/test/plugin.2.test.js +104 -86
  72. package/test/plugin.3.test.js +56 -35
  73. package/test/plugin.4.test.js +124 -119
  74. package/test/promises.test.js +36 -30
  75. package/test/proto-poisoning.test.js +78 -97
  76. package/test/put.error-handler.test.js +1 -1
  77. package/test/put.test.js +1 -1
  78. package/test/reply-error.test.js +169 -148
  79. package/test/reply-trailers.test.js +119 -108
  80. package/test/request-error.test.js +0 -46
  81. package/test/route-hooks.test.js +112 -92
  82. package/test/route-prefix.test.js +194 -133
  83. package/test/schema-feature.test.js +309 -238
  84. package/test/schema-serialization.test.js +177 -154
  85. package/test/schema-special-usage.test.js +165 -132
  86. package/test/schema-validation.test.js +278 -199
  87. package/test/set-error-handler.test.js +58 -1
  88. package/test/skip-reply-send.test.js +64 -69
  89. package/test/stream.1.test.js +30 -27
  90. package/test/stream.2.test.js +20 -10
  91. package/test/stream.3.test.js +37 -31
  92. package/test/trust-proxy.test.js +32 -58
  93. package/test/types/errors.test-d.ts +0 -1
  94. package/test/types/fastify.test-d.ts +3 -0
  95. package/test/types/plugin.test-d.ts +1 -1
  96. package/test/types/register.test-d.ts +1 -1
  97. package/test/types/request.test-d.ts +1 -0
  98. package/test/url-rewriting.test.js +45 -62
  99. package/test/use-semicolon-delimiter.test.js +1 -1
  100. package/types/errors.d.ts +0 -1
  101. package/types/request.d.ts +1 -0
  102. package/.taprc +0 -7
  103. package/test/http2/missing-http2-module.test.js +0 -17
@@ -45,6 +45,7 @@ describes the properties available in that options object.
45
45
  - [`clientErrorHandler`](#clienterrorhandler)
46
46
  - [`rewriteUrl`](#rewriteurl)
47
47
  - [`useSemicolonDelimiter`](#usesemicolondelimiter)
48
+ - [`allowErrorHandlerOverride`](#allowerrorhandleroverride)
48
49
  - [Instance](#instance)
49
50
  - [Server Methods](#server-methods)
50
51
  - [server](#server)
@@ -193,7 +194,7 @@ to understand the effect of this option. This option only applies when HTTP/1.1
193
194
  is in use. Also, when `serverFactory` option is specified, this option is
194
195
  ignored.
195
196
 
196
- > 🛈 Note:
197
+ > ℹ️ Note:
197
198
  > At the time of writing, only node >= v16.10.0 supports this option.
198
199
 
199
200
  ### `requestTimeout`
@@ -211,7 +212,7 @@ It must be set to a non-zero value (e.g. 120 seconds) to protect against potenti
211
212
  Denial-of-Service attacks in case the server is deployed without a reverse proxy
212
213
  in front.
213
214
 
214
- > 🛈 Note:
215
+ > ℹ️ Note:
215
216
  > At the time of writing, only node >= v14.11.0 supports this option
216
217
 
217
218
  ### `ignoreTrailingSlash`
@@ -529,7 +530,7 @@ Especially in distributed systems, you may want to override the default ID
529
530
  generation behavior as shown below. For generating `UUID`s you may want to check
530
531
  out [hyperid](https://github.com/mcollina/hyperid).
531
532
 
532
- > 🛈 Note:
533
+ > ℹ️ Note:
533
534
  > `genReqId` will be not called if the header set in
534
535
  > <code>[requestIdHeader](#requestidheader)</code> is available (defaults to
535
536
  > 'request-id').
@@ -581,7 +582,7 @@ fastify.get('/', (request, reply) => {
581
582
  })
582
583
  ```
583
584
 
584
- > 🛈 Note:
585
+ > ℹ️ Note:
585
586
  > If a request contains multiple `x-forwarded-host` or `x-forwarded-proto`
586
587
  > headers, it is only the last one that is used to derive `request.hostname`
587
588
  > and `request.protocol`.
@@ -736,7 +737,7 @@ Fastify provides default error handlers for the most common use cases. It is
736
737
  possible to override one or more of those handlers with custom code using this
737
738
  option.
738
739
 
739
- > 🛈 Note:
740
+ > ℹ️ Note:
740
741
  > Only `FST_ERR_BAD_URL` and `FST_ERR_ASYNC_CONSTRAINT` are implemented at present.
741
742
 
742
743
  ```js
@@ -789,7 +790,7 @@ function defaultClientErrorHandler (err, socket) {
789
790
  }
790
791
  ```
791
792
 
792
- > 🛈 Note:
793
+ > ℹ️ Note:
793
794
  > `clientErrorHandler` operates with raw sockets. The handler is expected to
794
795
  > return a properly formed HTTP response that includes a status line, HTTP headers
795
796
  > and a message body. Before attempting to write the socket, the handler should
@@ -866,6 +867,29 @@ fastify.get('/dev', async (request, reply) => {
866
867
  })
867
868
  ```
868
869
 
870
+ ### `allowErrorHandlerOverride`
871
+ <a id="allow-error-handler-override"></a>
872
+
873
+ * **Default:** `true`
874
+
875
+ > ⚠ **Warning:** This option will be set to `false` by default
876
+ > in the next major release.
877
+
878
+ When set to `false`, it prevents `setErrorHandler` from being called
879
+ multiple times within the same scope, ensuring that the previous error
880
+ handler is not unintentionally overridden.
881
+
882
+ #### Example of incorrect usage:
883
+
884
+ ```js
885
+ app.setErrorHandler(function freeSomeResources () {
886
+ // Never executed, memory leaks
887
+ })
888
+
889
+ app.setErrorHandler(function anotherErrorHandler () {
890
+ // Overrides the previous handler
891
+ })
892
+ ```
869
893
 
870
894
  ## Instance
871
895
 
@@ -1357,7 +1381,7 @@ Set the schema error formatter for all routes. See
1357
1381
  Set the schema serializer compiler for all routes. See
1358
1382
  [#schema-serializer](./Validation-and-Serialization.md#schema-serializer).
1359
1383
 
1360
- > 🛈 Note:
1384
+ > ℹ️ Note:
1361
1385
  > [`setReplySerializer`](#set-reply-serializer) has priority if set!
1362
1386
 
1363
1387
  #### validatorCompiler
@@ -1490,7 +1514,7 @@ lifecycle](./Lifecycle.md#lifecycle). *async-await* is supported as well.
1490
1514
  You can also register [`preValidation`](./Hooks.md#route-hooks) and
1491
1515
  [`preHandler`](./Hooks.md#route-hooks) hooks for the 404 handler.
1492
1516
 
1493
- > 🛈 Note:
1517
+ > ℹ️ Note:
1494
1518
  > The `preValidation` hook registered using this method will run for a
1495
1519
  > route that Fastify does not recognize and **not** when a route handler manually
1496
1520
  > calls [`reply.callNotFound`](./Reply.md#call-not-found). In which case, only
@@ -1526,10 +1550,10 @@ plugins are registered. If you would like to augment the behavior of the default
1526
1550
  arguments `fastify.setNotFoundHandler()` within the context of these registered
1527
1551
  plugins.
1528
1552
 
1529
- > 🛈 Note:
1553
+ > ℹ️ Note:
1530
1554
  > Some config properties from the request object will be
1531
1555
  > undefined inside the custom not found handler. E.g.:
1532
- > `request.routerPath`, `routerMethod` and `context.config`.
1556
+ > `request.routeOptions.url`, `routeOptions.method` and `routeOptions.config`.
1533
1557
  > This method design goal is to allow calling the common not found route.
1534
1558
  > To return a per-route customized 404 response, you can do it in
1535
1559
  > the response itself.
@@ -1574,6 +1598,12 @@ if (statusCode >= 500) {
1574
1598
  log.error(error)
1575
1599
  }
1576
1600
  ```
1601
+
1602
+ > ⚠ Warning:
1603
+ > Avoid calling setErrorHandler multiple times in the same scope.
1604
+ > See [`allowErrorHandlerOverride`](#allowerrorhandleroverride).
1605
+
1606
+
1577
1607
  #### setChildLoggerFactory
1578
1608
  <a id="set-child-logger-factory"></a>
1579
1609
 
@@ -432,7 +432,7 @@ fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
432
432
  return ajv.compile(schema)
433
433
  })
434
434
  ```
435
- > 🛈 Note: When using a custom validator instance, add schemas to the validator
435
+ > ℹ️ Note: When using a custom validator instance, add schemas to the validator
436
436
  > instead of Fastify. Fastify's `addSchema` method will not recognize the custom
437
437
  > validator.
438
438
 
package/eslint.config.js CHANGED
@@ -1,11 +1,19 @@
1
1
  'use strict'
2
+ const neostandard = require('neostandard')
2
3
 
3
- module.exports = require('neostandard')({
4
- ignores: [
5
- 'lib/configValidator.js',
6
- 'lib/error-serializer.js',
7
- 'test/same-shape.test.js',
8
- 'test/types/import.js'
9
- ],
10
- ts: true
11
- })
4
+ module.exports = [
5
+ ...neostandard({
6
+ ignores: [
7
+ 'lib/configValidator.js',
8
+ 'lib/error-serializer.js',
9
+ 'test/same-shape.test.js',
10
+ 'test/types/import.js'
11
+ ],
12
+ ts: true
13
+ }),
14
+ {
15
+ rules: {
16
+ 'comma-dangle': ['error', 'never']
17
+ }
18
+ }
19
+ ]
package/fastify.d.ts CHANGED
@@ -157,7 +157,8 @@ declare namespace fastify {
157
157
  * listener to error events emitted by client connections
158
158
  */
159
159
  clientErrorHandler?: (error: ConnectionError, socket: Socket) => void,
160
- childLoggerFactory?: FastifyChildLoggerFactory
160
+ childLoggerFactory?: FastifyChildLoggerFactory,
161
+ allowErrorHandlerOverride?: boolean
161
162
  }
162
163
 
163
164
  /**
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '5.3.2'
3
+ const VERSION = '5.4.0'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('node:http')
@@ -31,7 +31,8 @@ const {
31
31
  kErrorHandler,
32
32
  kKeepAliveConnections,
33
33
  kChildLoggerFactory,
34
- kGenReqId
34
+ kGenReqId,
35
+ kErrorHandlerAlreadySet
35
36
  } = require('./lib/symbols.js')
36
37
 
37
38
  const { createServer } = require('./lib/server')
@@ -72,10 +73,12 @@ const {
72
73
  FST_ERR_ROUTE_REWRITE_NOT_STR,
73
74
  FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN,
74
75
  FST_ERR_ERROR_HANDLER_NOT_FN,
76
+ FST_ERR_ERROR_HANDLER_ALREADY_SET,
75
77
  FST_ERR_ROUTE_METHOD_INVALID
76
78
  } = errorCodes
77
79
 
78
80
  const { buildErrorHandler } = require('./lib/error-handler.js')
81
+ const { FSTWRN004 } = require('./lib/warnings.js')
79
82
 
80
83
  const initChannel = diagnostics.channel('fastify.initialization')
81
84
 
@@ -149,6 +152,7 @@ function fastify (options) {
149
152
  options.disableRequestLogging = disableRequestLogging
150
153
  options.ajv = ajvOptions
151
154
  options.clientErrorHandler = options.clientErrorHandler || defaultClientErrorHandler
155
+ options.allowErrorHandlerOverride = options.allowErrorHandlerOverride ?? defaultInitOptions.allowErrorHandlerOverride
152
156
 
153
157
  const initialConfig = getSecuredInitialConfig(options)
154
158
 
@@ -237,6 +241,7 @@ function fastify (options) {
237
241
  [kSchemaController]: schemaController,
238
242
  [kSchemaErrorFormatter]: null,
239
243
  [kErrorHandler]: buildErrorHandler(),
244
+ [kErrorHandlerAlreadySet]: false,
240
245
  [kChildLoggerFactory]: defaultChildLoggerFactory,
241
246
  [kReplySerializerDefault]: null,
242
247
  [kContentTypeParser]: new ContentTypeParser(
@@ -681,8 +686,12 @@ function fastify (options) {
681
686
  this[kHooks].add(name, fn)
682
687
  } else {
683
688
  this.after((err, done) => {
684
- _addHook.call(this, name, fn)
685
- done(err)
689
+ try {
690
+ _addHook.call(this, name, fn)
691
+ done(err)
692
+ } catch (err) {
693
+ done(err)
694
+ }
686
695
  })
687
696
  }
688
697
  return this
@@ -854,6 +863,13 @@ function fastify (options) {
854
863
  throw new FST_ERR_ERROR_HANDLER_NOT_FN()
855
864
  }
856
865
 
866
+ if (!options.allowErrorHandlerOverride && this[kErrorHandlerAlreadySet]) {
867
+ throw new FST_ERR_ERROR_HANDLER_ALREADY_SET()
868
+ } else if (this[kErrorHandlerAlreadySet]) {
869
+ FSTWRN004("To disable this behavior, set 'allowErrorHandlerOverride' to false or ignore this message. For more information, visit: https://fastify.dev/docs/latest/Reference/Server/#allowerrorhandleroverride")
870
+ }
871
+
872
+ this[kErrorHandlerAlreadySet] = true
857
873
  this[kErrorHandler] = buildErrorHandler(this[kErrorHandler], func.bind(this))
858
874
  return this
859
875
  }
@@ -1100,5 +1100,5 @@ return errors === 0;
1100
1100
  }
1101
1101
 
1102
1102
 
1103
- module.exports.defaultInitOptions = {"connectionTimeout":0,"keepAliveTimeout":72000,"maxRequestsPerSocket":0,"requestTimeout":0,"bodyLimit":1048576,"caseSensitive":true,"allowUnsafeRegex":false,"disableRequestLogging":false,"ignoreTrailingSlash":false,"ignoreDuplicateSlashes":false,"maxParamLength":100,"onProtoPoisoning":"error","onConstructorPoisoning":"error","pluginTimeout":10000,"requestIdHeader":false,"requestIdLogLabel":"reqId","http2SessionTimeout":72000,"exposeHeadRoutes":true,"useSemicolonDelimiter":false}
1103
+ module.exports.defaultInitOptions = {"connectionTimeout":0,"keepAliveTimeout":72000,"maxRequestsPerSocket":0,"requestTimeout":0,"bodyLimit":1048576,"caseSensitive":true,"allowUnsafeRegex":false,"disableRequestLogging":false,"ignoreTrailingSlash":false,"ignoreDuplicateSlashes":false,"maxParamLength":100,"onProtoPoisoning":"error","onConstructorPoisoning":"error","pluginTimeout":10000,"requestIdHeader":false,"requestIdLogLabel":"reqId","http2SessionTimeout":72000,"exposeHeadRoutes":true,"useSemicolonDelimiter":false,"allowErrorHandlerOverride":true}
1104
1104
  /* c8 ignore stop */
package/lib/decorate.js CHANGED
@@ -4,7 +4,7 @@ const {
4
4
  kReply,
5
5
  kRequest,
6
6
  kState,
7
- kHasBeenDecorated,
7
+ kHasBeenDecorated
8
8
  } = require('./symbols.js')
9
9
 
10
10
  const {
@@ -13,7 +13,7 @@ const {
13
13
  FST_ERR_DEC_AFTER_START,
14
14
  FST_ERR_DEC_REFERENCE_TYPE,
15
15
  FST_ERR_DEC_DEPENDENCY_INVALID_TYPE,
16
- FST_ERR_DEC_UNDECLARED,
16
+ FST_ERR_DEC_UNDECLARED
17
17
  } = require('./errors')
18
18
 
19
19
  function decorate (instance, name, fn, dependencies) {
package/lib/errors.js CHANGED
@@ -64,6 +64,12 @@ const codes = {
64
64
  500,
65
65
  TypeError
66
66
  ),
67
+ FST_ERR_ERROR_HANDLER_ALREADY_SET: createError(
68
+ 'FST_ERR_ERROR_HANDLER_ALREADY_SET',
69
+ "Error Handler already set in this scope. Set 'allowErrorHandlerOverride: true' to allow overriding.",
70
+ 500,
71
+ TypeError
72
+ ),
67
73
 
68
74
  /**
69
75
  * ContentTypeParser
@@ -328,14 +334,6 @@ const codes = {
328
334
  'response schemas should be nested under a valid status code, e.g { 2xx: { type: "object" } }'
329
335
  ),
330
336
 
331
- /**
332
- * http2
333
- */
334
- FST_ERR_HTTP2_INVALID_VERSION: createError(
335
- 'FST_ERR_HTTP2_INVALID_VERSION',
336
- 'HTTP2 is available only from node >= 8.8.1'
337
- ),
338
-
339
337
  /**
340
338
  * initialConfig
341
339
  */
@@ -132,5 +132,5 @@ module.exports = {
132
132
  defaultChildLoggerFactory,
133
133
  createLogger,
134
134
  validateLogger,
135
- now,
135
+ now
136
136
  }
@@ -9,7 +9,7 @@
9
9
  const pino = require('pino')
10
10
  const { serializersSym } = pino.symbols
11
11
  const {
12
- FST_ERR_LOG_INVALID_DESTINATION,
12
+ FST_ERR_LOG_INVALID_DESTINATION
13
13
  } = require('./errors')
14
14
 
15
15
  function createPinoLogger (opts) {
@@ -64,5 +64,5 @@ const serializers = {
64
64
 
65
65
  module.exports = {
66
66
  serializers,
67
- createPinoLogger,
67
+ createPinoLogger
68
68
  }
@@ -12,7 +12,8 @@ const {
12
12
  kReply,
13
13
  kRequest,
14
14
  kFourOhFour,
15
- kPluginNameChain
15
+ kPluginNameChain,
16
+ kErrorHandlerAlreadySet
16
17
  } = require('./symbols.js')
17
18
 
18
19
  const Reply = require('./reply')
@@ -57,6 +58,7 @@ module.exports = function override (old, fn, opts) {
57
58
  // Track the plugin chain since the root instance.
58
59
  // When an non-encapsulated plugin is added, the chain will be updated.
59
60
  instance[kPluginNameChain] = [fnName]
61
+ instance[kErrorHandlerAlreadySet] = false
60
62
 
61
63
  if (instance[kLogSerializers] || opts.logSerializers) {
62
64
  instance[kLogSerializers] = Object.assign(Object.create(instance[kLogSerializers]), opts.logSerializers)
package/lib/reply.js CHANGED
@@ -22,7 +22,7 @@ const {
22
22
  kReplyCacheSerializeFns,
23
23
  kSchemaController,
24
24
  kOptions,
25
- kRouteContext,
25
+ kRouteContext
26
26
  } = require('./symbols.js')
27
27
  const {
28
28
  onSendHookRunner,
@@ -53,7 +53,7 @@ const {
53
53
  FST_ERR_BAD_TRAILER_VALUE,
54
54
  FST_ERR_MISSING_SERIALIZATION_FN,
55
55
  FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN,
56
- FST_ERR_DEC_UNDECLARED,
56
+ FST_ERR_DEC_UNDECLARED
57
57
  } = require('./errors')
58
58
  const decorators = require('./decorate')
59
59
 
@@ -215,12 +215,8 @@ Reply.prototype.send = function (payload) {
215
215
 
216
216
  Reply.prototype.getHeader = function (key) {
217
217
  key = key.toLowerCase()
218
- const res = this.raw
219
- let value = this[kReplyHeaders][key]
220
- if (value === undefined && res.hasHeader(key)) {
221
- value = res.getHeader(key)
222
- }
223
- return value
218
+ const value = this[kReplyHeaders][key]
219
+ return value !== undefined ? value : this.raw.getHeader(key)
224
220
  }
225
221
 
226
222
  Reply.prototype.getHeaders = function () {
@@ -315,12 +311,12 @@ Reply.prototype.removeTrailer = function (key) {
315
311
  }
316
312
 
317
313
  Reply.prototype.code = function (code) {
318
- const intValue = Number(code)
319
- if (isNaN(intValue) || intValue < 100 || intValue > 599) {
314
+ const statusCode = +code
315
+ if (!(statusCode >= 100 && statusCode <= 599)) {
320
316
  throw new FST_ERR_BAD_STATUS_CODE(code || String(code))
321
317
  }
322
318
 
323
- this.raw.statusCode = intValue
319
+ this.raw.statusCode = statusCode
324
320
  this[kReplyHasStatusCode] = true
325
321
  return this
326
322
  }
@@ -500,11 +496,11 @@ function preSerializationHook (reply, payload) {
500
496
  preSerializationHookEnd
501
497
  )
502
498
  } else {
503
- preSerializationHookEnd(null, reply.request, reply, payload)
499
+ preSerializationHookEnd(null, undefined, reply, payload)
504
500
  }
505
501
  }
506
502
 
507
- function preSerializationHookEnd (err, request, reply, payload) {
503
+ function preSerializationHookEnd (err, _request, reply, payload) {
508
504
  if (err != null) {
509
505
  onErrorHook(reply, err)
510
506
  return
package/lib/request.js CHANGED
@@ -11,7 +11,7 @@ const {
11
11
  kOptions,
12
12
  kRequestCacheValidateFns,
13
13
  kRouteContext,
14
- kRequestOriginalUrl,
14
+ kRequestOriginalUrl
15
15
  } = require('./symbols')
16
16
  const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION, FST_ERR_DEC_UNDECLARED } = require('./errors')
17
17
  const decorators = require('./decorate')
@@ -188,19 +188,12 @@ Object.defineProperties(Request.prototype, {
188
188
  exposeHeadRoute: context.exposeHeadRoute,
189
189
  prefixTrailingSlash: context.prefixTrailingSlash,
190
190
  handler: context.handler,
191
+ config: context.config,
192
+ schema: context.schema,
191
193
  version
192
194
  }
193
195
 
194
- Object.defineProperties(options, {
195
- config: {
196
- get: () => context.config
197
- },
198
- schema: {
199
- get: () => context.schema
200
- }
201
- })
202
-
203
- return Object.freeze(options)
196
+ return options
204
197
  }
205
198
  },
206
199
  is404: {
package/lib/server.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const http = require('node:http')
4
4
  const https = require('node:https')
5
+ const http2 = require('node:http2')
5
6
  const dns = require('node:dns')
6
7
  const os = require('node:os')
7
8
 
@@ -9,7 +10,6 @@ const { kState, kOptions, kServerBindings } = require('./symbols')
9
10
  const { FSTWRN003 } = require('./warnings')
10
11
  const { onListenHookRunner } = require('./hooks')
11
12
  const {
12
- FST_ERR_HTTP2_INVALID_VERSION,
13
13
  FST_ERR_REOPENED_CLOSE_SERVER,
14
14
  FST_ERR_REOPENED_SERVER,
15
15
  FST_ERR_LISTEN_OPTIONS_INVALID
@@ -98,7 +98,6 @@ function createServer (options, httpHandler) {
98
98
 
99
99
  if (cb === undefined) {
100
100
  const listening = listenPromise.call(this, server, listenOptions)
101
- /* istanbul ignore else */
102
101
  return listening.then(address => {
103
102
  return new Promise((resolve, reject) => {
104
103
  if (host === 'localhost') {
@@ -192,7 +191,6 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
192
191
  // to the secondary servers. It is valid only when the user is
193
192
  // listening on localhost
194
193
  const originUnref = mainServer.unref
195
- /* c8 ignore next 4 */
196
194
  mainServer.unref = function () {
197
195
  originUnref.call(mainServer)
198
196
  mainServer.emit('unref')
@@ -218,7 +216,8 @@ function listenCallback (server, listenOptions) {
218
216
 
219
217
  if (this[kState].listening && this[kState].closing) {
220
218
  return listenOptions.cb(new FST_ERR_REOPENED_CLOSE_SERVER(), null)
221
- } else if (this[kState].listening) {
219
+ }
220
+ if (this[kState].listening) {
222
221
  return listenOptions.cb(new FST_ERR_REOPENED_SERVER(), null)
223
222
  }
224
223
 
@@ -234,7 +233,8 @@ function listenCallback (server, listenOptions) {
234
233
  function listenPromise (server, listenOptions) {
235
234
  if (this[kState].listening && this[kState].closing) {
236
235
  return Promise.reject(new FST_ERR_REOPENED_CLOSE_SERVER())
237
- } else if (this[kState].listening) {
236
+ }
237
+ if (this[kState].listening) {
238
238
  return Promise.reject(new FST_ERR_REOPENED_SERVER())
239
239
  }
240
240
 
@@ -272,41 +272,38 @@ function listenPromise (server, listenOptions) {
272
272
  }
273
273
 
274
274
  function getServerInstance (options, httpHandler) {
275
- let server = null
276
- // node@20 do not accepts options as boolean
277
- // we need to provide proper https option
278
- const httpsOptions = options.https === true ? {} : options.https
279
275
  if (options.serverFactory) {
280
- server = options.serverFactory(httpHandler, options)
281
- } else if (options.http2) {
282
- if (typeof httpsOptions === 'object') {
283
- server = http2().createSecureServer(httpsOptions, httpHandler)
284
- } else {
285
- server = http2().createServer(httpHandler)
286
- }
287
- server.on('session', sessionTimeout(options.http2SessionTimeout))
288
- } else {
289
- // this is http1
290
- if (httpsOptions) {
291
- server = https.createServer(httpsOptions, httpHandler)
292
- } else {
293
- server = http.createServer(options.http, httpHandler)
294
- }
295
- server.keepAliveTimeout = options.keepAliveTimeout
296
- server.requestTimeout = options.requestTimeout
297
- // we treat zero as null
298
- // and null is the default setting from nodejs
299
- // so we do not pass the option to server
300
- if (options.maxRequestsPerSocket > 0) {
301
- server.maxRequestsPerSocket = options.maxRequestsPerSocket
302
- }
276
+ // User provided server instance
277
+ return options.serverFactory(httpHandler, options)
303
278
  }
304
279
 
305
- if (!options.serverFactory) {
280
+ // We have accepted true as a valid way to init https but node requires an options obj
281
+ const httpsOptions = options.https === true ? {} : options.https
282
+
283
+ if (options.http2) {
284
+ const server = typeof httpsOptions === 'object' ? http2.createSecureServer(httpsOptions, httpHandler) : http2.createServer(options.http, httpHandler)
285
+ server.on('session', (session) => session.setTimeout(options.http2SessionTimeout, function closeSession () {
286
+ this.close()
287
+ }))
288
+
306
289
  server.setTimeout(options.connectionTimeout)
290
+
291
+ return server
307
292
  }
293
+
294
+ // HTTP1 server instance
295
+ const server = httpsOptions ? https.createServer(httpsOptions, httpHandler) : http.createServer(options.http, httpHandler)
296
+ server.keepAliveTimeout = options.keepAliveTimeout
297
+ server.requestTimeout = options.requestTimeout
298
+ server.setTimeout(options.connectionTimeout)
299
+ // We treat zero as null(node default) so we do not pass zero to the server instance
300
+ if (options.maxRequestsPerSocket > 0) {
301
+ server.maxRequestsPerSocket = options.maxRequestsPerSocket
302
+ }
303
+
308
304
  return server
309
305
  }
306
+
310
307
  /**
311
308
  * Inspects the provided `server.address` object and returns a
312
309
  * normalized list of IP address strings. Normalization in this
@@ -355,21 +352,3 @@ function logServerAddress (server, listenTextResolver) {
355
352
  }
356
353
  return addresses[0]
357
354
  }
358
-
359
- function http2 () {
360
- try {
361
- return require('node:http2')
362
- } catch (err) {
363
- throw new FST_ERR_HTTP2_INVALID_VERSION()
364
- }
365
- }
366
-
367
- function sessionTimeout (timeout) {
368
- return function (session) {
369
- session.setTimeout(timeout, close)
370
- }
371
- }
372
-
373
- function close () {
374
- this.close()
375
- }
package/lib/symbols.js CHANGED
@@ -56,6 +56,7 @@ const keys = {
56
56
  // This symbol is only meant to be used for fastify tests and should not be used for any other purpose
57
57
  kTestInternals: Symbol('fastify.testInternals'),
58
58
  kErrorHandler: Symbol('fastify.errorHandler'),
59
+ kErrorHandlerAlreadySet: Symbol('fastify.errorHandlerAlreadySet'),
59
60
  kChildLoggerFactory: Symbol('fastify.childLoggerFactory'),
60
61
  kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
61
62
  kKeepAliveConnections: Symbol('fastify.keepAliveConnections'),
package/lib/warnings.js CHANGED
@@ -25,6 +25,13 @@ const FSTWRN003 = createWarning({
25
25
  unlimited: true
26
26
  })
27
27
 
28
+ const FSTWRN004 = createWarning({
29
+ name: 'FastifyWarning',
30
+ code: 'FSTWRN004',
31
+ message: 'It seems that you are overriding an errorHandler in the same scope, which can lead to subtle bugs.',
32
+ unlimited: true
33
+ })
34
+
28
35
  const FSTSEC001 = createWarning({
29
36
  name: 'FastifySecurity',
30
37
  code: 'FSTSEC001',
@@ -35,5 +42,6 @@ const FSTSEC001 = createWarning({
35
42
  module.exports = {
36
43
  FSTWRN001,
37
44
  FSTWRN003,
45
+ FSTWRN004,
38
46
  FSTSEC001
39
47
  }