fastify 4.2.1 → 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 +25 -0
- package/docs/Reference/HTTP2.md +1 -3
- package/docs/Reference/Hooks.md +4 -1
- package/docs/Reference/Logging.md +1 -1
- package/docs/Reference/Reply.md +177 -0
- package/docs/Reference/Request.md +171 -0
- package/docs/Reference/Routes.md +6 -3
- package/docs/Reference/Server.md +20 -7
- package/docs/Reference/Validation-and-Serialization.md +1 -1
- package/fastify.d.ts +2 -2
- package/fastify.js +5 -5
- package/lib/configValidator.js +68 -19
- package/lib/contentTypeParser.js +10 -2
- package/lib/context.js +10 -1
- package/lib/errors.js +8 -0
- package/lib/handleRequest.js +2 -2
- package/lib/httpMethods.js +22 -0
- package/lib/reply.js +80 -2
- package/lib/reqIdGenFactory.js +13 -2
- package/lib/request.js +97 -1
- package/lib/route.js +4 -4
- package/lib/server.js +1 -0
- package/lib/symbols.js +15 -9
- package/package.json +3 -9
- package/test/content-parser.test.js +15 -0
- package/test/copy.test.js +41 -0
- package/test/internals/all.test.js +8 -2
- package/test/internals/reply-serialize.test.js +583 -0
- package/test/internals/reply.test.js +4 -1
- package/test/internals/request-validate.test.js +1269 -0
- package/test/internals/request.test.js +11 -2
- package/test/lock.test.js +73 -0
- package/test/logger.test.js +108 -0
- package/test/mkcol.test.js +38 -0
- package/test/move.test.js +45 -0
- package/test/propfind.test.js +108 -0
- package/test/proppatch.test.js +78 -0
- package/test/request-error.test.js +44 -1
- package/test/schema-examples.test.js +54 -0
- package/test/search.test.js +100 -0
- package/test/trace.test.js +21 -0
- package/test/types/fastify.test-d.ts +1 -0
- package/test/types/hooks.test-d.ts +1 -2
- package/test/types/import.ts +1 -1
- package/test/types/instance.test-d.ts +1 -1
- package/test/types/logger.test-d.ts +4 -5
- package/test/types/reply.test-d.ts +44 -3
- package/test/types/request.test-d.ts +10 -29
- package/test/types/type-provider.test-d.ts +79 -7
- package/test/unlock.test.js +41 -0
- package/test/upgrade.test.js +53 -0
- package/types/hooks.d.ts +19 -39
- package/types/instance.d.ts +20 -40
- package/types/logger.d.ts +7 -4
- package/types/reply.d.ts +7 -1
- package/types/request.d.ts +16 -3
- package/types/route.d.ts +23 -29
- package/types/type-provider.d.ts +8 -20
- package/types/utils.d.ts +2 -1
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')
|
|
@@ -34,7 +34,7 @@ const {
|
|
|
34
34
|
const { createServer, compileValidateHTTPVersion } = require('./lib/server')
|
|
35
35
|
const Reply = require('./lib/reply')
|
|
36
36
|
const Request = require('./lib/request')
|
|
37
|
-
const supportedMethods =
|
|
37
|
+
const { supportedMethods } = require('./lib/httpMethods')
|
|
38
38
|
const decorator = require('./lib/decorate')
|
|
39
39
|
const ContentTypeParser = require('./lib/contentTypeParser')
|
|
40
40
|
const SchemaController = require('./lib/schema-controller')
|
|
@@ -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
|
|
@@ -621,7 +621,7 @@ function fastify (options) {
|
|
|
621
621
|
// https://github.com/nodejs/node/blob/6ca23d7846cb47e84fd344543e394e50938540be/lib/_http_server.js#L666
|
|
622
622
|
|
|
623
623
|
// If the socket is not writable, there is no reason to try to send data.
|
|
624
|
-
if (socket.writable
|
|
624
|
+
if (socket.writable) {
|
|
625
625
|
socket.write(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: application/json\r\n\r\n${body}`)
|
|
626
626
|
}
|
|
627
627
|
socket.destroy(err)
|
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/contentTypeParser.js
CHANGED
|
@@ -92,10 +92,18 @@ ContentTypeParser.prototype.existingParser = function (contentType) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
ContentTypeParser.prototype.getParser = function (contentType) {
|
|
95
|
+
if (contentType in this.customParsers) {
|
|
96
|
+
return this.customParsers[contentType]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.cache.has(contentType)) {
|
|
100
|
+
return this.cache.get(contentType)
|
|
101
|
+
}
|
|
102
|
+
|
|
95
103
|
// eslint-disable-next-line no-var
|
|
96
104
|
for (var i = 0; i !== this.parserList.length; ++i) {
|
|
97
105
|
const parserName = this.parserList[i]
|
|
98
|
-
if (contentType.indexOf(parserName)
|
|
106
|
+
if (contentType.indexOf(parserName) !== -1) {
|
|
99
107
|
const parser = this.customParsers[parserName]
|
|
100
108
|
this.cache.set(contentType, parser)
|
|
101
109
|
return parser
|
|
@@ -137,7 +145,7 @@ ContentTypeParser.prototype.remove = function (contentType) {
|
|
|
137
145
|
}
|
|
138
146
|
|
|
139
147
|
ContentTypeParser.prototype.run = function (contentType, handler, request, reply) {
|
|
140
|
-
const parser = this.
|
|
148
|
+
const parser = this.getParser(contentType)
|
|
141
149
|
const resource = new AsyncResource('content-type-parser:run', request)
|
|
142
150
|
|
|
143
151
|
if (parser === undefined) {
|
package/lib/context.js
CHANGED
|
@@ -10,7 +10,9 @@ const {
|
|
|
10
10
|
kBodyLimit,
|
|
11
11
|
kLogLevel,
|
|
12
12
|
kContentTypeParser,
|
|
13
|
-
kRouteByFastify
|
|
13
|
+
kRouteByFastify,
|
|
14
|
+
kRequestValidateWeakMap,
|
|
15
|
+
kReplySerializeWeakMap
|
|
14
16
|
} = require('./symbols.js')
|
|
15
17
|
|
|
16
18
|
// Objects that holds the context of every request
|
|
@@ -24,6 +26,8 @@ function Context ({
|
|
|
24
26
|
logLevel,
|
|
25
27
|
logSerializers,
|
|
26
28
|
attachValidation,
|
|
29
|
+
validatorCompiler,
|
|
30
|
+
serializerCompiler,
|
|
27
31
|
replySerializer,
|
|
28
32
|
schemaErrorFormatter,
|
|
29
33
|
server,
|
|
@@ -54,6 +58,11 @@ function Context ({
|
|
|
54
58
|
this.schemaErrorFormatter = schemaErrorFormatter || server[kSchemaErrorFormatter] || defaultSchemaErrorFormatter
|
|
55
59
|
this[kRouteByFastify] = isFastify
|
|
56
60
|
|
|
61
|
+
this[kRequestValidateWeakMap] = null
|
|
62
|
+
this[kReplySerializeWeakMap] = null
|
|
63
|
+
this.validatorCompiler = validatorCompiler || null
|
|
64
|
+
this.serializerCompiler = serializerCompiler || null
|
|
65
|
+
|
|
57
66
|
this.server = server
|
|
58
67
|
}
|
|
59
68
|
|
package/lib/errors.js
CHANGED
|
@@ -160,6 +160,14 @@ const codes = {
|
|
|
160
160
|
'FST_ERR_BAD_TRAILER_VALUE',
|
|
161
161
|
"Called reply.trailer('%s', fn) with an invalid type: %s. Expected a function."
|
|
162
162
|
),
|
|
163
|
+
FST_ERR_MISSING_SERIALIZATION_FN: createError(
|
|
164
|
+
'FST_ERR_MISSING_SERIALIZATION_FN',
|
|
165
|
+
'Missing serialization function. Key "%s"'
|
|
166
|
+
),
|
|
167
|
+
FST_ERR_REQ_INVALID_VALIDATION_INVOCATION: createError(
|
|
168
|
+
'FST_ERR_REQ_INVALID_VALIDATION_INVOCATION',
|
|
169
|
+
'Invalid validation invocation. Missing validation function for HTTP part "%s" nor schema provided.'
|
|
170
|
+
),
|
|
163
171
|
|
|
164
172
|
/**
|
|
165
173
|
* schemas
|
package/lib/handleRequest.js
CHANGED
|
@@ -18,14 +18,14 @@ function handleRequest (err, request, reply) {
|
|
|
18
18
|
const method = request.raw.method
|
|
19
19
|
const headers = request.headers
|
|
20
20
|
|
|
21
|
-
if (method === 'GET' || method === 'HEAD') {
|
|
21
|
+
if (method === 'GET' || method === 'HEAD' || method === 'SEARCH') {
|
|
22
22
|
handler(request, reply)
|
|
23
23
|
return
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
const contentType = headers['content-type']
|
|
27
27
|
|
|
28
|
-
if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
|
|
28
|
+
if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE') {
|
|
29
29
|
if (contentType === undefined) {
|
|
30
30
|
if (
|
|
31
31
|
headers['transfer-encoding'] === undefined &&
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
supportedMethods: [
|
|
5
|
+
'DELETE',
|
|
6
|
+
'GET',
|
|
7
|
+
'HEAD',
|
|
8
|
+
'PATCH',
|
|
9
|
+
'POST',
|
|
10
|
+
'PUT',
|
|
11
|
+
'OPTIONS',
|
|
12
|
+
'PROPFIND',
|
|
13
|
+
'PROPPATCH',
|
|
14
|
+
'MKCOL',
|
|
15
|
+
'COPY',
|
|
16
|
+
'MOVE',
|
|
17
|
+
'LOCK',
|
|
18
|
+
'UNLOCK',
|
|
19
|
+
'TRACE',
|
|
20
|
+
'SEARCH'
|
|
21
|
+
]
|
|
22
|
+
}
|
package/lib/reply.js
CHANGED
|
@@ -16,7 +16,11 @@ const {
|
|
|
16
16
|
kReplyHasStatusCode,
|
|
17
17
|
kReplyIsRunningOnErrorHook,
|
|
18
18
|
kReplyNextErrorHandler,
|
|
19
|
-
kDisableRequestLogging
|
|
19
|
+
kDisableRequestLogging,
|
|
20
|
+
kSchemaResponse,
|
|
21
|
+
kReplySerializeWeakMap,
|
|
22
|
+
kSchemaController,
|
|
23
|
+
kOptions
|
|
20
24
|
} = require('./symbols.js')
|
|
21
25
|
const { hookRunner, hookIterator, onSendHookRunner } = require('./hooks')
|
|
22
26
|
|
|
@@ -38,7 +42,8 @@ const {
|
|
|
38
42
|
FST_ERR_SEND_INSIDE_ONERR,
|
|
39
43
|
FST_ERR_BAD_STATUS_CODE,
|
|
40
44
|
FST_ERR_BAD_TRAILER_NAME,
|
|
41
|
-
FST_ERR_BAD_TRAILER_VALUE
|
|
45
|
+
FST_ERR_BAD_TRAILER_VALUE,
|
|
46
|
+
FST_ERR_MISSING_SERIALIZATION_FN
|
|
42
47
|
} = require('./errors')
|
|
43
48
|
const warning = require('./warnings')
|
|
44
49
|
|
|
@@ -299,6 +304,79 @@ Reply.prototype.code = function (code) {
|
|
|
299
304
|
|
|
300
305
|
Reply.prototype.status = Reply.prototype.code
|
|
301
306
|
|
|
307
|
+
Reply.prototype.getSerializationFunction = function (schemaOrStatus) {
|
|
308
|
+
let serialize
|
|
309
|
+
|
|
310
|
+
if (typeof schemaOrStatus === 'string' || typeof schemaOrStatus === 'number') {
|
|
311
|
+
serialize = this.context[kSchemaResponse]?.[schemaOrStatus]
|
|
312
|
+
} else if (typeof schemaOrStatus === 'object') {
|
|
313
|
+
serialize = this.context[kReplySerializeWeakMap]?.get(schemaOrStatus)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return serialize
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null) {
|
|
320
|
+
const { request } = this
|
|
321
|
+
const { method, url } = request
|
|
322
|
+
|
|
323
|
+
// Check if serialize function already compiled
|
|
324
|
+
if (this.context[kReplySerializeWeakMap]?.has(schema)) {
|
|
325
|
+
return this.context[kReplySerializeWeakMap].get(schema)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const serializerCompiler = this.context.serializerCompiler ||
|
|
329
|
+
this.server[kSchemaController].serializerCompiler ||
|
|
330
|
+
(
|
|
331
|
+
// We compile the schemas if no custom serializerCompiler is provided
|
|
332
|
+
// nor set
|
|
333
|
+
this.server[kSchemaController].setupSerializer(this.server[kOptions]) ||
|
|
334
|
+
this.server[kSchemaController].serializerCompiler
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
const serializeFn = serializerCompiler({
|
|
338
|
+
schema,
|
|
339
|
+
method,
|
|
340
|
+
url,
|
|
341
|
+
httpStatus
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
// We create a WeakMap to compile the schema only once
|
|
345
|
+
// Its done leazily to avoid add overhead by creating the WeakMap
|
|
346
|
+
// if it is not used
|
|
347
|
+
// TODO: Explore a central cache for all the schemas shared across
|
|
348
|
+
// encapsulated contexts
|
|
349
|
+
if (this.context[kReplySerializeWeakMap] == null) {
|
|
350
|
+
this.context[kReplySerializeWeakMap] = new WeakMap()
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
this.context[kReplySerializeWeakMap].set(schema, serializeFn)
|
|
354
|
+
|
|
355
|
+
return serializeFn
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
Reply.prototype.serializeInput = function (input, schema, httpStatus) {
|
|
359
|
+
let serialize
|
|
360
|
+
httpStatus = typeof schema === 'string' || typeof schema === 'number'
|
|
361
|
+
? schema
|
|
362
|
+
: httpStatus
|
|
363
|
+
|
|
364
|
+
if (httpStatus != null) {
|
|
365
|
+
serialize = this.context[kSchemaResponse]?.[httpStatus]
|
|
366
|
+
|
|
367
|
+
if (serialize == null) throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)
|
|
368
|
+
} else {
|
|
369
|
+
// Check if serialize function already compiled
|
|
370
|
+
if (this.context[kReplySerializeWeakMap]?.has(schema)) {
|
|
371
|
+
serialize = this.context[kReplySerializeWeakMap].get(schema)
|
|
372
|
+
} else {
|
|
373
|
+
serialize = this.compileSerializationSchema(schema, httpStatus)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return serialize(input)
|
|
378
|
+
}
|
|
379
|
+
|
|
302
380
|
Reply.prototype.serialize = function (payload) {
|
|
303
381
|
if (this[kReplySerializer] !== null) {
|
|
304
382
|
return this[kReplySerializer](payload)
|
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/request.js
CHANGED
|
@@ -4,8 +4,24 @@ const proxyAddr = require('proxy-addr')
|
|
|
4
4
|
const semver = require('semver')
|
|
5
5
|
const warning = require('./warnings')
|
|
6
6
|
const {
|
|
7
|
-
kHasBeenDecorated
|
|
7
|
+
kHasBeenDecorated,
|
|
8
|
+
kSchemaBody,
|
|
9
|
+
kSchemaHeaders,
|
|
10
|
+
kSchemaParams,
|
|
11
|
+
kSchemaQuerystring,
|
|
12
|
+
kSchemaController,
|
|
13
|
+
kOptions,
|
|
14
|
+
kRequestValidateWeakMap
|
|
8
15
|
} = require('./symbols')
|
|
16
|
+
const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')
|
|
17
|
+
|
|
18
|
+
const HTTP_PART_SYMBOL_MAP = {
|
|
19
|
+
body: kSchemaBody,
|
|
20
|
+
headers: kSchemaHeaders,
|
|
21
|
+
params: kSchemaParams,
|
|
22
|
+
querystring: kSchemaQuerystring,
|
|
23
|
+
query: kSchemaQuerystring
|
|
24
|
+
}
|
|
9
25
|
|
|
10
26
|
function Request (id, params, req, query, log, context) {
|
|
11
27
|
this.id = id
|
|
@@ -194,6 +210,86 @@ Object.defineProperties(Request.prototype, {
|
|
|
194
210
|
set (headers) {
|
|
195
211
|
this.additionalHeaders = headers
|
|
196
212
|
}
|
|
213
|
+
},
|
|
214
|
+
getValidationFunction: {
|
|
215
|
+
value: function (httpPartOrSchema) {
|
|
216
|
+
if (typeof httpPartOrSchema === 'string') {
|
|
217
|
+
const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
|
|
218
|
+
return this.context[symbol]
|
|
219
|
+
} else if (typeof httpPartOrSchema === 'object') {
|
|
220
|
+
return this.context[kRequestValidateWeakMap]?.get(httpPartOrSchema)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
compileValidationSchema: {
|
|
225
|
+
value: function (schema, httpPart = null) {
|
|
226
|
+
const { method, url } = this
|
|
227
|
+
|
|
228
|
+
if (this.context[kRequestValidateWeakMap]?.has(schema)) {
|
|
229
|
+
return this.context[kRequestValidateWeakMap].get(schema)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const validatorCompiler = this.context.validatorCompiler ||
|
|
233
|
+
this.server[kSchemaController].validatorCompiler ||
|
|
234
|
+
(
|
|
235
|
+
// We compile the schemas if no custom validatorCompiler is provided
|
|
236
|
+
// nor set
|
|
237
|
+
this.server[kSchemaController].setupValidator(this.server[kOptions]) ||
|
|
238
|
+
this.server[kSchemaController].validatorCompiler
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
const validateFn = validatorCompiler({
|
|
242
|
+
schema,
|
|
243
|
+
method,
|
|
244
|
+
url,
|
|
245
|
+
httpPart
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
// We create a WeakMap to compile the schema only once
|
|
249
|
+
// Its done leazily to avoid add overhead by creating the WeakMap
|
|
250
|
+
// if it is not used
|
|
251
|
+
// TODO: Explore a central cache for all the schemas shared across
|
|
252
|
+
// encapsulated contexts
|
|
253
|
+
if (this.context[kRequestValidateWeakMap] == null) {
|
|
254
|
+
this.context[kRequestValidateWeakMap] = new WeakMap()
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.context[kRequestValidateWeakMap].set(schema, validateFn)
|
|
258
|
+
|
|
259
|
+
return validateFn
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
validateInput: {
|
|
263
|
+
value: function (input, schema, httpPart) {
|
|
264
|
+
httpPart = typeof schema === 'string' ? schema : httpPart
|
|
265
|
+
|
|
266
|
+
const symbol = (httpPart != null && typeof httpPart === 'string') && HTTP_PART_SYMBOL_MAP[httpPart]
|
|
267
|
+
let validate
|
|
268
|
+
|
|
269
|
+
if (symbol) {
|
|
270
|
+
// Validate using the HTTP Request Part schema
|
|
271
|
+
validate = this.context[symbol]
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// We cannot compile if the schema is missed
|
|
275
|
+
if (validate == null && (schema == null ||
|
|
276
|
+
typeof schema !== 'object' ||
|
|
277
|
+
Array.isArray(schema))
|
|
278
|
+
) {
|
|
279
|
+
throw new FST_ERR_REQ_INVALID_VALIDATION_INVOCATION(httpPart)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (validate == null) {
|
|
283
|
+
if (this.context[kRequestValidateWeakMap]?.has(schema)) {
|
|
284
|
+
validate = this.context[kRequestValidateWeakMap].get(schema)
|
|
285
|
+
} else {
|
|
286
|
+
// We proceed to compile if there's no validate function yet
|
|
287
|
+
validate = this.compileValidationSchema(schema, httpPart)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return validate(input)
|
|
292
|
+
}
|
|
197
293
|
}
|
|
198
294
|
})
|
|
199
295
|
|
package/lib/route.js
CHANGED
|
@@ -4,7 +4,7 @@ const FindMyWay = require('find-my-way')
|
|
|
4
4
|
const Context = require('./context')
|
|
5
5
|
const handleRequest = require('./handleRequest')
|
|
6
6
|
const { hookRunner, hookIterator, lifecycleHooks } = require('./hooks')
|
|
7
|
-
const supportedMethods =
|
|
7
|
+
const { supportedMethods } = require('./httpMethods')
|
|
8
8
|
const { normalizeSchema } = require('./schemas')
|
|
9
9
|
const { parseHeadOnSendHandlers } = require('./headRoute')
|
|
10
10
|
const warning = require('./warnings')
|
|
@@ -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
|
|
@@ -241,6 +239,8 @@ function buildRouting (options) {
|
|
|
241
239
|
attachValidation: opts.attachValidation,
|
|
242
240
|
schemaErrorFormatter: opts.schemaErrorFormatter,
|
|
243
241
|
replySerializer: this[kReplySerializerDefault],
|
|
242
|
+
validatorCompiler: opts.validatorCompiler,
|
|
243
|
+
serializerCompiler: opts.serializerCompiler,
|
|
244
244
|
server: this,
|
|
245
245
|
isFastify
|
|
246
246
|
})
|
|
@@ -395,7 +395,7 @@ function buildRouting (options) {
|
|
|
395
395
|
req.headers[kRequestAcceptVersion] = undefined
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
const id =
|
|
398
|
+
const id = genReqId(req)
|
|
399
399
|
|
|
400
400
|
const loggerBinding = {
|
|
401
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)
|