fastify 3.25.2 → 3.27.1

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 (41) hide show
  1. package/LICENSE +1 -1
  2. package/build/build-validation.js +2 -0
  3. package/docs/Guides/Index.md +2 -0
  4. package/docs/Guides/Prototype-Poisoning.md +391 -0
  5. package/docs/Guides/Recommendations.md +1 -1
  6. package/docs/Reference/Plugins.md +4 -4
  7. package/docs/Reference/Request.md +3 -0
  8. package/docs/Reference/Server.md +16 -2
  9. package/docs/Reference/TypeScript.md +1 -1
  10. package/fastify.d.ts +2 -1
  11. package/fastify.js +23 -5
  12. package/lib/errors.js +5 -0
  13. package/lib/noop-set.js +10 -0
  14. package/lib/pluginUtils.js +5 -0
  15. package/lib/reply.js +16 -6
  16. package/lib/route.js +34 -1
  17. package/lib/symbols.js +3 -1
  18. package/lib/warnings.js +1 -1
  19. package/package.json +7 -7
  20. package/test/404s.test.js +1 -1
  21. package/test/bundler/esbuild/bundler-test.js +31 -0
  22. package/test/bundler/esbuild/package.json +10 -0
  23. package/test/bundler/esbuild/src/fail-plugin-version.js +12 -0
  24. package/test/bundler/esbuild/src/index.js +7 -0
  25. package/test/bundler/webpack/bundler-test.js +15 -4
  26. package/test/bundler/webpack/src/fail-plugin-version.js +1 -3
  27. package/test/bundler/webpack/src/index.js +1 -3
  28. package/test/close.test.js +38 -0
  29. package/test/custom-parser.test.js +30 -31
  30. package/test/noop-set.test.js +19 -0
  31. package/test/plugin.name.display.js +10 -0
  32. package/test/plugin.test.js +18 -0
  33. package/test/route.test.js +12 -0
  34. package/test/schema-serialization.test.js +41 -0
  35. package/test/types/fastify.test-d.ts +17 -0
  36. package/test/types/hooks.test-d.ts +28 -4
  37. package/test/types/instance.test-d.ts +25 -0
  38. package/test/versioned-routes.test.js +27 -3
  39. package/types/hooks.d.ts +19 -19
  40. package/types/instance.d.ts +13 -1
  41. package/types/schema.d.ts +14 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2016-2020 The Fastify Team
3
+ Copyright (c) 2016-2022 The Fastify Team
4
4
 
5
5
  The Fastify team members are listed at https://github.com/fastify/fastify#team
6
6
  and in the README file.
