fastify 4.16.3 → 4.18.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.
- package/GOVERNANCE.md +2 -2
- package/README.md +13 -10
- package/docs/Guides/Ecosystem.md +6 -0
- package/docs/Guides/Serverless.md +12 -1
- package/docs/Reference/Errors.md +5 -0
- package/docs/Reference/LTS.md +5 -5
- package/docs/Reference/Lifecycle.md +3 -0
- package/docs/Reference/Logging.md +34 -1
- package/docs/Reference/Reply.md +19 -0
- package/docs/Reference/Request.md +1 -1
- package/docs/Reference/Server.md +20 -2
- package/docs/Reference/Validation-and-Serialization.md +3 -2
- package/examples/benchmark/body.json +3 -0
- package/fastify.d.ts +6 -1
- package/fastify.js +18 -13
- package/lib/contentTypeParser.js +3 -1
- package/lib/errors.js +5 -0
- package/lib/handleRequest.js +15 -4
- package/lib/reply.js +3 -2
- package/lib/route.js +14 -3
- package/lib/schemas.js +18 -21
- package/lib/server.js +9 -4
- package/lib/validation.js +100 -16
- package/package.json +33 -33
- package/test/404s.test.js +4 -2
- package/test/custom-parser.0.test.js +777 -0
- package/test/{custom-parser.test.js → custom-parser.1.test.js} +1 -742
- package/test/delete.test.js +3 -0
- package/test/fastify-instance.test.js +39 -0
- package/test/fluent-schema.test.js +4 -4
- package/test/get.test.js +3 -0
- package/test/hooks-async.test.js +154 -0
- package/test/hooks.test.js +29 -29
- package/test/input-validation.js +10 -5
- package/test/internals/reply.test.js +114 -45
- package/test/internals/validation.test.js +58 -0
- package/test/reply-error.test.js +7 -7
- package/test/request-error.test.js +3 -0
- package/test/route-hooks.test.js +38 -0
- package/test/route.test.js +192 -74
- package/test/schema-examples.test.js +2 -0
- package/test/schema-serialization.test.js +7 -5
- package/test/schema-special-usage.test.js +569 -0
- package/test/schema-validation.test.js +8 -4
- package/test/search.test.js +3 -0
- package/test/types/fastify.test-d.ts +4 -1
- package/test/types/hooks.test-d.ts +42 -0
- package/test/types/instance.test-d.ts +1 -0
- package/test/types/logger.test-d.ts +10 -4
- package/test/types/reply.test-d.ts +4 -1
- package/test/types/route.test-d.ts +12 -0
- package/test/url-rewriting.test.js +3 -0
- package/test/validation-error-handling.test.js +18 -3
- package/types/.eslintrc.json +1 -1
- package/types/instance.d.ts +1 -1
- package/types/logger.d.ts +10 -1
- package/types/reply.d.ts +7 -1
- package/types/route.d.ts +11 -31
- package/types/type-provider.d.ts +0 -1
- package/types/utils.d.ts +3 -1
- /package/types/{tsconfig.json → tsconfig.eslint.json} +0 -0
package/GOVERNANCE.md
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
## Lead Maintainers
|
|
16
16
|
|
|
17
|
-
Fastify Lead Maintainers are the
|
|
18
|
-
|
|
17
|
+
Fastify Lead Maintainers are the organization owners.
|
|
18
|
+
They are the only members of the `@fastify/leads` team. The Lead
|
|
19
19
|
Maintainers are the curator of the Fastify project and their key responsibility
|
|
20
20
|
is to issue releases of Fastify and its dependencies.
|
|
21
21
|
|
package/README.md
CHANGED
|
@@ -38,6 +38,18 @@ resources of your server, knowing that you are serving the highest number of
|
|
|
38
38
|
requests as possible, without sacrificing security validations and handy
|
|
39
39
|
development?
|
|
40
40
|
|
|
41
|
+
Enter Fastify. Fastify is a web framework highly focused on providing the best
|
|
42
|
+
developer experience with the least overhead and a powerful plugin architecture.
|
|
43
|
+
It is inspired by Hapi and Express and as far as we know, it is one of the
|
|
44
|
+
fastest web frameworks in town.
|
|
45
|
+
|
|
46
|
+
The `main` branch refers to the Fastify `v4` release. Check out the
|
|
47
|
+
[`v3.x` branch](https://github.com/fastify/fastify/tree/3.x) for `v3`.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### Table of Contents
|
|
52
|
+
|
|
41
53
|
- [Quick start](#quick-start)
|
|
42
54
|
- [Install](#install)
|
|
43
55
|
- [Example](#example)
|
|
@@ -51,13 +63,6 @@ development?
|
|
|
51
63
|
- [Hosted by](#hosted-by)
|
|
52
64
|
- [License](#license)
|
|
53
65
|
|
|
54
|
-
Enter Fastify. Fastify is a web framework highly focused on providing the best
|
|
55
|
-
developer experience with the least overhead and a powerful plugin architecture.
|
|
56
|
-
It is inspired by Hapi and Express and as far as we know, it is one of the
|
|
57
|
-
fastest web frameworks in town.
|
|
58
|
-
|
|
59
|
-
This branch refers to the Fastify v4 release. Check out the
|
|
60
|
-
[v3.x](https://github.com/fastify/fastify/tree/v3.x) branch for v3.
|
|
61
66
|
|
|
62
67
|
### Quick start
|
|
63
68
|
|
|
@@ -189,7 +194,7 @@ changes should be based on **`branch 1.x`**. In a similar way, all Fastify
|
|
|
189
194
|
- **Highly performant:** as far as we know, Fastify is one of the fastest web
|
|
190
195
|
frameworks in town, depending on the code complexity we can serve up to 76+
|
|
191
196
|
thousand requests per second.
|
|
192
|
-
- **
|
|
197
|
+
- **Extensible:** Fastify is fully extensible via its hooks, plugins and
|
|
193
198
|
decorators.
|
|
194
199
|
- **Schema based:** even if it is not mandatory we recommend to use [JSON
|
|
195
200
|
Schema](https://json-schema.org/) to validate your routes and serialize your
|
|
@@ -339,8 +344,6 @@ listed in alphabetical order.
|
|
|
339
344
|
* [__Frazer Smith__](https://github.com/Fdawgs), <https://www.npmjs.com/~fdawgs>
|
|
340
345
|
* [__Manuel Spigolon__](https://github.com/eomm),
|
|
341
346
|
<https://twitter.com/manueomm>, <https://www.npmjs.com/~eomm>
|
|
342
|
-
* [__Rafael Gonzaga__](https://github.com/rafaelgss),
|
|
343
|
-
<https://twitter.com/_rafaelgss>, <https://www.npmjs.com/~rafaelgss>
|
|
344
347
|
* [__Simone Busoli__](https://github.com/simoneb),
|
|
345
348
|
<https://twitter.com/simonebu>, <https://www.npmjs.com/~simoneb>
|
|
346
349
|
|
package/docs/Guides/Ecosystem.md
CHANGED
|
@@ -310,6 +310,9 @@ section.
|
|
|
310
310
|
- [`fastify-esso`](https://github.com/patrickpissurno/fastify-esso) The easiest
|
|
311
311
|
authentication plugin for Fastify, with built-in support for Single sign-on
|
|
312
312
|
(and great documentation).
|
|
313
|
+
- [`fastify-evervault`](https://github.com/Briscoooe/fastify-evervault/) Fastify
|
|
314
|
+
plugin for instantiating and encapsulating the
|
|
315
|
+
[Evervault](https://evervault.com/) client.
|
|
313
316
|
- [`fastify-explorer`](https://github.com/Eomm/fastify-explorer) Get control of
|
|
314
317
|
your decorators across all the encapsulated contexts.
|
|
315
318
|
- [`fastify-favicon`](https://github.com/smartiniOnGitHub/fastify-favicon)
|
|
@@ -465,6 +468,9 @@ middlewares into Fastify plugins
|
|
|
465
468
|
ORM.
|
|
466
469
|
- [`fastify-objectionjs-classes`](https://github.com/kamikazechaser/fastify-objectionjs-classes)
|
|
467
470
|
Plugin to cherry-pick classes from objectionjs ORM.
|
|
471
|
+
- [`fastify-opaque-apake`](https://github.com/squirrelchat/fastify-opaque-apake)
|
|
472
|
+
A Fastify plugin to implement the OPAQUE aPAKE protocol. Uses
|
|
473
|
+
[@squirrelchat/opaque-wasm-server](https://github.com/squirrelchat/opaque-wasm).
|
|
468
474
|
- [`fastify-openapi-docs`](https://github.com/ShogunPanda/fastify-openapi-docs)
|
|
469
475
|
A Fastify plugin that generates OpenAPI spec automatically.
|
|
470
476
|
- [`fastify-openapi-glue`](https://github.com/seriousme/fastify-openapi-glue)
|
|
@@ -489,10 +489,21 @@ const app = Fastify({
|
|
|
489
489
|
});
|
|
490
490
|
|
|
491
491
|
// Register your application as a normal plugin.
|
|
492
|
-
app.register(import("../src/app"));
|
|
492
|
+
app.register(import("../src/app.js"));
|
|
493
493
|
|
|
494
494
|
export default async (req, res) => {
|
|
495
495
|
await app.ready();
|
|
496
496
|
app.server.emit('request', req, res);
|
|
497
497
|
}
|
|
498
498
|
```
|
|
499
|
+
|
|
500
|
+
In `src/app.js` define the plugin.
|
|
501
|
+
```js
|
|
502
|
+
async function routes (fastify, options) {
|
|
503
|
+
fastify.get('/', async (request, reply) => {
|
|
504
|
+
return { hello: 'world' }
|
|
505
|
+
})
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export default routes;
|
|
509
|
+
```
|
package/docs/Reference/Errors.md
CHANGED
|
@@ -429,6 +429,11 @@ Handler for the route must be a function.
|
|
|
429
429
|
|
|
430
430
|
Missing handler function for the route.
|
|
431
431
|
|
|
432
|
+
#### FST_ERR_ROUTE_METHOD_INVALID
|
|
433
|
+
<a id="FST_ERR_ROUTE_METHOD_INVALID"></a>
|
|
434
|
+
|
|
435
|
+
Method is not a valid value.
|
|
436
|
+
|
|
432
437
|
#### FST_ERR_ROUTE_METHOD_NOT_SUPPORTED
|
|
433
438
|
<a id="FST_ERR_ROUTE_METHOD_NOT_SUPPORTED"></a>
|
|
434
439
|
|
package/docs/Reference/LTS.md
CHANGED
|
@@ -48,7 +48,7 @@ A "month" is defined as 30 consecutive days.
|
|
|
48
48
|
| 1.0.0 | 2018-03-06 | 2019-09-01 | 6, 8, 9, 10, 11 |
|
|
49
49
|
| 2.0.0 | 2019-02-25 | 2021-01-31 | 6, 8, 10, 12, 14 |
|
|
50
50
|
| 3.0.0 | 2020-07-07 | 2023-06-30 | 10, 12, 14, 16, 18 |
|
|
51
|
-
| 4.0.0 | 2022-06-08 | TBD | 14, 16, 18
|
|
51
|
+
| 4.0.0 | 2022-06-08 | TBD | 14, 16, 18, 20 |
|
|
52
52
|
|
|
53
53
|
### CI tested operating systems
|
|
54
54
|
<a id="supported-os"></a>
|
|
@@ -61,10 +61,10 @@ YAML workflow labels below:
|
|
|
61
61
|
|
|
62
62
|
| OS | YAML Workflow Label | Package Manager | Node.js |
|
|
63
63
|
|---------|------------------------|---------------------------|--------------|
|
|
64
|
-
| Linux | `ubuntu-latest` | npm | 14,16,18
|
|
65
|
-
| Linux | `ubuntu-latest` | yarn,pnpm | 14,16,18
|
|
66
|
-
| Windows | `windows-latest` | npm | 14,16,18
|
|
67
|
-
| MacOS | `macos-latest` | npm | 14,16,18
|
|
64
|
+
| Linux | `ubuntu-latest` | npm | 14,16,18,20 |
|
|
65
|
+
| Linux | `ubuntu-latest` | yarn,pnpm | 14,16,18,20 |
|
|
66
|
+
| Windows | `windows-latest` | npm | 14,16,18,20 |
|
|
67
|
+
| MacOS | `macos-latest` | npm | 14,16,18,20 |
|
|
68
68
|
|
|
69
69
|
Using [yarn](https://yarnpkg.com/) might require passing the `--ignore-engines`
|
|
70
70
|
flag.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<h1 align="center">Fastify</h1>
|
|
2
2
|
|
|
3
3
|
## Lifecycle
|
|
4
|
+
<a id="lifecycle"></a>
|
|
5
|
+
|
|
4
6
|
Following the schema of the internal lifecycle of Fastify.
|
|
5
7
|
|
|
6
8
|
On the right branch of every section there is the next phase of the lifecycle,
|
|
@@ -49,6 +51,7 @@ NB (*): If `reply.raw` is used to send a response back to the user, `onResponse`
|
|
|
49
51
|
hooks will still be executed
|
|
50
52
|
|
|
51
53
|
## Reply Lifecycle
|
|
54
|
+
<a id="reply-lifecycle"></a>
|
|
52
55
|
|
|
53
56
|
Whenever the user handles the request, the result may be:
|
|
54
57
|
|
|
@@ -22,7 +22,7 @@ const fastify = require('fastify')({
|
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
Enabling the logger with appropriate configuration for both local development
|
|
25
|
-
and production and test environment requires bit more configuration:
|
|
25
|
+
and production and test environment requires a bit more configuration:
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
28
|
const envToLogger = {
|
|
@@ -108,6 +108,7 @@ serialize objects with `req`, `res`, and `err` properties. The object received
|
|
|
108
108
|
by `req` is the Fastify [`Request`](./Request.md) object, while the object
|
|
109
109
|
received by `res` is the Fastify [`Reply`](./Reply.md) object. This behaviour
|
|
110
110
|
can be customized by specifying custom serializers.
|
|
111
|
+
|
|
111
112
|
```js
|
|
112
113
|
const fastify = require('fastify')({
|
|
113
114
|
logger: {
|
|
@@ -152,6 +153,34 @@ const fastify = require('fastify')({
|
|
|
152
153
|
}
|
|
153
154
|
});
|
|
154
155
|
```
|
|
156
|
+
|
|
157
|
+
**Note**: In certain cases, the [`Reply`](./Reply.md) object passed to the `res`
|
|
158
|
+
serializer cannot be fully constructed. When writing a custom `res` serializer,
|
|
159
|
+
it is necessary to check for the existence of any properties on `reply` aside
|
|
160
|
+
from `statusCode`, which is always present. For example, the existence of
|
|
161
|
+
`getHeaders` must be verified before it can be called:
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
const fastify = require('fastify')({
|
|
165
|
+
logger: {
|
|
166
|
+
transport: {
|
|
167
|
+
target: 'pino-pretty'
|
|
168
|
+
},
|
|
169
|
+
serializers: {
|
|
170
|
+
res (reply) {
|
|
171
|
+
// The default
|
|
172
|
+
return {
|
|
173
|
+
statusCode: reply.statusCode
|
|
174
|
+
headers: typeof reply.getHeaders === 'function'
|
|
175
|
+
? reply.getHeaders()
|
|
176
|
+
: {}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
155
184
|
**Note**: The body cannot be serialized inside a `req` method because the
|
|
156
185
|
request is serialized when we create the child logger. At that time, the body is
|
|
157
186
|
not yet parsed.
|
|
@@ -167,6 +196,10 @@ app.addHook('preHandler', function (req, reply, done) {
|
|
|
167
196
|
})
|
|
168
197
|
```
|
|
169
198
|
|
|
199
|
+
**Note**: Care should be take to ensure serializers never throw, as an error
|
|
200
|
+
thrown from a serializer has the potential to cause the Node process to exit.
|
|
201
|
+
See the [Pino documentation](https://getpino.io/#/docs/api?id=opt-serializers)
|
|
202
|
+
on serializers for more information.
|
|
170
203
|
|
|
171
204
|
*Any logger other than Pino will ignore this option.*
|
|
172
205
|
|
package/docs/Reference/Reply.md
CHANGED
|
@@ -708,6 +708,20 @@ fastify.get('/streams', async function (request, reply) {
|
|
|
708
708
|
return reply
|
|
709
709
|
})
|
|
710
710
|
```
|
|
711
|
+
|
|
712
|
+
#### TypedArrays
|
|
713
|
+
<a id="send-typedarrays"></a>
|
|
714
|
+
|
|
715
|
+
`send` manages TypedArray and sets the `'Content-Type'=application/octet-stream'`
|
|
716
|
+
header if not already set.
|
|
717
|
+
```js
|
|
718
|
+
const fs = require('fs')
|
|
719
|
+
fastify.get('/streams', function (request, reply) {
|
|
720
|
+
const typedArray = new Uint16Array(10)
|
|
721
|
+
reply.send(typedArray)
|
|
722
|
+
})
|
|
723
|
+
```
|
|
724
|
+
|
|
711
725
|
#### Errors
|
|
712
726
|
<a id="errors"></a>
|
|
713
727
|
|
|
@@ -789,6 +803,11 @@ fastify.setErrorHandler(function (error, request, reply) {
|
|
|
789
803
|
})
|
|
790
804
|
```
|
|
791
805
|
|
|
806
|
+
Beware that calling `reply.send(error)` in your custom error handler will send
|
|
807
|
+
the error to the default error handler.
|
|
808
|
+
Check out the [Reply Lifecycle](./Lifecycle.md#reply-lifecycle)
|
|
809
|
+
for more information.
|
|
810
|
+
|
|
792
811
|
The not found errors generated by the router will use the
|
|
793
812
|
[`setNotFoundHandler`](./Server.md#setnotfoundhandler)
|
|
794
813
|
|
|
@@ -145,7 +145,7 @@ console.log(validate({ foo: 0.5 })) // false
|
|
|
145
145
|
console.log(validate.errors) // validation errors
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
See [.
|
|
148
|
+
See [.compileValidationSchema(schema, [httpStatus])](#compilevalidationschema)
|
|
149
149
|
for more information on how to compile validation function.
|
|
150
150
|
|
|
151
151
|
### .compileValidationSchema(schema, [httpPart])
|
package/docs/Reference/Server.md
CHANGED
|
@@ -63,6 +63,7 @@ describes the properties available in that options object.
|
|
|
63
63
|
- [prefix](#prefix)
|
|
64
64
|
- [pluginName](#pluginname)
|
|
65
65
|
- [hasPlugin](#hasplugin)
|
|
66
|
+
- [listeningOrigin](#listeningOrigin)
|
|
66
67
|
- [log](#log)
|
|
67
68
|
- [version](#version)
|
|
68
69
|
- [inject](#inject)
|
|
@@ -828,8 +829,16 @@ URLs.
|
|
|
828
829
|
> Rewriting a URL will modify the `url` property of the `req` object
|
|
829
830
|
|
|
830
831
|
```js
|
|
831
|
-
|
|
832
|
-
|
|
832
|
+
// @param {object} req The raw Node.js HTTP request, not the `FastifyRequest` object.
|
|
833
|
+
// @this Fastify The root Fastify instance (not an encapsulated instance).
|
|
834
|
+
// @returns {string} The path that the request should be mapped to.
|
|
835
|
+
function rewriteUrl (req) {
|
|
836
|
+
if (req.url === '/hi') {
|
|
837
|
+
this.log.debug({ originalUrl: req.url, url: '/hello' }, 'rewrite url');
|
|
838
|
+
return '/hello'
|
|
839
|
+
} else {
|
|
840
|
+
return req.url;
|
|
841
|
+
}
|
|
833
842
|
}
|
|
834
843
|
```
|
|
835
844
|
|
|
@@ -1231,6 +1240,15 @@ fastify.ready(() => {
|
|
|
1231
1240
|
})
|
|
1232
1241
|
```
|
|
1233
1242
|
|
|
1243
|
+
### listeningOrigin
|
|
1244
|
+
<a id="listeningOrigin"></a>
|
|
1245
|
+
|
|
1246
|
+
The current origin the server is listening to.
|
|
1247
|
+
For example, a TCP socket based server returns
|
|
1248
|
+
a base address like `http://127.0.0.1:3000`,
|
|
1249
|
+
and a Unix socket server will return the socket
|
|
1250
|
+
path, e.g. `fastify.temp.sock`.
|
|
1251
|
+
|
|
1234
1252
|
#### log
|
|
1235
1253
|
<a id="log"></a>
|
|
1236
1254
|
|
|
@@ -19,8 +19,9 @@ All the examples in this section are using the [JSON Schema Draft
|
|
|
19
19
|
> use with user-provided schemas. See [Ajv](https://npm.im/ajv) and
|
|
20
20
|
> [fast-json-stringify](https://npm.im/fast-json-stringify) for more details.
|
|
21
21
|
>
|
|
22
|
-
>
|
|
23
|
-
> feature](https://ajv.js.org/guide/async-validation.html)
|
|
22
|
+
> Regardless the [`$async` Ajv
|
|
23
|
+
> feature](https://ajv.js.org/guide/async-validation.html) is supported
|
|
24
|
+
> by Fastify, it should not be used as
|
|
24
25
|
> part of the first validation strategy. This option is used to access Databases
|
|
25
26
|
> and reading them during the validation process may lead to Denial of Service
|
|
26
27
|
> Attacks to your application. If you need to run `async` tasks, use [Fastify's
|
package/fastify.d.ts
CHANGED
|
@@ -148,7 +148,12 @@ declare namespace fastify {
|
|
|
148
148
|
req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, FastifySchema, TypeProvider>,
|
|
149
149
|
res: FastifyReply<RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>, RequestGeneric, FastifyContextConfig, SchemaCompiler, TypeProvider>
|
|
150
150
|
) => void,
|
|
151
|
-
rewriteUrl?: (
|
|
151
|
+
rewriteUrl?: (
|
|
152
|
+
// The RawRequestDefaultExpression, RawReplyDefaultExpression, and FastifyTypeProviderDefault parameters
|
|
153
|
+
// should be narrowed further but those generic parameters are not passed to this FastifyServerOptions type
|
|
154
|
+
this: FastifyInstance<RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>, Logger, FastifyTypeProviderDefault>,
|
|
155
|
+
req: RawRequestDefaultExpression<RawServer>
|
|
156
|
+
) => string,
|
|
152
157
|
schemaErrorFormatter?: SchemaErrorFormatter,
|
|
153
158
|
/**
|
|
154
159
|
* listener to error events emitted by client connections
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '4.
|
|
3
|
+
const VERSION = '4.18.0'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('http')
|
|
@@ -348,6 +348,16 @@ function fastify (options) {
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
Object.defineProperties(fastify, {
|
|
351
|
+
listeningOrigin: {
|
|
352
|
+
get () {
|
|
353
|
+
const address = this.addresses().slice(-1).pop()
|
|
354
|
+
/* istanbul ignore if windows: unix socket is not testable on Windows platform */
|
|
355
|
+
if (typeof address === 'string') {
|
|
356
|
+
return address
|
|
357
|
+
}
|
|
358
|
+
return `${this[kOptions].https ? 'https' : 'http'}://${address.address}:${address.port}`
|
|
359
|
+
}
|
|
360
|
+
},
|
|
351
361
|
pluginName: {
|
|
352
362
|
configurable: true,
|
|
353
363
|
get () {
|
|
@@ -606,7 +616,6 @@ function fastify (options) {
|
|
|
606
616
|
} else if (name === 'onReady') {
|
|
607
617
|
this[kHooks].add(name, fn)
|
|
608
618
|
} else if (name === 'onRoute') {
|
|
609
|
-
this[kHooks].validate(name, fn)
|
|
610
619
|
this[kHooks].add(name, fn)
|
|
611
620
|
} else {
|
|
612
621
|
this.after((err, done) => {
|
|
@@ -689,7 +698,7 @@ function fastify (options) {
|
|
|
689
698
|
const reply = new Reply(res, request, childLogger)
|
|
690
699
|
return frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)
|
|
691
700
|
}
|
|
692
|
-
const body = `{"error":"Bad Request","message":"'${path}' is not a valid url component","statusCode":400}`
|
|
701
|
+
const body = `{"error":"Bad Request","code":"FST_ERR_BAD_URL","message":"'${path}' is not a valid url component","statusCode":400}`
|
|
693
702
|
res.writeHead(400, {
|
|
694
703
|
'Content-Type': 'application/json',
|
|
695
704
|
'Content-Length': body.length
|
|
@@ -784,16 +793,12 @@ function fastify (options) {
|
|
|
784
793
|
// only call isAsyncConstraint once
|
|
785
794
|
if (isAsync === undefined) isAsync = router.isAsyncConstraint()
|
|
786
795
|
if (rewriteUrl) {
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
} else {
|
|
794
|
-
const err = new FST_ERR_ROUTE_REWRITE_NOT_STR(req.url, typeof url)
|
|
795
|
-
req.destroy(err)
|
|
796
|
-
}
|
|
796
|
+
const url = rewriteUrl.call(fastify, req)
|
|
797
|
+
if (typeof url === 'string') {
|
|
798
|
+
req.url = url
|
|
799
|
+
} else {
|
|
800
|
+
const err = new FST_ERR_ROUTE_REWRITE_NOT_STR(req.url, typeof url)
|
|
801
|
+
req.destroy(err)
|
|
797
802
|
}
|
|
798
803
|
}
|
|
799
804
|
router.routing(req, res, buildAsyncConstraintCallback(isAsync, req, res))
|
package/lib/contentTypeParser.js
CHANGED
|
@@ -248,7 +248,9 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
248
248
|
payload.removeListener('error', onEnd)
|
|
249
249
|
|
|
250
250
|
if (err !== undefined) {
|
|
251
|
-
err.statusCode
|
|
251
|
+
if (!(typeof err.statusCode === 'number' && err.statusCode >= 400)) {
|
|
252
|
+
err.statusCode = 400
|
|
253
|
+
}
|
|
252
254
|
reply[kReplyIsError] = true
|
|
253
255
|
reply.code(err.statusCode).send(err)
|
|
254
256
|
return
|
package/lib/errors.js
CHANGED
|
@@ -336,6 +336,11 @@ const codes = {
|
|
|
336
336
|
'Missing handler function for "%s:%s" route.',
|
|
337
337
|
500
|
|
338
338
|
),
|
|
339
|
+
FST_ERR_ROUTE_METHOD_INVALID: createError(
|
|
340
|
+
'FST_ERR_ROUTE_METHOD_INVALID',
|
|
341
|
+
'Provided method is invalid!',
|
|
342
|
+
500
|
|
343
|
+
),
|
|
339
344
|
FST_ERR_ROUTE_METHOD_NOT_SUPPORTED: createError(
|
|
340
345
|
'FST_ERR_ROUTE_METHOD_NOT_SUPPORTED',
|
|
341
346
|
'%s method is not supported.',
|
package/lib/handleRequest.js
CHANGED
|
@@ -89,14 +89,25 @@ function preValidationCallback (err, request, reply) {
|
|
|
89
89
|
return
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
const
|
|
93
|
-
|
|
92
|
+
const validationErr = validateSchema(reply[kRouteContext], request)
|
|
93
|
+
const isAsync = (validationErr && typeof validationErr.then === 'function') || false
|
|
94
|
+
|
|
95
|
+
if (isAsync) {
|
|
96
|
+
const cb = validationCompleted.bind(null, request, reply)
|
|
97
|
+
validationErr.then(cb, cb)
|
|
98
|
+
} else {
|
|
99
|
+
validationCompleted(request, reply, validationErr)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function validationCompleted (request, reply, validationErr) {
|
|
104
|
+
if (validationErr) {
|
|
94
105
|
if (reply[kRouteContext].attachValidation === false) {
|
|
95
|
-
reply.send(
|
|
106
|
+
reply.send(validationErr)
|
|
96
107
|
return
|
|
97
108
|
}
|
|
98
109
|
|
|
99
|
-
reply.request.validationError =
|
|
110
|
+
reply.request.validationError = validationErr
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
// preHandler hook
|
package/lib/reply.js
CHANGED
|
@@ -148,11 +148,12 @@ Reply.prototype.send = function (payload) {
|
|
|
148
148
|
return this
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
if (
|
|
151
|
+
if (payload?.buffer instanceof ArrayBuffer) {
|
|
152
152
|
if (hasContentType === false) {
|
|
153
153
|
this[kReplyHeaders]['content-type'] = CONTENT_TYPE.OCTET
|
|
154
154
|
}
|
|
155
|
-
|
|
155
|
+
const payloadToSend = Buffer.isBuffer(payload) ? payload : Buffer.from(payload.buffer)
|
|
156
|
+
onSendHook(this, payloadToSend)
|
|
156
157
|
return this
|
|
157
158
|
}
|
|
158
159
|
|
package/lib/route.js
CHANGED
|
@@ -27,6 +27,7 @@ const {
|
|
|
27
27
|
FST_ERR_ROUTE_HANDLER_NOT_FN,
|
|
28
28
|
FST_ERR_ROUTE_MISSING_HANDLER,
|
|
29
29
|
FST_ERR_ROUTE_METHOD_NOT_SUPPORTED,
|
|
30
|
+
FST_ERR_ROUTE_METHOD_INVALID,
|
|
30
31
|
FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED,
|
|
31
32
|
FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT
|
|
32
33
|
} = require('./errors')
|
|
@@ -185,10 +186,12 @@ function buildRouting (options) {
|
|
|
185
186
|
if (Array.isArray(opts.method)) {
|
|
186
187
|
// eslint-disable-next-line no-var
|
|
187
188
|
for (var i = 0; i < opts.method.length; ++i) {
|
|
188
|
-
|
|
189
|
+
opts.method[i] = normalizeAndValidateMethod(opts.method[i])
|
|
190
|
+
validateSchemaBodyOption(opts.method[i], path, opts.schema)
|
|
189
191
|
}
|
|
190
192
|
} else {
|
|
191
|
-
|
|
193
|
+
opts.method = normalizeAndValidateMethod(opts.method)
|
|
194
|
+
validateSchemaBodyOption(opts.method, path, opts.schema)
|
|
192
195
|
}
|
|
193
196
|
|
|
194
197
|
if (!opts.handler) {
|
|
@@ -526,11 +529,19 @@ function handleTimeout () {
|
|
|
526
529
|
)
|
|
527
530
|
}
|
|
528
531
|
|
|
529
|
-
function
|
|
532
|
+
function normalizeAndValidateMethod (method) {
|
|
533
|
+
if (typeof method !== 'string') {
|
|
534
|
+
throw new FST_ERR_ROUTE_METHOD_INVALID()
|
|
535
|
+
}
|
|
536
|
+
method = method.toUpperCase()
|
|
530
537
|
if (supportedMethods.indexOf(method) === -1) {
|
|
531
538
|
throw new FST_ERR_ROUTE_METHOD_NOT_SUPPORTED(method)
|
|
532
539
|
}
|
|
533
540
|
|
|
541
|
+
return method
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function validateSchemaBodyOption (method, path, schema) {
|
|
534
545
|
if ((method === 'GET' || method === 'HEAD') && schema && schema.body) {
|
|
535
546
|
throw new FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED(method, path)
|
|
536
547
|
}
|
package/lib/schemas.js
CHANGED
|
@@ -44,6 +44,16 @@ Schemas.prototype.getSchema = function (schemaId) {
|
|
|
44
44
|
return this.store[schemaId]
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Checks whether a schema is a non-plain object.
|
|
49
|
+
*
|
|
50
|
+
* @param {*} schema the schema to check
|
|
51
|
+
* @returns {boolean} true if schema has a custom prototype
|
|
52
|
+
*/
|
|
53
|
+
function isCustomSchemaPrototype (schema) {
|
|
54
|
+
return typeof schema === 'object' && Object.getPrototypeOf(schema) !== Object.prototype
|
|
55
|
+
}
|
|
56
|
+
|
|
47
57
|
function normalizeSchema (routeSchemas, serverOptions) {
|
|
48
58
|
if (routeSchemas[kSchemaVisited]) {
|
|
49
59
|
return routeSchemas
|
|
@@ -60,33 +70,20 @@ function normalizeSchema (routeSchemas, serverOptions) {
|
|
|
60
70
|
|
|
61
71
|
generateFluentSchema(routeSchemas)
|
|
62
72
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
routeSchemas[
|
|
67
|
-
return routeSchemas
|
|
73
|
+
for (const key of SCHEMAS_SOURCE) {
|
|
74
|
+
const schema = routeSchemas[key]
|
|
75
|
+
if (schema && !isCustomSchemaPrototype(schema)) {
|
|
76
|
+
routeSchemas[key] = getSchemaAnyway(schema, serverOptions.jsonShorthand)
|
|
68
77
|
}
|
|
69
78
|
}
|
|
70
79
|
|
|
71
|
-
if (routeSchemas.body) {
|
|
72
|
-
routeSchemas.body = getSchemaAnyway(routeSchemas.body, serverOptions.jsonShorthand)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (routeSchemas.headers) {
|
|
76
|
-
routeSchemas.headers = getSchemaAnyway(routeSchemas.headers, serverOptions.jsonShorthand)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (routeSchemas.querystring) {
|
|
80
|
-
routeSchemas.querystring = getSchemaAnyway(routeSchemas.querystring, serverOptions.jsonShorthand)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (routeSchemas.params) {
|
|
84
|
-
routeSchemas.params = getSchemaAnyway(routeSchemas.params, serverOptions.jsonShorthand)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
80
|
if (routeSchemas.response) {
|
|
88
81
|
const httpCodes = Object.keys(routeSchemas.response)
|
|
89
82
|
for (const code of httpCodes) {
|
|
83
|
+
if (isCustomSchemaPrototype(routeSchemas.response[code])) {
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
|
|
90
87
|
const contentProperty = routeSchemas.response[code].content
|
|
91
88
|
|
|
92
89
|
let hasContentMultipleContentTypes = false
|
package/lib/server.js
CHANGED
|
@@ -127,6 +127,7 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
|
|
|
127
127
|
cb: (_ignoreErr) => {
|
|
128
128
|
binded++
|
|
129
129
|
|
|
130
|
+
/* istanbul ignore next: the else won't be taken unless listening fails */
|
|
130
131
|
if (!_ignoreErr) {
|
|
131
132
|
this[kServerBindings].push(secondaryServer)
|
|
132
133
|
}
|
|
@@ -144,6 +145,7 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
|
|
|
144
145
|
mainServer.on('unref', closeSecondary)
|
|
145
146
|
mainServer.on('close', closeSecondary)
|
|
146
147
|
mainServer.on('error', closeSecondary)
|
|
148
|
+
this[kState].listening = false
|
|
147
149
|
listenCallback.call(this, secondaryServer, secondaryOpts)()
|
|
148
150
|
}
|
|
149
151
|
}
|
|
@@ -278,19 +280,22 @@ function compileValidateHTTPVersion (options) {
|
|
|
278
280
|
|
|
279
281
|
function getServerInstance (options, httpHandler) {
|
|
280
282
|
let server = null
|
|
283
|
+
// node@20 do not accepts options as boolean
|
|
284
|
+
// we need to provide proper https option
|
|
285
|
+
const httpsOptions = options.https === true ? {} : options.https
|
|
281
286
|
if (options.serverFactory) {
|
|
282
287
|
server = options.serverFactory(httpHandler, options)
|
|
283
288
|
} else if (options.http2) {
|
|
284
|
-
if (
|
|
285
|
-
server = http2().createSecureServer(
|
|
289
|
+
if (typeof httpsOptions === 'object') {
|
|
290
|
+
server = http2().createSecureServer(httpsOptions, httpHandler)
|
|
286
291
|
} else {
|
|
287
292
|
server = http2().createServer(httpHandler)
|
|
288
293
|
}
|
|
289
294
|
server.on('session', sessionTimeout(options.http2SessionTimeout))
|
|
290
295
|
} else {
|
|
291
296
|
// this is http1
|
|
292
|
-
if (
|
|
293
|
-
server = https.createServer(
|
|
297
|
+
if (httpsOptions) {
|
|
298
|
+
server = https.createServer(httpsOptions, httpHandler)
|
|
294
299
|
} else {
|
|
295
300
|
server = http.createServer(options.http, httpHandler)
|
|
296
301
|
}
|