node-fastify 5.8.3
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/.borp.yaml +3 -0
- package/.markdownlint-cli2.yaml +22 -0
- package/.prettierignore +1 -0
- package/GOVERNANCE.md +4 -0
- package/LICENSE +21 -0
- package/PROJECT_CHARTER.md +126 -0
- package/README.md +423 -0
- package/SECURITY.md +220 -0
- package/SPONSORS.md +24 -0
- package/build/build-error-serializer.js +35 -0
- package/build/build-validation.js +169 -0
- package/build/sync-version.js +11 -0
- package/docs/Guides/Benchmarking.md +60 -0
- package/docs/Guides/Database.md +321 -0
- package/docs/Guides/Delay-Accepting-Requests.md +608 -0
- package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
- package/docs/Guides/Ecosystem.md +726 -0
- package/docs/Guides/Fluent-Schema.md +127 -0
- package/docs/Guides/Getting-Started.md +620 -0
- package/docs/Guides/Index.md +43 -0
- package/docs/Guides/Migration-Guide-V3.md +287 -0
- package/docs/Guides/Migration-Guide-V4.md +267 -0
- package/docs/Guides/Migration-Guide-V5.md +727 -0
- package/docs/Guides/Plugins-Guide.md +520 -0
- package/docs/Guides/Prototype-Poisoning.md +383 -0
- package/docs/Guides/Recommendations.md +378 -0
- package/docs/Guides/Serverless.md +604 -0
- package/docs/Guides/Style-Guide.md +246 -0
- package/docs/Guides/Testing.md +481 -0
- package/docs/Guides/Write-Plugin.md +103 -0
- package/docs/Guides/Write-Type-Provider.md +34 -0
- package/docs/Reference/ContentTypeParser.md +271 -0
- package/docs/Reference/Decorators.md +436 -0
- package/docs/Reference/Encapsulation.md +194 -0
- package/docs/Reference/Errors.md +377 -0
- package/docs/Reference/HTTP2.md +94 -0
- package/docs/Reference/Hooks.md +958 -0
- package/docs/Reference/Index.md +73 -0
- package/docs/Reference/LTS.md +86 -0
- package/docs/Reference/Lifecycle.md +99 -0
- package/docs/Reference/Logging.md +268 -0
- package/docs/Reference/Middleware.md +79 -0
- package/docs/Reference/Plugins.md +245 -0
- package/docs/Reference/Principles.md +73 -0
- package/docs/Reference/Reply.md +1001 -0
- package/docs/Reference/Request.md +295 -0
- package/docs/Reference/Routes.md +802 -0
- package/docs/Reference/Server.md +2389 -0
- package/docs/Reference/Type-Providers.md +256 -0
- package/docs/Reference/TypeScript.md +1729 -0
- package/docs/Reference/Validation-and-Serialization.md +1130 -0
- package/docs/Reference/Warnings.md +58 -0
- package/docs/index.md +24 -0
- package/docs/resources/encapsulation_context.drawio +1 -0
- package/docs/resources/encapsulation_context.svg +3 -0
- package/eslint.config.js +35 -0
- package/examples/asyncawait.js +38 -0
- package/examples/benchmark/body.json +3 -0
- package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
- package/examples/benchmark/hooks-benchmark.js +52 -0
- package/examples/benchmark/parser.js +47 -0
- package/examples/benchmark/simple.js +30 -0
- package/examples/benchmark/webstream.js +27 -0
- package/examples/hooks.js +91 -0
- package/examples/http2.js +39 -0
- package/examples/https.js +38 -0
- package/examples/parser.js +53 -0
- package/examples/plugin.js +12 -0
- package/examples/route-prefix.js +38 -0
- package/examples/shared-schema.js +38 -0
- package/examples/simple-stream.js +20 -0
- package/examples/simple.js +32 -0
- package/examples/simple.mjs +27 -0
- package/examples/typescript-server.ts +79 -0
- package/examples/use-plugin.js +29 -0
- package/fastify.d.ts +253 -0
- package/fastify.js +985 -0
- package/integration/server.js +29 -0
- package/integration/test.sh +23 -0
- package/lib/config-validator.js +1266 -0
- package/lib/content-type-parser.js +413 -0
- package/lib/content-type.js +160 -0
- package/lib/context.js +98 -0
- package/lib/decorate.js +152 -0
- package/lib/error-handler.js +173 -0
- package/lib/error-serializer.js +134 -0
- package/lib/error-status.js +14 -0
- package/lib/errors.js +516 -0
- package/lib/four-oh-four.js +190 -0
- package/lib/handle-request.js +195 -0
- package/lib/head-route.js +45 -0
- package/lib/hooks.js +429 -0
- package/lib/initial-config-validation.js +37 -0
- package/lib/logger-factory.js +136 -0
- package/lib/logger-pino.js +68 -0
- package/lib/noop-set.js +10 -0
- package/lib/plugin-override.js +90 -0
- package/lib/plugin-utils.js +169 -0
- package/lib/promise.js +23 -0
- package/lib/reply.js +1030 -0
- package/lib/req-id-gen-factory.js +52 -0
- package/lib/request.js +391 -0
- package/lib/route.js +686 -0
- package/lib/schema-controller.js +164 -0
- package/lib/schemas.js +207 -0
- package/lib/server.js +441 -0
- package/lib/symbols.js +71 -0
- package/lib/validation.js +280 -0
- package/lib/warnings.js +57 -0
- package/lib/wrap-thenable.js +84 -0
- package/package.json +225 -0
- package/scripts/validate-ecosystem-links.js +179 -0
- package/test/404s.test.js +2035 -0
- package/test/500s.test.js +422 -0
- package/test/allow-unsafe-regex.test.js +92 -0
- package/test/als.test.js +65 -0
- package/test/async-await.test.js +705 -0
- package/test/async-dispose.test.js +20 -0
- package/test/async_hooks.test.js +52 -0
- package/test/body-limit.test.js +224 -0
- package/test/buffer.test.js +74 -0
- package/test/build/error-serializer.test.js +36 -0
- package/test/build/version.test.js +14 -0
- package/test/build-certificate.js +109 -0
- package/test/bundler/README.md +29 -0
- package/test/bundler/esbuild/bundler-test.js +32 -0
- package/test/bundler/esbuild/package.json +10 -0
- package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
- package/test/bundler/esbuild/src/index.js +9 -0
- package/test/bundler/webpack/bundler-test.js +32 -0
- package/test/bundler/webpack/package.json +11 -0
- package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
- package/test/bundler/webpack/src/index.js +9 -0
- package/test/bundler/webpack/webpack.config.js +15 -0
- package/test/case-insensitive.test.js +102 -0
- package/test/chainable.test.js +40 -0
- package/test/child-logger-factory.test.js +128 -0
- package/test/client-timeout.test.js +38 -0
- package/test/close-pipelining.test.js +78 -0
- package/test/close.test.js +706 -0
- package/test/conditional-pino.test.js +47 -0
- package/test/connection-timeout.test.js +42 -0
- package/test/constrained-routes.test.js +1138 -0
- package/test/content-length.test.js +174 -0
- package/test/content-parser.test.js +739 -0
- package/test/content-type.test.js +181 -0
- package/test/context-config.test.js +164 -0
- package/test/custom-http-server.test.js +118 -0
- package/test/custom-parser-async.test.js +59 -0
- package/test/custom-parser.0.test.js +701 -0
- package/test/custom-parser.1.test.js +266 -0
- package/test/custom-parser.2.test.js +91 -0
- package/test/custom-parser.3.test.js +208 -0
- package/test/custom-parser.4.test.js +218 -0
- package/test/custom-parser.5.test.js +130 -0
- package/test/custom-querystring-parser.test.js +129 -0
- package/test/decorator.test.js +1330 -0
- package/test/delete.test.js +344 -0
- package/test/diagnostics-channel/404.test.js +49 -0
- package/test/diagnostics-channel/async-delay-request.test.js +65 -0
- package/test/diagnostics-channel/async-request.test.js +64 -0
- package/test/diagnostics-channel/error-before-handler.test.js +35 -0
- package/test/diagnostics-channel/error-request.test.js +53 -0
- package/test/diagnostics-channel/error-status.test.js +123 -0
- package/test/diagnostics-channel/init.test.js +50 -0
- package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
- package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
- package/test/diagnostics-channel/sync-request.test.js +54 -0
- package/test/encapsulated-child-logger-factory.test.js +69 -0
- package/test/encapsulated-error-handler.test.js +237 -0
- package/test/esm/errorCodes.test.mjs +10 -0
- package/test/esm/esm.test.mjs +13 -0
- package/test/esm/index.test.js +8 -0
- package/test/esm/named-exports.mjs +14 -0
- package/test/esm/other.mjs +8 -0
- package/test/esm/plugin.mjs +8 -0
- package/test/fastify-instance.test.js +300 -0
- package/test/find-route.test.js +152 -0
- package/test/fluent-schema.test.js +209 -0
- package/test/genReqId.test.js +426 -0
- package/test/handler-context.test.js +45 -0
- package/test/handler-timeout.test.js +367 -0
- package/test/has-route.test.js +88 -0
- package/test/header-overflow.test.js +55 -0
- package/test/helper.js +496 -0
- package/test/hooks-async.test.js +1099 -0
- package/test/hooks.on-listen.test.js +1162 -0
- package/test/hooks.on-ready.test.js +421 -0
- package/test/hooks.test.js +3578 -0
- package/test/http-methods/copy.test.js +35 -0
- package/test/http-methods/custom-http-methods.test.js +114 -0
- package/test/http-methods/get.test.js +412 -0
- package/test/http-methods/head.test.js +263 -0
- package/test/http-methods/lock.test.js +108 -0
- package/test/http-methods/mkcalendar.test.js +143 -0
- package/test/http-methods/mkcol.test.js +35 -0
- package/test/http-methods/move.test.js +42 -0
- package/test/http-methods/propfind.test.js +136 -0
- package/test/http-methods/proppatch.test.js +105 -0
- package/test/http-methods/report.test.js +142 -0
- package/test/http-methods/search.test.js +233 -0
- package/test/http-methods/trace.test.js +21 -0
- package/test/http-methods/unlock.test.js +38 -0
- package/test/http2/closing.test.js +270 -0
- package/test/http2/constraint.test.js +109 -0
- package/test/http2/head.test.js +34 -0
- package/test/http2/plain.test.js +68 -0
- package/test/http2/secure-with-fallback.test.js +113 -0
- package/test/http2/secure.test.js +67 -0
- package/test/http2/unknown-http-method.test.js +34 -0
- package/test/https/custom-https-server.test.js +58 -0
- package/test/https/https.test.js +136 -0
- package/test/imports.test.js +17 -0
- package/test/inject.test.js +502 -0
- package/test/input-validation.js +335 -0
- package/test/internals/all.test.js +38 -0
- package/test/internals/content-type-parser.test.js +111 -0
- package/test/internals/context.test.js +31 -0
- package/test/internals/decorator.test.js +156 -0
- package/test/internals/errors.test.js +982 -0
- package/test/internals/handle-request.test.js +270 -0
- package/test/internals/hook-runner.test.js +449 -0
- package/test/internals/hooks.test.js +96 -0
- package/test/internals/initial-config.test.js +383 -0
- package/test/internals/logger.test.js +163 -0
- package/test/internals/plugin.test.js +170 -0
- package/test/internals/promise.test.js +63 -0
- package/test/internals/reply-serialize.test.js +714 -0
- package/test/internals/reply.test.js +1920 -0
- package/test/internals/req-id-gen-factory.test.js +133 -0
- package/test/internals/request-validate.test.js +1402 -0
- package/test/internals/request.test.js +506 -0
- package/test/internals/schema-controller-perf.test.js +40 -0
- package/test/internals/server.test.js +91 -0
- package/test/internals/validation.test.js +352 -0
- package/test/issue-4959.test.js +118 -0
- package/test/keep-alive-timeout.test.js +42 -0
- package/test/listen.1.test.js +154 -0
- package/test/listen.2.test.js +113 -0
- package/test/listen.3.test.js +83 -0
- package/test/listen.4.test.js +168 -0
- package/test/listen.5.test.js +122 -0
- package/test/logger/instantiation.test.js +341 -0
- package/test/logger/logger-test-utils.js +47 -0
- package/test/logger/logging.test.js +460 -0
- package/test/logger/options.test.js +579 -0
- package/test/logger/request.test.js +292 -0
- package/test/logger/response.test.js +183 -0
- package/test/logger/tap-parallel-not-ok +0 -0
- package/test/max-requests-per-socket.test.js +113 -0
- package/test/middleware.test.js +37 -0
- package/test/noop-set.test.js +19 -0
- package/test/nullable-validation.test.js +187 -0
- package/test/options.error-handler.test.js +5 -0
- package/test/options.test.js +5 -0
- package/test/output-validation.test.js +140 -0
- package/test/patch.error-handler.test.js +5 -0
- package/test/patch.test.js +5 -0
- package/test/plugin.1.test.js +230 -0
- package/test/plugin.2.test.js +314 -0
- package/test/plugin.3.test.js +287 -0
- package/test/plugin.4.test.js +504 -0
- package/test/plugin.helper.js +8 -0
- package/test/plugin.name.display.js +10 -0
- package/test/post-empty-body.test.js +38 -0
- package/test/pretty-print.test.js +366 -0
- package/test/promises.test.js +125 -0
- package/test/proto-poisoning.test.js +145 -0
- package/test/put.error-handler.test.js +5 -0
- package/test/put.test.js +5 -0
- package/test/register.test.js +184 -0
- package/test/reply-code.test.js +148 -0
- package/test/reply-early-hints.test.js +100 -0
- package/test/reply-error.test.js +815 -0
- package/test/reply-trailers.test.js +445 -0
- package/test/reply-web-stream-locked.test.js +37 -0
- package/test/request-error.test.js +624 -0
- package/test/request-header-host.test.js +339 -0
- package/test/request-id.test.js +118 -0
- package/test/request-timeout.test.js +53 -0
- package/test/route-hooks.test.js +635 -0
- package/test/route-prefix.test.js +904 -0
- package/test/route-shorthand.test.js +48 -0
- package/test/route.1.test.js +259 -0
- package/test/route.2.test.js +100 -0
- package/test/route.3.test.js +213 -0
- package/test/route.4.test.js +127 -0
- package/test/route.5.test.js +211 -0
- package/test/route.6.test.js +306 -0
- package/test/route.7.test.js +406 -0
- package/test/route.8.test.js +225 -0
- package/test/router-options.test.js +1108 -0
- package/test/same-shape.test.js +124 -0
- package/test/schema-examples.test.js +661 -0
- package/test/schema-feature.test.js +2198 -0
- package/test/schema-serialization.test.js +1171 -0
- package/test/schema-special-usage.test.js +1348 -0
- package/test/schema-validation.test.js +1572 -0
- package/test/scripts/validate-ecosystem-links.test.js +339 -0
- package/test/serialize-response.test.js +186 -0
- package/test/server.test.js +347 -0
- package/test/set-error-handler.test.js +69 -0
- package/test/skip-reply-send.test.js +317 -0
- package/test/stream-serializers.test.js +40 -0
- package/test/stream.1.test.js +94 -0
- package/test/stream.2.test.js +129 -0
- package/test/stream.3.test.js +198 -0
- package/test/stream.4.test.js +176 -0
- package/test/stream.5.test.js +188 -0
- package/test/sync-routes.test.js +32 -0
- package/test/throw.test.js +359 -0
- package/test/toolkit.js +63 -0
- package/test/trust-proxy.test.js +162 -0
- package/test/type-provider.test.js +22 -0
- package/test/types/content-type-parser.test-d.ts +72 -0
- package/test/types/decorate-request-reply.test-d.ts +18 -0
- package/test/types/dummy-plugin.ts +9 -0
- package/test/types/errors.test-d.ts +90 -0
- package/test/types/fastify.test-d.ts +352 -0
- package/test/types/hooks.test-d.ts +550 -0
- package/test/types/import.ts +2 -0
- package/test/types/instance.test-d.ts +588 -0
- package/test/types/logger.test-d.ts +277 -0
- package/test/types/plugin.test-d.ts +97 -0
- package/test/types/register.test-d.ts +237 -0
- package/test/types/reply.test-d.ts +254 -0
- package/test/types/request.test-d.ts +188 -0
- package/test/types/route.test-d.ts +553 -0
- package/test/types/schema.test-d.ts +135 -0
- package/test/types/serverFactory.test-d.ts +37 -0
- package/test/types/type-provider.test-d.ts +1213 -0
- package/test/types/using.test-d.ts +17 -0
- package/test/upgrade.test.js +52 -0
- package/test/url-rewriting.test.js +122 -0
- package/test/use-semicolon-delimiter.test.js +168 -0
- package/test/validation-error-handling.test.js +900 -0
- package/test/versioned-routes.test.js +603 -0
- package/test/web-api.test.js +616 -0
- package/test/wrap-thenable.test.js +30 -0
- package/types/content-type-parser.d.ts +75 -0
- package/types/context.d.ts +22 -0
- package/types/errors.d.ts +92 -0
- package/types/hooks.d.ts +875 -0
- package/types/instance.d.ts +609 -0
- package/types/logger.d.ts +107 -0
- package/types/plugin.d.ts +44 -0
- package/types/register.d.ts +42 -0
- package/types/reply.d.ts +81 -0
- package/types/request.d.ts +95 -0
- package/types/route.d.ts +199 -0
- package/types/schema.d.ts +61 -0
- package/types/server-factory.d.ts +19 -0
- package/types/type-provider.d.ts +130 -0
- package/types/utils.d.ts +98 -0
package/fastify.js
ADDED
|
@@ -0,0 +1,985 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const VERSION = '5.8.2'
|
|
4
|
+
|
|
5
|
+
const Avvio = require('avvio')
|
|
6
|
+
const http = require('node:http')
|
|
7
|
+
const diagnostics = require('node:diagnostics_channel')
|
|
8
|
+
let lightMyRequest
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
kAvvioBoot,
|
|
12
|
+
kChildren,
|
|
13
|
+
kServerBindings,
|
|
14
|
+
kBodyLimit,
|
|
15
|
+
kSupportedHTTPMethods,
|
|
16
|
+
kRoutePrefix,
|
|
17
|
+
kLogLevel,
|
|
18
|
+
kLogSerializers,
|
|
19
|
+
kHooks,
|
|
20
|
+
kSchemaController,
|
|
21
|
+
kRequestAcceptVersion,
|
|
22
|
+
kReplySerializerDefault,
|
|
23
|
+
kContentTypeParser,
|
|
24
|
+
kReply,
|
|
25
|
+
kRequest,
|
|
26
|
+
kFourOhFour,
|
|
27
|
+
kState,
|
|
28
|
+
kOptions,
|
|
29
|
+
kPluginNameChain,
|
|
30
|
+
kSchemaErrorFormatter,
|
|
31
|
+
kErrorHandler,
|
|
32
|
+
kKeepAliveConnections,
|
|
33
|
+
kChildLoggerFactory,
|
|
34
|
+
kGenReqId,
|
|
35
|
+
kErrorHandlerAlreadySet,
|
|
36
|
+
kHandlerTimeout
|
|
37
|
+
} = require('./lib/symbols.js')
|
|
38
|
+
|
|
39
|
+
const { createServer } = require('./lib/server')
|
|
40
|
+
const Reply = require('./lib/reply')
|
|
41
|
+
const Request = require('./lib/request')
|
|
42
|
+
const Context = require('./lib/context.js')
|
|
43
|
+
const decorator = require('./lib/decorate')
|
|
44
|
+
const ContentTypeParser = require('./lib/content-type-parser.js')
|
|
45
|
+
const SchemaController = require('./lib/schema-controller')
|
|
46
|
+
const { Hooks, hookRunnerApplication, supportedHooks } = require('./lib/hooks')
|
|
47
|
+
const { createChildLogger, defaultChildLoggerFactory, createLogger } = require('./lib/logger-factory')
|
|
48
|
+
const pluginUtils = require('./lib/plugin-utils.js')
|
|
49
|
+
const { getGenReqId, reqIdGenFactory } = require('./lib/req-id-gen-factory.js')
|
|
50
|
+
const { buildRouting, validateBodyLimitOption, buildRouterOptions } = require('./lib/route')
|
|
51
|
+
const build404 = require('./lib/four-oh-four')
|
|
52
|
+
const getSecuredInitialConfig = require('./lib/initial-config-validation.js')
|
|
53
|
+
const override = require('./lib/plugin-override')
|
|
54
|
+
const {
|
|
55
|
+
appendStackTrace,
|
|
56
|
+
AVVIO_ERRORS_MAP,
|
|
57
|
+
...errorCodes
|
|
58
|
+
} = require('./lib/errors')
|
|
59
|
+
const PonyPromise = require('./lib/promise')
|
|
60
|
+
|
|
61
|
+
const { defaultInitOptions } = getSecuredInitialConfig
|
|
62
|
+
|
|
63
|
+
const {
|
|
64
|
+
FST_ERR_ASYNC_CONSTRAINT,
|
|
65
|
+
FST_ERR_BAD_URL,
|
|
66
|
+
FST_ERR_OPTIONS_NOT_OBJ,
|
|
67
|
+
FST_ERR_QSP_NOT_FN,
|
|
68
|
+
FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN,
|
|
69
|
+
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ,
|
|
70
|
+
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR,
|
|
71
|
+
FST_ERR_INSTANCE_ALREADY_LISTENING,
|
|
72
|
+
FST_ERR_REOPENED_CLOSE_SERVER,
|
|
73
|
+
FST_ERR_ROUTE_REWRITE_NOT_STR,
|
|
74
|
+
FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN,
|
|
75
|
+
FST_ERR_ERROR_HANDLER_NOT_FN,
|
|
76
|
+
FST_ERR_ERROR_HANDLER_ALREADY_SET,
|
|
77
|
+
FST_ERR_ROUTE_METHOD_INVALID
|
|
78
|
+
} = errorCodes
|
|
79
|
+
|
|
80
|
+
const { buildErrorHandler } = require('./lib/error-handler.js')
|
|
81
|
+
const { FSTWRN004 } = require('./lib/warnings.js')
|
|
82
|
+
|
|
83
|
+
const initChannel = diagnostics.channel('fastify.initialization')
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {import('./fastify.js').FastifyServerOptions} serverOptions
|
|
87
|
+
*/
|
|
88
|
+
function fastify (serverOptions) {
|
|
89
|
+
const {
|
|
90
|
+
options,
|
|
91
|
+
genReqId,
|
|
92
|
+
disableRequestLogging,
|
|
93
|
+
hasLogger,
|
|
94
|
+
initialConfig
|
|
95
|
+
} = processOptions(serverOptions, defaultRoute, onBadUrl)
|
|
96
|
+
|
|
97
|
+
// Default router
|
|
98
|
+
const router = buildRouting(options.routerOptions)
|
|
99
|
+
|
|
100
|
+
// 404 router, used for handling encapsulated 404 handlers
|
|
101
|
+
const fourOhFour = build404(options)
|
|
102
|
+
|
|
103
|
+
// HTTP server and its handler
|
|
104
|
+
const httpHandler = wrapRouting(router, options)
|
|
105
|
+
|
|
106
|
+
const {
|
|
107
|
+
server,
|
|
108
|
+
listen,
|
|
109
|
+
forceCloseConnections,
|
|
110
|
+
serverHasCloseAllConnections,
|
|
111
|
+
serverHasCloseHttp2Sessions,
|
|
112
|
+
keepAliveConnections
|
|
113
|
+
} = createServer(options, httpHandler)
|
|
114
|
+
|
|
115
|
+
const setupResponseListeners = Reply.setupResponseListeners
|
|
116
|
+
const schemaController = SchemaController.buildSchemaController(null, options.schemaController)
|
|
117
|
+
|
|
118
|
+
// Public API
|
|
119
|
+
const fastify = {
|
|
120
|
+
// Fastify internals
|
|
121
|
+
[kState]: {
|
|
122
|
+
listening: false,
|
|
123
|
+
closing: false,
|
|
124
|
+
started: false,
|
|
125
|
+
ready: false,
|
|
126
|
+
booting: false,
|
|
127
|
+
aborted: false,
|
|
128
|
+
readyResolver: null
|
|
129
|
+
},
|
|
130
|
+
[kKeepAliveConnections]: keepAliveConnections,
|
|
131
|
+
[kSupportedHTTPMethods]: {
|
|
132
|
+
bodyless: new Set([
|
|
133
|
+
// Standard
|
|
134
|
+
'GET',
|
|
135
|
+
'HEAD',
|
|
136
|
+
'TRACE'
|
|
137
|
+
]),
|
|
138
|
+
bodywith: new Set([
|
|
139
|
+
// Standard
|
|
140
|
+
'DELETE',
|
|
141
|
+
'OPTIONS',
|
|
142
|
+
'PATCH',
|
|
143
|
+
'PUT',
|
|
144
|
+
'POST'
|
|
145
|
+
])
|
|
146
|
+
},
|
|
147
|
+
[kOptions]: options,
|
|
148
|
+
[kChildren]: [],
|
|
149
|
+
[kServerBindings]: [],
|
|
150
|
+
[kBodyLimit]: options.bodyLimit,
|
|
151
|
+
[kHandlerTimeout]: options.handlerTimeout,
|
|
152
|
+
[kRoutePrefix]: '',
|
|
153
|
+
[kLogLevel]: '',
|
|
154
|
+
[kLogSerializers]: null,
|
|
155
|
+
[kHooks]: new Hooks(),
|
|
156
|
+
[kSchemaController]: schemaController,
|
|
157
|
+
[kSchemaErrorFormatter]: null,
|
|
158
|
+
[kErrorHandler]: buildErrorHandler(),
|
|
159
|
+
[kErrorHandlerAlreadySet]: false,
|
|
160
|
+
[kChildLoggerFactory]: options.childLoggerFactory || defaultChildLoggerFactory,
|
|
161
|
+
[kReplySerializerDefault]: null,
|
|
162
|
+
[kContentTypeParser]: new ContentTypeParser(
|
|
163
|
+
options.bodyLimit,
|
|
164
|
+
(options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning),
|
|
165
|
+
(options.onConstructorPoisoning || defaultInitOptions.onConstructorPoisoning)
|
|
166
|
+
),
|
|
167
|
+
[kReply]: Reply.buildReply(Reply),
|
|
168
|
+
[kRequest]: Request.buildRequest(Request, options.trustProxy),
|
|
169
|
+
[kFourOhFour]: fourOhFour,
|
|
170
|
+
[pluginUtils.kRegisteredPlugins]: [],
|
|
171
|
+
[kPluginNameChain]: ['fastify'],
|
|
172
|
+
[kAvvioBoot]: null,
|
|
173
|
+
[kGenReqId]: genReqId,
|
|
174
|
+
// routing method
|
|
175
|
+
routing: httpHandler,
|
|
176
|
+
// routes shorthand methods
|
|
177
|
+
delete: function _delete (url, options, handler) {
|
|
178
|
+
return router.prepareRoute.call(this, { method: 'DELETE', url, options, handler })
|
|
179
|
+
},
|
|
180
|
+
get: function _get (url, options, handler) {
|
|
181
|
+
return router.prepareRoute.call(this, { method: 'GET', url, options, handler })
|
|
182
|
+
},
|
|
183
|
+
head: function _head (url, options, handler) {
|
|
184
|
+
return router.prepareRoute.call(this, { method: 'HEAD', url, options, handler })
|
|
185
|
+
},
|
|
186
|
+
trace: function _trace (url, options, handler) {
|
|
187
|
+
return router.prepareRoute.call(this, { method: 'TRACE', url, options, handler })
|
|
188
|
+
},
|
|
189
|
+
patch: function _patch (url, options, handler) {
|
|
190
|
+
return router.prepareRoute.call(this, { method: 'PATCH', url, options, handler })
|
|
191
|
+
},
|
|
192
|
+
post: function _post (url, options, handler) {
|
|
193
|
+
return router.prepareRoute.call(this, { method: 'POST', url, options, handler })
|
|
194
|
+
},
|
|
195
|
+
put: function _put (url, options, handler) {
|
|
196
|
+
return router.prepareRoute.call(this, { method: 'PUT', url, options, handler })
|
|
197
|
+
},
|
|
198
|
+
options: function _options (url, options, handler) {
|
|
199
|
+
return router.prepareRoute.call(this, { method: 'OPTIONS', url, options, handler })
|
|
200
|
+
},
|
|
201
|
+
all: function _all (url, options, handler) {
|
|
202
|
+
return router.prepareRoute.call(this, { method: this.supportedMethods, url, options, handler })
|
|
203
|
+
},
|
|
204
|
+
// extended route
|
|
205
|
+
route: function _route (options) {
|
|
206
|
+
// we need the fastify object that we are producing so we apply a lazy loading of the function,
|
|
207
|
+
// otherwise we should bind it after the declaration
|
|
208
|
+
return router.route.call(this, { options })
|
|
209
|
+
},
|
|
210
|
+
hasRoute: function _route (options) {
|
|
211
|
+
return router.hasRoute.call(this, { options })
|
|
212
|
+
},
|
|
213
|
+
findRoute: function _findRoute (options) {
|
|
214
|
+
return router.findRoute(options)
|
|
215
|
+
},
|
|
216
|
+
// expose logger instance
|
|
217
|
+
log: options.logger,
|
|
218
|
+
// type provider
|
|
219
|
+
withTypeProvider,
|
|
220
|
+
// hooks
|
|
221
|
+
addHook,
|
|
222
|
+
// schemas
|
|
223
|
+
addSchema,
|
|
224
|
+
getSchema: schemaController.getSchema.bind(schemaController),
|
|
225
|
+
getSchemas: schemaController.getSchemas.bind(schemaController),
|
|
226
|
+
setValidatorCompiler,
|
|
227
|
+
setSerializerCompiler,
|
|
228
|
+
setSchemaController,
|
|
229
|
+
setReplySerializer,
|
|
230
|
+
setSchemaErrorFormatter,
|
|
231
|
+
// set generated request id
|
|
232
|
+
setGenReqId,
|
|
233
|
+
// custom parsers
|
|
234
|
+
addContentTypeParser: ContentTypeParser.helpers.addContentTypeParser,
|
|
235
|
+
hasContentTypeParser: ContentTypeParser.helpers.hasContentTypeParser,
|
|
236
|
+
getDefaultJsonParser: ContentTypeParser.defaultParsers.getDefaultJsonParser,
|
|
237
|
+
defaultTextParser: ContentTypeParser.defaultParsers.defaultTextParser,
|
|
238
|
+
removeContentTypeParser: ContentTypeParser.helpers.removeContentTypeParser,
|
|
239
|
+
removeAllContentTypeParsers: ContentTypeParser.helpers.removeAllContentTypeParsers,
|
|
240
|
+
// Fastify architecture methods (initialized by Avvio)
|
|
241
|
+
register: null,
|
|
242
|
+
after: null,
|
|
243
|
+
ready: null,
|
|
244
|
+
onClose: null,
|
|
245
|
+
close: null,
|
|
246
|
+
printPlugins: null,
|
|
247
|
+
hasPlugin: function (name) {
|
|
248
|
+
return this[pluginUtils.kRegisteredPlugins].includes(name) || this[kPluginNameChain].includes(name)
|
|
249
|
+
},
|
|
250
|
+
// http server
|
|
251
|
+
listen,
|
|
252
|
+
server,
|
|
253
|
+
addresses: function () {
|
|
254
|
+
/* istanbul ignore next */
|
|
255
|
+
const binded = this[kServerBindings].map(b => b.address())
|
|
256
|
+
binded.push(this.server.address())
|
|
257
|
+
return binded.filter(adr => adr)
|
|
258
|
+
},
|
|
259
|
+
// extend fastify objects
|
|
260
|
+
decorate: decorator.add,
|
|
261
|
+
hasDecorator: decorator.exist,
|
|
262
|
+
decorateReply: decorator.decorateReply,
|
|
263
|
+
decorateRequest: decorator.decorateRequest,
|
|
264
|
+
hasRequestDecorator: decorator.existRequest,
|
|
265
|
+
hasReplyDecorator: decorator.existReply,
|
|
266
|
+
getDecorator: decorator.getInstanceDecorator,
|
|
267
|
+
addHttpMethod,
|
|
268
|
+
// fake http injection
|
|
269
|
+
inject,
|
|
270
|
+
// pretty print of the registered routes
|
|
271
|
+
printRoutes,
|
|
272
|
+
// custom error handling
|
|
273
|
+
setNotFoundHandler,
|
|
274
|
+
setErrorHandler,
|
|
275
|
+
// child logger
|
|
276
|
+
setChildLoggerFactory,
|
|
277
|
+
// Set fastify initial configuration options read-only object
|
|
278
|
+
initialConfig,
|
|
279
|
+
// constraint strategies
|
|
280
|
+
addConstraintStrategy: router.addConstraintStrategy.bind(router),
|
|
281
|
+
hasConstraintStrategy: router.hasConstraintStrategy.bind(router)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
Object.defineProperties(fastify, {
|
|
285
|
+
listeningOrigin: {
|
|
286
|
+
get () {
|
|
287
|
+
const address = this.addresses().slice(-1).pop()
|
|
288
|
+
/* ignore if windows: unix socket is not testable on Windows platform */
|
|
289
|
+
/* c8 ignore next 3 */
|
|
290
|
+
if (typeof address === 'string') {
|
|
291
|
+
return address
|
|
292
|
+
}
|
|
293
|
+
const host = address.family === 'IPv6' ? `[${address.address}]` : address.address
|
|
294
|
+
return `${this[kOptions].https ? 'https' : 'http'}://${host}:${address.port}`
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
pluginName: {
|
|
298
|
+
configurable: true,
|
|
299
|
+
get () {
|
|
300
|
+
if (this[kPluginNameChain].length > 1) {
|
|
301
|
+
return this[kPluginNameChain].join(' -> ')
|
|
302
|
+
}
|
|
303
|
+
return this[kPluginNameChain][0]
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
prefix: {
|
|
307
|
+
configurable: true,
|
|
308
|
+
get () { return this[kRoutePrefix] }
|
|
309
|
+
},
|
|
310
|
+
validatorCompiler: {
|
|
311
|
+
configurable: true,
|
|
312
|
+
get () { return this[kSchemaController].getValidatorCompiler() }
|
|
313
|
+
},
|
|
314
|
+
serializerCompiler: {
|
|
315
|
+
configurable: true,
|
|
316
|
+
get () { return this[kSchemaController].getSerializerCompiler() }
|
|
317
|
+
},
|
|
318
|
+
childLoggerFactory: {
|
|
319
|
+
configurable: true,
|
|
320
|
+
get () { return this[kChildLoggerFactory] }
|
|
321
|
+
},
|
|
322
|
+
version: {
|
|
323
|
+
configurable: true,
|
|
324
|
+
get () { return VERSION }
|
|
325
|
+
},
|
|
326
|
+
errorHandler: {
|
|
327
|
+
configurable: true,
|
|
328
|
+
get () {
|
|
329
|
+
return this[kErrorHandler].func
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
genReqId: {
|
|
333
|
+
configurable: true,
|
|
334
|
+
get () { return this[kGenReqId] }
|
|
335
|
+
},
|
|
336
|
+
supportedMethods: {
|
|
337
|
+
configurable: false,
|
|
338
|
+
get () {
|
|
339
|
+
return [
|
|
340
|
+
...this[kSupportedHTTPMethods].bodyless,
|
|
341
|
+
...this[kSupportedHTTPMethods].bodywith
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
if (options.schemaErrorFormatter) {
|
|
348
|
+
validateSchemaErrorFormatter(options.schemaErrorFormatter)
|
|
349
|
+
fastify[kSchemaErrorFormatter] = options.schemaErrorFormatter.bind(fastify)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Install and configure Avvio
|
|
353
|
+
// Avvio will update the following Fastify methods:
|
|
354
|
+
// - register
|
|
355
|
+
// - after
|
|
356
|
+
// - ready
|
|
357
|
+
// - onClose
|
|
358
|
+
// - close
|
|
359
|
+
|
|
360
|
+
const avvioPluginTimeout = Number(options.pluginTimeout)
|
|
361
|
+
const avvio = Avvio(fastify, {
|
|
362
|
+
autostart: false,
|
|
363
|
+
timeout: isNaN(avvioPluginTimeout) === false ? avvioPluginTimeout : defaultInitOptions.pluginTimeout,
|
|
364
|
+
expose: {
|
|
365
|
+
use: 'register'
|
|
366
|
+
}
|
|
367
|
+
})
|
|
368
|
+
// Override to allow the plugin encapsulation
|
|
369
|
+
avvio.override = override
|
|
370
|
+
avvio.on('start', () => (fastify[kState].started = true))
|
|
371
|
+
fastify[kAvvioBoot] = fastify.ready // the avvio ready function
|
|
372
|
+
fastify.ready = ready // overwrite the avvio ready function
|
|
373
|
+
fastify.printPlugins = avvio.prettyPrint.bind(avvio)
|
|
374
|
+
|
|
375
|
+
// cache the closing value, since we are checking it in an hot path
|
|
376
|
+
avvio.once('preReady', () => {
|
|
377
|
+
fastify.onClose((instance, done) => {
|
|
378
|
+
fastify[kState].closing = true
|
|
379
|
+
router.closeRoutes()
|
|
380
|
+
|
|
381
|
+
hookRunnerApplication('preClose', fastify[kAvvioBoot], fastify, function () {
|
|
382
|
+
if (fastify[kState].listening) {
|
|
383
|
+
/* istanbul ignore next: Cannot test this without Node.js core support */
|
|
384
|
+
if (forceCloseConnections === 'idle') {
|
|
385
|
+
// Not needed in Node 19
|
|
386
|
+
instance.server.closeIdleConnections()
|
|
387
|
+
/* istanbul ignore next: Cannot test this without Node.js core support */
|
|
388
|
+
} else if (serverHasCloseAllConnections && forceCloseConnections) {
|
|
389
|
+
instance.server.closeAllConnections()
|
|
390
|
+
} else if (forceCloseConnections === true) {
|
|
391
|
+
for (const conn of fastify[kKeepAliveConnections]) {
|
|
392
|
+
// We must invoke the destroy method instead of merely unreffing
|
|
393
|
+
// the sockets. If we only unref, then the callback passed to
|
|
394
|
+
// `fastify.close` will never be invoked; nor will any of the
|
|
395
|
+
// registered `onClose` hooks.
|
|
396
|
+
conn.destroy()
|
|
397
|
+
fastify[kKeepAliveConnections].delete(conn)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (serverHasCloseHttp2Sessions) {
|
|
403
|
+
instance.server.closeHttp2Sessions()
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// No new TCP connections are accepted.
|
|
407
|
+
// We must call close on the server even if we are not listening
|
|
408
|
+
// otherwise memory will be leaked.
|
|
409
|
+
// https://github.com/nodejs/node/issues/48604
|
|
410
|
+
if (!options.serverFactory || fastify[kState].listening) {
|
|
411
|
+
instance.server.close(function (err) {
|
|
412
|
+
/* c8 ignore next 6 */
|
|
413
|
+
if (err && err.code !== 'ERR_SERVER_NOT_RUNNING') {
|
|
414
|
+
done(null)
|
|
415
|
+
} else {
|
|
416
|
+
done()
|
|
417
|
+
}
|
|
418
|
+
})
|
|
419
|
+
} else {
|
|
420
|
+
process.nextTick(done, null)
|
|
421
|
+
}
|
|
422
|
+
})
|
|
423
|
+
})
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
// Create bad URL context
|
|
427
|
+
const onBadUrlContext = new Context({
|
|
428
|
+
server: fastify,
|
|
429
|
+
config: {}
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
// Set the default 404 handler
|
|
433
|
+
fastify.setNotFoundHandler()
|
|
434
|
+
fourOhFour.arrange404(fastify)
|
|
435
|
+
|
|
436
|
+
router.setup(options, {
|
|
437
|
+
avvio,
|
|
438
|
+
fourOhFour,
|
|
439
|
+
hasLogger,
|
|
440
|
+
setupResponseListeners,
|
|
441
|
+
throwIfAlreadyStarted,
|
|
442
|
+
keepAliveConnections
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
// Delay configuring clientError handler so that it can access fastify state.
|
|
446
|
+
server.on('clientError', options.clientErrorHandler.bind(fastify))
|
|
447
|
+
|
|
448
|
+
if (initChannel.hasSubscribers) {
|
|
449
|
+
initChannel.publish({ fastify })
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Older nodejs versions may not have asyncDispose
|
|
453
|
+
if ('asyncDispose' in Symbol) {
|
|
454
|
+
fastify[Symbol.asyncDispose] = function dispose () {
|
|
455
|
+
return fastify.close()
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return fastify
|
|
460
|
+
|
|
461
|
+
function throwIfAlreadyStarted (msg) {
|
|
462
|
+
if (fastify[kState].started) throw new FST_ERR_INSTANCE_ALREADY_LISTENING(msg)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// HTTP injection handling
|
|
466
|
+
// If the server is not ready yet, this
|
|
467
|
+
// utility will automatically force it.
|
|
468
|
+
function inject (opts, cb) {
|
|
469
|
+
// lightMyRequest is dynamically loaded as it seems very expensive
|
|
470
|
+
// because of Ajv
|
|
471
|
+
if (lightMyRequest === undefined) {
|
|
472
|
+
lightMyRequest = require('light-my-request')
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (fastify[kState].started) {
|
|
476
|
+
if (fastify[kState].closing) {
|
|
477
|
+
// Force to return an error
|
|
478
|
+
const error = new FST_ERR_REOPENED_CLOSE_SERVER()
|
|
479
|
+
if (cb) {
|
|
480
|
+
cb(error)
|
|
481
|
+
return
|
|
482
|
+
} else {
|
|
483
|
+
return Promise.reject(error)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return lightMyRequest(httpHandler, opts, cb)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (cb) {
|
|
490
|
+
this.ready(err => {
|
|
491
|
+
if (err) cb(err, null)
|
|
492
|
+
else lightMyRequest(httpHandler, opts, cb)
|
|
493
|
+
})
|
|
494
|
+
} else {
|
|
495
|
+
return lightMyRequest((req, res) => {
|
|
496
|
+
this.ready(function (err) {
|
|
497
|
+
if (err) {
|
|
498
|
+
res.emit('error', err)
|
|
499
|
+
return
|
|
500
|
+
}
|
|
501
|
+
httpHandler(req, res)
|
|
502
|
+
})
|
|
503
|
+
}, opts)
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function ready (cb) {
|
|
508
|
+
if (this[kState].readyResolver !== null) {
|
|
509
|
+
if (cb != null) {
|
|
510
|
+
this[kState].readyResolver.promise.then(() => cb(null, fastify), cb)
|
|
511
|
+
return
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return this[kState].readyResolver.promise
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// run the hooks after returning the promise
|
|
518
|
+
process.nextTick(runHooks)
|
|
519
|
+
|
|
520
|
+
// Create a promise no matter what
|
|
521
|
+
// It will work as a barrier for all the .ready() calls (ensuring single hook execution)
|
|
522
|
+
// as well as a flow control mechanism to chain cbs and further
|
|
523
|
+
// promises
|
|
524
|
+
this[kState].readyResolver = PonyPromise.withResolvers()
|
|
525
|
+
|
|
526
|
+
if (!cb) {
|
|
527
|
+
return this[kState].readyResolver.promise
|
|
528
|
+
} else {
|
|
529
|
+
this[kState].readyResolver.promise.then(() => cb(null, fastify), cb)
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function runHooks () {
|
|
533
|
+
// start loading
|
|
534
|
+
fastify[kAvvioBoot]((err, done) => {
|
|
535
|
+
if (err || fastify[kState].started || fastify[kState].ready || fastify[kState].booting) {
|
|
536
|
+
manageErr(err)
|
|
537
|
+
} else {
|
|
538
|
+
fastify[kState].booting = true
|
|
539
|
+
hookRunnerApplication('onReady', fastify[kAvvioBoot], fastify, manageErr)
|
|
540
|
+
}
|
|
541
|
+
done()
|
|
542
|
+
})
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function manageErr (err) {
|
|
546
|
+
// If the error comes out of Avvio's Error codes
|
|
547
|
+
// We create a make and preserve the previous error
|
|
548
|
+
// as cause
|
|
549
|
+
err = err != null && AVVIO_ERRORS_MAP[err.code] != null
|
|
550
|
+
? appendStackTrace(err, new AVVIO_ERRORS_MAP[err.code](err.message))
|
|
551
|
+
: err
|
|
552
|
+
|
|
553
|
+
if (err) {
|
|
554
|
+
return fastify[kState].readyResolver.reject(err)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
fastify[kState].readyResolver.resolve(fastify)
|
|
558
|
+
fastify[kState].booting = false
|
|
559
|
+
fastify[kState].ready = true
|
|
560
|
+
fastify[kState].readyResolver = null
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Used exclusively in TypeScript contexts to enable auto type inference from JSON schema.
|
|
565
|
+
function withTypeProvider () {
|
|
566
|
+
return this
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// wrapper that we expose to the user for hooks handling
|
|
570
|
+
function addHook (name, fn) {
|
|
571
|
+
throwIfAlreadyStarted('Cannot call "addHook"!')
|
|
572
|
+
|
|
573
|
+
if (fn == null) {
|
|
574
|
+
throw new errorCodes.FST_ERR_HOOK_INVALID_HANDLER(name, fn)
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (name === 'onSend' || name === 'preSerialization' || name === 'onError' || name === 'preParsing') {
|
|
578
|
+
if (fn.constructor.name === 'AsyncFunction' && fn.length === 4) {
|
|
579
|
+
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
580
|
+
}
|
|
581
|
+
} else if (name === 'onReady' || name === 'onListen') {
|
|
582
|
+
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 0) {
|
|
583
|
+
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
584
|
+
}
|
|
585
|
+
} else if (name === 'onRequestAbort') {
|
|
586
|
+
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 1) {
|
|
587
|
+
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {
|
|
591
|
+
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (name === 'onClose') {
|
|
596
|
+
this.onClose(fn.bind(this))
|
|
597
|
+
} else if (name === 'onReady' || name === 'onListen' || name === 'onRoute') {
|
|
598
|
+
this[kHooks].add(name, fn)
|
|
599
|
+
} else {
|
|
600
|
+
this.after((err, done) => {
|
|
601
|
+
try {
|
|
602
|
+
_addHook.call(this, name, fn)
|
|
603
|
+
done(err)
|
|
604
|
+
} catch (err) {
|
|
605
|
+
done(err)
|
|
606
|
+
}
|
|
607
|
+
})
|
|
608
|
+
}
|
|
609
|
+
return this
|
|
610
|
+
|
|
611
|
+
function _addHook (name, fn) {
|
|
612
|
+
this[kHooks].add(name, fn)
|
|
613
|
+
this[kChildren].forEach(child => _addHook.call(child, name, fn))
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// wrapper that we expose to the user for schemas handling
|
|
618
|
+
function addSchema (schema) {
|
|
619
|
+
throwIfAlreadyStarted('Cannot call "addSchema"!')
|
|
620
|
+
this[kSchemaController].add(schema)
|
|
621
|
+
this[kChildren].forEach(child => child.addSchema(schema))
|
|
622
|
+
return this
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// If the router does not match any route, every request will land here
|
|
626
|
+
// req and res are Node.js core objects
|
|
627
|
+
function defaultRoute (req, res) {
|
|
628
|
+
if (req.headers['accept-version'] !== undefined) {
|
|
629
|
+
// we remove the accept-version header for performance result
|
|
630
|
+
// because we do not want to go through the constraint checking
|
|
631
|
+
// the usage of symbol here to prevent any collision on custom header name
|
|
632
|
+
req.headers[kRequestAcceptVersion] = req.headers['accept-version']
|
|
633
|
+
req.headers['accept-version'] = undefined
|
|
634
|
+
}
|
|
635
|
+
fourOhFour.router.lookup(req, res)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function onBadUrl (path, req, res) {
|
|
639
|
+
if (options.frameworkErrors) {
|
|
640
|
+
const id = getGenReqId(onBadUrlContext.server, req)
|
|
641
|
+
const childLogger = createChildLogger(onBadUrlContext, options.logger, req, id)
|
|
642
|
+
|
|
643
|
+
const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
|
|
644
|
+
const reply = new Reply(res, request, childLogger)
|
|
645
|
+
|
|
646
|
+
const resolvedDisableRequestLogging = typeof disableRequestLogging === 'function' ? disableRequestLogging(req) : disableRequestLogging
|
|
647
|
+
if (resolvedDisableRequestLogging === false) {
|
|
648
|
+
childLogger.info({ req: request }, 'incoming request')
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return options.frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)
|
|
652
|
+
}
|
|
653
|
+
const body = JSON.stringify({
|
|
654
|
+
error: 'Bad Request',
|
|
655
|
+
code: 'FST_ERR_BAD_URL',
|
|
656
|
+
message: `'${path}' is not a valid url component`,
|
|
657
|
+
statusCode: 400
|
|
658
|
+
})
|
|
659
|
+
res.writeHead(400, {
|
|
660
|
+
'Content-Type': 'application/json',
|
|
661
|
+
'Content-Length': Buffer.byteLength(body)
|
|
662
|
+
})
|
|
663
|
+
res.end(body)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function buildAsyncConstraintCallback (isAsync, req, res) {
|
|
667
|
+
if (isAsync === false) return undefined
|
|
668
|
+
return function onAsyncConstraintError (err) {
|
|
669
|
+
if (err) {
|
|
670
|
+
if (options.frameworkErrors) {
|
|
671
|
+
const id = getGenReqId(onBadUrlContext.server, req)
|
|
672
|
+
const childLogger = createChildLogger(onBadUrlContext, options.logger, req, id)
|
|
673
|
+
|
|
674
|
+
const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
|
|
675
|
+
const reply = new Reply(res, request, childLogger)
|
|
676
|
+
|
|
677
|
+
const resolvedDisableRequestLogging = typeof disableRequestLogging === 'function' ? disableRequestLogging(req) : disableRequestLogging
|
|
678
|
+
if (resolvedDisableRequestLogging === false) {
|
|
679
|
+
childLogger.info({ req: request }, 'incoming request')
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return options.frameworkErrors(new FST_ERR_ASYNC_CONSTRAINT(), request, reply)
|
|
683
|
+
}
|
|
684
|
+
const body = '{"error":"Internal Server Error","message":"Unexpected error from async constraint","statusCode":500}'
|
|
685
|
+
res.writeHead(500, {
|
|
686
|
+
'Content-Type': 'application/json',
|
|
687
|
+
'Content-Length': body.length
|
|
688
|
+
})
|
|
689
|
+
res.end(body)
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function setNotFoundHandler (opts, handler) {
|
|
695
|
+
throwIfAlreadyStarted('Cannot call "setNotFoundHandler"!')
|
|
696
|
+
|
|
697
|
+
fourOhFour.setNotFoundHandler.call(this, opts, handler, avvio, router.routeHandler)
|
|
698
|
+
return this
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function setValidatorCompiler (validatorCompiler) {
|
|
702
|
+
throwIfAlreadyStarted('Cannot call "setValidatorCompiler"!')
|
|
703
|
+
this[kSchemaController].setValidatorCompiler(validatorCompiler)
|
|
704
|
+
return this
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function setSchemaErrorFormatter (errorFormatter) {
|
|
708
|
+
throwIfAlreadyStarted('Cannot call "setSchemaErrorFormatter"!')
|
|
709
|
+
validateSchemaErrorFormatter(errorFormatter)
|
|
710
|
+
this[kSchemaErrorFormatter] = errorFormatter.bind(this)
|
|
711
|
+
return this
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function setSerializerCompiler (serializerCompiler) {
|
|
715
|
+
throwIfAlreadyStarted('Cannot call "setSerializerCompiler"!')
|
|
716
|
+
this[kSchemaController].setSerializerCompiler(serializerCompiler)
|
|
717
|
+
return this
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function setSchemaController (schemaControllerOpts) {
|
|
721
|
+
throwIfAlreadyStarted('Cannot call "setSchemaController"!')
|
|
722
|
+
const old = this[kSchemaController]
|
|
723
|
+
const schemaController = SchemaController.buildSchemaController(
|
|
724
|
+
old,
|
|
725
|
+
Object.assign({}, old.opts, schemaControllerOpts)
|
|
726
|
+
)
|
|
727
|
+
this[kSchemaController] = schemaController
|
|
728
|
+
this.getSchema = schemaController.getSchema.bind(schemaController)
|
|
729
|
+
this.getSchemas = schemaController.getSchemas.bind(schemaController)
|
|
730
|
+
return this
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function setReplySerializer (replySerializer) {
|
|
734
|
+
throwIfAlreadyStarted('Cannot call "setReplySerializer"!')
|
|
735
|
+
|
|
736
|
+
this[kReplySerializerDefault] = replySerializer
|
|
737
|
+
return this
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// wrapper that we expose to the user for configure the custom error handler
|
|
741
|
+
function setErrorHandler (func) {
|
|
742
|
+
throwIfAlreadyStarted('Cannot call "setErrorHandler"!')
|
|
743
|
+
|
|
744
|
+
if (typeof func !== 'function') {
|
|
745
|
+
throw new FST_ERR_ERROR_HANDLER_NOT_FN()
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (!options.allowErrorHandlerOverride && this[kErrorHandlerAlreadySet]) {
|
|
749
|
+
throw new FST_ERR_ERROR_HANDLER_ALREADY_SET()
|
|
750
|
+
} else if (this[kErrorHandlerAlreadySet]) {
|
|
751
|
+
FSTWRN004("To disable this behavior, set 'allowErrorHandlerOverride' to false or ignore this message. For more information, visit: https://fastify.dev/docs/latest/Reference/Server/#allowerrorhandleroverride")
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
this[kErrorHandlerAlreadySet] = true
|
|
755
|
+
this[kErrorHandler] = buildErrorHandler(this[kErrorHandler], func.bind(this))
|
|
756
|
+
return this
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function setChildLoggerFactory (factory) {
|
|
760
|
+
throwIfAlreadyStarted('Cannot call "setChildLoggerFactory"!')
|
|
761
|
+
|
|
762
|
+
this[kChildLoggerFactory] = factory
|
|
763
|
+
return this
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
function printRoutes (opts = {}) {
|
|
767
|
+
// includeHooks:true - shortcut to include all supported hooks exported by fastify.Hooks
|
|
768
|
+
opts.includeMeta = opts.includeHooks
|
|
769
|
+
? opts.includeMeta ? supportedHooks.concat(opts.includeMeta) : supportedHooks
|
|
770
|
+
: opts.includeMeta
|
|
771
|
+
return router.printRoutes(opts)
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function wrapRouting (router, { rewriteUrl, logger }) {
|
|
775
|
+
let isAsync
|
|
776
|
+
return function preRouting (req, res) {
|
|
777
|
+
// only call isAsyncConstraint once
|
|
778
|
+
if (isAsync === undefined) isAsync = router.isAsyncConstraint()
|
|
779
|
+
if (rewriteUrl) {
|
|
780
|
+
req.originalUrl = req.url
|
|
781
|
+
const url = rewriteUrl.call(fastify, req)
|
|
782
|
+
if (typeof url === 'string') {
|
|
783
|
+
req.url = url
|
|
784
|
+
} else {
|
|
785
|
+
const err = new FST_ERR_ROUTE_REWRITE_NOT_STR(req.url, typeof url)
|
|
786
|
+
req.destroy(err)
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
router.routing(req, res, buildAsyncConstraintCallback(isAsync, req, res))
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function setGenReqId (func) {
|
|
794
|
+
throwIfAlreadyStarted('Cannot call "setGenReqId"!')
|
|
795
|
+
|
|
796
|
+
this[kGenReqId] = reqIdGenFactory(this[kOptions].requestIdHeader, func)
|
|
797
|
+
return this
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function addHttpMethod (method, { hasBody = false } = {}) {
|
|
801
|
+
if (typeof method !== 'string' || http.METHODS.indexOf(method) === -1) {
|
|
802
|
+
throw new FST_ERR_ROUTE_METHOD_INVALID()
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (hasBody === true) {
|
|
806
|
+
this[kSupportedHTTPMethods].bodywith.add(method)
|
|
807
|
+
this[kSupportedHTTPMethods].bodyless.delete(method)
|
|
808
|
+
} else {
|
|
809
|
+
this[kSupportedHTTPMethods].bodywith.delete(method)
|
|
810
|
+
this[kSupportedHTTPMethods].bodyless.add(method)
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const _method = method.toLowerCase()
|
|
814
|
+
if (!this.hasDecorator(_method)) {
|
|
815
|
+
this.decorate(_method, function (url, options, handler) {
|
|
816
|
+
return router.prepareRoute.call(this, { method, url, options, handler })
|
|
817
|
+
})
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
return this
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function processOptions (options, defaultRoute, onBadUrl) {
|
|
825
|
+
// Options validations
|
|
826
|
+
if (options && typeof options !== 'object') {
|
|
827
|
+
throw new FST_ERR_OPTIONS_NOT_OBJ()
|
|
828
|
+
} else {
|
|
829
|
+
// Shallow copy options object to prevent mutations outside of this function
|
|
830
|
+
options = Object.assign({}, options)
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
if (
|
|
834
|
+
(options.querystringParser && typeof options.querystringParser !== 'function') ||
|
|
835
|
+
(
|
|
836
|
+
options.routerOptions?.querystringParser &&
|
|
837
|
+
typeof options.routerOptions.querystringParser !== 'function'
|
|
838
|
+
)
|
|
839
|
+
) {
|
|
840
|
+
throw new FST_ERR_QSP_NOT_FN(typeof (options.querystringParser ?? options.routerOptions.querystringParser))
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if (options.schemaController && options.schemaController.bucket && typeof options.schemaController.bucket !== 'function') {
|
|
844
|
+
throw new FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN(typeof options.schemaController.bucket)
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
validateBodyLimitOption(options.bodyLimit)
|
|
848
|
+
|
|
849
|
+
const requestIdHeader = typeof options.requestIdHeader === 'string' && options.requestIdHeader.length !== 0 ? options.requestIdHeader.toLowerCase() : (options.requestIdHeader === true && 'request-id')
|
|
850
|
+
const genReqId = reqIdGenFactory(requestIdHeader, options.genReqId)
|
|
851
|
+
const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
|
|
852
|
+
options.bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
|
|
853
|
+
const disableRequestLogging = options.disableRequestLogging || false
|
|
854
|
+
|
|
855
|
+
const ajvOptions = Object.assign({
|
|
856
|
+
customOptions: {},
|
|
857
|
+
plugins: []
|
|
858
|
+
}, options.ajv)
|
|
859
|
+
|
|
860
|
+
if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {
|
|
861
|
+
throw new FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ(typeof ajvOptions.customOptions)
|
|
862
|
+
}
|
|
863
|
+
if (!ajvOptions.plugins || !Array.isArray(ajvOptions.plugins)) {
|
|
864
|
+
throw new FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR(typeof ajvOptions.plugins)
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const { logger, hasLogger } = createLogger(options)
|
|
868
|
+
|
|
869
|
+
// Update the options with the fixed values
|
|
870
|
+
options.connectionTimeout = options.connectionTimeout || defaultInitOptions.connectionTimeout
|
|
871
|
+
options.keepAliveTimeout = options.keepAliveTimeout || defaultInitOptions.keepAliveTimeout
|
|
872
|
+
options.maxRequestsPerSocket = options.maxRequestsPerSocket || defaultInitOptions.maxRequestsPerSocket
|
|
873
|
+
options.requestTimeout = options.requestTimeout || defaultInitOptions.requestTimeout
|
|
874
|
+
options.logger = logger
|
|
875
|
+
options.requestIdHeader = requestIdHeader
|
|
876
|
+
options.requestIdLogLabel = requestIdLogLabel
|
|
877
|
+
options.disableRequestLogging = disableRequestLogging
|
|
878
|
+
options.ajv = ajvOptions
|
|
879
|
+
options.clientErrorHandler = options.clientErrorHandler || defaultClientErrorHandler
|
|
880
|
+
options.allowErrorHandlerOverride = options.allowErrorHandlerOverride ?? defaultInitOptions.allowErrorHandlerOverride
|
|
881
|
+
|
|
882
|
+
const initialConfig = getSecuredInitialConfig(options)
|
|
883
|
+
|
|
884
|
+
// exposeHeadRoutes have its default set from the validator
|
|
885
|
+
options.exposeHeadRoutes = initialConfig.exposeHeadRoutes
|
|
886
|
+
|
|
887
|
+
// we need to set this before calling createServer
|
|
888
|
+
options.http2SessionTimeout = initialConfig.http2SessionTimeout
|
|
889
|
+
|
|
890
|
+
options.routerOptions = buildRouterOptions(options, {
|
|
891
|
+
defaultRoute,
|
|
892
|
+
onBadUrl,
|
|
893
|
+
ignoreTrailingSlash: defaultInitOptions.ignoreTrailingSlash,
|
|
894
|
+
ignoreDuplicateSlashes: defaultInitOptions.ignoreDuplicateSlashes,
|
|
895
|
+
maxParamLength: defaultInitOptions.maxParamLength,
|
|
896
|
+
allowUnsafeRegex: defaultInitOptions.allowUnsafeRegex,
|
|
897
|
+
buildPrettyMeta: defaultBuildPrettyMeta,
|
|
898
|
+
useSemicolonDelimiter: defaultInitOptions.useSemicolonDelimiter
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
return {
|
|
902
|
+
options,
|
|
903
|
+
genReqId,
|
|
904
|
+
disableRequestLogging,
|
|
905
|
+
hasLogger,
|
|
906
|
+
initialConfig
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function defaultBuildPrettyMeta (route) {
|
|
911
|
+
// return a shallow copy of route's sanitized context
|
|
912
|
+
|
|
913
|
+
const cleanKeys = {}
|
|
914
|
+
const allowedProps = ['errorHandler', 'logLevel', 'logSerializers']
|
|
915
|
+
|
|
916
|
+
allowedProps.concat(supportedHooks).forEach(k => {
|
|
917
|
+
cleanKeys[k] = route.store[k]
|
|
918
|
+
})
|
|
919
|
+
|
|
920
|
+
return Object.assign({}, cleanKeys)
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function defaultClientErrorHandler (err, socket) {
|
|
924
|
+
// In case of a connection reset, the socket has been destroyed and there is nothing that needs to be done.
|
|
925
|
+
// https://nodejs.org/api/http.html#http_event_clienterror
|
|
926
|
+
if (err.code === 'ECONNRESET' || socket.destroyed) {
|
|
927
|
+
return
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
let body, errorCode, errorStatus, errorLabel
|
|
931
|
+
|
|
932
|
+
if (err.code === 'ERR_HTTP_REQUEST_TIMEOUT') {
|
|
933
|
+
errorCode = '408'
|
|
934
|
+
errorStatus = http.STATUS_CODES[errorCode]
|
|
935
|
+
body = `{"error":"${errorStatus}","message":"Client Timeout","statusCode":408}`
|
|
936
|
+
errorLabel = 'timeout'
|
|
937
|
+
} else if (err.code === 'HPE_HEADER_OVERFLOW') {
|
|
938
|
+
errorCode = '431'
|
|
939
|
+
errorStatus = http.STATUS_CODES[errorCode]
|
|
940
|
+
body = `{"error":"${errorStatus}","message":"Exceeded maximum allowed HTTP header size","statusCode":431}`
|
|
941
|
+
errorLabel = 'header_overflow'
|
|
942
|
+
} else {
|
|
943
|
+
errorCode = '400'
|
|
944
|
+
errorStatus = http.STATUS_CODES[errorCode]
|
|
945
|
+
body = `{"error":"${errorStatus}","message":"Client Error","statusCode":400}`
|
|
946
|
+
errorLabel = 'error'
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Most devs do not know what to do with this error.
|
|
950
|
+
// In the vast majority of cases, it's a network error and/or some
|
|
951
|
+
// config issue on the load balancer side.
|
|
952
|
+
this.log.trace({ err }, `client ${errorLabel}`)
|
|
953
|
+
// Copying standard node behavior
|
|
954
|
+
// https://github.com/nodejs/node/blob/6ca23d7846cb47e84fd344543e394e50938540be/lib/_http_server.js#L666
|
|
955
|
+
|
|
956
|
+
// If the socket is not writable, there is no reason to try to send data.
|
|
957
|
+
if (socket.writable) {
|
|
958
|
+
socket.write(`HTTP/1.1 ${errorCode} ${errorStatus}\r\nContent-Length: ${body.length}\r\nContent-Type: application/json\r\n\r\n${body}`)
|
|
959
|
+
}
|
|
960
|
+
socket.destroy(err)
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function validateSchemaErrorFormatter (schemaErrorFormatter) {
|
|
964
|
+
if (typeof schemaErrorFormatter !== 'function') {
|
|
965
|
+
throw new FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN(typeof schemaErrorFormatter)
|
|
966
|
+
} else if (schemaErrorFormatter.constructor.name === 'AsyncFunction') {
|
|
967
|
+
throw new FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN('AsyncFunction')
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* These export configurations enable JS and TS developers
|
|
973
|
+
* to consume fastify in whatever way best suits their needs.
|
|
974
|
+
* Some examples of supported import syntax includes:
|
|
975
|
+
* - `const fastify = require('fastify')`
|
|
976
|
+
* - `const { fastify } = require('fastify')`
|
|
977
|
+
* - `import * as Fastify from 'fastify'`
|
|
978
|
+
* - `import { fastify, TSC_definition } from 'fastify'`
|
|
979
|
+
* - `import fastify from 'fastify'`
|
|
980
|
+
* - `import fastify, { TSC_definition } from 'fastify'`
|
|
981
|
+
*/
|
|
982
|
+
module.exports = fastify
|
|
983
|
+
module.exports.errorCodes = errorCodes
|
|
984
|
+
module.exports.fastify = fastify
|
|
985
|
+
module.exports.default = fastify
|