fastify 5.3.3 → 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 (70) 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 +9 -5
  5. package/docs/Reference/ContentTypeParser.md +1 -1
  6. package/docs/Reference/Errors.md +2 -2
  7. package/docs/Reference/Hooks.md +14 -14
  8. package/docs/Reference/Logging.md +3 -3
  9. package/docs/Reference/Middleware.md +1 -1
  10. package/docs/Reference/Reply.md +8 -8
  11. package/docs/Reference/Request.md +1 -1
  12. package/docs/Reference/Routes.md +3 -3
  13. package/docs/Reference/Server.md +35 -21
  14. package/docs/Reference/Validation-and-Serialization.md +1 -1
  15. package/fastify.d.ts +2 -1
  16. package/fastify.js +14 -2
  17. package/lib/configValidator.js +1 -1
  18. package/lib/errors.js +6 -0
  19. package/lib/pluginOverride.js +3 -1
  20. package/lib/reply.js +7 -11
  21. package/lib/request.js +3 -10
  22. package/lib/symbols.js +1 -0
  23. package/lib/warnings.js +8 -0
  24. package/package.json +8 -4
  25. package/test/404s.test.js +226 -325
  26. package/test/allow-unsafe-regex.test.js +19 -48
  27. package/test/als.test.js +28 -40
  28. package/test/async-await.test.js +11 -2
  29. package/test/body-limit.test.js +41 -65
  30. package/test/build-certificate.js +1 -1
  31. package/test/custom-parser-async.test.js +17 -22
  32. package/test/decorator-namespace.test._js_ +3 -4
  33. package/test/diagnostics-channel/async-delay-request.test.js +7 -16
  34. package/test/diagnostics-channel/sync-delay-request.test.js +7 -16
  35. package/test/helper.js +1 -1
  36. package/test/hooks-async.test.js +248 -218
  37. package/test/hooks.test.js +910 -769
  38. package/test/http-methods/lock.test.js +31 -31
  39. package/test/http-methods/mkcol.test.js +5 -9
  40. package/test/http-methods/proppatch.test.js +23 -29
  41. package/test/http-methods/report.test.js +44 -69
  42. package/test/http-methods/search.test.js +67 -82
  43. package/test/http2/closing.test.js +38 -20
  44. package/test/http2/secure-with-fallback.test.js +28 -27
  45. package/test/https/https.test.js +56 -53
  46. package/test/internals/errors.test.js +1 -1
  47. package/test/internals/handle-request.test.js +49 -66
  48. package/test/issue-4959.test.js +12 -3
  49. package/test/listen.4.test.js +31 -43
  50. package/test/nullable-validation.test.js +33 -46
  51. package/test/output-validation.test.js +24 -26
  52. package/test/plugin.2.test.js +104 -86
  53. package/test/plugin.3.test.js +56 -35
  54. package/test/plugin.4.test.js +124 -119
  55. package/test/proto-poisoning.test.js +78 -97
  56. package/test/request-error.test.js +0 -46
  57. package/test/route-hooks.test.js +112 -92
  58. package/test/route-prefix.test.js +194 -133
  59. package/test/schema-serialization.test.js +177 -154
  60. package/test/schema-special-usage.test.js +165 -132
  61. package/test/schema-validation.test.js +242 -205
  62. package/test/set-error-handler.test.js +58 -1
  63. package/test/skip-reply-send.test.js +64 -69
  64. package/test/trust-proxy.test.js +32 -58
  65. package/test/types/fastify.test-d.ts +3 -0
  66. package/test/types/request.test-d.ts +1 -0
  67. package/test/url-rewriting.test.js +45 -62
  68. package/types/request.d.ts +1 -0
  69. package/.taprc +0 -7
  70. package/.vscode/settings.json +0 -22
