fastify 6.0.0-alpha.4 → 6.0.0-alpha.6
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/README.md +1 -0
- package/SECURITY.md +6 -0
- package/build/build-validation.js +2 -0
- package/docs/Guides/Ecosystem.md +12 -53
- package/docs/Guides/Migration-Guide-V5.md +2 -2
- package/docs/Guides/Serverless.md +33 -10
- package/docs/Reference/Errors.md +13 -2
- package/docs/Reference/Hooks.md +13 -0
- package/docs/Reference/Reply.md +787 -681
- package/docs/Reference/Request.md +308 -104
- package/docs/Reference/Routes.md +6 -2
- package/docs/Reference/Server.md +41 -4
- package/docs/Reference/Validation-and-Serialization.md +48 -1
- package/docs/Reference/Warnings.md +2 -0
- package/fastify.d.ts +1 -0
- package/fastify.js +35 -1
- package/lib/config-validator.js +102 -73
- package/lib/content-type-parser.js +35 -14
- package/lib/content-type.js +4 -4
- package/lib/context.js +2 -1
- package/lib/decorate.js +4 -3
- package/lib/error-handler.js +21 -4
- package/lib/error-status.js +4 -2
- package/lib/errors.js +15 -14
- package/lib/handle-request.js +7 -20
- package/lib/reply.js +18 -13
- package/lib/request.js +12 -7
- package/lib/route.js +40 -7
- package/lib/schemas.js +44 -32
- package/lib/validation.js +67 -10
- package/lib/warnings.js +10 -1
- package/package.json +8 -3
- package/test/child-logger-factory.test.js +25 -0
- package/test/close-disposable.test.js +154 -0
- package/test/constrained-routes.test.js +4 -2
- package/test/content-parser.test.js +99 -0
- package/test/decorator.test.js +29 -0
- package/test/diagnostics-channel/error-status.test.js +35 -2
- package/test/handler-timeout.test.js +224 -0
- package/test/internals/all.test.js +5 -0
- package/test/internals/errors.test.js +19 -20
- package/test/internals/initial-config.test.js +2 -0
- package/test/internals/reply-serialize.test.js +20 -0
- package/test/internals/reply.test.js +30 -1
- package/test/internals/request.test.js +50 -0
- package/test/internals/server.test.js +6 -4
- package/test/reply-error.test.js +82 -0
- package/test/reply-media-type.test.js +57 -0
- package/test/reply-trailers.test.js +159 -1
- package/test/rfc-10008.test.js +6 -9
- package/test/router-compile.test.js +241 -0
- package/test/schema-feature.test.js +1 -1
- package/test/schema-serialization.test.js +120 -7
- package/test/schema-validation.test.js +251 -1
- package/test/types/errors.tst.ts +0 -1
- package/test/types/fastify.tst.ts +9 -0
- package/test/types/instance.tst.ts +1 -1
- package/test/types/route.tst.ts +37 -0
- package/test/types/using.tst.ts +20 -0
- package/test/validation-error-handling.test.js +21 -1
- package/types/errors.d.ts +0 -1
- package/types/instance.d.ts +9 -5
- package/types/route.d.ts +4 -2
package/fastify.d.ts
CHANGED
|
@@ -96,6 +96,7 @@ declare namespace fastify {
|
|
|
96
96
|
type TrustProxyFunction = (address: string, hop: number) => boolean
|
|
97
97
|
|
|
98
98
|
export type FastifyRouterOptions<RawServer extends RawServerBase> = Omit<FindMyWayConfigForServer<RawServer>, 'defaultRoute' | 'onBadUrl' | 'onMaxParamLength' | 'querystringParser' | 'constraints'> & {
|
|
99
|
+
compile?: boolean,
|
|
99
100
|
constraints?: {
|
|
100
101
|
[name: string]: ConstraintStrategy<FindMyWayVersion<RawServer>, unknown>,
|
|
101
102
|
},
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '6.0.0-alpha.
|
|
3
|
+
const VERSION = '6.0.0-alpha.6'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('node:http')
|
|
@@ -83,6 +83,23 @@ const { buildErrorHandler } = require('./lib/error-handler.js')
|
|
|
83
83
|
|
|
84
84
|
const initChannel = diagnostics.channel('fastify.initialization')
|
|
85
85
|
|
|
86
|
+
function asyncDisposableHook (disposable) {
|
|
87
|
+
return function (instance, done) {
|
|
88
|
+
disposable[Symbol.asyncDispose]().then(() => done(), done)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function disposableHook (disposable) {
|
|
93
|
+
return function (instance, done) {
|
|
94
|
+
try {
|
|
95
|
+
disposable[Symbol.dispose]()
|
|
96
|
+
done()
|
|
97
|
+
} catch (err) {
|
|
98
|
+
done(err)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
86
103
|
/**
|
|
87
104
|
* @param {import('./fastify.js').FastifyServerOptions} serverOptions
|
|
88
105
|
*/
|
|
@@ -381,6 +398,13 @@ function fastify (serverOptions) {
|
|
|
381
398
|
|
|
382
399
|
// cache the closing value, since we are checking it in an hot path
|
|
383
400
|
avvio.once('preReady', () => {
|
|
401
|
+
// All the routes are registered at this point. Routes added or removed
|
|
402
|
+
// afterwards make find-my-way compile the changed trees again lazily.
|
|
403
|
+
if (options.routerOptions.compile === true) {
|
|
404
|
+
router.compileRoutes()
|
|
405
|
+
fourOhFour.router.compile()
|
|
406
|
+
}
|
|
407
|
+
|
|
384
408
|
fastify.onClose((instance, done) => {
|
|
385
409
|
fastify[kState].closing = true
|
|
386
410
|
router.closeRoutes()
|
|
@@ -598,6 +622,16 @@ function fastify (serverOptions) {
|
|
|
598
622
|
}
|
|
599
623
|
}
|
|
600
624
|
|
|
625
|
+
if (name === 'onClose' && typeof fn !== 'function') {
|
|
626
|
+
if (typeof fn[Symbol.asyncDispose] === 'function') {
|
|
627
|
+
fn = asyncDisposableHook(fn)
|
|
628
|
+
} else if (typeof fn[Symbol.dispose] === 'function') {
|
|
629
|
+
fn = disposableHook(fn)
|
|
630
|
+
} else {
|
|
631
|
+
throw new errorCodes.FST_ERR_HOOK_INVALID_HANDLER(name, Object.prototype.toString.call(fn))
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
601
635
|
if (name === 'onClose') {
|
|
602
636
|
this.onClose(fn.bind(this))
|
|
603
637
|
} else if (name === 'onReady' || name === 'onListen' || name === 'onRoute' || name === 'preClose') {
|
package/lib/config-validator.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},"handlerTimeout":{"type":"integer","default":0},"bodyLimit":{"type":"integer","default":1048576},"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"},"ignoreDuplicateSlashes":{"type":"boolean"},"onProtoPoisoning":{"type":"string","default":"error"},"onConstructorPoisoning":{"type":"string","default":"error"},"pluginTimeout":{"type":"integer","default":10000},"requestIdHeader":{"anyOf":[{"type":"boolean"},{"type":"string"}],"default":false},"http2SessionTimeout":{"type":"integer","default":72000},"exposeHeadRoutes":{"type":"boolean","default":true},"routerOptions":{"type":"object","additionalProperties":false,"properties":{"allowUnsafeRegex":{"type":"boolean","default":false},"caseSensitive":{"type":"boolean","default":true},"constraints":{"type":"object","additionalProperties":{"type":"object","required":["name","storage","validate","deriveConstraint"],"additionalProperties":true,"properties":{"name":{"type":"string"},"storage":{},"validate":{},"deriveConstraint":{}}}},"ignoreTrailingSlash":{"type":"boolean","default":false},"ignoreDuplicateSlashes":{"type":"boolean","default":false},"maxParamLength":{"type":"integer","default":100},"useSemicolonDelimiter":{"type":"boolean","default":false}}}}};
|
|
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},"handlerTimeout":{"type":"integer","default":0},"bodyLimit":{"type":"integer","default":1048576},"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"},"ignoreDuplicateSlashes":{"type":"boolean"},"onProtoPoisoning":{"type":"string","default":"error"},"onConstructorPoisoning":{"type":"string","default":"error"},"pluginTimeout":{"type":"integer","default":10000},"requestIdHeader":{"anyOf":[{"type":"boolean"},{"type":"string"}],"default":false},"http2SessionTimeout":{"type":"integer","default":72000},"exposeHeadRoutes":{"type":"boolean","default":true},"routerOptions":{"type":"object","additionalProperties":false,"properties":{"allowUnsafeRegex":{"type":"boolean","default":false},"caseSensitive":{"type":"boolean","default":true},"compile":{"type":"boolean","default":false},"constraints":{"type":"object","additionalProperties":{"type":"object","required":["name","storage","validate","deriveConstraint"],"additionalProperties":true,"properties":{"name":{"type":"string"},"storage":{},"validate":{},"deriveConstraint":{}}}},"ignoreTrailingSlash":{"type":"boolean","default":false},"ignoreDuplicateSlashes":{"type":"boolean","default":false},"maxParamLength":{"type":"integer","default":100},"useSemicolonDelimiter":{"type":"boolean","default":false}}}}};
|
|
7
7
|
const func2 = Object.prototype.hasOwnProperty;
|
|
8
8
|
const pattern0 = new RegExp("idle", "u");
|
|
9
9
|
|
|
@@ -876,6 +876,9 @@ data18.allowUnsafeRegex = false;
|
|
|
876
876
|
if(data18.caseSensitive === undefined){
|
|
877
877
|
data18.caseSensitive = true;
|
|
878
878
|
}
|
|
879
|
+
if(data18.compile === undefined){
|
|
880
|
+
data18.compile = false;
|
|
881
|
+
}
|
|
879
882
|
if(data18.ignoreTrailingSlash === undefined){
|
|
880
883
|
data18.ignoreTrailingSlash = false;
|
|
881
884
|
}
|
|
@@ -890,7 +893,7 @@ data18.useSemicolonDelimiter = false;
|
|
|
890
893
|
}
|
|
891
894
|
const _errs61 = errors;
|
|
892
895
|
for(const key2 in data18){
|
|
893
|
-
if(!(((((((key2 === "allowUnsafeRegex") || (key2 === "caseSensitive")) || (key2 === "constraints")) || (key2 === "ignoreTrailingSlash")) || (key2 === "ignoreDuplicateSlashes")) || (key2 === "maxParamLength")) || (key2 === "useSemicolonDelimiter"))){
|
|
896
|
+
if(!((((((((key2 === "allowUnsafeRegex") || (key2 === "caseSensitive")) || (key2 === "compile")) || (key2 === "constraints")) || (key2 === "ignoreTrailingSlash")) || (key2 === "ignoreDuplicateSlashes")) || (key2 === "maxParamLength")) || (key2 === "useSemicolonDelimiter"))){
|
|
894
897
|
delete data18[key2];
|
|
895
898
|
}
|
|
896
899
|
}
|
|
@@ -945,43 +948,68 @@ data18["caseSensitive"] = coerced22;
|
|
|
945
948
|
}
|
|
946
949
|
var valid7 = _errs64 === errors;
|
|
947
950
|
if(valid7){
|
|
948
|
-
|
|
949
|
-
let data21 = data18.constraints;
|
|
951
|
+
let data21 = data18.compile;
|
|
950
952
|
const _errs66 = errors;
|
|
951
|
-
if(
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
953
|
+
if(typeof data21 !== "boolean"){
|
|
954
|
+
let coerced23 = undefined;
|
|
955
|
+
if(!(coerced23 !== undefined)){
|
|
956
|
+
if(data21 === "false" || data21 === 0 || data21 === null){
|
|
957
|
+
coerced23 = false;
|
|
958
|
+
}
|
|
959
|
+
else if(data21 === "true" || data21 === 1){
|
|
960
|
+
coerced23 = true;
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
validate10.errors = [{instancePath:instancePath+"/routerOptions/compile",schemaPath:"#/properties/routerOptions/properties/compile/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
if(coerced23 !== undefined){
|
|
968
|
+
data21 = coerced23;
|
|
969
|
+
if(data18 !== undefined){
|
|
970
|
+
data18["compile"] = coerced23;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
var valid7 = _errs66 === errors;
|
|
975
|
+
if(valid7){
|
|
976
|
+
if(data18.constraints !== undefined){
|
|
977
|
+
let data22 = data18.constraints;
|
|
978
|
+
const _errs68 = errors;
|
|
979
|
+
if(errors === _errs68){
|
|
957
980
|
if(data22 && typeof data22 == "object" && !Array.isArray(data22)){
|
|
981
|
+
for(const key3 in data22){
|
|
982
|
+
let data23 = data22[key3];
|
|
983
|
+
const _errs71 = errors;
|
|
984
|
+
if(errors === _errs71){
|
|
985
|
+
if(data23 && typeof data23 == "object" && !Array.isArray(data23)){
|
|
958
986
|
let missing1;
|
|
959
|
-
if(((((
|
|
987
|
+
if(((((data23.name === undefined) && (missing1 = "name")) || ((data23.storage === undefined) && (missing1 = "storage"))) || ((data23.validate === undefined) && (missing1 = "validate"))) || ((data23.deriveConstraint === undefined) && (missing1 = "deriveConstraint"))){
|
|
960
988
|
validate10.errors = [{instancePath:instancePath+"/routerOptions/constraints/" + key3.replace(/~/g, "~0").replace(/\//g, "~1"),schemaPath:"#/properties/routerOptions/properties/constraints/additionalProperties/required",keyword:"required",params:{missingProperty: missing1},message:"must have required property '"+missing1+"'"}];
|
|
961
989
|
return false;
|
|
962
990
|
}
|
|
963
991
|
else {
|
|
964
|
-
if(
|
|
965
|
-
let
|
|
966
|
-
if(typeof
|
|
967
|
-
let
|
|
968
|
-
let
|
|
969
|
-
if(!(
|
|
970
|
-
if(
|
|
971
|
-
|
|
992
|
+
if(data23.name !== undefined){
|
|
993
|
+
let data24 = data23.name;
|
|
994
|
+
if(typeof data24 !== "string"){
|
|
995
|
+
let dataType24 = typeof data24;
|
|
996
|
+
let coerced24 = undefined;
|
|
997
|
+
if(!(coerced24 !== undefined)){
|
|
998
|
+
if(dataType24 == "number" || dataType24 == "boolean"){
|
|
999
|
+
coerced24 = "" + data24;
|
|
972
1000
|
}
|
|
973
|
-
else if(
|
|
974
|
-
|
|
1001
|
+
else if(data24 === null){
|
|
1002
|
+
coerced24 = "";
|
|
975
1003
|
}
|
|
976
1004
|
else {
|
|
977
1005
|
validate10.errors = [{instancePath:instancePath+"/routerOptions/constraints/" + key3.replace(/~/g, "~0").replace(/\//g, "~1")+"/name",schemaPath:"#/properties/routerOptions/properties/constraints/additionalProperties/properties/name/type",keyword:"type",params:{type: "string"},message:"must be string"}];
|
|
978
1006
|
return false;
|
|
979
1007
|
}
|
|
980
1008
|
}
|
|
981
|
-
if(
|
|
982
|
-
|
|
983
|
-
if(
|
|
984
|
-
|
|
1009
|
+
if(coerced24 !== undefined){
|
|
1010
|
+
data24 = coerced24;
|
|
1011
|
+
if(data23 !== undefined){
|
|
1012
|
+
data23["name"] = coerced24;
|
|
985
1013
|
}
|
|
986
1014
|
}
|
|
987
1015
|
}
|
|
@@ -993,7 +1021,7 @@ validate10.errors = [{instancePath:instancePath+"/routerOptions/constraints/" +
|
|
|
993
1021
|
return false;
|
|
994
1022
|
}
|
|
995
1023
|
}
|
|
996
|
-
var valid8 =
|
|
1024
|
+
var valid8 = _errs71 === errors;
|
|
997
1025
|
if(!valid8){
|
|
998
1026
|
break;
|
|
999
1027
|
}
|
|
@@ -1004,38 +1032,13 @@ validate10.errors = [{instancePath:instancePath+"/routerOptions/constraints",sch
|
|
|
1004
1032
|
return false;
|
|
1005
1033
|
}
|
|
1006
1034
|
}
|
|
1007
|
-
var valid7 =
|
|
1035
|
+
var valid7 = _errs68 === errors;
|
|
1008
1036
|
}
|
|
1009
1037
|
else {
|
|
1010
1038
|
var valid7 = true;
|
|
1011
1039
|
}
|
|
1012
1040
|
if(valid7){
|
|
1013
|
-
let
|
|
1014
|
-
const _errs74 = errors;
|
|
1015
|
-
if(typeof data24 !== "boolean"){
|
|
1016
|
-
let coerced24 = undefined;
|
|
1017
|
-
if(!(coerced24 !== undefined)){
|
|
1018
|
-
if(data24 === "false" || data24 === 0 || data24 === null){
|
|
1019
|
-
coerced24 = false;
|
|
1020
|
-
}
|
|
1021
|
-
else if(data24 === "true" || data24 === 1){
|
|
1022
|
-
coerced24 = true;
|
|
1023
|
-
}
|
|
1024
|
-
else {
|
|
1025
|
-
validate10.errors = [{instancePath:instancePath+"/routerOptions/ignoreTrailingSlash",schemaPath:"#/properties/routerOptions/properties/ignoreTrailingSlash/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];
|
|
1026
|
-
return false;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
if(coerced24 !== undefined){
|
|
1030
|
-
data24 = coerced24;
|
|
1031
|
-
if(data18 !== undefined){
|
|
1032
|
-
data18["ignoreTrailingSlash"] = coerced24;
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
var valid7 = _errs74 === errors;
|
|
1037
|
-
if(valid7){
|
|
1038
|
-
let data25 = data18.ignoreDuplicateSlashes;
|
|
1041
|
+
let data25 = data18.ignoreTrailingSlash;
|
|
1039
1042
|
const _errs76 = errors;
|
|
1040
1043
|
if(typeof data25 !== "boolean"){
|
|
1041
1044
|
let coerced25 = undefined;
|
|
@@ -1047,67 +1050,93 @@ else if(data25 === "true" || data25 === 1){
|
|
|
1047
1050
|
coerced25 = true;
|
|
1048
1051
|
}
|
|
1049
1052
|
else {
|
|
1050
|
-
validate10.errors = [{instancePath:instancePath+"/routerOptions/
|
|
1053
|
+
validate10.errors = [{instancePath:instancePath+"/routerOptions/ignoreTrailingSlash",schemaPath:"#/properties/routerOptions/properties/ignoreTrailingSlash/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];
|
|
1051
1054
|
return false;
|
|
1052
1055
|
}
|
|
1053
1056
|
}
|
|
1054
1057
|
if(coerced25 !== undefined){
|
|
1055
1058
|
data25 = coerced25;
|
|
1056
1059
|
if(data18 !== undefined){
|
|
1057
|
-
data18["
|
|
1060
|
+
data18["ignoreTrailingSlash"] = coerced25;
|
|
1058
1061
|
}
|
|
1059
1062
|
}
|
|
1060
1063
|
}
|
|
1061
1064
|
var valid7 = _errs76 === errors;
|
|
1062
1065
|
if(valid7){
|
|
1063
|
-
let data26 = data18.
|
|
1066
|
+
let data26 = data18.ignoreDuplicateSlashes;
|
|
1064
1067
|
const _errs78 = errors;
|
|
1065
|
-
if(
|
|
1066
|
-
let dataType26 = typeof data26;
|
|
1068
|
+
if(typeof data26 !== "boolean"){
|
|
1067
1069
|
let coerced26 = undefined;
|
|
1068
1070
|
if(!(coerced26 !== undefined)){
|
|
1069
|
-
if(
|
|
1070
|
-
|
|
1071
|
-
|
|
1071
|
+
if(data26 === "false" || data26 === 0 || data26 === null){
|
|
1072
|
+
coerced26 = false;
|
|
1073
|
+
}
|
|
1074
|
+
else if(data26 === "true" || data26 === 1){
|
|
1075
|
+
coerced26 = true;
|
|
1072
1076
|
}
|
|
1073
1077
|
else {
|
|
1074
|
-
validate10.errors = [{instancePath:instancePath+"/routerOptions/
|
|
1078
|
+
validate10.errors = [{instancePath:instancePath+"/routerOptions/ignoreDuplicateSlashes",schemaPath:"#/properties/routerOptions/properties/ignoreDuplicateSlashes/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];
|
|
1075
1079
|
return false;
|
|
1076
1080
|
}
|
|
1077
1081
|
}
|
|
1078
1082
|
if(coerced26 !== undefined){
|
|
1079
1083
|
data26 = coerced26;
|
|
1080
1084
|
if(data18 !== undefined){
|
|
1081
|
-
data18["
|
|
1085
|
+
data18["ignoreDuplicateSlashes"] = coerced26;
|
|
1082
1086
|
}
|
|
1083
1087
|
}
|
|
1084
1088
|
}
|
|
1085
1089
|
var valid7 = _errs78 === errors;
|
|
1086
1090
|
if(valid7){
|
|
1087
|
-
let data27 = data18.
|
|
1091
|
+
let data27 = data18.maxParamLength;
|
|
1088
1092
|
const _errs80 = errors;
|
|
1089
|
-
if(typeof data27
|
|
1093
|
+
if(!(((typeof data27 == "number") && (!(data27 % 1) && !isNaN(data27))) && (isFinite(data27)))){
|
|
1094
|
+
let dataType27 = typeof data27;
|
|
1090
1095
|
let coerced27 = undefined;
|
|
1091
1096
|
if(!(coerced27 !== undefined)){
|
|
1092
|
-
if(
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
else if(data27 === "true" || data27 === 1){
|
|
1096
|
-
coerced27 = true;
|
|
1097
|
+
if(dataType27 === "boolean" || data27 === null
|
|
1098
|
+
|| (dataType27 === "string" && data27 && data27 == +data27 && !(data27 % 1))){
|
|
1099
|
+
coerced27 = +data27;
|
|
1097
1100
|
}
|
|
1098
1101
|
else {
|
|
1099
|
-
validate10.errors = [{instancePath:instancePath+"/routerOptions/
|
|
1102
|
+
validate10.errors = [{instancePath:instancePath+"/routerOptions/maxParamLength",schemaPath:"#/properties/routerOptions/properties/maxParamLength/type",keyword:"type",params:{type: "integer"},message:"must be integer"}];
|
|
1100
1103
|
return false;
|
|
1101
1104
|
}
|
|
1102
1105
|
}
|
|
1103
1106
|
if(coerced27 !== undefined){
|
|
1104
1107
|
data27 = coerced27;
|
|
1105
1108
|
if(data18 !== undefined){
|
|
1106
|
-
data18["
|
|
1109
|
+
data18["maxParamLength"] = coerced27;
|
|
1107
1110
|
}
|
|
1108
1111
|
}
|
|
1109
1112
|
}
|
|
1110
1113
|
var valid7 = _errs80 === errors;
|
|
1114
|
+
if(valid7){
|
|
1115
|
+
let data28 = data18.useSemicolonDelimiter;
|
|
1116
|
+
const _errs82 = errors;
|
|
1117
|
+
if(typeof data28 !== "boolean"){
|
|
1118
|
+
let coerced28 = undefined;
|
|
1119
|
+
if(!(coerced28 !== undefined)){
|
|
1120
|
+
if(data28 === "false" || data28 === 0 || data28 === null){
|
|
1121
|
+
coerced28 = false;
|
|
1122
|
+
}
|
|
1123
|
+
else if(data28 === "true" || data28 === 1){
|
|
1124
|
+
coerced28 = true;
|
|
1125
|
+
}
|
|
1126
|
+
else {
|
|
1127
|
+
validate10.errors = [{instancePath:instancePath+"/routerOptions/useSemicolonDelimiter",schemaPath:"#/properties/routerOptions/properties/useSemicolonDelimiter/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];
|
|
1128
|
+
return false;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
if(coerced28 !== undefined){
|
|
1132
|
+
data28 = coerced28;
|
|
1133
|
+
if(data18 !== undefined){
|
|
1134
|
+
data18["useSemicolonDelimiter"] = coerced28;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
var valid7 = _errs82 === errors;
|
|
1139
|
+
}
|
|
1111
1140
|
}
|
|
1112
1141
|
}
|
|
1113
1142
|
}
|
|
@@ -1155,5 +1184,5 @@ return errors === 0;
|
|
|
1155
1184
|
}
|
|
1156
1185
|
|
|
1157
1186
|
|
|
1158
|
-
module.exports.defaultInitOptions = {"connectionTimeout":0,"keepAliveTimeout":72000,"maxRequestsPerSocket":0,"requestTimeout":0,"handlerTimeout":0,"bodyLimit":1048576,"onProtoPoisoning":"error","onConstructorPoisoning":"error","pluginTimeout":10000,"requestIdHeader":false,"http2SessionTimeout":72000,"exposeHeadRoutes":true,"allowErrorHandlerOverride":false,"routerOptions":{"allowUnsafeRegex":false,"caseSensitive":true,"ignoreTrailingSlash":false,"ignoreDuplicateSlashes":false,"maxParamLength":100,"useSemicolonDelimiter":false}}
|
|
1187
|
+
module.exports.defaultInitOptions = {"connectionTimeout":0,"keepAliveTimeout":72000,"maxRequestsPerSocket":0,"requestTimeout":0,"handlerTimeout":0,"bodyLimit":1048576,"onProtoPoisoning":"error","onConstructorPoisoning":"error","pluginTimeout":10000,"requestIdHeader":false,"http2SessionTimeout":72000,"exposeHeadRoutes":true,"allowErrorHandlerOverride":false,"routerOptions":{"allowUnsafeRegex":false,"caseSensitive":true,"compile":false,"ignoreTrailingSlash":false,"ignoreDuplicateSlashes":false,"maxParamLength":100,"useSemicolonDelimiter":false}}
|
|
1159
1188
|
/* c8 ignore stop */
|
|
@@ -222,7 +222,13 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply
|
|
|
222
222
|
return
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
let result
|
|
226
|
+
try {
|
|
227
|
+
result = parser.fn(request, request[kRequestPayloadStream], done)
|
|
228
|
+
} catch (error) {
|
|
229
|
+
done(error, undefined)
|
|
230
|
+
return
|
|
231
|
+
}
|
|
226
232
|
if (result && typeof result.then === 'function') {
|
|
227
233
|
result.then(body => { done(null, body) }, done)
|
|
228
234
|
}
|
|
@@ -253,12 +259,12 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
253
259
|
}
|
|
254
260
|
|
|
255
261
|
let receivedLength = 0
|
|
256
|
-
|
|
262
|
+
const chunks = []
|
|
257
263
|
const payload = request[kRequestPayloadStream] || request.raw
|
|
258
264
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
265
|
+
// Always read raw Buffers and decode once at the end: setEncoding() would
|
|
266
|
+
// allocate a StringDecoder per request and force a Buffer.byteLength()
|
|
267
|
+
// rescan of every decoded chunk just to enforce the byte limit.
|
|
262
268
|
|
|
263
269
|
payload.on('data', onData)
|
|
264
270
|
payload.on('end', onEnd)
|
|
@@ -266,7 +272,11 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
266
272
|
payload.resume()
|
|
267
273
|
|
|
268
274
|
function onData (chunk) {
|
|
269
|
-
|
|
275
|
+
// A custom preParsing stream may still emit strings
|
|
276
|
+
if (typeof chunk === 'string') {
|
|
277
|
+
chunk = Buffer.from(chunk)
|
|
278
|
+
}
|
|
279
|
+
receivedLength += chunk.length
|
|
270
280
|
const { receivedEncodedLength = 0 } = payload
|
|
271
281
|
// The resulting body length must not exceed bodyLimit (see "zip bomb").
|
|
272
282
|
// The case when encoded length is larger than received length is rather theoretical,
|
|
@@ -279,11 +289,7 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
279
289
|
return
|
|
280
290
|
}
|
|
281
291
|
|
|
282
|
-
|
|
283
|
-
body += chunk
|
|
284
|
-
} else {
|
|
285
|
-
body.push(chunk)
|
|
286
|
-
}
|
|
292
|
+
chunks.push(chunk)
|
|
287
293
|
}
|
|
288
294
|
|
|
289
295
|
function onEnd (err) {
|
|
@@ -304,11 +310,26 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
304
310
|
return
|
|
305
311
|
}
|
|
306
312
|
|
|
307
|
-
|
|
308
|
-
|
|
313
|
+
let body
|
|
314
|
+
if (asString) {
|
|
315
|
+
// Decoding the whole body at once also keeps multi-byte characters
|
|
316
|
+
// split across chunks intact
|
|
317
|
+
body = chunks.length === 1
|
|
318
|
+
? chunks[0].toString('utf8')
|
|
319
|
+
: Buffer.concat(chunks, receivedLength).toString('utf8')
|
|
320
|
+
} else {
|
|
321
|
+
// Always copy so the parser never receives a view over the shared
|
|
322
|
+
// socket read buffer
|
|
323
|
+
body = Buffer.concat(chunks, receivedLength)
|
|
309
324
|
}
|
|
310
325
|
|
|
311
|
-
|
|
326
|
+
let result
|
|
327
|
+
try {
|
|
328
|
+
result = parser.fn(request, body, done)
|
|
329
|
+
} catch (error) {
|
|
330
|
+
done(error, undefined)
|
|
331
|
+
return
|
|
332
|
+
}
|
|
312
333
|
if (result && typeof result.then === 'function') {
|
|
313
334
|
result.then(body => { done(null, body) }, done)
|
|
314
335
|
}
|
package/lib/content-type.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { FifoMap: Fifo } = require('toad-cache')
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* keyValuePairsReg matches a single `parameter-name "=" parameter-value`
|
|
@@ -61,9 +61,9 @@ const subtypeNameReg = /^[\w!#$%&'*+.^`|~-]+\s*$/
|
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Content-Type internal shared cache
|
|
64
|
-
* @type {
|
|
64
|
+
* @type {Fifo<ContentType>}
|
|
65
65
|
*/
|
|
66
|
-
const cache = new
|
|
66
|
+
const cache = new Fifo(100)
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* ContentType parses and represents the value of the content-type header.
|
|
@@ -82,7 +82,7 @@ class ContentType {
|
|
|
82
82
|
/**
|
|
83
83
|
* The shared cache of ContentType instances. The cache is used to avoid
|
|
84
84
|
* creating multiple instances of ContentType for the same header value.
|
|
85
|
-
* @type {
|
|
85
|
+
* @type {Fifo<ContentType>}
|
|
86
86
|
*/
|
|
87
87
|
static get cache () { return cache }
|
|
88
88
|
|
package/lib/context.js
CHANGED
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
kReplyCacheSerializeFns,
|
|
17
17
|
kHandlerTimeout
|
|
18
18
|
} = require('./symbols.js')
|
|
19
|
+
const { FST_ERR_VALIDATION } = require('./errors.js')
|
|
19
20
|
|
|
20
21
|
// Object that holds the context of every request
|
|
21
22
|
// Every route holds an instance of this object.
|
|
@@ -89,7 +90,7 @@ function defaultSchemaErrorFormatter (errors, dataVar) {
|
|
|
89
90
|
const e = errors[i]
|
|
90
91
|
text += dataVar + (e.instancePath || '') + ' ' + e.message + separator
|
|
91
92
|
}
|
|
92
|
-
return new
|
|
93
|
+
return new FST_ERR_VALIDATION(text.slice(0, -separator.length))
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
module.exports = Context
|
package/lib/decorate.js
CHANGED
|
@@ -38,11 +38,12 @@ function getInstanceDecorator (name) {
|
|
|
38
38
|
throw new FST_ERR_DEC_UNDECLARED(name, 'instance')
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const decorator = this[name]
|
|
42
|
+
if (typeof decorator === 'function') {
|
|
43
|
+
return decorator.bind(this)
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
return
|
|
46
|
+
return decorator
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
function decorateConstructor (konstructor, name, fn, dependencies) {
|
package/lib/error-handler.js
CHANGED
|
@@ -33,10 +33,27 @@ function handleError (reply, error, cb) {
|
|
|
33
33
|
const context = reply[kRouteContext]
|
|
34
34
|
if (reply[kReplyNextErrorHandler] === false) {
|
|
35
35
|
fallbackErrorHandler(error, reply, function (reply, payload) {
|
|
36
|
+
if (reply.raw.headersSent === true || reply.raw.destroyed === true) {
|
|
37
|
+
// The response has already started transmitting (e.g. an active
|
|
38
|
+
// stream that timed out after its headers were sent). We cannot
|
|
39
|
+
// write a new error response, so abort the in-flight response
|
|
40
|
+
// instead of raising an uncaught ERR_HTTP_HEADERS_SENT.
|
|
41
|
+
reply.raw.destroy(error)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
36
44
|
try {
|
|
37
45
|
reply.raw.writeHead(reply.raw.statusCode, reply[kReplyHeaders])
|
|
38
46
|
} catch (error) {
|
|
39
47
|
reply.server[kLogController].writeHeadError(error, reply.request, reply)
|
|
48
|
+
// If the response has started transmitting, retrying writeHead
|
|
49
|
+
// would raise an uncaught ERR_HTTP_HEADERS_SENT and terminate the
|
|
50
|
+
// process, so abort the in-flight response instead.
|
|
51
|
+
if (error.code === 'ERR_HTTP_HEADERS_SENT' || reply.raw.headersSent === true || reply.raw.destroyed === true) {
|
|
52
|
+
reply.raw.destroy(error)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
// For other writeHead failures (e.g. an invalid header value in
|
|
56
|
+
// the error payload) retry without the offending headers.
|
|
40
57
|
reply.raw.writeHead(reply.raw.statusCode)
|
|
41
58
|
}
|
|
42
59
|
reply.raw.end(payload)
|
|
@@ -134,16 +151,16 @@ function buildErrorHandler (parent = rootErrorHandler, func) {
|
|
|
134
151
|
function setErrorHeaders (error, reply) {
|
|
135
152
|
const res = reply.raw
|
|
136
153
|
let statusCode = res.statusCode
|
|
137
|
-
statusCode = (statusCode >= 400) ? statusCode : 500
|
|
154
|
+
statusCode = (statusCode >= 400 && statusCode <= 599) ? statusCode : 500
|
|
138
155
|
// treat undefined and null as same
|
|
139
156
|
if (error != null) {
|
|
140
157
|
if (error.headers !== undefined) {
|
|
141
158
|
reply.headers(error.headers)
|
|
142
159
|
}
|
|
143
|
-
if (error.
|
|
144
|
-
statusCode = error.status
|
|
145
|
-
} else if (error.statusCode >= 400) {
|
|
160
|
+
if (error.statusCode >= 400 && error.statusCode <= 599) {
|
|
146
161
|
statusCode = error.statusCode
|
|
162
|
+
} else if (error.status >= 400 && error.status <= 599) {
|
|
163
|
+
statusCode = error.status
|
|
147
164
|
}
|
|
148
165
|
}
|
|
149
166
|
res.statusCode = statusCode
|
package/lib/error-status.js
CHANGED
|
@@ -6,8 +6,10 @@ const {
|
|
|
6
6
|
|
|
7
7
|
function setErrorStatusCode (reply, err) {
|
|
8
8
|
if (!reply[kReplyHasStatusCode] || reply.statusCode === 200) {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
let statusCode = 500
|
|
10
|
+
if (err?.statusCode >= 400 && err.statusCode <= 599) statusCode = err.statusCode
|
|
11
|
+
else if (err?.status >= 400 && err.status <= 599) statusCode = err.status
|
|
12
|
+
reply.code(statusCode)
|
|
11
13
|
}
|
|
12
14
|
}
|
|
13
15
|
|