fastify 5.8.5 → 5.10.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/PROJECT_CHARTER.md +1 -1
- package/README.md +7 -10
- package/SECURITY.md +1 -1
- package/SPONSORS.md +6 -4
- package/build/build-validation.js +2 -2
- package/docs/Guides/Database.md +0 -28
- package/docs/Guides/Delay-Accepting-Requests.md +1 -1
- package/docs/Guides/Ecosystem.md +23 -9
- package/docs/Guides/Getting-Started.md +2 -2
- package/docs/Guides/Migration-Guide-V4.md +2 -2
- package/docs/Guides/Migration-Guide-V5.md +1 -1
- package/docs/Guides/Plugins-Guide.md +1 -1
- package/docs/Guides/Prototype-Poisoning.md +3 -3
- package/docs/Guides/Serverless.md +8 -15
- package/docs/Guides/Style-Guide.md +1 -1
- package/docs/Guides/Write-Plugin.md +1 -1
- package/docs/Reference/Encapsulation.md +27 -26
- package/docs/Reference/Errors.md +12 -4
- package/docs/Reference/HTTP2.md +10 -10
- package/docs/Reference/Hooks.md +5 -5
- package/docs/Reference/Index.md +14 -16
- package/docs/Reference/LTS.md +12 -13
- package/docs/Reference/Lifecycle.md +9 -8
- package/docs/Reference/Logging.md +47 -39
- package/docs/Reference/Middleware.md +21 -25
- package/docs/Reference/Principles.md +2 -2
- package/docs/Reference/Reply.md +6 -1
- package/docs/Reference/Request.md +29 -18
- package/docs/Reference/Routes.md +5 -2
- package/docs/Reference/Server.md +138 -11
- package/docs/Reference/Type-Providers.md +53 -9
- package/docs/Reference/TypeScript.md +3 -3
- package/docs/Reference/Validation-and-Serialization.md +15 -2
- package/docs/Reference/Warnings.md +11 -6
- package/eslint.config.js +7 -2
- package/fastify.d.ts +13 -3
- package/fastify.js +60 -31
- package/lib/content-type-parser.js +2 -0
- package/lib/content-type.js +34 -1
- package/lib/context.js +0 -3
- package/lib/decorate.js +11 -3
- package/lib/error-handler.js +10 -28
- package/lib/error-serializer.js +59 -59
- package/lib/errors.js +23 -1
- package/lib/four-oh-four.js +22 -19
- package/lib/handle-request.js +12 -5
- package/lib/log-controller.js +169 -0
- package/lib/logger-factory.js +25 -4
- package/lib/plugin-override.js +2 -1
- package/lib/plugin-utils.js +5 -5
- package/lib/reply.js +96 -50
- package/lib/req-id-gen-factory.js +4 -1
- package/lib/request.js +16 -6
- package/lib/route.js +47 -41
- package/lib/schema-controller.js +1 -1
- package/lib/schemas.js +37 -30
- package/lib/symbols.js +4 -2
- package/lib/validation.js +10 -13
- package/lib/warnings.js +22 -4
- package/package.json +15 -17
- package/scripts/validate-ecosystem-links.js +1 -0
- package/test/bundler/esbuild/package.json +1 -1
- package/test/close-pipelining.test.js +1 -2
- package/test/content-type.test.js +20 -0
- package/test/custom-http-server.test.js +38 -0
- package/test/decorator-instance-properties.test.js +63 -0
- package/test/diagnostics-channel/async-error-handler.test.js +74 -0
- package/test/genReqId.test.js +24 -0
- package/test/hooks.test.js +94 -0
- package/test/http-methods/get.test.js +1 -1
- package/test/http2/plain.test.js +135 -0
- package/test/http2/secure-with-fallback.test.js +1 -1
- package/test/https/https.test.js +1 -2
- package/test/internals/errors.test.js +31 -1
- package/test/internals/logger.test.js +322 -0
- package/test/internals/plugin.test.js +3 -1
- package/test/internals/reply.test.js +35 -4
- package/test/internals/request.test.js +37 -10
- package/test/internals/schema-controller-perf.test.js +33 -0
- package/test/logger/logging.test.js +57 -1
- package/test/logger/options.test.js +38 -1
- package/test/reply-error.test.js +1 -1
- package/test/reply-trailers.test.js +70 -0
- package/test/request-media-type.test.js +105 -0
- package/test/route-prefix.test.js +34 -0
- package/test/router-options.test.js +222 -11
- package/test/schema-serialization.test.js +108 -0
- package/test/schema-validation.test.js +24 -0
- package/test/scripts/validate-ecosystem-links.test.js +40 -57
- package/test/stream.4.test.js +2 -2
- package/test/throw.test.js +14 -0
- package/test/trust-proxy.test.js +70 -30
- package/test/types/content-type-parser.tst.ts +70 -0
- package/test/types/{decorate-request-reply.test-d.ts → decorate-request-reply.tst.ts} +4 -4
- package/test/types/{dummy-plugin.ts → dummy-plugin.mts} +1 -1
- package/test/types/errors.tst.ts +91 -0
- package/test/types/fastify.tst.ts +351 -0
- package/test/types/hooks.tst.ts +578 -0
- package/test/types/instance.tst.ts +597 -0
- package/test/types/{logger.test-d.ts → logger.tst.ts} +61 -62
- package/test/types/plugin.tst.ts +96 -0
- package/test/types/register.tst.ts +245 -0
- package/test/types/reply.tst.ts +297 -0
- package/test/types/request.tst.ts +199 -0
- package/test/types/route.tst.ts +576 -0
- package/test/types/{schema.test-d.ts → schema.tst.ts} +22 -22
- package/test/types/{serverFactory.test-d.ts → serverFactory.tst.ts} +4 -4
- package/test/types/tsconfig.json +9 -0
- package/test/types/{type-provider.test-d.ts → type-provider.tst.ts} +256 -250
- package/test/types/using.tst.ts +14 -0
- package/types/errors.d.ts +4 -0
- package/types/instance.d.ts +2 -0
- package/types/logger.d.ts +22 -0
- package/types/request.d.ts +23 -2
- package/test/types/content-type-parser.test-d.ts +0 -72
- package/test/types/errors.test-d.ts +0 -90
- package/test/types/fastify.test-d.ts +0 -352
- package/test/types/hooks.test-d.ts +0 -550
- package/test/types/import.ts +0 -2
- package/test/types/instance.test-d.ts +0 -588
- package/test/types/plugin.test-d.ts +0 -97
- package/test/types/register.test-d.ts +0 -237
- package/test/types/reply.test-d.ts +0 -254
- package/test/types/request.test-d.ts +0 -188
- package/test/types/route.test-d.ts +0 -553
- package/test/types/using.test-d.ts +0 -17
package/fastify.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Socket } from 'node:net'
|
|
|
6
6
|
import { BuildCompilerFromPool, ValidatorFactory } from '@fastify/ajv-compiler'
|
|
7
7
|
import { FastifyError } from '@fastify/error'
|
|
8
8
|
import { Options as FJSOptions, SerializerFactory } from '@fastify/fast-json-stringify-compiler'
|
|
9
|
-
import { Config as FindMyWayConfig,
|
|
9
|
+
import { ConstraintStrategy, Config as FindMyWayConfig, HTTPVersion } from 'find-my-way'
|
|
10
10
|
import { InjectOptions, CallbackFunc as LightMyRequestCallback, Chain as LightMyRequestChain, Response as LightMyRequestResponse } from 'light-my-request'
|
|
11
11
|
|
|
12
12
|
import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, FastifyContentTypeParser, getDefaultJsonParser, hasContentTypeParser, ProtoAction } from './types/content-type-parser'
|
|
@@ -17,6 +17,7 @@ import { FastifyInstance, FastifyListenOptions, PrintRoutesOptions } from './typ
|
|
|
17
17
|
import {
|
|
18
18
|
FastifyBaseLogger,
|
|
19
19
|
FastifyChildLoggerFactory,
|
|
20
|
+
LogController as LogControllerClass,
|
|
20
21
|
FastifyLogFn,
|
|
21
22
|
FastifyLoggerInstance,
|
|
22
23
|
FastifyLoggerOptions,
|
|
@@ -28,7 +29,7 @@ import { FastifyRegister, FastifyRegisterOptions, RegisterOptions } from './type
|
|
|
28
29
|
import { FastifyReply } from './types/reply'
|
|
29
30
|
import { FastifyRequest, RequestGenericInterface } from './types/request'
|
|
30
31
|
import { RouteGenericInterface, RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler } from './types/route'
|
|
31
|
-
import { FastifySchema,
|
|
32
|
+
import { FastifySchema, FastifySchemaCompiler, FastifySchemaValidationError, FastifySerializerCompiler, SchemaErrorDataVar, SchemaErrorFormatter } from './types/schema'
|
|
32
33
|
import { FastifyServerFactory, FastifyServerFactoryHandler } from './types/server-factory'
|
|
33
34
|
import { FastifyTypeProvider, FastifyTypeProviderDefault, SafePromiseLike } from './types/type-provider'
|
|
34
35
|
import { ContextConfigDefault, HTTPMethods, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault, RequestBodyDefault, RequestHeadersDefault, RequestParamsDefault, RequestQuerystringDefault } from './types/utils'
|
|
@@ -44,6 +45,7 @@ type Fastify = typeof fastify
|
|
|
44
45
|
|
|
45
46
|
declare namespace fastify {
|
|
46
47
|
export const errorCodes: FastifyErrorCodes
|
|
48
|
+
export { LogControllerClass as LogController }
|
|
47
49
|
|
|
48
50
|
export type FastifyHttp2SecureOptions<
|
|
49
51
|
Server extends http2.Http2SecureServer,
|
|
@@ -90,7 +92,7 @@ declare namespace fastify {
|
|
|
90
92
|
|
|
91
93
|
type TrustProxyFunction = (address: string, hop: number) => boolean
|
|
92
94
|
|
|
93
|
-
export type FastifyRouterOptions<RawServer extends RawServerBase> = Omit<FindMyWayConfigForServer<RawServer>, 'defaultRoute' | 'onBadUrl' | 'querystringParser'> & {
|
|
95
|
+
export type FastifyRouterOptions<RawServer extends RawServerBase> = Omit<FindMyWayConfigForServer<RawServer>, 'defaultRoute' | 'onBadUrl' | 'onMaxParamLength' | 'querystringParser'> & {
|
|
94
96
|
defaultRoute?: (
|
|
95
97
|
req: RawRequestDefaultExpression<RawServer>,
|
|
96
98
|
res: RawReplyDefaultExpression<RawServer>
|
|
@@ -100,6 +102,11 @@ declare namespace fastify {
|
|
|
100
102
|
req: RawRequestDefaultExpression<RawServer>,
|
|
101
103
|
res: RawReplyDefaultExpression<RawServer>
|
|
102
104
|
) => void,
|
|
105
|
+
onMaxParamLength?: (
|
|
106
|
+
path: string,
|
|
107
|
+
req: RawRequestDefaultExpression<RawServer>,
|
|
108
|
+
res: RawReplyDefaultExpression<RawServer>
|
|
109
|
+
) => void,
|
|
103
110
|
querystringParser?: (str: string) => { [key: string]: unknown }
|
|
104
111
|
}
|
|
105
112
|
|
|
@@ -121,7 +128,9 @@ declare namespace fastify {
|
|
|
121
128
|
bodyLimit?: number,
|
|
122
129
|
handlerTimeout?: number,
|
|
123
130
|
maxParamLength?: number,
|
|
131
|
+
/** @deprecated Use the `logController` option with `disableRequestLogging` or `isLogDisabled` override instead. Will be removed in `fastify@6`. */
|
|
124
132
|
disableRequestLogging?: boolean | ((req: FastifyRequest) => boolean),
|
|
133
|
+
logController?: LogControllerClass,
|
|
125
134
|
exposeHeadRoutes?: boolean,
|
|
126
135
|
onProtoPoisoning?: ProtoAction,
|
|
127
136
|
onConstructorPoisoning?: ConstructorAction,
|
|
@@ -132,6 +141,7 @@ declare namespace fastify {
|
|
|
132
141
|
caseSensitive?: boolean,
|
|
133
142
|
allowUnsafeRegex?: boolean,
|
|
134
143
|
requestIdHeader?: string | false,
|
|
144
|
+
/** @deprecated Use the `logController` option with `requestIdLogLabel` instead. Will be removed in `fastify@6`. */
|
|
135
145
|
requestIdLogLabel?: string;
|
|
136
146
|
useSemicolonDelimiter?: boolean,
|
|
137
147
|
genReqId?: (req: RawRequestDefaultExpression<RawServer>) => string,
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '5.
|
|
3
|
+
const VERSION = '5.10.0'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('node:http')
|
|
@@ -33,7 +33,8 @@ const {
|
|
|
33
33
|
kChildLoggerFactory,
|
|
34
34
|
kGenReqId,
|
|
35
35
|
kErrorHandlerAlreadySet,
|
|
36
|
-
kHandlerTimeout
|
|
36
|
+
kHandlerTimeout,
|
|
37
|
+
kLogController
|
|
37
38
|
} = require('./lib/symbols.js')
|
|
38
39
|
|
|
39
40
|
const { createServer } = require('./lib/server')
|
|
@@ -44,7 +45,7 @@ const decorator = require('./lib/decorate')
|
|
|
44
45
|
const ContentTypeParser = require('./lib/content-type-parser.js')
|
|
45
46
|
const SchemaController = require('./lib/schema-controller')
|
|
46
47
|
const { Hooks, hookRunnerApplication, supportedHooks } = require('./lib/hooks')
|
|
47
|
-
const { createChildLogger, defaultChildLoggerFactory, createLogger } = require('./lib/logger-factory')
|
|
48
|
+
const { createChildLogger, defaultChildLoggerFactory, createLogger, createLogController, LogController } = require('./lib/logger-factory')
|
|
48
49
|
const pluginUtils = require('./lib/plugin-utils.js')
|
|
49
50
|
const { getGenReqId, reqIdGenFactory } = require('./lib/req-id-gen-factory.js')
|
|
50
51
|
const { buildRouting, validateBodyLimitOption, buildRouterOptions } = require('./lib/route')
|
|
@@ -63,6 +64,7 @@ const { defaultInitOptions } = getSecuredInitialConfig
|
|
|
63
64
|
const {
|
|
64
65
|
FST_ERR_ASYNC_CONSTRAINT,
|
|
65
66
|
FST_ERR_BAD_URL,
|
|
67
|
+
FST_ERR_MAX_PARAM_LENGTH,
|
|
66
68
|
FST_ERR_OPTIONS_NOT_OBJ,
|
|
67
69
|
FST_ERR_QSP_NOT_FN,
|
|
68
70
|
FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN,
|
|
@@ -78,7 +80,7 @@ const {
|
|
|
78
80
|
} = errorCodes
|
|
79
81
|
|
|
80
82
|
const { buildErrorHandler } = require('./lib/error-handler.js')
|
|
81
|
-
const { FSTWRN004 } = require('./lib/warnings.js')
|
|
83
|
+
const { FSTWRN004, FSTDEP023, FSTDEP024 } = require('./lib/warnings.js')
|
|
82
84
|
|
|
83
85
|
const initChannel = diagnostics.channel('fastify.initialization')
|
|
84
86
|
|
|
@@ -89,10 +91,10 @@ function fastify (serverOptions) {
|
|
|
89
91
|
const {
|
|
90
92
|
options,
|
|
91
93
|
genReqId,
|
|
92
|
-
|
|
94
|
+
logController,
|
|
93
95
|
hasLogger,
|
|
94
96
|
initialConfig
|
|
95
|
-
} = processOptions(serverOptions, defaultRoute, onBadUrl)
|
|
97
|
+
} = processOptions(serverOptions, defaultRoute, onBadUrl, onMaxParamLength)
|
|
96
98
|
|
|
97
99
|
// Default router
|
|
98
100
|
const router = buildRouting(options.routerOptions)
|
|
@@ -215,6 +217,7 @@ function fastify (serverOptions) {
|
|
|
215
217
|
},
|
|
216
218
|
// expose logger instance
|
|
217
219
|
log: options.logger,
|
|
220
|
+
[kLogController]: logController,
|
|
218
221
|
// type provider
|
|
219
222
|
withTypeProvider,
|
|
220
223
|
// hooks
|
|
@@ -381,8 +384,7 @@ function fastify (serverOptions) {
|
|
|
381
384
|
hookRunnerApplication('preClose', fastify[kAvvioBoot], fastify, function () {
|
|
382
385
|
if (fastify[kState].listening) {
|
|
383
386
|
/* istanbul ignore next: Cannot test this without Node.js core support */
|
|
384
|
-
if (forceCloseConnections === 'idle') {
|
|
385
|
-
// Not needed in Node 19
|
|
387
|
+
if (forceCloseConnections === 'idle' && options.serverFactory) {
|
|
386
388
|
instance.server.closeIdleConnections()
|
|
387
389
|
/* istanbul ignore next: Cannot test this without Node.js core support */
|
|
388
390
|
} else if (serverHasCloseAllConnections && forceCloseConnections) {
|
|
@@ -423,8 +425,8 @@ function fastify (serverOptions) {
|
|
|
423
425
|
})
|
|
424
426
|
})
|
|
425
427
|
|
|
426
|
-
// Create bad URL context
|
|
427
|
-
const
|
|
428
|
+
// Create route event (bad URL, max param length, etc) context
|
|
429
|
+
const routeEventContext = new Context({
|
|
428
430
|
server: fastify,
|
|
429
431
|
config: {}
|
|
430
432
|
})
|
|
@@ -637,16 +639,13 @@ function fastify (serverOptions) {
|
|
|
637
639
|
|
|
638
640
|
function onBadUrl (path, req, res) {
|
|
639
641
|
if (options.frameworkErrors) {
|
|
640
|
-
const id = getGenReqId(
|
|
641
|
-
const childLogger = createChildLogger(
|
|
642
|
+
const id = getGenReqId(routeEventContext.server, req)
|
|
643
|
+
const childLogger = createChildLogger(routeEventContext, options.logger, req, id)
|
|
642
644
|
|
|
643
|
-
const request = new Request(id, null, req, null, childLogger,
|
|
645
|
+
const request = new Request(id, null, req, null, childLogger, routeEventContext)
|
|
644
646
|
const reply = new Reply(res, request, childLogger)
|
|
645
647
|
|
|
646
|
-
|
|
647
|
-
if (resolvedDisableRequestLogging === false) {
|
|
648
|
-
childLogger.info({ req: request }, 'incoming request')
|
|
649
|
-
}
|
|
648
|
+
routeEventContext.server[kLogController].incomingRequest(request, reply)
|
|
650
649
|
|
|
651
650
|
return options.frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)
|
|
652
651
|
}
|
|
@@ -663,21 +662,43 @@ function fastify (serverOptions) {
|
|
|
663
662
|
res.end(body)
|
|
664
663
|
}
|
|
665
664
|
|
|
665
|
+
function onMaxParamLength (path, req, res) {
|
|
666
|
+
if (options.frameworkErrors) {
|
|
667
|
+
const id = getGenReqId(routeEventContext.server, req)
|
|
668
|
+
const childLogger = createChildLogger(routeEventContext, options.logger, req, id)
|
|
669
|
+
|
|
670
|
+
const request = new Request(id, null, req, null, childLogger, routeEventContext)
|
|
671
|
+
const reply = new Reply(res, request, childLogger)
|
|
672
|
+
|
|
673
|
+
routeEventContext.server[kLogController].incomingRequest(request, reply)
|
|
674
|
+
|
|
675
|
+
return options.frameworkErrors(new FST_ERR_MAX_PARAM_LENGTH(path), request, reply)
|
|
676
|
+
}
|
|
677
|
+
const body = JSON.stringify({
|
|
678
|
+
error: 'Bad Request',
|
|
679
|
+
code: 'FST_ERR_MAX_PARAM_LENGTH',
|
|
680
|
+
message: `'${path}' is exceeding the max param length`,
|
|
681
|
+
statusCode: 414
|
|
682
|
+
})
|
|
683
|
+
res.writeHead(414, {
|
|
684
|
+
'Content-Type': 'application/json',
|
|
685
|
+
'Content-Length': Buffer.byteLength(body)
|
|
686
|
+
})
|
|
687
|
+
res.end(body)
|
|
688
|
+
}
|
|
689
|
+
|
|
666
690
|
function buildAsyncConstraintCallback (isAsync, req, res) {
|
|
667
691
|
if (isAsync === false) return undefined
|
|
668
692
|
return function onAsyncConstraintError (err) {
|
|
669
693
|
if (err) {
|
|
670
694
|
if (options.frameworkErrors) {
|
|
671
|
-
const id = getGenReqId(
|
|
672
|
-
const childLogger = createChildLogger(
|
|
695
|
+
const id = getGenReqId(routeEventContext.server, req)
|
|
696
|
+
const childLogger = createChildLogger(routeEventContext, options.logger, req, id)
|
|
673
697
|
|
|
674
|
-
const request = new Request(id, null, req, null, childLogger,
|
|
698
|
+
const request = new Request(id, null, req, null, childLogger, routeEventContext)
|
|
675
699
|
const reply = new Reply(res, request, childLogger)
|
|
676
700
|
|
|
677
|
-
|
|
678
|
-
if (resolvedDisableRequestLogging === false) {
|
|
679
|
-
childLogger.info({ req: request }, 'incoming request')
|
|
680
|
-
}
|
|
701
|
+
routeEventContext.server[kLogController].incomingRequest(request, reply)
|
|
681
702
|
|
|
682
703
|
return options.frameworkErrors(new FST_ERR_ASYNC_CONSTRAINT(), request, reply)
|
|
683
704
|
}
|
|
@@ -748,7 +769,7 @@ function fastify (serverOptions) {
|
|
|
748
769
|
if (!options.allowErrorHandlerOverride && this[kErrorHandlerAlreadySet]) {
|
|
749
770
|
throw new FST_ERR_ERROR_HANDLER_ALREADY_SET()
|
|
750
771
|
} else if (this[kErrorHandlerAlreadySet]) {
|
|
751
|
-
FSTWRN004(
|
|
772
|
+
FSTWRN004()
|
|
752
773
|
}
|
|
753
774
|
|
|
754
775
|
this[kErrorHandlerAlreadySet] = true
|
|
@@ -821,7 +842,7 @@ function fastify (serverOptions) {
|
|
|
821
842
|
}
|
|
822
843
|
}
|
|
823
844
|
|
|
824
|
-
function processOptions (options, defaultRoute, onBadUrl) {
|
|
845
|
+
function processOptions (options, defaultRoute, onBadUrl, onMaxParamLength) {
|
|
825
846
|
// Options validations
|
|
826
847
|
if (options && typeof options !== 'object') {
|
|
827
848
|
throw new FST_ERR_OPTIONS_NOT_OBJ()
|
|
@@ -848,9 +869,13 @@ function processOptions (options, defaultRoute, onBadUrl) {
|
|
|
848
869
|
|
|
849
870
|
const requestIdHeader = typeof options.requestIdHeader === 'string' && options.requestIdHeader.length !== 0 ? options.requestIdHeader.toLowerCase() : (options.requestIdHeader === true && 'request-id')
|
|
850
871
|
const genReqId = reqIdGenFactory(requestIdHeader, options.genReqId)
|
|
851
|
-
|
|
872
|
+
if (options.requestIdLogLabel !== undefined) {
|
|
873
|
+
FSTDEP024()
|
|
874
|
+
}
|
|
852
875
|
options.bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
|
|
853
|
-
|
|
876
|
+
if (options.disableRequestLogging !== undefined) {
|
|
877
|
+
FSTDEP023()
|
|
878
|
+
}
|
|
854
879
|
|
|
855
880
|
const ajvOptions = Object.assign({
|
|
856
881
|
customOptions: {},
|
|
@@ -866,6 +891,10 @@ function processOptions (options, defaultRoute, onBadUrl) {
|
|
|
866
891
|
|
|
867
892
|
const { logger, hasLogger } = createLogger(options)
|
|
868
893
|
|
|
894
|
+
// the internal logger uses the input logger to execute the logging. This allows the user
|
|
895
|
+
// to customize every internal log line
|
|
896
|
+
const logController = createLogController(options)
|
|
897
|
+
|
|
869
898
|
// Update the options with the fixed values
|
|
870
899
|
options.connectionTimeout = options.connectionTimeout || defaultInitOptions.connectionTimeout
|
|
871
900
|
options.keepAliveTimeout = options.keepAliveTimeout || defaultInitOptions.keepAliveTimeout
|
|
@@ -873,8 +902,6 @@ function processOptions (options, defaultRoute, onBadUrl) {
|
|
|
873
902
|
options.requestTimeout = options.requestTimeout || defaultInitOptions.requestTimeout
|
|
874
903
|
options.logger = logger
|
|
875
904
|
options.requestIdHeader = requestIdHeader
|
|
876
|
-
options.requestIdLogLabel = requestIdLogLabel
|
|
877
|
-
options.disableRequestLogging = disableRequestLogging
|
|
878
905
|
options.ajv = ajvOptions
|
|
879
906
|
options.clientErrorHandler = options.clientErrorHandler || defaultClientErrorHandler
|
|
880
907
|
options.allowErrorHandlerOverride = options.allowErrorHandlerOverride ?? defaultInitOptions.allowErrorHandlerOverride
|
|
@@ -890,6 +917,7 @@ function processOptions (options, defaultRoute, onBadUrl) {
|
|
|
890
917
|
options.routerOptions = buildRouterOptions(options, {
|
|
891
918
|
defaultRoute,
|
|
892
919
|
onBadUrl,
|
|
920
|
+
onMaxParamLength,
|
|
893
921
|
ignoreTrailingSlash: defaultInitOptions.ignoreTrailingSlash,
|
|
894
922
|
ignoreDuplicateSlashes: defaultInitOptions.ignoreDuplicateSlashes,
|
|
895
923
|
maxParamLength: defaultInitOptions.maxParamLength,
|
|
@@ -901,7 +929,7 @@ function processOptions (options, defaultRoute, onBadUrl) {
|
|
|
901
929
|
return {
|
|
902
930
|
options,
|
|
903
931
|
genReqId,
|
|
904
|
-
|
|
932
|
+
logController,
|
|
905
933
|
hasLogger,
|
|
906
934
|
initialConfig
|
|
907
935
|
}
|
|
@@ -981,5 +1009,6 @@ function validateSchemaErrorFormatter (schemaErrorFormatter) {
|
|
|
981
1009
|
*/
|
|
982
1010
|
module.exports = fastify
|
|
983
1011
|
module.exports.errorCodes = errorCodes
|
|
1012
|
+
module.exports.LogController = LogController
|
|
984
1013
|
module.exports.fastify = fastify
|
|
985
1014
|
module.exports.default = fastify
|
|
@@ -118,6 +118,8 @@ ContentTypeParser.prototype.existingParser = function (contentType) {
|
|
|
118
118
|
|
|
119
119
|
ContentTypeParser.prototype.getParser = function (contentType) {
|
|
120
120
|
if (typeof contentType === 'string') {
|
|
121
|
+
const cached = this.cache.get(contentType)
|
|
122
|
+
if (cached !== undefined) return cached
|
|
121
123
|
contentType = new ContentType(contentType)
|
|
122
124
|
}
|
|
123
125
|
const ct = contentType.toString()
|
package/lib/content-type.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { LruMap: Lru } = require('toad-cache')
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* keyValuePairsReg is used to split the parameters list into associated
|
|
5
7
|
* key value pairings. The leading `(?:^|;)\s*` anchor ensures the regex
|
|
@@ -35,6 +37,12 @@ const typeNameReg = /^[\w!#$%&'*+.^`|~-]+$/
|
|
|
35
37
|
*/
|
|
36
38
|
const subtypeNameReg = /^[\w!#$%&'*+.^`|~-]+\s*$/
|
|
37
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Content-Type internal shared cache
|
|
42
|
+
* @type {Lru<ContentType>}
|
|
43
|
+
*/
|
|
44
|
+
const cache = new Lru(100)
|
|
45
|
+
|
|
38
46
|
/**
|
|
39
47
|
* ContentType parses and represents the value of the content-type header.
|
|
40
48
|
*
|
|
@@ -49,6 +57,27 @@ class ContentType {
|
|
|
49
57
|
#parameters = new Map()
|
|
50
58
|
#string
|
|
51
59
|
|
|
60
|
+
/**
|
|
61
|
+
* The shared cache of ContentType instances. The cache is used to avoid
|
|
62
|
+
* creating multiple instances of ContentType for the same header value.
|
|
63
|
+
* @type {Lru<ContentType>}
|
|
64
|
+
*/
|
|
65
|
+
static get cache () { return cache }
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a ContentType instance from a header value. If the value has been
|
|
69
|
+
* previously parsed, the cached instance will be returned.
|
|
70
|
+
* @param {string} headerValue
|
|
71
|
+
* @returns {ContentType | undefined}
|
|
72
|
+
*/
|
|
73
|
+
static from (headerValue) {
|
|
74
|
+
let contentType = cache.get(headerValue)
|
|
75
|
+
if (contentType !== undefined) return contentType
|
|
76
|
+
contentType = new ContentType(headerValue)
|
|
77
|
+
cache.set(headerValue, contentType)
|
|
78
|
+
return contentType
|
|
79
|
+
}
|
|
80
|
+
|
|
52
81
|
constructor (headerValue) {
|
|
53
82
|
if (headerValue == null || headerValue === '' || headerValue === 'undefined') {
|
|
54
83
|
return
|
|
@@ -106,7 +135,11 @@ class ContentType {
|
|
|
106
135
|
|
|
107
136
|
let matches = keyValuePairsReg.exec(paramsList)
|
|
108
137
|
while (matches) {
|
|
109
|
-
|
|
138
|
+
// https://httpwg.org/specs/rfc9110.html#parameter
|
|
139
|
+
// Parameter names are case-insensitive.
|
|
140
|
+
const key = matches[1].toLowerCase()
|
|
141
|
+
// Parameter values might or might not be case-sensitive,
|
|
142
|
+
// depending on the semantics of the parameter name.
|
|
110
143
|
const value = matches[2]
|
|
111
144
|
if (value[0] === '"') {
|
|
112
145
|
if (value.at(-1) !== '"') {
|
package/lib/context.js
CHANGED
|
@@ -6,7 +6,6 @@ const {
|
|
|
6
6
|
kSchemaErrorFormatter,
|
|
7
7
|
kErrorHandler,
|
|
8
8
|
kChildLoggerFactory,
|
|
9
|
-
kOptions,
|
|
10
9
|
kReply,
|
|
11
10
|
kRequest,
|
|
12
11
|
kBodyLimit,
|
|
@@ -24,7 +23,6 @@ function Context ({
|
|
|
24
23
|
schema,
|
|
25
24
|
handler,
|
|
26
25
|
config,
|
|
27
|
-
requestIdLogLabel,
|
|
28
26
|
childLoggerFactory,
|
|
29
27
|
errorHandler,
|
|
30
28
|
bodyLimit,
|
|
@@ -56,7 +54,6 @@ function Context ({
|
|
|
56
54
|
this.onRequestAbort = null
|
|
57
55
|
this.config = config
|
|
58
56
|
this.errorHandler = errorHandler || server[kErrorHandler]
|
|
59
|
-
this.requestIdLogLabel = requestIdLogLabel || server[kOptions].requestIdLogLabel
|
|
60
57
|
this.childLoggerFactory = childLoggerFactory || server[kChildLoggerFactory]
|
|
61
58
|
this._middie = null
|
|
62
59
|
this._parserOptions = {
|
package/lib/decorate.js
CHANGED
|
@@ -47,7 +47,7 @@ function getInstanceDecorator (name) {
|
|
|
47
47
|
|
|
48
48
|
function decorateConstructor (konstructor, name, fn, dependencies) {
|
|
49
49
|
const instance = konstructor.prototype
|
|
50
|
-
if (Object.hasOwn(instance, name) || hasKey(konstructor, name)) {
|
|
50
|
+
if (Object.hasOwn(instance, name) || hasKey(konstructor, name) || hasInstanceProperty(konstructor, name)) {
|
|
51
51
|
throw new FST_ERR_DEC_ALREADY_PRESENT(name)
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -87,19 +87,27 @@ function checkExistence (instance, name) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function hasKey (fn, name) {
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
return fn.props ? fn.props.some(p => p.key === name) : false
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function hasInstanceProperty (konstructor, name) {
|
|
94
|
+
let k = konstructor
|
|
95
|
+
while (k) {
|
|
96
|
+
if (k.instanceProperties && k.instanceProperties.has(name)) return true
|
|
97
|
+
k = k.parent
|
|
92
98
|
}
|
|
93
99
|
return false
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
function checkRequestExistence (name) {
|
|
97
103
|
if (name && hasKey(this[kRequest], name)) return true
|
|
104
|
+
if (name && hasInstanceProperty(this[kRequest], name)) return true
|
|
98
105
|
return checkExistence(this[kRequest].prototype, name)
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
function checkReplyExistence (name) {
|
|
102
109
|
if (name && hasKey(this[kReply], name)) return true
|
|
110
|
+
if (name && hasInstanceProperty(this[kReply], name)) return true
|
|
103
111
|
return checkExistence(this[kReply].prototype, name)
|
|
104
112
|
}
|
|
105
113
|
|
package/lib/error-handler.js
CHANGED
|
@@ -8,7 +8,8 @@ const {
|
|
|
8
8
|
kReplyNextErrorHandler,
|
|
9
9
|
kReplyIsRunningOnErrorHook,
|
|
10
10
|
kRouteContext,
|
|
11
|
-
|
|
11
|
+
kLogController,
|
|
12
|
+
kDiagnosticsStore
|
|
12
13
|
} = require('./symbols.js')
|
|
13
14
|
|
|
14
15
|
const {
|
|
@@ -19,7 +20,6 @@ const {
|
|
|
19
20
|
const { getSchemaSerializer } = require('./schemas')
|
|
20
21
|
|
|
21
22
|
const serializeError = require('./error-serializer')
|
|
22
|
-
|
|
23
23
|
const rootErrorHandler = {
|
|
24
24
|
func: defaultErrorHandler,
|
|
25
25
|
toJSON () {
|
|
@@ -36,12 +36,7 @@ function handleError (reply, error, cb) {
|
|
|
36
36
|
try {
|
|
37
37
|
reply.raw.writeHead(reply.raw.statusCode, reply[kReplyHeaders])
|
|
38
38
|
} catch (error) {
|
|
39
|
-
|
|
40
|
-
reply.log.warn(
|
|
41
|
-
{ req: reply.request, res: reply, err: error },
|
|
42
|
-
error?.message
|
|
43
|
-
)
|
|
44
|
-
}
|
|
39
|
+
reply.server[kLogController].writeHeadError(error, reply.request, reply)
|
|
45
40
|
reply.raw.writeHead(reply.raw.statusCode)
|
|
46
41
|
}
|
|
47
42
|
reply.raw.end(payload)
|
|
@@ -69,7 +64,8 @@ function handleError (reply, error, cb) {
|
|
|
69
64
|
const result = func(error, reply.request, reply)
|
|
70
65
|
if (result !== undefined) {
|
|
71
66
|
if (result !== null && typeof result.then === 'function') {
|
|
72
|
-
|
|
67
|
+
const store = reply[kDiagnosticsStore] || null
|
|
68
|
+
wrapThenable(result, reply, store)
|
|
73
69
|
} else {
|
|
74
70
|
reply.send(result)
|
|
75
71
|
}
|
|
@@ -82,21 +78,7 @@ function handleError (reply, error, cb) {
|
|
|
82
78
|
function defaultErrorHandler (error, request, reply) {
|
|
83
79
|
setErrorHeaders(error, reply)
|
|
84
80
|
setErrorStatusCode(reply, error)
|
|
85
|
-
|
|
86
|
-
if (!reply.log[kDisableRequestLogging]) {
|
|
87
|
-
reply.log.info(
|
|
88
|
-
{ res: reply, err: error },
|
|
89
|
-
error?.message
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
if (!reply.log[kDisableRequestLogging]) {
|
|
94
|
-
reply.log.error(
|
|
95
|
-
{ req: request, res: reply, err: error },
|
|
96
|
-
error?.message
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
81
|
+
request.server[kLogController].defaultErrorLog(error, request, reply)
|
|
100
82
|
reply.send(error)
|
|
101
83
|
}
|
|
102
84
|
|
|
@@ -122,10 +104,10 @@ function fallbackErrorHandler (error, reply, cb) {
|
|
|
122
104
|
}))
|
|
123
105
|
}
|
|
124
106
|
} catch (err) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
107
|
+
// error is always FST_ERR_SCH_SERIALIZATION_BUILD
|
|
108
|
+
// because this is called from route/compileSchemasForSerialization
|
|
109
|
+
reply.server[kLogController].serializerError(err, reply.request, reply, { statusCode: res.statusCode })
|
|
110
|
+
|
|
129
111
|
reply.code(500)
|
|
130
112
|
payload = serializeError(new FST_ERR_FAILED_ERROR_SERIALIZATION(err.message, error.message))
|
|
131
113
|
}
|
package/lib/error-serializer.js
CHANGED
|
@@ -40,93 +40,93 @@ const asInteger = serializer.asInteger.bind(serializer)
|
|
|
40
40
|
const JSON_STR_NULL = 'null'
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const obj = (input && typeof input.toJSON === 'function')
|
|
43
|
+
// #
|
|
44
|
+
function anonymous0 (input) {
|
|
45
|
+
const obj = (input && typeof input.toJSON === 'function')
|
|
47
46
|
? input.toJSON()
|
|
48
47
|
: input
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
if (obj === null) return JSON_STR_EMPTY_OBJECT
|
|
50
|
+
let json = ''
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
let
|
|
54
|
-
let addComma = false
|
|
52
|
+
json += JSON_STR_BEGIN_OBJECT
|
|
53
|
+
let addComma_0 = false
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
const value_statusCode_1 = obj["statusCode"]
|
|
56
|
+
if (value_statusCode_1 !== undefined) {
|
|
57
|
+
!addComma_0 && (addComma_0 = true) || (json += JSON_STR_COMMA)
|
|
58
|
+
json += "\"statusCode\":"
|
|
59
|
+
json += asNumber(value_statusCode_1)
|
|
60
|
+
}
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (typeof
|
|
69
|
-
if (
|
|
62
|
+
const value_code_2 = obj["code"]
|
|
63
|
+
if (value_code_2 !== undefined) {
|
|
64
|
+
!addComma_0 && (addComma_0 = true) || (json += JSON_STR_COMMA)
|
|
65
|
+
json += "\"code\":"
|
|
66
|
+
|
|
67
|
+
if (typeof value_code_2 !== 'string') {
|
|
68
|
+
if (value_code_2 === null) {
|
|
70
69
|
json += JSON_STR_EMPTY_STRING
|
|
71
|
-
} else if (
|
|
72
|
-
json += JSON_STR_QUOTE +
|
|
73
|
-
} else if (
|
|
74
|
-
json += asString(
|
|
70
|
+
} else if (value_code_2 instanceof Date) {
|
|
71
|
+
json += JSON_STR_QUOTE + value_code_2.toISOString() + JSON_STR_QUOTE
|
|
72
|
+
} else if (value_code_2 instanceof RegExp) {
|
|
73
|
+
json += asString(value_code_2.source)
|
|
75
74
|
} else {
|
|
76
|
-
json += asString(
|
|
75
|
+
json += asString(value_code_2.toString())
|
|
77
76
|
}
|
|
78
77
|
} else {
|
|
79
|
-
json += asString(
|
|
78
|
+
json += asString(value_code_2)
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
|
|
81
|
+
}
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (typeof
|
|
90
|
-
if (
|
|
83
|
+
const value_error_3 = obj["error"]
|
|
84
|
+
if (value_error_3 !== undefined) {
|
|
85
|
+
!addComma_0 && (addComma_0 = true) || (json += JSON_STR_COMMA)
|
|
86
|
+
json += "\"error\":"
|
|
87
|
+
|
|
88
|
+
if (typeof value_error_3 !== 'string') {
|
|
89
|
+
if (value_error_3 === null) {
|
|
91
90
|
json += JSON_STR_EMPTY_STRING
|
|
92
|
-
} else if (
|
|
93
|
-
json += JSON_STR_QUOTE +
|
|
94
|
-
} else if (
|
|
95
|
-
json += asString(
|
|
91
|
+
} else if (value_error_3 instanceof Date) {
|
|
92
|
+
json += JSON_STR_QUOTE + value_error_3.toISOString() + JSON_STR_QUOTE
|
|
93
|
+
} else if (value_error_3 instanceof RegExp) {
|
|
94
|
+
json += asString(value_error_3.source)
|
|
96
95
|
} else {
|
|
97
|
-
json += asString(
|
|
96
|
+
json += asString(value_error_3.toString())
|
|
98
97
|
}
|
|
99
98
|
} else {
|
|
100
|
-
json += asString(
|
|
99
|
+
json += asString(value_error_3)
|
|
101
100
|
}
|
|
102
101
|
|
|
103
|
-
|
|
102
|
+
}
|
|
104
103
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (typeof
|
|
111
|
-
if (
|
|
104
|
+
const value_message_4 = obj["message"]
|
|
105
|
+
if (value_message_4 !== undefined) {
|
|
106
|
+
!addComma_0 && (addComma_0 = true) || (json += JSON_STR_COMMA)
|
|
107
|
+
json += "\"message\":"
|
|
108
|
+
|
|
109
|
+
if (typeof value_message_4 !== 'string') {
|
|
110
|
+
if (value_message_4 === null) {
|
|
112
111
|
json += JSON_STR_EMPTY_STRING
|
|
113
|
-
} else if (
|
|
114
|
-
json += JSON_STR_QUOTE +
|
|
115
|
-
} else if (
|
|
116
|
-
json += asString(
|
|
112
|
+
} else if (value_message_4 instanceof Date) {
|
|
113
|
+
json += JSON_STR_QUOTE + value_message_4.toISOString() + JSON_STR_QUOTE
|
|
114
|
+
} else if (value_message_4 instanceof RegExp) {
|
|
115
|
+
json += asString(value_message_4.source)
|
|
117
116
|
} else {
|
|
118
|
-
json += asString(
|
|
117
|
+
json += asString(value_message_4.toString())
|
|
119
118
|
}
|
|
120
119
|
} else {
|
|
121
|
-
json += asString(
|
|
120
|
+
json += asString(value_message_4)
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
|
|
123
|
+
}
|
|
125
124
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
125
|
+
json += JSON_STR_END_OBJECT
|
|
129
126
|
|
|
127
|
+
return json
|
|
128
|
+
}
|
|
129
|
+
|
|
130
130
|
const main = anonymous0
|
|
131
131
|
return main
|
|
132
132
|
|