package/README.md CHANGED
@@ -306,6 +306,8 @@ listed in alphabetical order.
306
306
  * [__Vincent Le Goff__](https://github.com/zekth)
307
307
  * [__Luciano Mammino__](https://github.com/lmammino),
308
308
  <https://twitter.com/loige>, <https://www.npmjs.com/~lmammino>
309
+ * [__Jean Michelet__](https://github.com/jean-michelet),
310
+ <https://www.npmjs.com/~jean-michelet>
309
311
  * [__KaKa Ng__](https://github.com/climba03003),
310
312
  <https://www.npmjs.com/~climba03003>
311
313
  * [__Luis Orbaiceta__](https://github.com/luisorbaiceta),
@@ -42,7 +42,8 @@ const defaultInitOptions = {
42
42
  requestIdLogLabel: 'reqId',
43
43
  http2SessionTimeout: 72000, // 72 seconds
44
44
  exposeHeadRoutes: true,
45
- useSemicolonDelimiter: false
45
+ useSemicolonDelimiter: false,
46
+ allowErrorHandlerOverride: true // TODO: set to false in v6
46
47
  }
47
48
 
48
49
  const schema = {
@@ -529,9 +529,9 @@ since that was not one of the requests we asked our plugin to filter, it
529
529
  succeeded. That could also be used as a means of informing an interested party
530
530
  whether or not we were ready to serve requests (although `/ping` is more
531
531
  commonly associated with *liveness* checks and that would be the responsibility
532
- of a *readiness* check -- the curious reader can get more info on these terms
533
- [here](https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes))
534
- with the `ready` field. Below is the response to that request:
532
+ of a *readiness* check -- the curious reader can get more info on these
533
+ [terms](https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes))
534
+ here with the `ready` field. Below is the response to that request:
535
535
 
536
536
  ```sh
537
537
  HTTP/1.1 200 OK
@@ -152,6 +152,15 @@ section.
152
152
 
153
153
  #### [Community](#community)
154
154
 
155
+ > ℹ️ Note:
156
+ > Fastify community plugins are part of the broader community efforts,
157
+ > and we are thankful for these contributions. However, they are not
158
+ > maintained by the Fastify team.
159
+ > Use them at your own discretion.
160
+ > If you find malicious code, please
161
+ > [open an issue](https://github.com/fastify/fastify/issues/new/choose) or
162
+ > submit a PR to remove the plugin from the list.
163
+
155
164
  - [`@aaroncadillac/crudify-mongo`](https://github.com/aaroncadillac/crudify-mongo)
156
165
  A simple way to add a crud in your fastify project.
157
166
  - [`@applicazza/fastify-nextjs`](https://github.com/applicazza/fastify-nextjs)
@@ -193,11 +202,6 @@ section.
193
202
  - [`@immobiliarelabs/fastify-metrics`](https://github.com/immobiliare/fastify-metrics)
194
203
  Minimalistic and opinionated plugin that collects usage/process metrics and
195
204
  dispatches to [statsd](https://github.com/statsd/statsd).
196
- - [`@immobiliarelabs/fastify-sentry`](https://github.com/immobiliare/fastify-sentry)
197
- Sentry errors handler that just works! Install, add your DSN and you're good
198
- to go!
199
- A plugin to implement [Lyra](https://github.com/nearform/lyra) search engine
200
- on Fastify
201
205
  - [`@inaiat/fastify-papr`](https://github.com/inaiat/fastify-papr)
202
206
  A plugin to integrate [Papr](https://github.com/plexinc/papr),
203
207
  the MongoDB ORM for TypeScript & MongoDB, with Fastify.
@@ -152,7 +152,7 @@ fastify.addContentTypeParser('text/xml', function (request, payload, done) {
152
152
  })
153
153
  ```
154
154
 
155
- > 🛈 Note: `function(req, done)` and `async function(req)` are
155
+ > ℹ️ Note: `function(req, done)` and `async function(req)` are
156
156
  > still supported but deprecated.
157
157
 
158
158
  #### Body Parser
@@ -97,6 +97,7 @@
97
97
  - [FST_ERR_VALIDATION](#fst_err_validation)
98
98
  - [FST_ERR_LISTEN_OPTIONS_INVALID](#fst_err_listen_options_invalid)
99
99
  - [FST_ERR_ERROR_HANDLER_NOT_FN](#fst_err_error_handler_not_fn)
100
+ - [FST_ERR_ERROR_HANDLER_ALREADY_SET](#fst_err_error_handler_already_set)
100
101
 
101
102
  ### Error Handling In Node.js
102
103
  <a id="error-handling"></a>
@@ -366,5 +367,4 @@ Below is a table with all the error codes used by Fastify.
366
367
  | <a id="fst_err_plugin_invalid_async_handler">FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER</a> | The plugin being registered mixes async and callback styles. | - | [#5141](https://github.com/fastify/fastify/pull/5141) |
367
368
  | <a id="fst_err_validation">FST_ERR_VALIDATION</a> | The Request failed the payload validation. | Check the request payload. | [#4824](https://github.com/fastify/fastify/pull/4824) |
368
369
  | <a id="fst_err_listen_options_invalid">FST_ERR_LISTEN_OPTIONS_INVALID</a> | Invalid listen options. | Check the listen options. | [#4886](https://github.com/fastify/fastify/pull/4886) |
369
- | <a id="fst_err_error_handler_not_fn">FST_ERR_ERROR_HANDLER_NOT_FN</a> | Error Handler must be a function | Provide a function to `setErrorHandler`. | [#5317](https://github.com/fastify/fastify/pull/5317) |
370
-
370
+ | <a id="fst_err_error_handler_not_fn">FST_ERR_ERROR_HANDLER_NOT_FN</a> | Error Handler must be a function | Provide a function to `setErrorHandler`. | [#5317](https://github.com/fastify/fastify/pull/5317) | <a id="fst_err_error_handler_already_set">FST_ERR_ERROR_HANDLER_ALREADY_SET</a> | Error Handler already set in this scope. Set `allowErrorHandlerOverride: true` to allow overriding. | By default, `setErrorHandler` can only be called once per encapsulation context. | [#6097](https://github.com/fastify/fastify/pull/6098) |
@@ -34,7 +34,7 @@ are Request/Reply hooks and application hooks:
34
34
  - [Using Hooks to Inject Custom Properties](#using-hooks-to-inject-custom-properties)
35
35
  - [Diagnostics Channel Hooks](#diagnostics-channel-hooks)
36
36
 
37
- > 🛈 Note: The `done` callback is not available when using `async`/`await` or
37
+ > ℹ️ Note: The `done` callback is not available when using `async`/`await` or
38
38
  > returning a `Promise`. If you do invoke a `done` callback in this situation
39
39
  > unexpected behavior may occur, e.g. duplicate invocation of handlers.
40
40
 
@@ -68,7 +68,7 @@ fastify.addHook('onRequest', async (request, reply) => {
68
68
  })
69
69
  ```
70
70
 
71
- > 🛈 Note: In the [onRequest](#onrequest) hook, `request.body` will always be
71
+ > ℹ️ Note: In the [onRequest](#onrequest) hook, `request.body` will always be
72
72
  > `undefined`, because the body parsing happens before the
73
73
  > [preValidation](#prevalidation) hook.
74
74
 
@@ -98,16 +98,16 @@ fastify.addHook('preParsing', async (request, reply, payload) => {
98
98
  })
99
99
  ```
100
100
 
101
- > 🛈 Note: In the [preParsing](#preparsing) hook, `request.body` will always be
101
+ > ℹ️ Note: In the [preParsing](#preparsing) hook, `request.body` will always be
102
102
  > `undefined`, because the body parsing happens before the
103
103
  > [preValidation](#prevalidation) hook.
104
104
 
105
- > 🛈 Note: You should also add a `receivedEncodedLength` property to the
105
+ > ℹ️ Note: You should also add a `receivedEncodedLength` property to the
106
106
  > returned stream. This property is used to correctly match the request payload
107
107
  > with the `Content-Length` header value. Ideally, this property should be updated
108
108
  > on each received chunk.
109
109
 
110
- > 🛈 Note: The size of the returned stream is checked to not exceed the limit
110
+ > ℹ️ Note: The size of the returned stream is checked to not exceed the limit
111
111
  > set in [`bodyLimit`](./Server.md#bodylimit) option.
112
112
 
113
113
  ### preValidation
@@ -166,7 +166,7 @@ fastify.addHook('preSerialization', async (request, reply, payload) => {
166
166
  })
167
167
  ```
168
168
 
169
- > 🛈 Note: The hook is NOT called if the payload is a `string`, a `Buffer`, a
169
+ > ℹ️ Note: The hook is NOT called if the payload is a `string`, a `Buffer`, a
170
170
  > `stream`, or `null`.
171
171
 
172
172
  ### onError
@@ -196,7 +196,7 @@ user
196
196
  *(Note that the default error handler always sends the error back to the
197
197
  user)*.
198
198
 
199
- > 🛈 Note: Unlike the other hooks, passing an error to the `done` function is not
199
+ > ℹ️ Note: Unlike the other hooks, passing an error to the `done` function is not
200
200
  > supported.
201
201
 
202
202
  ### onSend
@@ -233,7 +233,7 @@ fastify.addHook('onSend', (request, reply, payload, done) => {
233
233
  > to `0`, whereas the `Content-Length` header will not be set if the payload is
234
234
  > `null`.
235
235
 
236
- > 🛈 Note: If you change the payload, you may only change it to a `string`, a
236
+ > ℹ️ Note: If you change the payload, you may only change it to a `string`, a
237
237
  > `Buffer`, a `stream`, a `ReadableStream`, a `Response`, or `null`.
238
238
 
239
239
 
@@ -256,7 +256,7 @@ The `onResponse` hook is executed when a response has been sent, so you will not
256
256
  be able to send more data to the client. It can however be useful for sending
257
257
  data to external services, for example, to gather statistics.
258
258
 
259
- > 🛈 Note: Setting `disableRequestLogging` to `true` will disable any error log
259
+ > ℹ️ Note: Setting `disableRequestLogging` to `true` will disable any error log
260
260
  > inside the `onResponse` hook. In this case use `try - catch` to log errors.
261
261
 
262
262
  ### onTimeout
@@ -298,7 +298,7 @@ The `onRequestAbort` hook is executed when a client closes the connection before
298
298
  the entire request has been processed. Therefore, you will not be able to send
299
299
  data to the client.
300
300
 
301
- > 🛈 Note: Client abort detection is not completely reliable.
301
+ > ℹ️ Note: Client abort detection is not completely reliable.
302
302
  > See: [`Detecting-When-Clients-Abort.md`](../Guides/Detecting-When-Clients-Abort.md)
303
303
 
304
304
  ### Manage Errors from a hook
@@ -452,7 +452,7 @@ fastify.addHook('onListen', async function () {
452
452
  })
453
453
  ```
454
454
 
455
- > 🛈 Note: This hook will not run when the server is started using
455
+ > ℹ️ Note: This hook will not run when the server is started using
456
456
  > fastify.inject()` or `fastify.ready()`.
457
457
 
458
458
  ### onClose
@@ -576,7 +576,7 @@ This hook can be useful if you are developing a plugin that needs to know when a
576
576
  plugin context is formed, and you want to operate in that specific context, thus
577
577
  this hook is encapsulated.
578
578
 
579
- > 🛈 Note: This hook will not be called if a plugin is wrapped inside
579
+ > ℹ️ Note: This hook will not be called if a plugin is wrapped inside
580
580
  > [`fastify-plugin`](https://github.com/fastify/fastify-plugin).
581
581
  ```js
582
582
  fastify.decorate('data', [])
@@ -774,7 +774,7 @@ fastify.route({
774
774
  })
775
775
  ```
776
776
 
777
- > 🛈 Note: Both options also accept an array of functions.
777
+ > ℹ️ Note: Both options also accept an array of functions.
778
778
 
779
779
  ## Using Hooks to Inject Custom Properties
780
780
  <a id="using-hooks-to-inject-custom-properties"></a>
@@ -861,7 +861,7 @@ channel.subscribe(function ({ fastify }) {
861
861
  })
862
862
  ```
863
863
 
864
- > 🛈 Note: The TracingChannel class API is currently experimental and may undergo
864
+ > ℹ️ Note: The TracingChannel class API is currently experimental and may undergo
865
865
  > breaking changes even in semver-patch releases of Node.js.
866
866
 
867
867
  Five other events are published on a per-request basis following the
@@ -157,7 +157,7 @@ const fastify = require('fastify')({
157
157
  });
158
158
  ```
159
159
 
160
- > 🛈 Note: In some cases, the [`Reply`](./Reply.md) object passed to the `res`
160
+ > ℹ️ Note: In some cases, the [`Reply`](./Reply.md) object passed to the `res`
161
161
  > serializer cannot be fully constructed. When writing a custom `res`
162
162
  > serializer, check for the existence of any properties on `reply` aside from
163
163
  > `statusCode`, which is always present. For example, verify the existence of
@@ -184,7 +184,7 @@ const fastify = require('fastify')({
184
184
  });
185
185
  ```
186
186
 
187
- > 🛈 Note: The body cannot be serialized inside a `req` method because the
187
+ > ℹ️ Note: The body cannot be serialized inside a `req` method because the
188
188
  request is serialized when the child logger is created. At that time, the body
189
189
  is not yet parsed.
190
190
 
@@ -199,7 +199,7 @@ app.addHook('preHandler', function (req, reply, done) {
199
199
  })
200
200
  ```
201
201
 
202
- > 🛈 Note: Ensure serializers never throw errors, as this can cause the Node
202
+ > ℹ️ Note: Ensure serializers never throw errors, as this can cause the Node
203
203
  > process to exit. See the
204
204
  > [Pino documentation](https://getpino.io/#/docs/api?id=opt-serializers) for more
205
205
  > information.
@@ -50,7 +50,7 @@ that already has the Fastify [Request](./Request.md#request) and
50
50
  To run middleware under certain paths, pass the path as the first parameter to
51
51
  `use`.
52
52
 
53
- > 🛈 Note: This does not support routes with parameters
53
+ > ℹ️ Note: This does not support routes with parameters
54
54
  > (e.g. `/user/:id/comments`) and wildcards are not supported in multiple paths.
55
55
 
56
56
  ```js
@@ -151,7 +151,7 @@ fastify.get('/', async function (req, rep) {
151
151
  Sets a response header. If the value is omitted or undefined, it is coerced to
152
152
  `''`.
153
153
 
154
- > 🛈 Note: The header's value must be properly encoded using
154
+ > ℹ️ Note: The header's value must be properly encoded using
155
155
  > [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
156
156
  > or similar modules such as
157
157
  > [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid characters
@@ -260,10 +260,10 @@ requires heavy resources to be sent after the `data`, for example,
260
260
  `Server-Timing` and `Etag`. It can ensure the client receives the response data
261
261
  as soon as possible.
262
262
 
263
- > 🛈 Note: The header `Transfer-Encoding: chunked` will be added once you use
263
+ > ℹ️ Note: The header `Transfer-Encoding: chunked` will be added once you use
264
264
  > the trailer. It is a hard requirement for using trailer in Node.js.
265
265
 
266
- > 🛈 Note: Any error passed to `done` callback will be ignored. If you interested
266
+ > ℹ️ Note: Any error passed to `done` callback will be ignored. If you interested
267
267
  > in the error, you can turn on `debug` level logging.*
268
268
 
269
269
  ```js
@@ -279,14 +279,14 @@ const { createHash } = require('node:crypto')
279
279
  reply.trailer('content-md5', function(reply, payload, done) {
280
280
  const hash = createHash('md5')
281
281
  hash.update(payload)
282
- done(null, hash.disgest('hex'))
282
+ done(null, hash.digest('hex'))
283
283
  })
284
284
 
285
285
  // when you prefer async-await
286
286
  reply.trailer('content-md5', async function(reply, payload) {
287
287
  const hash = createHash('md5')
288
288
  hash.update(payload)
289
- return hash.disgest('hex')
289
+ return hash.digest('hex')
290
290
  })
291
291
  ```
292
292
 
@@ -314,7 +314,7 @@ reply.getTrailer('server-timing') // undefined
314
314
  Redirects a request to the specified URL, the status code is optional, default
315
315
  to `302` (if status code is not already set by calling `code`).
316
316
 
317
- > 🛈 Note: The input URL must be properly encoded using
317
+ > ℹ️ Note: The input URL must be properly encoded using
318
318
  > [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
319
319
  > or similar modules such as
320
320
  > [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid URLs will
@@ -823,7 +823,7 @@ automatically create an error structured as the following:
823
823
  You can add custom properties to the Error object, such as `headers`, that will
824
824
  be used to enhance the HTTP response.
825
825
 
826
- > 🛈 Note: If you are passing an error to `send` and the statusCode is less than
826
+ > ℹ️ Note: If you are passing an error to `send` and the statusCode is less than
827
827
  > 400, Fastify will automatically set it at 500.
828
828
 
829
829
  Tip: you can simplify errors by using the
@@ -871,7 +871,7 @@ fastify.get('/', {
871
871
  If you want to customize error handling, check out
872
872
  [`setErrorHandler`](./Server.md#seterrorhandler) API.
873
873
 
874
- > 🛈 Note: you are responsible for logging when customizing the error handler.
874
+ > ℹ️ Note: you are responsible for logging when customizing the error handler.
875
875
 
876
876
  API:
877
877
 
@@ -84,7 +84,7 @@ This operation adds new values to the request headers, accessible via
84
84
  For performance reasons, `Symbol('fastify.RequestAcceptVersion')` may be added
85
85
  to headers on `not found` routes.
86
86
 
87
- > 🛈 Note: Schema validation may mutate the `request.headers` and
87
+ > ℹ️ Note: Schema validation may mutate the `request.headers` and
88
88
  > `request.raw.headers` objects, causing the headers to become empty.
89
89
 
90
90
  ```js
@@ -137,7 +137,7 @@ fastify.route(options)
137
137
 
138
138
  * `reply` is defined in [Reply](./Reply.md).
139
139
 
140
- > 🛈 Note: The documentation for `onRequest`, `preParsing`, `preValidation`,
140
+ > ℹ️ Note: The documentation for `onRequest`, `preParsing`, `preValidation`,
141
141
  > `preHandler`, `preSerialization`, `onSend`, and `onResponse` is detailed in
142
142
  > [Hooks](./Hooks.md). To send a response before the request is handled by the
143
143
  > `handler`, see [Respond to a request from
@@ -233,7 +233,7 @@ const opts = {
233
233
  fastify.get('/', opts)
234
234
  ```
235
235
 
236
- > 🛈 Note: Specifying the handler in both `options` and as the third parameter to
236
+ > ℹ️ Note: Specifying the handler in both `options` and as the third parameter to
237
237
  > the shortcut method throws a duplicate `handler` error.
238
238
 
239
239
  ### Url building
@@ -402,7 +402,7 @@ This approach supports both `callback-style` and `async-await` with minimal
402
402
  trade-off. However, it is recommended to use only one style for consistent
403
403
  error handling within your application.
404
404
 
405
- > 🛈 Note: Every async function returns a promise by itself.
405
+ > ℹ️ Note: Every async function returns a promise by itself.
406
406
 
407
407
  ### Route Prefixing
408
408
  <a id="route-prefixing"></a>
@@ -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,7 +1550,7 @@ 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
1556
  > `request.routeOptions.url`, `routeOptions.method` and `routeOptions.config`.
@@ -1577,18 +1601,8 @@ if (statusCode >= 500) {
1577
1601
 
1578
1602
  > ⚠ Warning:
1579
1603
  > Avoid calling setErrorHandler multiple times in the same scope.
1580
- > Only the last handler will take effect, and previous ones will be silently overridden.
1581
- >
1582
- > Incorrect usage:
1583
- > ```js
1584
- > app.setErrorHandler(function freeSomeResources () {
1585
- > // Never executed, memory leaks
1586
- > })
1587
- >
1588
- > app.setErrorHandler(function anotherErrorHandler () {
1589
- > // Overrides the previous handler
1590
- > })
1591
- > ```
1604
+ > See [`allowErrorHandlerOverride`](#allowerrorhandleroverride).
1605
+
1592
1606
 
1593
1607
  #### setChildLoggerFactory
1594
1608
  <a id="set-child-logger-factory"></a>
@@ -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/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(
@@ -858,6 +863,13 @@ function fastify (options) {
858
863
  throw new FST_ERR_ERROR_HANDLER_NOT_FN()
859
864
  }
860
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
861
873
  this[kErrorHandler] = buildErrorHandler(this[kErrorHandler], func.bind(this))
862
874
  return this
863
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/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
@@ -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
@@ -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