fastify 4.4.0 → 4.5.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/build/build-validation.js +1 -1
- package/docs/Guides/Ecosystem.md +4 -0
- package/docs/Reference/Routes.md +2 -1
- package/docs/Reference/Server.md +16 -6
- package/docs/Reference/Validation-and-Serialization.md +1 -1
- package/fastify.d.ts +1 -1
- package/fastify.js +3 -3
- package/lib/configValidator.js +68 -19
- package/lib/reqIdGenFactory.js +13 -2
- package/lib/route.js +1 -3
- package/lib/server.js +1 -0
- package/package.json +2 -8
- package/test/logger.test.js +108 -0
- package/test/schema-examples.test.js +54 -0
- package/test/types/fastify.test-d.ts +1 -0
- package/test/types/instance.test-d.ts +1 -1
- package/test/upgrade.test.js +53 -0
- package/types/instance.d.ts +1 -1
|
@@ -97,7 +97,7 @@ const schema = {
|
|
|
97
97
|
onProtoPoisoning: { type: 'string', default: defaultInitOptions.onProtoPoisoning },
|
|
98
98
|
onConstructorPoisoning: { type: 'string', default: defaultInitOptions.onConstructorPoisoning },
|
|
99
99
|
pluginTimeout: { type: 'integer', default: defaultInitOptions.pluginTimeout },
|
|
100
|
-
requestIdHeader: { type: 'string', default: defaultInitOptions.requestIdHeader },
|
|
100
|
+
requestIdHeader: { anyOf: [{ enum: [false] }, { type: 'string' }], default: defaultInitOptions.requestIdHeader },
|
|
101
101
|
requestIdLogLabel: { type: 'string', default: defaultInitOptions.requestIdLogLabel },
|
|
102
102
|
http2SessionTimeout: { type: 'integer', default: defaultInitOptions.http2SessionTimeout },
|
|
103
103
|
exposeHeadRoutes: { type: 'boolean', default: defaultInitOptions.exposeHeadRoutes },
|
package/docs/Guides/Ecosystem.md
CHANGED
|
@@ -328,6 +328,8 @@ section.
|
|
|
328
328
|
- [`fastify-http2https`](https://github.com/lolo32/fastify-http2https) Redirect
|
|
329
329
|
HTTP requests to HTTPS, both using the same port number, or different response
|
|
330
330
|
on HTTP and HTTPS.
|
|
331
|
+
- [`fastify-https-always`](https://github.com/mattbishop/fastify-https-always)
|
|
332
|
+
Lightweight, proxy-aware redirect plugin from HTTP to HTTPS.
|
|
331
333
|
- [`fastify-https-redirect`](https://github.com/tomsvogel/fastify-https-redirect)
|
|
332
334
|
Fastify plugin for auto-redirect from HTTP to HTTPS.
|
|
333
335
|
- [`fastify-impressions`](https://github.com/manju4ever/fastify-impressions)
|
|
@@ -426,6 +428,8 @@ section.
|
|
|
426
428
|
- [`fastify-orientdb`](https://github.com/mahmed8003/fastify-orientdb) Fastify
|
|
427
429
|
OrientDB connection plugin, with which you can share the OrientDB connection
|
|
428
430
|
across every part of your server.
|
|
431
|
+
- [`fastify-osm`](https://github.com/gzileni/fastify-osm) Fastify
|
|
432
|
+
OSM plugin to run overpass queries by OpenStreetMap.
|
|
429
433
|
- [`fastify-peekaboo`](https://github.com/simone-sanfratello/fastify-peekaboo)
|
|
430
434
|
Fastify plugin for memoize responses by expressive settings.
|
|
431
435
|
- [`fastify-piscina`](https://github.com/piscinajs/fastify-piscina) A worker
|
package/docs/Reference/Routes.md
CHANGED
|
@@ -658,7 +658,7 @@ fastify.inject({
|
|
|
658
658
|
>
|
|
659
659
|
> ```js
|
|
660
660
|
> const append = require('vary').append
|
|
661
|
-
> fastify.addHook('onSend',
|
|
661
|
+
> fastify.addHook('onSend', (req, reply, payload, done) => {
|
|
662
662
|
> if (req.headers['accept-version']) { // or the custom header you are using
|
|
663
663
|
> let value = reply.getHeader('Vary') || ''
|
|
664
664
|
> const header = Array.isArray(value) ? value.join(', ') : String(value)
|
|
@@ -666,6 +666,7 @@ fastify.inject({
|
|
|
666
666
|
> reply.header('Vary', value)
|
|
667
667
|
> }
|
|
668
668
|
> }
|
|
669
|
+
> done()
|
|
669
670
|
> })
|
|
670
671
|
> ```
|
|
671
672
|
|
package/docs/Reference/Server.md
CHANGED
|
@@ -55,7 +55,7 @@ describes the properties available in that options object.
|
|
|
55
55
|
- [routing](#routing)
|
|
56
56
|
- [route](#route)
|
|
57
57
|
- [close](#close)
|
|
58
|
-
- [decorate
|
|
58
|
+
- [decorate*](#decorate)
|
|
59
59
|
- [register](#register)
|
|
60
60
|
- [addHook](#addhook)
|
|
61
61
|
- [prefix](#prefix)
|
|
@@ -488,11 +488,18 @@ about safe regexp: [Safe-regex2](https://www.npmjs.com/package/safe-regex2)
|
|
|
488
488
|
### `requestIdHeader`
|
|
489
489
|
<a id="factory-request-id-header"></a>
|
|
490
490
|
|
|
491
|
-
The header name used to
|
|
491
|
+
The header name used to set the request-id. See [the
|
|
492
492
|
request-id](./Logging.md#logging-request-id) section.
|
|
493
|
+
Setting `requestIdHeader` to `false` will always use [genReqId](#genreqid)
|
|
493
494
|
|
|
494
495
|
+ Default: `'request-id'`
|
|
495
|
-
|
|
496
|
+
|
|
497
|
+
```js
|
|
498
|
+
const fastify = require('fastify')({
|
|
499
|
+
requestIdHeader: 'x-custom-id', // -> use 'X-Custom-Id' header if available
|
|
500
|
+
//requestIdHeader: false, // -> always use genReqId
|
|
501
|
+
})
|
|
502
|
+
```
|
|
496
503
|
### `requestIdLogLabel`
|
|
497
504
|
<a id="factory-request-id-log-label"></a>
|
|
498
505
|
|
|
@@ -1112,12 +1119,15 @@ fastify.register(function (instance, opts, done) {
|
|
|
1112
1119
|
<a id="pluginName"></a>
|
|
1113
1120
|
|
|
1114
1121
|
Name of the current plugin. The root plugin is called `'fastify'`. There are
|
|
1115
|
-
|
|
1122
|
+
different ways to define a name (in order).
|
|
1116
1123
|
|
|
1117
1124
|
1. If you use [fastify-plugin](https://github.com/fastify/fastify-plugin) the
|
|
1118
1125
|
metadata `name` is used.
|
|
1119
|
-
2. If
|
|
1120
|
-
|
|
1126
|
+
2. If the exported plugin has the `Symbol.for('fastify.display-name')` property,
|
|
1127
|
+
then the value of that property is used.
|
|
1128
|
+
Example: `pluginFn[Symbol.for('fastify.display-name')] = "Custom Name"`
|
|
1129
|
+
3. If you `module.exports` a plugin the filename is used.
|
|
1130
|
+
4. If you use a regular [function
|
|
1121
1131
|
declaration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Defining_functions)
|
|
1122
1132
|
the function name is used.
|
|
1123
1133
|
|
|
@@ -268,7 +268,7 @@ fastify.listen({ port: 3000 }, (err) => {
|
|
|
268
268
|
```sh
|
|
269
269
|
curl -X GET "http://localhost:3000/?ids=1
|
|
270
270
|
|
|
271
|
-
{"params":{"
|
|
271
|
+
{"params":{"ids":["1"]}}
|
|
272
272
|
```
|
|
273
273
|
|
|
274
274
|
You can also specify a custom schema validator for each parameter type (body,
|
package/fastify.d.ts
CHANGED
|
@@ -123,7 +123,7 @@ export type FastifyServerOptions<
|
|
|
123
123
|
serializerOpts?: FJSOptions | Record<string, unknown>,
|
|
124
124
|
serverFactory?: FastifyServerFactory<RawServer>,
|
|
125
125
|
caseSensitive?: boolean,
|
|
126
|
-
requestIdHeader?: string,
|
|
126
|
+
requestIdHeader?: string | false,
|
|
127
127
|
requestIdLogLabel?: string;
|
|
128
128
|
jsonShorthand?: boolean;
|
|
129
129
|
genReqId?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault>(req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, FastifySchema, TypeProvider>) => string,
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '4.
|
|
3
|
+
const VERSION = '4.5.0'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('http')
|
|
@@ -98,8 +98,8 @@ function fastify (options) {
|
|
|
98
98
|
|
|
99
99
|
validateBodyLimitOption(options.bodyLimit)
|
|
100
100
|
|
|
101
|
-
const requestIdHeader = options.requestIdHeader || defaultInitOptions.requestIdHeader
|
|
102
|
-
const genReqId = options.genReqId
|
|
101
|
+
const requestIdHeader = (options.requestIdHeader === false) ? false : (options.requestIdHeader || defaultInitOptions.requestIdHeader)
|
|
102
|
+
const genReqId = reqIdGenFactory(requestIdHeader, options.genReqId)
|
|
103
103
|
const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
|
|
104
104
|
const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
|
|
105
105
|
const disableRequestLogging = options.disableRequestLogging || false
|
package/lib/configValidator.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
module.exports = validate10;
|
|
5
5
|
module.exports.default = validate10;
|
|
6
|
-
const schema11 = {"type":"object","additionalProperties":false,"properties":{"connectionTimeout":{"type":"integer","default":0},"keepAliveTimeout":{"type":"integer","default":72000},"forceCloseConnections":{"oneOf":[{"type":"string","pattern":"idle"},{"type":"boolean"}]},"maxRequestsPerSocket":{"type":"integer","default":0,"nullable":true},"requestTimeout":{"type":"integer","default":0},"bodyLimit":{"type":"integer","default":1048576},"caseSensitive":{"type":"boolean","default":true},"allowUnsafeRegex":{"type":"boolean","default":false},"http2":{"type":"boolean"},"https":{"if":{"not":{"oneOf":[{"type":"boolean"},{"type":"null"},{"type":"object","additionalProperties":false,"required":["allowHTTP1"],"properties":{"allowHTTP1":{"type":"boolean"}}}]}},"then":{"setDefaultValue":true}},"ignoreTrailingSlash":{"type":"boolean","default":false},"ignoreDuplicateSlashes":{"type":"boolean","default":false},"disableRequestLogging":{"type":"boolean","default":false},"jsonShorthand":{"type":"boolean","default":true},"maxParamLength":{"type":"integer","default":100},"onProtoPoisoning":{"type":"string","default":"error"},"onConstructorPoisoning":{"type":"string","default":"error"},"pluginTimeout":{"type":"integer","default":10000},"requestIdHeader":{"type":"string","default":"request-id"},"requestIdLogLabel":{"type":"string","default":"reqId"},"http2SessionTimeout":{"type":"integer","default":72000},"exposeHeadRoutes":{"type":"boolean","default":true},"versioning":{"type":"object","additionalProperties":true,"required":["storage","deriveVersion"],"properties":{"storage":{},"deriveVersion":{}}},"constraints":{"type":"object","additionalProperties":{"type":"object","required":["name","storage","validate","deriveConstraint"],"additionalProperties":true,"properties":{"name":{"type":"string"},"storage":{},"validate":{},"deriveConstraint":{}}}}}};
|
|
6
|
+
const schema11 = {"type":"object","additionalProperties":false,"properties":{"connectionTimeout":{"type":"integer","default":0},"keepAliveTimeout":{"type":"integer","default":72000},"forceCloseConnections":{"oneOf":[{"type":"string","pattern":"idle"},{"type":"boolean"}]},"maxRequestsPerSocket":{"type":"integer","default":0,"nullable":true},"requestTimeout":{"type":"integer","default":0},"bodyLimit":{"type":"integer","default":1048576},"caseSensitive":{"type":"boolean","default":true},"allowUnsafeRegex":{"type":"boolean","default":false},"http2":{"type":"boolean"},"https":{"if":{"not":{"oneOf":[{"type":"boolean"},{"type":"null"},{"type":"object","additionalProperties":false,"required":["allowHTTP1"],"properties":{"allowHTTP1":{"type":"boolean"}}}]}},"then":{"setDefaultValue":true}},"ignoreTrailingSlash":{"type":"boolean","default":false},"ignoreDuplicateSlashes":{"type":"boolean","default":false},"disableRequestLogging":{"type":"boolean","default":false},"jsonShorthand":{"type":"boolean","default":true},"maxParamLength":{"type":"integer","default":100},"onProtoPoisoning":{"type":"string","default":"error"},"onConstructorPoisoning":{"type":"string","default":"error"},"pluginTimeout":{"type":"integer","default":10000},"requestIdHeader":{"anyOf":[{"enum":[false]},{"type":"string"}],"default":"request-id"},"requestIdLogLabel":{"type":"string","default":"reqId"},"http2SessionTimeout":{"type":"integer","default":72000},"exposeHeadRoutes":{"type":"boolean","default":true},"versioning":{"type":"object","additionalProperties":true,"required":["storage","deriveVersion"],"properties":{"storage":{},"deriveVersion":{}}},"constraints":{"type":"object","additionalProperties":{"type":"object","required":["name","storage","validate","deriveConstraint"],"additionalProperties":true,"properties":{"name":{"type":"string"},"storage":{},"validate":{},"deriveConstraint":{}}}}}};
|
|
7
7
|
const func2 = Object.prototype.hasOwnProperty;
|
|
8
8
|
const pattern0 = new RegExp("idle", "u");
|
|
9
9
|
|
|
@@ -837,6 +837,23 @@ var valid0 = _errs55 === errors;
|
|
|
837
837
|
if(valid0){
|
|
838
838
|
let data19 = data.requestIdHeader;
|
|
839
839
|
const _errs57 = errors;
|
|
840
|
+
const _errs58 = errors;
|
|
841
|
+
let valid6 = false;
|
|
842
|
+
const _errs59 = errors;
|
|
843
|
+
if(!(data19 === false)){
|
|
844
|
+
const err12 = {instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/anyOf/0/enum",keyword:"enum",params:{allowedValues: schema11.properties.requestIdHeader.anyOf[0].enum},message:"must be equal to one of the allowed values"};
|
|
845
|
+
if(vErrors === null){
|
|
846
|
+
vErrors = [err12];
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
vErrors.push(err12);
|
|
850
|
+
}
|
|
851
|
+
errors++;
|
|
852
|
+
}
|
|
853
|
+
var _valid3 = _errs59 === errors;
|
|
854
|
+
valid6 = valid6 || _valid3;
|
|
855
|
+
if(!valid6){
|
|
856
|
+
const _errs60 = errors;
|
|
840
857
|
if(typeof data19 !== "string"){
|
|
841
858
|
let dataType21 = typeof data19;
|
|
842
859
|
let coerced21 = undefined;
|
|
@@ -848,8 +865,14 @@ else if(data19 === null){
|
|
|
848
865
|
coerced21 = "";
|
|
849
866
|
}
|
|
850
867
|
else {
|
|
851
|
-
|
|
852
|
-
|
|
868
|
+
const err13 = {instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/anyOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};
|
|
869
|
+
if(vErrors === null){
|
|
870
|
+
vErrors = [err13];
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
vErrors.push(err13);
|
|
874
|
+
}
|
|
875
|
+
errors++;
|
|
853
876
|
}
|
|
854
877
|
}
|
|
855
878
|
if(coerced21 !== undefined){
|
|
@@ -859,10 +882,36 @@ data["requestIdHeader"] = coerced21;
|
|
|
859
882
|
}
|
|
860
883
|
}
|
|
861
884
|
}
|
|
885
|
+
var _valid3 = _errs60 === errors;
|
|
886
|
+
valid6 = valid6 || _valid3;
|
|
887
|
+
}
|
|
888
|
+
if(!valid6){
|
|
889
|
+
const err14 = {instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};
|
|
890
|
+
if(vErrors === null){
|
|
891
|
+
vErrors = [err14];
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
vErrors.push(err14);
|
|
895
|
+
}
|
|
896
|
+
errors++;
|
|
897
|
+
validate10.errors = vErrors;
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
errors = _errs58;
|
|
902
|
+
if(vErrors !== null){
|
|
903
|
+
if(_errs58){
|
|
904
|
+
vErrors.length = _errs58;
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
vErrors = null;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
862
911
|
var valid0 = _errs57 === errors;
|
|
863
912
|
if(valid0){
|
|
864
913
|
let data20 = data.requestIdLogLabel;
|
|
865
|
-
const
|
|
914
|
+
const _errs62 = errors;
|
|
866
915
|
if(typeof data20 !== "string"){
|
|
867
916
|
let dataType22 = typeof data20;
|
|
868
917
|
let coerced22 = undefined;
|
|
@@ -885,10 +934,10 @@ data["requestIdLogLabel"] = coerced22;
|
|
|
885
934
|
}
|
|
886
935
|
}
|
|
887
936
|
}
|
|
888
|
-
var valid0 =
|
|
937
|
+
var valid0 = _errs62 === errors;
|
|
889
938
|
if(valid0){
|
|
890
939
|
let data21 = data.http2SessionTimeout;
|
|
891
|
-
const
|
|
940
|
+
const _errs64 = errors;
|
|
892
941
|
if(!(((typeof data21 == "number") && (!(data21 % 1) && !isNaN(data21))) && (isFinite(data21)))){
|
|
893
942
|
let dataType23 = typeof data21;
|
|
894
943
|
let coerced23 = undefined;
|
|
@@ -909,10 +958,10 @@ data["http2SessionTimeout"] = coerced23;
|
|
|
909
958
|
}
|
|
910
959
|
}
|
|
911
960
|
}
|
|
912
|
-
var valid0 =
|
|
961
|
+
var valid0 = _errs64 === errors;
|
|
913
962
|
if(valid0){
|
|
914
963
|
let data22 = data.exposeHeadRoutes;
|
|
915
|
-
const
|
|
964
|
+
const _errs66 = errors;
|
|
916
965
|
if(typeof data22 !== "boolean"){
|
|
917
966
|
let coerced24 = undefined;
|
|
918
967
|
if(!(coerced24 !== undefined)){
|
|
@@ -934,12 +983,12 @@ data["exposeHeadRoutes"] = coerced24;
|
|
|
934
983
|
}
|
|
935
984
|
}
|
|
936
985
|
}
|
|
937
|
-
var valid0 =
|
|
986
|
+
var valid0 = _errs66 === errors;
|
|
938
987
|
if(valid0){
|
|
939
988
|
if(data.versioning !== undefined){
|
|
940
989
|
let data23 = data.versioning;
|
|
941
|
-
const
|
|
942
|
-
if(errors ===
|
|
990
|
+
const _errs68 = errors;
|
|
991
|
+
if(errors === _errs68){
|
|
943
992
|
if(data23 && typeof data23 == "object" && !Array.isArray(data23)){
|
|
944
993
|
let missing1;
|
|
945
994
|
if(((data23.storage === undefined) && (missing1 = "storage")) || ((data23.deriveVersion === undefined) && (missing1 = "deriveVersion"))){
|
|
@@ -952,7 +1001,7 @@ validate10.errors = [{instancePath:instancePath+"/versioning",schemaPath:"#/prop
|
|
|
952
1001
|
return false;
|
|
953
1002
|
}
|
|
954
1003
|
}
|
|
955
|
-
var valid0 =
|
|
1004
|
+
var valid0 = _errs68 === errors;
|
|
956
1005
|
}
|
|
957
1006
|
else {
|
|
958
1007
|
var valid0 = true;
|
|
@@ -960,13 +1009,13 @@ var valid0 = true;
|
|
|
960
1009
|
if(valid0){
|
|
961
1010
|
if(data.constraints !== undefined){
|
|
962
1011
|
let data24 = data.constraints;
|
|
963
|
-
const
|
|
964
|
-
if(errors ===
|
|
1012
|
+
const _errs71 = errors;
|
|
1013
|
+
if(errors === _errs71){
|
|
965
1014
|
if(data24 && typeof data24 == "object" && !Array.isArray(data24)){
|
|
966
1015
|
for(const key2 in data24){
|
|
967
1016
|
let data25 = data24[key2];
|
|
968
|
-
const
|
|
969
|
-
if(errors ===
|
|
1017
|
+
const _errs74 = errors;
|
|
1018
|
+
if(errors === _errs74){
|
|
970
1019
|
if(data25 && typeof data25 == "object" && !Array.isArray(data25)){
|
|
971
1020
|
let missing2;
|
|
972
1021
|
if(((((data25.name === undefined) && (missing2 = "name")) || ((data25.storage === undefined) && (missing2 = "storage"))) || ((data25.validate === undefined) && (missing2 = "validate"))) || ((data25.deriveConstraint === undefined) && (missing2 = "deriveConstraint"))){
|
|
@@ -1006,8 +1055,8 @@ validate10.errors = [{instancePath:instancePath+"/constraints/" + key2.replace(/
|
|
|
1006
1055
|
return false;
|
|
1007
1056
|
}
|
|
1008
1057
|
}
|
|
1009
|
-
var
|
|
1010
|
-
if(!
|
|
1058
|
+
var valid7 = _errs74 === errors;
|
|
1059
|
+
if(!valid7){
|
|
1011
1060
|
break;
|
|
1012
1061
|
}
|
|
1013
1062
|
}
|
|
@@ -1017,7 +1066,7 @@ validate10.errors = [{instancePath:instancePath+"/constraints",schemaPath:"#/pro
|
|
|
1017
1066
|
return false;
|
|
1018
1067
|
}
|
|
1019
1068
|
}
|
|
1020
|
-
var valid0 =
|
|
1069
|
+
var valid0 = _errs71 === errors;
|
|
1021
1070
|
}
|
|
1022
1071
|
else {
|
|
1023
1072
|
var valid0 = true;
|
package/lib/reqIdGenFactory.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
module.exports = function () {
|
|
3
|
+
module.exports = function (requestIdHeader, optGenReqId) {
|
|
4
4
|
// 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
|
|
5
5
|
// With this upper bound, if you'll be generating 1k ids/sec, you're going to hit it in ~25 days.
|
|
6
6
|
// This is very likely to happen in real-world applications, hence the limit is enforced.
|
|
@@ -8,8 +8,19 @@ module.exports = function () {
|
|
|
8
8
|
// In the worst cases, it will become a float, losing accuracy.
|
|
9
9
|
const maxInt = 2147483647
|
|
10
10
|
let nextReqId = 0
|
|
11
|
-
|
|
11
|
+
function defaultGenReqId (req) {
|
|
12
12
|
nextReqId = (nextReqId + 1) & maxInt
|
|
13
13
|
return `req-${nextReqId.toString(36)}`
|
|
14
14
|
}
|
|
15
|
+
|
|
16
|
+
const genReqId = optGenReqId || defaultGenReqId
|
|
17
|
+
|
|
18
|
+
if (requestIdHeader) {
|
|
19
|
+
// requestIdHeader = typeof requestIdHeader === 'string' ? requestIdHeader : 'request-id'
|
|
20
|
+
return function (req) {
|
|
21
|
+
return req.headers[requestIdHeader] || genReqId(req)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return genReqId
|
|
15
26
|
}
|
package/lib/route.js
CHANGED
|
@@ -46,7 +46,6 @@ function buildRouting (options) {
|
|
|
46
46
|
|
|
47
47
|
let avvio
|
|
48
48
|
let fourOhFour
|
|
49
|
-
let requestIdHeader
|
|
50
49
|
let requestIdLogLabel
|
|
51
50
|
let logger
|
|
52
51
|
let hasLogger
|
|
@@ -74,7 +73,6 @@ function buildRouting (options) {
|
|
|
74
73
|
validateHTTPVersion = fastifyArgs.validateHTTPVersion
|
|
75
74
|
|
|
76
75
|
globalExposeHeadRoutes = options.exposeHeadRoutes
|
|
77
|
-
requestIdHeader = options.requestIdHeader
|
|
78
76
|
requestIdLogLabel = options.requestIdLogLabel
|
|
79
77
|
genReqId = options.genReqId
|
|
80
78
|
disableRequestLogging = options.disableRequestLogging
|
|
@@ -397,7 +395,7 @@ function buildRouting (options) {
|
|
|
397
395
|
req.headers[kRequestAcceptVersion] = undefined
|
|
398
396
|
}
|
|
399
397
|
|
|
400
|
-
const id =
|
|
398
|
+
const id = genReqId(req)
|
|
401
399
|
|
|
402
400
|
const loggerBinding = {
|
|
403
401
|
[requestIdLogLabel]: id
|
package/lib/server.js
CHANGED
|
@@ -132,6 +132,7 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
|
|
|
132
132
|
|
|
133
133
|
const secondaryServer = getServerInstance(serverOpts, httpHandler)
|
|
134
134
|
const closeSecondary = () => { secondaryServer.close(() => {}) }
|
|
135
|
+
secondaryServer.on('upgrade', mainServer.emit.bind(mainServer, 'upgrade'))
|
|
135
136
|
mainServer.on('unref', closeSecondary)
|
|
136
137
|
mainServer.on('close', closeSecondary)
|
|
137
138
|
mainServer.on('error', closeSecondary)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -137,8 +137,6 @@
|
|
|
137
137
|
"ajv-i18n": "^4.2.0",
|
|
138
138
|
"ajv-merge-patch": "^5.0.1",
|
|
139
139
|
"branch-comparer": "^1.1.0",
|
|
140
|
-
"cors": "^2.8.5",
|
|
141
|
-
"dns-prefetch-control": "^0.3.0",
|
|
142
140
|
"eslint": "^8.16.0",
|
|
143
141
|
"eslint-config-standard": "^17.0.0-1",
|
|
144
142
|
"eslint-import-resolver-node": "^0.3.6",
|
|
@@ -150,10 +148,7 @@
|
|
|
150
148
|
"fastify-plugin": "^4.0.0",
|
|
151
149
|
"fluent-json-schema": "^3.1.0",
|
|
152
150
|
"form-data": "^4.0.0",
|
|
153
|
-
"frameguard": "^4.0.0",
|
|
154
151
|
"h2url": "^0.2.0",
|
|
155
|
-
"helmet": "^5.1.0",
|
|
156
|
-
"hide-powered-by": "^1.1.0",
|
|
157
152
|
"http-errors": "^2.0.0",
|
|
158
153
|
"joi": "^17.6.0",
|
|
159
154
|
"json-schema-to-ts": "^2.5.3",
|
|
@@ -164,7 +159,6 @@
|
|
|
164
159
|
"pump": "^3.0.0",
|
|
165
160
|
"self-cert": "^2.0.0",
|
|
166
161
|
"send": "^0.18.0",
|
|
167
|
-
"serve-static": "^1.15.0",
|
|
168
162
|
"simple-get": "^4.0.1",
|
|
169
163
|
"snazzy": "^9.0.0",
|
|
170
164
|
"split2": "^4.1.0",
|
|
@@ -173,7 +167,7 @@
|
|
|
173
167
|
"tsd": "^0.22.0",
|
|
174
168
|
"typescript": "^4.7.2",
|
|
175
169
|
"undici": "^5.4.0",
|
|
176
|
-
"
|
|
170
|
+
"vary": "^1.1.2",
|
|
177
171
|
"yup": "^0.32.11"
|
|
178
172
|
},
|
|
179
173
|
"dependencies": {
|
package/test/logger.test.js
CHANGED
|
@@ -330,6 +330,51 @@ test('The request id header key can be customized', t => {
|
|
|
330
330
|
})
|
|
331
331
|
})
|
|
332
332
|
|
|
333
|
+
test('The request id header key can be ignored', t => {
|
|
334
|
+
t.plan(9)
|
|
335
|
+
const REQUEST_ID = 'ignore-me'
|
|
336
|
+
|
|
337
|
+
const stream = split(JSON.parse)
|
|
338
|
+
const fastify = Fastify({
|
|
339
|
+
logger: { stream, level: 'info' },
|
|
340
|
+
requestIdHeader: false
|
|
341
|
+
})
|
|
342
|
+
t.teardown(() => fastify.close())
|
|
343
|
+
|
|
344
|
+
fastify.get('/', (req, reply) => {
|
|
345
|
+
t.equal(req.id, 'req-1')
|
|
346
|
+
req.log.info('some log message')
|
|
347
|
+
reply.send({ id: req.id })
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
fastify.inject({
|
|
351
|
+
method: 'GET',
|
|
352
|
+
url: '/',
|
|
353
|
+
headers: {
|
|
354
|
+
'request-id': REQUEST_ID
|
|
355
|
+
}
|
|
356
|
+
}, (err, res) => {
|
|
357
|
+
t.error(err)
|
|
358
|
+
const payload = JSON.parse(res.payload)
|
|
359
|
+
t.equal(payload.id, 'req-1')
|
|
360
|
+
|
|
361
|
+
stream.once('data', line => {
|
|
362
|
+
t.equal(line.reqId, 'req-1')
|
|
363
|
+
t.equal(line.msg, 'incoming request', 'message is set')
|
|
364
|
+
|
|
365
|
+
stream.once('data', line => {
|
|
366
|
+
t.equal(line.reqId, 'req-1')
|
|
367
|
+
t.equal(line.msg, 'some log message', 'message is set')
|
|
368
|
+
|
|
369
|
+
stream.once('data', line => {
|
|
370
|
+
t.equal(line.reqId, 'req-1')
|
|
371
|
+
t.equal(line.msg, 'request completed', 'message is set')
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
|
|
333
378
|
test('The request id header key can be customized along with a custom id generator', t => {
|
|
334
379
|
t.plan(12)
|
|
335
380
|
const REQUEST_ID = '42'
|
|
@@ -393,6 +438,69 @@ test('The request id header key can be customized along with a custom id generat
|
|
|
393
438
|
})
|
|
394
439
|
})
|
|
395
440
|
|
|
441
|
+
test('The request id header key can be ignored along with a custom id generator', t => {
|
|
442
|
+
t.plan(12)
|
|
443
|
+
const REQUEST_ID = 'ignore-me'
|
|
444
|
+
|
|
445
|
+
const stream = split(JSON.parse)
|
|
446
|
+
const fastify = Fastify({
|
|
447
|
+
logger: { stream, level: 'info' },
|
|
448
|
+
requestIdHeader: false,
|
|
449
|
+
genReqId (req) {
|
|
450
|
+
return 'foo'
|
|
451
|
+
}
|
|
452
|
+
})
|
|
453
|
+
t.teardown(() => fastify.close())
|
|
454
|
+
|
|
455
|
+
fastify.get('/one', (req, reply) => {
|
|
456
|
+
t.equal(req.id, 'foo')
|
|
457
|
+
req.log.info('some log message')
|
|
458
|
+
reply.send({ id: req.id })
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
fastify.get('/two', (req, reply) => {
|
|
462
|
+
t.equal(req.id, 'foo')
|
|
463
|
+
req.log.info('some log message 2')
|
|
464
|
+
reply.send({ id: req.id })
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
const matches = [
|
|
468
|
+
{ reqId: 'foo', msg: /incoming request/ },
|
|
469
|
+
{ reqId: 'foo', msg: /some log message/ },
|
|
470
|
+
{ reqId: 'foo', msg: /request completed/ },
|
|
471
|
+
{ reqId: 'foo', msg: /incoming request/ },
|
|
472
|
+
{ reqId: 'foo', msg: /some log message 2/ },
|
|
473
|
+
{ reqId: 'foo', msg: /request completed/ }
|
|
474
|
+
]
|
|
475
|
+
|
|
476
|
+
let i = 0
|
|
477
|
+
stream.on('data', line => {
|
|
478
|
+
t.match(line, matches[i])
|
|
479
|
+
i += 1
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
fastify.inject({
|
|
483
|
+
method: 'GET',
|
|
484
|
+
url: '/one',
|
|
485
|
+
headers: {
|
|
486
|
+
'request-id': REQUEST_ID
|
|
487
|
+
}
|
|
488
|
+
}, (err, res) => {
|
|
489
|
+
t.error(err)
|
|
490
|
+
const payload = JSON.parse(res.payload)
|
|
491
|
+
t.equal(payload.id, 'foo')
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
fastify.inject({
|
|
495
|
+
method: 'GET',
|
|
496
|
+
url: '/two'
|
|
497
|
+
}, (err, res) => {
|
|
498
|
+
t.error(err)
|
|
499
|
+
const payload = JSON.parse(res.payload)
|
|
500
|
+
t.equal(payload.id, 'foo')
|
|
501
|
+
})
|
|
502
|
+
})
|
|
503
|
+
|
|
396
504
|
test('The request id log label can be changed', t => {
|
|
397
505
|
t.plan(6)
|
|
398
506
|
const REQUEST_ID = '42'
|
|
@@ -509,6 +509,60 @@ test('should return custom error messages with ajv-errors', t => {
|
|
|
509
509
|
})
|
|
510
510
|
})
|
|
511
511
|
|
|
512
|
+
test('should be able to handle formats of ajv-formats when added by plugins option', t => {
|
|
513
|
+
t.plan(3)
|
|
514
|
+
|
|
515
|
+
const fastify = Fastify({
|
|
516
|
+
ajv: {
|
|
517
|
+
plugins: [
|
|
518
|
+
require('ajv-formats')
|
|
519
|
+
]
|
|
520
|
+
}
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
const schema = {
|
|
524
|
+
body: {
|
|
525
|
+
type: 'object',
|
|
526
|
+
properties: {
|
|
527
|
+
id: { type: 'string', format: 'uuid' },
|
|
528
|
+
email: { type: 'string', format: 'email' }
|
|
529
|
+
},
|
|
530
|
+
required: ['id', 'email']
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
fastify.post('/', { schema }, function (req, reply) {
|
|
535
|
+
reply.code(200).send(req.body.id)
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
fastify.inject({
|
|
539
|
+
method: 'POST',
|
|
540
|
+
payload: {
|
|
541
|
+
id: '254381a5-888c-4b41-8116-e3b1a54980bd',
|
|
542
|
+
email: 'info@fastify.io'
|
|
543
|
+
},
|
|
544
|
+
url: '/'
|
|
545
|
+
}, (_err, res) => {
|
|
546
|
+
t.equal(res.body, '254381a5-888c-4b41-8116-e3b1a54980bd')
|
|
547
|
+
t.equal(res.statusCode, 200)
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
fastify.inject({
|
|
551
|
+
method: 'POST',
|
|
552
|
+
payload: {
|
|
553
|
+
id: 'invalid',
|
|
554
|
+
email: 'info@fastify.io'
|
|
555
|
+
},
|
|
556
|
+
url: '/'
|
|
557
|
+
}, (_err, res) => {
|
|
558
|
+
t.same(JSON.parse(res.payload), {
|
|
559
|
+
statusCode: 400,
|
|
560
|
+
error: 'Bad Request',
|
|
561
|
+
message: 'body/id must match format "uuid"'
|
|
562
|
+
})
|
|
563
|
+
})
|
|
564
|
+
})
|
|
565
|
+
|
|
512
566
|
test('should return localized error messages with ajv-i18n', t => {
|
|
513
567
|
t.plan(3)
|
|
514
568
|
|
|
@@ -121,6 +121,7 @@ expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerR
|
|
|
121
121
|
expectAssignable<FastifyInstance>(fastify({ serverFactory: () => http.createServer() }))
|
|
122
122
|
expectAssignable<FastifyInstance>(fastify({ caseSensitive: true }))
|
|
123
123
|
expectAssignable<FastifyInstance>(fastify({ requestIdHeader: 'request-id' }))
|
|
124
|
+
expectAssignable<FastifyInstance>(fastify({ requestIdHeader: false }))
|
|
124
125
|
expectAssignable<FastifyInstance>(fastify({ genReqId: () => 'request-id' }))
|
|
125
126
|
expectAssignable<FastifyInstance>(fastify({ trustProxy: true }))
|
|
126
127
|
expectAssignable<FastifyInstance>(fastify({ querystringParser: () => ({ foo: 'bar' }) }))
|
|
@@ -267,7 +267,7 @@ type InitialConfig = Readonly<{
|
|
|
267
267
|
onProtoPoisoning?: 'error' | 'remove' | 'ignore',
|
|
268
268
|
onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
|
|
269
269
|
pluginTimeout?: number,
|
|
270
|
-
requestIdHeader?: string,
|
|
270
|
+
requestIdHeader?: string | false,
|
|
271
271
|
requestIdLogLabel?: string,
|
|
272
272
|
http2SessionTimeout?: number
|
|
273
273
|
}>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test, skip } = require('tap')
|
|
4
|
+
const { lookup } = require('dns').promises
|
|
5
|
+
const Fastify = require('..')
|
|
6
|
+
const { connect } = require('net')
|
|
7
|
+
const { once } = require('events')
|
|
8
|
+
|
|
9
|
+
async function setup () {
|
|
10
|
+
const results = await lookup('localhost', { all: true })
|
|
11
|
+
if (results.length === 1) {
|
|
12
|
+
skip('requires both IPv4 and IPv6')
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test('upgrade to both servers', async t => {
|
|
17
|
+
t.plan(2)
|
|
18
|
+
const app = Fastify()
|
|
19
|
+
app.server.on('upgrade', (req, socket, head) => {
|
|
20
|
+
t.pass(`upgrade event ${JSON.stringify(socket.address())}`)
|
|
21
|
+
socket.end()
|
|
22
|
+
})
|
|
23
|
+
app.get('/', (req, res) => {
|
|
24
|
+
})
|
|
25
|
+
await app.listen()
|
|
26
|
+
t.teardown(app.close.bind(app))
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
const client = connect(app.server.address().port, '127.0.0.1')
|
|
30
|
+
client.write('GET / HTTP/1.1\r\n')
|
|
31
|
+
client.write('Upgrade: websocket\r\n')
|
|
32
|
+
client.write('Connection: Upgrade\r\n')
|
|
33
|
+
client.write('Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n')
|
|
34
|
+
client.write('Sec-WebSocket-Protocol: com.xxx.service.v1\r\n')
|
|
35
|
+
client.write('Sec-WebSocket-Version: 13\r\n\r\n')
|
|
36
|
+
client.write('\r\n\r\n')
|
|
37
|
+
await once(client, 'close')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
const client = connect(app.server.address().port, '::1')
|
|
42
|
+
client.write('GET / HTTP/1.1\r\n')
|
|
43
|
+
client.write('Upgrade: websocket\r\n')
|
|
44
|
+
client.write('Connection: Upgrade\r\n')
|
|
45
|
+
client.write('Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n')
|
|
46
|
+
client.write('Sec-WebSocket-Protocol: com.xxx.service.v1\r\n')
|
|
47
|
+
client.write('Sec-WebSocket-Version: 13\r\n\r\n')
|
|
48
|
+
await once(client, 'close')
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setup()
|
package/types/instance.d.ts
CHANGED
|
@@ -573,7 +573,7 @@ export interface FastifyInstance<
|
|
|
573
573
|
onProtoPoisoning?: ProtoAction,
|
|
574
574
|
onConstructorPoisoning?: ConstructorAction,
|
|
575
575
|
pluginTimeout?: number,
|
|
576
|
-
requestIdHeader?: string,
|
|
576
|
+
requestIdHeader?: string | false,
|
|
577
577
|
requestIdLogLabel?: string,
|
|
578
578
|
http2SessionTimeout?: number
|
|
579
579
|
}>
|