@@ -15,6 +15,7 @@ const ajv = new Ajv({
15
15
  const defaultInitOptions = {
16
16
  connectionTimeout: 0, // 0 sec
17
17
  keepAliveTimeout: 5000, // 5 sec
18
+ forceCloseConnections: false, // keep-alive connections
18
19
  maxRequestsPerSocket: 0, // no limit
19
20
  requestTimeout: 0, // no limit
20
21
  bodyLimit: 1024 * 1024, // 1 MiB
@@ -49,6 +50,7 @@ const schema = {
49
50
  properties: {
50
51
  connectionTimeout: { type: 'integer', default: defaultInitOptions.connectionTimeout },
51
52
  keepAliveTimeout: { type: 'integer', default: defaultInitOptions.keepAliveTimeout },
53
+ forceCloseConnections: { type: 'boolean', default: defaultInitOptions.forceCloseConnections },
52
54
  maxRequestsPerSocket: { type: 'integer', default: defaultInitOptions.maxRequestsPerSocket, nullable: true },
53
55
  requestTimeout: { type: 'integer', default: defaultInitOptions.requestTimeout },
54
56
  bodyLimit: { type: 'integer', default: defaultInitOptions.bodyLimit },
@@ -20,6 +20,8 @@ This table of contents is in alphabetical order.
20
20
  Fastify v3 from earlier versions.
21
21
  + [Plugins Guide](./Plugins-Guide.md): An informal introduction to writing
22
22
  Fastify plugins.
23
+ + [Prototype Poisoning](./Prototype-Poisoning.md): A description of how the
24
+ prototype poisoning attack works and is mitigated.
23
25
  + [Recommendations](./Recommendations.md): Recommendations for how to deploy
24
26
  Fastify into production environments.
25
27
  + [Serverless](./Serverless.md): Details on how to deploy Fastify applications
@@ -0,0 +1,391 @@
1
+ > The following is an article written by Eran Hammer.
2
+ > It is reproduced here for posterity [with permission](https://github.com/fastify/fastify/issues/1426#issuecomment-817957913).
3
+ > It has been reformatted from the original HTML source to Markdown source,
4
+ > but otherwise remains the same. The original HTML can be retrieved from the
5
+ > above permission link.
6
+
7
+ ## A Tale of (prototype) Poisoning
8
+ <a id="pp"></a>
9
+
10
+ This story is a behind-the-scenes look at the process and drama created by a
11
+ particularity interesting web security issue. It is also a perfect illustration
12
+ of the efforts required to maintain popular pieces of open source software and
13
+ the limitations of existing communication channels.
14
+
15
+ But first, if you use a JavaScript framework to process incoming JSON data, take
16
+ a moment to read up on [Prototype
17
+ Poisoning](https://medium.com/intrinsic/javascript-prototype-poisoning-vulnerabilities-in-the-wild-7bc15347c96)
18
+ in general, and the specific [technical
19
+ details](https://github.com/hapijs/hapi/issues/3916) of this issue. I'll explain
20
+ it all in a bit, but since this could be a critical issue, you might want to
21
+ verify your own code first. While this story is focused on a specific framework,
22
+ any solution that uses `JSON.parse()` to process external data is potentially at
23
+ risk.
24
+
25
+ ### BOOM
26
+ <a id="pp-boom"></a>
27
+
28
+ Our story begins with a bang.
29
+
30
+ The engineering team at Lob (long time generous supporters of my work!) reported
31
+ a critical security vulnerability they identified in our data validation
32
+ module — [joi](https://github.com/hapijs/joi). They provided some technical
33
+ details and a proposed solution.
34
+
35
+ The main purpose of a data validation library is to ensure the output fully
36
+ complies with the rules defined. If it doesn't, validation fails. If it passes,
37
+ your can blindly trust that the data you are working with is safe. In fact, most
38
+ developers treat validated input as completely safe from a system integrity
39
+ perspective. This is crucial.
40
+
41
+ In our case, the Lob team provided an example where some data was able to sneak
42
+ by the validation logic and pass through undetected. This is the worst possible
43
+ defect a validation library can have.
44
+
45
+ ### Prototype in a nutshell
46
+ <a id="pp-nutshell"></a>
47
+
48
+ To understand this story, you need to understand how JavaScript works a bit.
49
+ Every object in JavaScript can have a prototype. It is a set of methods and
50
+ properties it "inherits" from another object. I put inherits in quotes because
51
+ JavaScript isn't really an object oriented language.
52
+
53
+ A long time ago, for a bunch of irrelevant reasons, someone decided that it
54
+ would be a good idea to use the special property name `__proto__` to access (and
55
+ set) an object's prototype. This has since been deprecated but nevertheless,
56
+ fully supported.
57
+
58
+ To demonstrate:
59
+
60
+ ```
61
+ > const a = { b: 5 };
62
+ > a.b;
63
+ 5
64
+ > a.__proto__ = { c: 6 };
65
+ > a.c;
66
+ 6
67
+ > a;
68
+ { b: 5 }
69
+ ```
70
+
71
+ As you can see, the object doesn't have a `c` property, but its prototype does.
72
+ When validating the object, the validation library ignores the prototype and
73
+ only validates the object's own properties. This allows `c` to sneak in via the
74
+ prototype.
75
+
76
+ Another important part of this story is the way `JSON.parse()` — a utility
77
+ provided by the language to convert JSON formatted text into objects  —  handles
78
+ this magic `__proto__` property name.
79
+
80
+ ```
81
+ > const text = '{ "b": 5, "__proto__": { "c": 6 } }';
82
+ > const a = JSON.parse(text);
83
+ > a;
84
+ { b: 5, __proto__: { c: 6 } }
85
+ ```
86
+
87
+ Notice how `a` has a `__proto__` property. This is not a prototype reference. It
88
+ is a simple object property key, just like `b`. As we've seen from the first
89
+ example, we can't actually create this key through assignment as that invokes
90
+ the prototype magic and sets an actual prototype. `JSON.parse()` however, sets a
91
+ simple property with that poisonous name.
92
+
93
+ By itself, the object created by `JSON.parse()` is perfectly safe. It doesn't
94
+ have a prototype of its own. It has a seemingly harmless property that just
95
+ happens to overlap with a built-in JavaScript magic name.
96
+
97
+ However, other methods are not as lucky:
98
+
99
+ ```
100
+ > const x = Object.assign({}, a);
101
+ > x;
102
+ { b: 5}
103
+ > x.c;
104
+ 6;
105
+ ```
106
+
107
+ If we take the `a` object created earlier by `JSON.parse()` and pass it to the
108
+ helpful `Object.assign()` method (used to perform a shallow copy of all the top
109
+ level properties of `a` into the provided empty `{}` object), the magic
110
+ `__proto__` property "leaks" and becomes `x` 's actual prototype.
111
+
112
+ Surprise!
113
+
114
+ Put together, if you get some external text input, parse it with `JSON.parse()`
115
+ then perform some simple manipulation of that object (say, shallow clone and add
116
+ an `id` ), and then pass it to our validation library, anything passed through
117
+ via `__proto__` would sneak in undetected.
118
+
119
+ ### Oh joi!
120
+ <a id="pp-oh-joi"></a>
121
+
122
+ The first question is, of course, why does the validation module **joi** ignore
123
+ the prototype and let potentially harmful data through? We asked ourselves the
124
+ same question and our instant thought was "it was an oversight". A bug. A really
125
+ big mistake. The joi module should not have allowed this to happen. But…
126
+
127
+ While joi is used primarily for validating web input data, it also has a
128
+ significant user base using it to validate internal objects, some of which have
129
+ prototypes. The fact that joi ignores the prototype is a helpful "feature". It
130
+ allows validating the object's own properties while ignoring what could be a
131
+ very complicated prototype structure (with many methods and literal properties).
132
+
133
+ Any solution at the joi level would mean breaking some currently working code.
134
+
135
+ ### The right thing
136
+ <a id="pp-right-thing"></a>
137
+
138
+ At this point, we were looking at a devastatingly bad security vulnerability.
139
+ Right up there in the upper echelons of epic security failures. All we knew is
140
+ that our extremely popular data validation library fails to block harmful data,
141
+ and that this data is trivial to sneak through. All you need to do is add
142
+ `__proto__` and some crap to a JSON input and send it on its way to an
143
+ application built using our tools.
144
+
145
+ (Dramatic pause)
146
+
147
+ We knew we had to fix joi to prevent this but given the scale of this issue, we
148
+ had to do it in a way that will put a fix out without drawing too much attention
149
+ to it — without making it too easy to exploit — at least for a few days until
150
+ most systems received the update.
151
+
152
+ Sneaking a fix isn't the hardest thing to accomplish. If you combine it with an
153
+ otherwise purposeless refactor of the code, and throw in a few unrelated bug
154
+ fixes and maybe a cool new feature, you can publish a new version without
155
+ drawing attention to the real issue being fixed.
156
+
157
+ The problem was, the right fix was going to break valid use cases. You see, joi
158
+ has no way of knowing if you want it to ignore the prototype you set, or block
159
+ the prototype set by an attacker. A solution that fixes the exploit will break
160
+ code and breaking code tends to get a lot of attention.
161
+
162
+ On the other hand, if we released a proper ([semantically
163
+ versioned](https://semver.org/)) fix, mark it as a breaking change, and add a
164
+ new API to explicitly tell joi what you want it to do with the prototype, we
165
+ will share with the world how to exploit this vulnerability while also making it
166
+ more time consuming for systems to upgrade (breaking changes never get applied
167
+ automatically by build tools).
168
+
169
+ Lose — Lose.
170
+
171
+ ### A detour
172
+ <a id="pp-detour"></a>
173
+
174
+ While the issue at hand was about incoming request payloads, we had to pause and
175
+ check if it could also impact data coming via the query string, cookies, and
176
+ headers. Basically, anything that gets serialized into objects from text.
177
+
178
+ We quickly confirmed node default query string parser was fine as well as its
179
+ header parser. I identified one potential issue with base64-encoded JSON cookies
180
+ as well as the usage of custom query string parsers. We also wrote some tests to
181
+ confirm that the most popular third-party query string parser  —
182
+ [qs](https://www.npmjs.com/package/qs) —  was not vulnerable (it is not!).
183
+
184
+ ### A development
185
+ <a id="pp-a-development"></a>
186
+
187
+ Throughout this triage, we just assumed that the offending input with its
188
+ poisoned prototype was coming into joi from hapi, the web framework connecting
189
+ the hapi.js ecosystem. Further investigation by the Lob team found that the
190
+ problem was a bit more nuanced.
191
+
192
+ hapi used `JSON.parse()` to process incoming data. It first set the result
193
+ object as a `payload` property of the incoming request, and then passed that
194
+ same object for validation by joi before being passed to the application
195
+ business logic for processing. Since `JSON.parse()` doesn't actually leak the
196
+ `__proto__` property, it would arrive to joi with an invalid key and fail
197
+ validation.
198
+
199
+ However, hapi provides two extension points where the payload data can be
200
+ inspected (and processed) prior to validation. It is all properly documented and
201
+ well understood by most developers. The extension points are there to allow you
202
+ to interact with the raw inputs prior to validation for legitimate (and often
203
+ security related) reasons.
204
+
205
+ If during one of these two extension points, a developer used `Object.assign()`
206
+ or a similar method on the payload, the `__proto__` property would leak and
207
+ become an actual prototype.
208
+
209
+ ### Sigh of relief
210
+ <a id="pp-sigh-of-relief"></a>
211
+
212
+ We were now dealing with a much different level of awfulness. Manipulating the
213
+ payload object prior to validation is not common which meant this was no longer
214
+ a doomsday scenario. It was still potentially catastrophic but the exposure
215
+ dropped from every joi user to some very specific implementations.
216
+
217
+ We were no longer looking at a secretive joi release. The issue in joi is still
218
+ there, but we can now address it properly with a new API and breaking release
219
+ over the next few weeks.
220
+
221
+ We also knew that we can easily mitigate this vulnerability at the framework
222
+ level since it knows which data is coming from the outside and which is
223
+ internally generated. The framework is really the only piece that can protect
224
+ developers against making such unexpected mistakes.
225
+
226
+ ### Good news, bad news, no news?
227
+ <a id="pp-good-news-no-news"></a>
228
+
229
+ The good news was that this wasn't our fault. It wasn't a bug in hapi or joi. It
230
+ was only possible through a complex combination of actions that was not unique
231
+ to hapi or joi. This can happen with every other JavaScript framework. If hapi
232
+ is broken, then the world is broken.
233
+
234
+ Great — we solved the blame game.
235
+
236
+ The bad news is that when there is nothing to blame (other than JavaScript
237
+ itself), it is much harder getting it fixed.
238
+
239
+ The first question people ask once a security issue is found is if there is
240
+ going to be a CVE published. A CVE — Common Vulnerabilities and Exposures — is a
241
+ [database](https://cve.mitre.org/) of known security issues. It is a critical
242
+ component of web security. The benefit of publishing a CVE is that it
243
+ immediately triggers alarms and informs and often breaks automated builds until
244
+ the issue is resolved.
245
+
246
+ But what do we pin this to?
247
+
248
+ Probably, nothing. We are still debating whether we should tag some versions of
249
+ hapi with a warning. The "we" is the node security process. Since we now have a
250
+ new version of hapi that mitigate the problem by default, it can be considered a
251
+ fix. But because the fix isn't to a problem in hapi itself, it is not exactly
252
+ kosher to declare older versions harmful.
253
+
254
+ Publishing an advisory on previous versions of hapi for the sole purpose of
255
+ nudging people into awareness and upgrade is an abuse of the advisory process.
256
+ I'm personally fine with abusing it for the purpose of improving security but
257
+ that's not my call. As of this writing, it is still being debated.
258
+
259
+ ### The solution business
260
+ <a id="pp-solution-business"></a>
261
+
262
+ Mitigating the issue wasn't hard. Making it scale and safe was a bit more
263
+ involved. Since we knew where harmful data can enter the system, and we knew
264
+ where we used the problematic `JSON.parse()` we could replace it with a safe
265
+ implementation.
266
+
267
+ One problem. Validating data can be costly and we are now planning on validating
268
+ every incoming JSON text. The built-in `JSON.parse()` implementation is fast.
269
+ Really really fast. It is unlikely we can build a replacement that will be more
270
+ secure and anywhere as fast. Especially not overnight and without introducing
271
+ new bugs.
272
+
273
+ It was obvious we were going to wrap the existing `JSON.parse()` method with
274
+ some additional logic. We just had to make sure it was not adding too much
275
+ overhead. This isn't just a performance consideration but also a security one.
276
+ If we make it easy to slow down a system by simply sending specific data, we
277
+ make it easy to execute a [DoS
278
+ attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) at very low
279
+ cost.
280
+
281
+ I came up with a stupidly simple solution: first parse the text using the
282
+ existing tools. If this didn't fail, scan the original raw text for the
283
+ offending string "__proto__". Only if we find it, perform an actual scan of the
284
+ object. We can't block every reference to "__proto__" — sometimes it is
285
+ perfectly valid value (like when writing about it here and sending this text
286
+ over to Medium for publication).
287
+
288
+ This made the "happy path" practically as fast as before. It just added one
289
+ function call, a quick text scan (again, very fast built-in implementation), and
290
+ a conditional return. The solution had negligible impact on the vast majority of
291
+ data expected to pass through it.
292
+
293
+ Next problem. The prototype property doesn't have to be at the top level of the
294
+ incoming object. It can be nested deep inside. This means we cannot just check
295
+ for the presence of it at the top level. We need to recursively iterate through
296
+ the object.
297
+
298
+ While recursive functions are a favorite tool, they could be disastrous when
299
+ writing security-conscious code. You see, recursive function increase the size
300
+ of the runtime call stack. The more times you loop, the longer the call stack
301
+ gets. At some point — KABOOM— you reach the maximum length and the process dies.
302
+
303
+ If you cannot guarantee the shape of the incoming data, recursive iteration
304
+ becomes an open threat. An attacker only needs to craft a deep enough object to
305
+ crash your servers.
306
+
307
+ I used a flat loop implementation that is both more memory efficient (less
308
+ function calls, less passing of temporary arguments) and more secure. I am not
309
+ pointing this out to brag, but to highlight how basic engineering practices can
310
+ create (or avoid) security pitfalls.
311
+
312
+ ### Putting it to the test
313
+ <a id="pp-putting-to-test"></a>
314
+
315
+ I sent the code to two people. First to [Nathan
316
+ LaFreniere](https://github.com/nlf) to double check the security properties of
317
+ the solution, and then to [Matteo Collina](https://github.com/mcollina) to
318
+ review the performance. They are among the very best at what they do and often
319
+ my go-to people.
320
+
321
+ The performance benchmarks confirmed that the "happy path" was practically
322
+ unaffected. The interesting findings was that removing the offending values was
323
+ faster then throwing an exception. This raised the question of what should be
324
+ the default behavior of the new module — which I called
325
+ [**bourne**](https://github.com/hapijs/bourne) —  error or sanitize.
326
+
327
+ The concern, again, was exposing the application to a DoS attack. If sending a
328
+ request with `__proto__` makes things 500% slower, that could be an easy vector
329
+ to exploit. But after a bit more testing we confirmed that sending **any**
330
+ invalid JSON text was creating a very similar cost.
331
+
332
+ In other words, if you parse JSON, invalid values are going to cost you more,
333
+ regardless of what makes them invalid. It is also important to remember that
334
+ while the benchmark showed the significant % cost of scanning suspected objects,
335
+ the actual cost in CPU time was still in the fraction of milliseconds. Important
336
+ to note and measure but not actually harmful.
337
+
338
+ ### hapi ever-after
339
+ <a id="pp-hapi-ever-after"></a>
340
+
341
+ There are a bunch of things to be grateful for.
342
+
343
+ The initial disclosure by the Lob team was perfect. It was reported privately,
344
+ to the right people, with the right information. They followed up with
345
+ additional findings, and gave us the time and space to resolve it the right way.
346
+ Lob also was a major sponsor of my work on hapi over the years and that
347
+ financial support is critical to allow everything else to happen. More on that
348
+ in a bit.
349
+
350
+ Triage was stressful but staffed with the right people. Having folks like
351
+ [Nicolas Morel](https://github.com/Marsup), Nathan, and Matteo, available and
352
+ eager to help is critical. This isn't easy to deal with without the pressure,
353
+ but with it, mistakes are likely without proper team collaboration.
354
+
355
+ We got lucky with the actual vulnerability. What started up looking like a
356
+ catastrophic problem, ended up being a delicate but straight-forward problem to
357
+ address.
358
+
359
+ We also got lucky by having full access to mitigate it at the source — didn't
360
+ need to send emails to some unknown framework maintainer and hope for a quick
361
+ answer. hapi's total control over all of its dependencies proved its usefulness
362
+ and security again. Not using [hapi](http://hapijs.com)? [Maybe you
363
+ should](https://hueniverse.com/why-you-should-consider-hapi-6163689bd7c2).
364
+
365
+ ### The after in happy ever-after
366
+ <a id="pp-after-ever-after"></a>
367
+
368
+ This is where I have to take advantage of this incident to reiterate the cost
369
+ and need for sustainable and secure open source.
370
+
371
+ My time alone on this one issue exceeded 20 hours. That's half a working week.
372
+ It came at the end of a month were I already spent over 30 hours publishing a
373
+ new major release of hapi (most of the work was done in December). This puts me
374
+ at a personal financial loss of over $5000 this month (I had to cut back on paid
375
+ client work to make time for it).
376
+
377
+ If you rely on code I maintain, this is exactly the level of support, quality,
378
+ and commitment you want (and lets be honest — expect). Most of you take it for
379
+ granted — not just my work but the work of hundreds of other dedicated open
380
+ source maintainers.
381
+
382
+ Because this work is important, I decided to try and make it not just
383
+ financially sustainable but to grow and expand it. There is so much to improve.
384
+ This is exactly what motivates me to implement the new [commercial licensing
385
+ plan](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898)
386
+ coming in March. You can read more about it
387
+ [here](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898).
388
+
389
+ Of all the time consuming things, security is at the very top. I hope this story
390
+ successfully conveyed not just the technical details, but also the human drama and
391
+ what it takes to keep the web secure.
@@ -255,7 +255,7 @@ server {
255
255
  # group specified above. Note the additional headers that forward
256
256
  # information about the original request. You might want to set
257
257
  # trustProxy to the address of your NGINX server so the X-Forwarded
258
- * fields are used by fastify.
258
+ # fields are used by fastify.
259
259
  location / {
260
260
  # more info: http://nginx.org/en/docs/http/ngx_http_proxy_module.html
261
261
  proxy_http_version 1.1;
@@ -181,7 +181,7 @@ callback.
181
181
  Example:
182
182
  ```js
183
183
  module.exports = function (fastify, opts, done) {
184
- fastify.decorate('utility', () => {})
184
+ fastify.decorate('utility', function () {})
185
185
 
186
186
  fastify.get('/', handler)
187
187
 
@@ -191,7 +191,7 @@ module.exports = function (fastify, opts, done) {
191
191
  You can also use `register` inside another `register`:
192
192
  ```js
193
193
  module.exports = function (fastify, opts, done) {
194
- fastify.decorate('utility', () => {})
194
+ fastify.decorate('utility', function () {})
195
195
 
196
196
  fastify.get('/', handler)
197
197
 
@@ -226,7 +226,7 @@ plugin will support.
226
226
  const fp = require('fastify-plugin')
227
227
 
228
228
  module.exports = fp(function (fastify, opts, done) {
229
- fastify.decorate('utility', () => {})
229
+ fastify.decorate('utility', function () {})
230
230
  done()
231
231
  }, '0.x')
232
232
  ```
@@ -239,7 +239,7 @@ changes it will be your responsibility to update the module, while if you use
239
239
  `fastify-plugin`, you can be sure about backward compatibility.
240
240
  ```js
241
241
  function yourPlugin (fastify, opts, done) {
242
- fastify.decorate('utility', () => {})
242
+ fastify.decorate('utility', function () {})
243
243
  done()
244
244
  }
245
245
  yourPlugin[Symbol.for('skip-override')] = true
@@ -57,6 +57,9 @@ This operation will add to the request headers the new values that can be read
57
57
  calling `request.headers.bar`. Moreover, you can still access the standard
58
58
  request's headers with the `request.raw.headers` property.
59
59
 
60
+ > Note: For performance reason on `not found` route, you may see that we will add
61
+ an extra property `Symbol('fastify.RequestAcceptVersion')` on the headers.
62
+
60
63
  ```js
61
64
  fastify.post('/:params', options, function (request, reply) {
62
65
  console.log(request.body)
@@ -13,6 +13,7 @@ describes the properties available in that options object.
13
13
  - [`https`](#https)
14
14
  - [`connectionTimeout`](#connectiontimeout)
15
15
  - [`keepAliveTimeout`](#keepalivetimeout)
16
+ - [`forceCloseConnections`](#forcecloseconnections)
16
17
  - [`maxRequestsPerSocket`](#maxrequestspersocket)
17
18
  - [`requestTimeout`](#requesttimeout)
18
19
  - [`ignoreTrailingSlash`](#ignoretrailingslash)
@@ -124,6 +125,19 @@ use. Also, when `serverFactory` option is specified, this option is ignored.
124
125
 
125
126
  + Default: `5000` (5 seconds)
126
127
 
128
+ ### `forceCloseConnections`
129
+ <a id="forcecloseconnections"></a>
130
+
131
+ When set to `true` requests with the header `connection: keep-alive` will be
132
+ tracked by the server. Upon [`close`](#close), the server will iterate the
133
+ current persistent connections and [destroy their
134
+ sockets](https://nodejs.org/dist/latest-v16.x/docs/api/net.html#socketdestroyerror).
135
+ This means the server will shutdown immediately instead of waiting for existing
136
+ persistent connections to timeout first. Important: connections are not
137
+ inspected to determine if requests have been completed.
138
+
139
+ + Default: `false`
140
+
127
141
  ### `maxRequestsPerSocket`
128
142
  <a id="factory-max-requests-per-socket"></a>
129
143
 
@@ -207,7 +221,7 @@ Defines the maximum payload, in bytes, the server is allowed to accept.
207
221
  Defines what action the framework must take when parsing a JSON object with
208
222
  `__proto__`. This functionality is provided by
209
223
  [secure-json-parse](https://github.com/fastify/secure-json-parse). See
210
- https://hueniverse.com/a-tale-of-prototype-poisoning-2610fa170061 for more
224
+ [Prototype Poisoning](../Guides/Prototype-Poisoning.md) for more
211
225
  details about prototype poisoning attacks.
212
226
 
213
227
  Possible values are `'error'`, `'remove'` and `'ignore'`.
@@ -220,7 +234,7 @@ Possible values are `'error'`, `'remove'` and `'ignore'`.
220
234
  Defines what action the framework must take when parsing a JSON object with
221
235
  `constructor`. This functionality is provided by
222
236
  [secure-json-parse](https://github.com/fastify/secure-json-parse). See
223
- https://hueniverse.com/a-tale-of-prototype-poisoning-2610fa170061 for more
237
+ [Prototype Poisoning](../Guides/Prototype-Poisoning.md) for more
224
238
  details about prototype poisoning attacks.
225
239
 
226
240
  Possible values are `'error'`, `'remove'` and `'ignore'`.
@@ -1384,7 +1384,7 @@ body parsing happens before the `preHandler` hook.
1384
1384
 
1385
1385
  [src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L35)
1386
1386
 
1387
- preParsing` is the second hook to be executed in the request lifecycle. The
1387
+ `preParsing` is the second hook to be executed in the request lifecycle. The
1388
1388
  previous hook was `onRequest`, the next hook will be `preValidation`.
1389
1389
 
1390
1390
  Notice: in the `preParsing` hook, request.body will always be null, because the
package/fastify.d.ts CHANGED
@@ -98,6 +98,7 @@ export type FastifyServerOptions<
98
98
  connectionTimeout?: number,
99
99
  keepAliveTimeout?: number,
100
100
  maxRequestsPerSocket?: number,
101
+ forceCloseConnections?: boolean,
101
102
  requestTimeout?: number,
102
103
  pluginTimeout?: number,
103
104
  bodyLimit?: number,
@@ -144,7 +145,7 @@ export type FastifyServerOptions<
144
145
  return503OnClosing?: boolean,
145
146
  ajv?: {
146
147
  customOptions?: AjvOptions,
147
- plugins?: Function[]
148
+ plugins?: (Function | [Function, unknown])[]
148
149
  },
149
150
  frameworkErrors?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface>(
150
151
  error: FastifyError,
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '3.25.2'
3
+ const VERSION = '3.27.1'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
@@ -16,6 +16,7 @@ const {
16
16
  kLogSerializers,
17
17
  kHooks,
18
18
  kSchemaController,
19
+ kRequestAcceptVersion,
19
20
  kReplySerializerDefault,
20
21
  kContentTypeParser,
21
22
  kReply,
@@ -25,7 +26,8 @@ const {
25
26
  kOptions,
26
27
  kPluginNameChain,
27
28
  kSchemaErrorFormatter,
28
- kErrorHandler
29
+ kErrorHandler,
30
+ kKeepAliveConnections
29
31
  } = require('./lib/symbols.js')
30
32
 
31
33
  const { createServer } = require('./lib/server')
@@ -44,6 +46,7 @@ const build404 = require('./lib/fourOhFour')
44
46
  const getSecuredInitialConfig = require('./lib/initialConfigValidation')
45
47
  const override = require('./lib/pluginOverride')
46
48
  const warning = require('./lib/warnings')
49
+ const noopSet = require('./lib/noop-set')
47
50
  const { defaultInitOptions } = getSecuredInitialConfig
48
51
 
49
52
  const {
@@ -132,6 +135,7 @@ function fastify (options) {
132
135
  // Update the options with the fixed values
133
136
  options.connectionTimeout = options.connectionTimeout || defaultInitOptions.connectionTimeout
134
137
  options.keepAliveTimeout = options.keepAliveTimeout || defaultInitOptions.keepAliveTimeout
138
+ options.forceCloseConnections = typeof options.forceCloseConnections === 'boolean' ? options.forceCloseConnections : defaultInitOptions.forceCloseConnections
135
139
  options.maxRequestsPerSocket = options.maxRequestsPerSocket || defaultInitOptions.maxRequestsPerSocket
136
140
  options.requestTimeout = options.requestTimeout || defaultInitOptions.requestTimeout
137
141
  options.logger = logger
@@ -145,6 +149,7 @@ function fastify (options) {
145
149
  options.exposeHeadRoutes = exposeHeadRoutes
146
150
 
147
151
  const initialConfig = getSecuredInitialConfig(options)
152
+ const keepAliveConnections = options.forceCloseConnections === true ? new Set() : noopSet()
148
153
 
149
154
  let constraints = options.constraints
150
155
  if (options.versioning) {
@@ -175,7 +180,8 @@ function fastify (options) {
175
180
  maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,
176
181
  caseSensitive: options.caseSensitive,
177
182
  buildPrettyMeta: defaultBuildPrettyMeta
178
- }
183
+ },
184
+ keepAliveConnections
179
185
  })
180
186
 
181
187
  // 404 router, used for handling encapsulated 404 handlers
@@ -199,6 +205,7 @@ function fastify (options) {
199
205
  closing: false,
200
206
  started: false
201
207
  },
208
+ [kKeepAliveConnections]: keepAliveConnections,
202
209
  [kOptions]: options,
203
210
  [kChildren]: [],
204
211
  [kBodyLimit]: bodyLimit,
@@ -374,6 +381,15 @@ function fastify (options) {
374
381
  if (fastify[kState].listening) {
375
382
  // No new TCP connections are accepted
376
383
  instance.server.close(done)
384
+
385
+ for (const conn of fastify[kKeepAliveConnections]) {
386
+ // We must invoke the destroy method instead of merely unreffing
387
+ // the sockets. If we only unref, then the callback passed to
388
+ // `fastify.close` will never be invoked; nor will any of the
389
+ // registered `onClose` hooks.
390
+ conn.destroy()
391
+ fastify[kKeepAliveConnections].delete(conn)
392
+ }
377
393
  } else {
378
394
  done(null)
379
395
  }
@@ -520,10 +536,8 @@ function fastify (options) {
520
536
  }
521
537
 
522
538
  if (name === 'onClose') {
523
- this[kHooks].validate(name, fn)
524
539
  this.onClose(fn)
525
540
  } else if (name === 'onReady') {
526
- this[kHooks].validate(name, fn)
527
541
  this[kHooks].add(name, fn)
528
542
  } else {
529
543
  this.after((err, done) => {
@@ -578,6 +592,10 @@ function fastify (options) {
578
592
  // req and res are Node.js core objects
579
593
  function defaultRoute (req, res) {
580
594
  if (req.headers['accept-version'] !== undefined) {
595
+ // we remove the accept-version header for performance result
596
+ // because we do not want to go through the constraint checking
597
+ // the usage of symbol here to prevent any colision on custom header name
598
+ req.headers[kRequestAcceptVersion] = req.headers['accept-version']
581
599
  req.headers['accept-version'] = undefined
582
600
  }
583
601
  fourOhFour.router.lookup(req, res)
package/lib/errors.js CHANGED
@@ -210,6 +210,11 @@ const codes = {
210
210
  500,
211
211
  TypeError
212
212
  ),
213
+ FST_ERR_INVALID_URL: createError(
214
+ 'FST_ERR_INVALID_URL',
215
+ "URL must be a string. Received '%s'",
216
+ 400
217
+ ),
213
218
 
214
219
  /**
215
220
  * again listen when close server