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/lib/route.js
ADDED
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const FindMyWay = require('find-my-way')
|
|
4
|
+
const Context = require('./context')
|
|
5
|
+
const handleRequest = require('./handle-request.js')
|
|
6
|
+
const { onRequestAbortHookRunner, lifecycleHooks, preParsingHookRunner, onTimeoutHookRunner, onRequestHookRunner } = require('./hooks')
|
|
7
|
+
const { normalizeSchema } = require('./schemas')
|
|
8
|
+
const { parseHeadOnSendHandlers } = require('./head-route.js')
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
compileSchemasForValidation,
|
|
12
|
+
compileSchemasForSerialization
|
|
13
|
+
} = require('./validation')
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
FST_ERR_SCH_VALIDATION_BUILD,
|
|
17
|
+
FST_ERR_SCH_SERIALIZATION_BUILD,
|
|
18
|
+
FST_ERR_DUPLICATED_ROUTE,
|
|
19
|
+
FST_ERR_INVALID_URL,
|
|
20
|
+
FST_ERR_HOOK_INVALID_HANDLER,
|
|
21
|
+
FST_ERR_ROUTE_OPTIONS_NOT_OBJ,
|
|
22
|
+
FST_ERR_ROUTE_DUPLICATED_HANDLER,
|
|
23
|
+
FST_ERR_ROUTE_HANDLER_NOT_FN,
|
|
24
|
+
FST_ERR_ROUTE_MISSING_HANDLER,
|
|
25
|
+
FST_ERR_ROUTE_METHOD_NOT_SUPPORTED,
|
|
26
|
+
FST_ERR_ROUTE_METHOD_INVALID,
|
|
27
|
+
FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED,
|
|
28
|
+
FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT,
|
|
29
|
+
FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT,
|
|
30
|
+
FST_ERR_HANDLER_TIMEOUT,
|
|
31
|
+
FST_ERR_HOOK_INVALID_ASYNC_HANDLER
|
|
32
|
+
} = require('./errors')
|
|
33
|
+
|
|
34
|
+
const {
|
|
35
|
+
kRoutePrefix,
|
|
36
|
+
kSupportedHTTPMethods,
|
|
37
|
+
kLogLevel,
|
|
38
|
+
kLogSerializers,
|
|
39
|
+
kHooks,
|
|
40
|
+
kSchemaController,
|
|
41
|
+
kOptions,
|
|
42
|
+
kReplySerializerDefault,
|
|
43
|
+
kReplyIsError,
|
|
44
|
+
kRequestPayloadStream,
|
|
45
|
+
kDisableRequestLogging,
|
|
46
|
+
kSchemaErrorFormatter,
|
|
47
|
+
kErrorHandler,
|
|
48
|
+
kHasBeenDecorated,
|
|
49
|
+
kRequestAcceptVersion,
|
|
50
|
+
kRouteByFastify,
|
|
51
|
+
kRouteContext,
|
|
52
|
+
kRequestSignal,
|
|
53
|
+
kTimeoutTimer,
|
|
54
|
+
kOnAbort
|
|
55
|
+
} = require('./symbols.js')
|
|
56
|
+
const { buildErrorHandler } = require('./error-handler')
|
|
57
|
+
const { createChildLogger } = require('./logger-factory.js')
|
|
58
|
+
const { getGenReqId } = require('./req-id-gen-factory.js')
|
|
59
|
+
const { FSTDEP022 } = require('./warnings')
|
|
60
|
+
|
|
61
|
+
const routerKeys = [
|
|
62
|
+
'allowUnsafeRegex',
|
|
63
|
+
'buildPrettyMeta',
|
|
64
|
+
'caseSensitive',
|
|
65
|
+
'constraints',
|
|
66
|
+
'defaultRoute',
|
|
67
|
+
'ignoreDuplicateSlashes',
|
|
68
|
+
'ignoreTrailingSlash',
|
|
69
|
+
'maxParamLength',
|
|
70
|
+
'onBadUrl',
|
|
71
|
+
'querystringParser',
|
|
72
|
+
'useSemicolonDelimiter'
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
function buildRouting (options) {
|
|
76
|
+
const router = FindMyWay(options)
|
|
77
|
+
|
|
78
|
+
let avvio
|
|
79
|
+
let fourOhFour
|
|
80
|
+
let logger
|
|
81
|
+
let hasLogger
|
|
82
|
+
let setupResponseListeners
|
|
83
|
+
let throwIfAlreadyStarted
|
|
84
|
+
let disableRequestLogging
|
|
85
|
+
let disableRequestLoggingFn
|
|
86
|
+
let ignoreTrailingSlash
|
|
87
|
+
let ignoreDuplicateSlashes
|
|
88
|
+
let return503OnClosing
|
|
89
|
+
let globalExposeHeadRoutes
|
|
90
|
+
let keepAliveConnections
|
|
91
|
+
|
|
92
|
+
let closing = false
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
/**
|
|
96
|
+
* @param {import('../fastify').FastifyServerOptions} options
|
|
97
|
+
* @param {*} fastifyArgs
|
|
98
|
+
*/
|
|
99
|
+
setup (options, fastifyArgs) {
|
|
100
|
+
avvio = fastifyArgs.avvio
|
|
101
|
+
fourOhFour = fastifyArgs.fourOhFour
|
|
102
|
+
logger = options.logger
|
|
103
|
+
hasLogger = fastifyArgs.hasLogger
|
|
104
|
+
setupResponseListeners = fastifyArgs.setupResponseListeners
|
|
105
|
+
throwIfAlreadyStarted = fastifyArgs.throwIfAlreadyStarted
|
|
106
|
+
|
|
107
|
+
globalExposeHeadRoutes = options.exposeHeadRoutes
|
|
108
|
+
disableRequestLogging = options.disableRequestLogging
|
|
109
|
+
if (typeof disableRequestLogging === 'function') {
|
|
110
|
+
disableRequestLoggingFn = options.disableRequestLogging
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
ignoreTrailingSlash = options.routerOptions.ignoreTrailingSlash
|
|
114
|
+
ignoreDuplicateSlashes = options.routerOptions.ignoreDuplicateSlashes
|
|
115
|
+
return503OnClosing = Object.hasOwn(options, 'return503OnClosing') ? options.return503OnClosing : true
|
|
116
|
+
keepAliveConnections = fastifyArgs.keepAliveConnections
|
|
117
|
+
},
|
|
118
|
+
routing: router.lookup.bind(router), // router func to find the right handler to call
|
|
119
|
+
route, // configure a route in the fastify instance
|
|
120
|
+
hasRoute,
|
|
121
|
+
prepareRoute,
|
|
122
|
+
routeHandler,
|
|
123
|
+
closeRoutes: () => { closing = true },
|
|
124
|
+
printRoutes: router.prettyPrint.bind(router),
|
|
125
|
+
addConstraintStrategy,
|
|
126
|
+
hasConstraintStrategy,
|
|
127
|
+
isAsyncConstraint,
|
|
128
|
+
findRoute
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function addConstraintStrategy (strategy) {
|
|
132
|
+
throwIfAlreadyStarted('Cannot add constraint strategy!')
|
|
133
|
+
return router.addConstraintStrategy(strategy)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function hasConstraintStrategy (strategyName) {
|
|
137
|
+
return router.hasConstraintStrategy(strategyName)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isAsyncConstraint () {
|
|
141
|
+
return router.constrainer.asyncStrategiesInUse.size > 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Convert shorthand to extended route declaration
|
|
145
|
+
function prepareRoute ({ method, url, options, handler, isFastify }) {
|
|
146
|
+
if (typeof url !== 'string') {
|
|
147
|
+
throw new FST_ERR_INVALID_URL(typeof url)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!handler && typeof options === 'function') {
|
|
151
|
+
handler = options // for support over direct function calls such as fastify.get() options are reused as the handler
|
|
152
|
+
options = {}
|
|
153
|
+
} else if (handler && typeof handler === 'function') {
|
|
154
|
+
if (Object.prototype.toString.call(options) !== '[object Object]') {
|
|
155
|
+
throw new FST_ERR_ROUTE_OPTIONS_NOT_OBJ(method, url)
|
|
156
|
+
} else if (options.handler) {
|
|
157
|
+
if (typeof options.handler === 'function') {
|
|
158
|
+
throw new FST_ERR_ROUTE_DUPLICATED_HANDLER(method, url)
|
|
159
|
+
} else {
|
|
160
|
+
throw new FST_ERR_ROUTE_HANDLER_NOT_FN(method, url)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
options = Object.assign({}, options, {
|
|
166
|
+
method,
|
|
167
|
+
url,
|
|
168
|
+
path: url,
|
|
169
|
+
handler: handler || (options && options.handler)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
return route.call(this, { options, isFastify })
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function hasRoute ({ options }) {
|
|
176
|
+
const normalizedMethod = options.method?.toUpperCase() ?? ''
|
|
177
|
+
return router.hasRoute(
|
|
178
|
+
normalizedMethod,
|
|
179
|
+
options.url || '',
|
|
180
|
+
options.constraints
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function findRoute (options) {
|
|
185
|
+
const route = router.find(
|
|
186
|
+
options.method,
|
|
187
|
+
options.url || '',
|
|
188
|
+
options.constraints
|
|
189
|
+
)
|
|
190
|
+
if (route) {
|
|
191
|
+
// we must reduce the expose surface, otherwise
|
|
192
|
+
// we provide the ability for the user to modify
|
|
193
|
+
// all the route and server information in runtime
|
|
194
|
+
return {
|
|
195
|
+
handler: route.handler,
|
|
196
|
+
params: route.params,
|
|
197
|
+
searchParams: route.searchParams
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Route management
|
|
206
|
+
* @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
|
|
207
|
+
*/
|
|
208
|
+
function route ({ options, isFastify }) {
|
|
209
|
+
throwIfAlreadyStarted('Cannot add route!')
|
|
210
|
+
|
|
211
|
+
// Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
|
|
212
|
+
const opts = { ...options }
|
|
213
|
+
|
|
214
|
+
const path = opts.url || opts.path || ''
|
|
215
|
+
|
|
216
|
+
if (!opts.handler) {
|
|
217
|
+
throw new FST_ERR_ROUTE_MISSING_HANDLER(opts.method, path)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (opts.errorHandler !== undefined && typeof opts.errorHandler !== 'function') {
|
|
221
|
+
throw new FST_ERR_ROUTE_HANDLER_NOT_FN(opts.method, path)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
validateBodyLimitOption(opts.bodyLimit)
|
|
225
|
+
validateHandlerTimeoutOption(opts.handlerTimeout)
|
|
226
|
+
|
|
227
|
+
const shouldExposeHead = opts.exposeHeadRoute ?? globalExposeHeadRoutes
|
|
228
|
+
|
|
229
|
+
let isGetRoute = false
|
|
230
|
+
let isHeadRoute = false
|
|
231
|
+
|
|
232
|
+
if (Array.isArray(opts.method)) {
|
|
233
|
+
for (let i = 0; i < opts.method.length; ++i) {
|
|
234
|
+
opts.method[i] = normalizeAndValidateMethod.call(this, opts.method[i])
|
|
235
|
+
validateSchemaBodyOption.call(this, opts.method[i], path, opts.schema)
|
|
236
|
+
|
|
237
|
+
isGetRoute = opts.method.includes('GET')
|
|
238
|
+
isHeadRoute = opts.method.includes('HEAD')
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
opts.method = normalizeAndValidateMethod.call(this, opts.method)
|
|
242
|
+
validateSchemaBodyOption.call(this, opts.method, path, opts.schema)
|
|
243
|
+
|
|
244
|
+
isGetRoute = opts.method === 'GET'
|
|
245
|
+
isHeadRoute = opts.method === 'HEAD'
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// we need to clone a set of initial options for HEAD route
|
|
249
|
+
const headOpts = shouldExposeHead && isGetRoute ? { ...options } : null
|
|
250
|
+
|
|
251
|
+
const prefix = this[kRoutePrefix]
|
|
252
|
+
|
|
253
|
+
if (path === '/' && prefix.length > 0 && opts.method !== 'HEAD') {
|
|
254
|
+
switch (opts.prefixTrailingSlash) {
|
|
255
|
+
case 'slash':
|
|
256
|
+
addNewRoute.call(this, { path, isFastify })
|
|
257
|
+
break
|
|
258
|
+
case 'no-slash':
|
|
259
|
+
addNewRoute.call(this, { path: '', isFastify })
|
|
260
|
+
break
|
|
261
|
+
case 'both':
|
|
262
|
+
default:
|
|
263
|
+
addNewRoute.call(this, { path: '', isFastify })
|
|
264
|
+
// If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
|
|
265
|
+
if (ignoreTrailingSlash !== true && (ignoreDuplicateSlashes !== true || !prefix.endsWith('/'))) {
|
|
266
|
+
addNewRoute.call(this, { path, prefixing: true, isFastify })
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} else if (path[0] === '/' && prefix.endsWith('/')) {
|
|
270
|
+
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
|
|
271
|
+
addNewRoute.call(this, { path: path.slice(1), isFastify })
|
|
272
|
+
} else {
|
|
273
|
+
addNewRoute.call(this, { path, isFastify })
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// chainable api
|
|
277
|
+
return this
|
|
278
|
+
|
|
279
|
+
function addNewRoute ({ path, prefixing = false, isFastify = false }) {
|
|
280
|
+
const url = prefix + path
|
|
281
|
+
|
|
282
|
+
opts.url = url
|
|
283
|
+
opts.path = url
|
|
284
|
+
opts.routePath = path
|
|
285
|
+
opts.prefix = prefix
|
|
286
|
+
opts.logLevel = opts.logLevel || this[kLogLevel]
|
|
287
|
+
|
|
288
|
+
if (this[kLogSerializers] || opts.logSerializers) {
|
|
289
|
+
opts.logSerializers = Object.assign(Object.create(this[kLogSerializers]), opts.logSerializers)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (opts.attachValidation == null) {
|
|
293
|
+
opts.attachValidation = false
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (prefixing === false) {
|
|
297
|
+
// run 'onRoute' hooks
|
|
298
|
+
for (const hook of this[kHooks].onRoute) {
|
|
299
|
+
hook.call(this, opts)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
for (const hook of lifecycleHooks) {
|
|
304
|
+
if (opts && hook in opts) {
|
|
305
|
+
if (Array.isArray(opts[hook])) {
|
|
306
|
+
for (const func of opts[hook]) {
|
|
307
|
+
if (typeof func !== 'function') {
|
|
308
|
+
throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(func))
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (hook === 'onSend' || hook === 'preSerialization' || hook === 'onError' || hook === 'preParsing') {
|
|
312
|
+
if (func.constructor.name === 'AsyncFunction' && func.length === 4) {
|
|
313
|
+
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
314
|
+
}
|
|
315
|
+
} else if (hook === 'onRequestAbort') {
|
|
316
|
+
if (func.constructor.name === 'AsyncFunction' && func.length !== 1) {
|
|
317
|
+
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
if (func.constructor.name === 'AsyncFunction' && func.length === 3) {
|
|
321
|
+
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
} else if (opts[hook] !== undefined && typeof opts[hook] !== 'function') {
|
|
326
|
+
throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(opts[hook]))
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const constraints = opts.constraints || {}
|
|
332
|
+
const config = {
|
|
333
|
+
...opts.config,
|
|
334
|
+
url,
|
|
335
|
+
method: opts.method
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const context = new Context({
|
|
339
|
+
schema: opts.schema,
|
|
340
|
+
handler: opts.handler.bind(this),
|
|
341
|
+
config,
|
|
342
|
+
errorHandler: opts.errorHandler,
|
|
343
|
+
childLoggerFactory: opts.childLoggerFactory,
|
|
344
|
+
bodyLimit: opts.bodyLimit,
|
|
345
|
+
logLevel: opts.logLevel,
|
|
346
|
+
logSerializers: opts.logSerializers,
|
|
347
|
+
attachValidation: opts.attachValidation,
|
|
348
|
+
schemaErrorFormatter: opts.schemaErrorFormatter,
|
|
349
|
+
replySerializer: this[kReplySerializerDefault],
|
|
350
|
+
validatorCompiler: opts.validatorCompiler,
|
|
351
|
+
serializerCompiler: opts.serializerCompiler,
|
|
352
|
+
exposeHeadRoute: shouldExposeHead,
|
|
353
|
+
prefixTrailingSlash: (opts.prefixTrailingSlash || 'both'),
|
|
354
|
+
server: this,
|
|
355
|
+
isFastify,
|
|
356
|
+
handlerTimeout: opts.handlerTimeout
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
const headHandler = router.findRoute('HEAD', opts.url, constraints)
|
|
360
|
+
const hasHEADHandler = headHandler !== null
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
router.on(opts.method, opts.url, { constraints }, routeHandler, context)
|
|
364
|
+
} catch (error) {
|
|
365
|
+
// any route insertion error created by fastify can be safely ignore
|
|
366
|
+
// because it only duplicate route for head
|
|
367
|
+
if (!context[kRouteByFastify]) {
|
|
368
|
+
const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route`)
|
|
369
|
+
if (isDuplicatedRoute) {
|
|
370
|
+
throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
throw error
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
this.after((notHandledErr, done) => {
|
|
378
|
+
// Send context async
|
|
379
|
+
context.errorHandler = opts.errorHandler
|
|
380
|
+
? buildErrorHandler(this[kErrorHandler], opts.errorHandler)
|
|
381
|
+
: this[kErrorHandler]
|
|
382
|
+
context._parserOptions.limit = opts.bodyLimit || null
|
|
383
|
+
context.logLevel = opts.logLevel
|
|
384
|
+
context.logSerializers = opts.logSerializers
|
|
385
|
+
context.attachValidation = opts.attachValidation
|
|
386
|
+
context[kReplySerializerDefault] = this[kReplySerializerDefault]
|
|
387
|
+
context.schemaErrorFormatter =
|
|
388
|
+
opts.schemaErrorFormatter || this[kSchemaErrorFormatter] || context.schemaErrorFormatter
|
|
389
|
+
|
|
390
|
+
// Run hooks and more
|
|
391
|
+
avvio.once('preReady', () => {
|
|
392
|
+
for (const hook of lifecycleHooks) {
|
|
393
|
+
const toSet = this[kHooks][hook]
|
|
394
|
+
.concat(opts[hook] || [])
|
|
395
|
+
.map(h => h.bind(this))
|
|
396
|
+
context[hook] = toSet.length ? toSet : null
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Optimization: avoid encapsulation if no decoration has been done.
|
|
400
|
+
while (!context.Request[kHasBeenDecorated] && context.Request.parent) {
|
|
401
|
+
context.Request = context.Request.parent
|
|
402
|
+
}
|
|
403
|
+
while (!context.Reply[kHasBeenDecorated] && context.Reply.parent) {
|
|
404
|
+
context.Reply = context.Reply.parent
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Must store the 404 Context in 'preReady' because it is only guaranteed to
|
|
408
|
+
// be available after all of the plugins and routes have been loaded.
|
|
409
|
+
fourOhFour.setContext(this, context)
|
|
410
|
+
|
|
411
|
+
if (opts.schema) {
|
|
412
|
+
context.schema = normalizeSchema(context.schema, this.initialConfig)
|
|
413
|
+
|
|
414
|
+
const schemaController = this[kSchemaController]
|
|
415
|
+
const hasValidationSchema = opts.schema.body ||
|
|
416
|
+
opts.schema.headers ||
|
|
417
|
+
opts.schema.querystring ||
|
|
418
|
+
opts.schema.params
|
|
419
|
+
if (!opts.validatorCompiler && hasValidationSchema) {
|
|
420
|
+
schemaController.setupValidator(this[kOptions])
|
|
421
|
+
}
|
|
422
|
+
try {
|
|
423
|
+
const isCustom = typeof opts?.validatorCompiler === 'function' ||
|
|
424
|
+
schemaController.isCustomValidatorCompiler
|
|
425
|
+
compileSchemasForValidation(
|
|
426
|
+
context,
|
|
427
|
+
opts.validatorCompiler || schemaController.validatorCompiler,
|
|
428
|
+
isCustom
|
|
429
|
+
)
|
|
430
|
+
} catch (error) {
|
|
431
|
+
throw new FST_ERR_SCH_VALIDATION_BUILD(opts.method, url, error.message)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (opts.schema.response && !opts.serializerCompiler) {
|
|
435
|
+
schemaController.setupSerializer(this[kOptions])
|
|
436
|
+
}
|
|
437
|
+
try {
|
|
438
|
+
compileSchemasForSerialization(context, opts.serializerCompiler || schemaController.serializerCompiler)
|
|
439
|
+
} catch (error) {
|
|
440
|
+
throw new FST_ERR_SCH_SERIALIZATION_BUILD(opts.method, url, error.message)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
done(notHandledErr)
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
// register head route in sync
|
|
449
|
+
// we must place it after the `this.after`
|
|
450
|
+
|
|
451
|
+
if (shouldExposeHead && isGetRoute && !isHeadRoute && !hasHEADHandler) {
|
|
452
|
+
const onSendHandlers = parseHeadOnSendHandlers(headOpts.onSend)
|
|
453
|
+
prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...headOpts, onSend: onSendHandlers }, isFastify: true })
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// HTTP request entry point, the routing has already been executed
|
|
459
|
+
function routeHandler (req, res, params, context, query) {
|
|
460
|
+
const id = getGenReqId(context.server, req)
|
|
461
|
+
|
|
462
|
+
const loggerOpts = {
|
|
463
|
+
level: context.logLevel
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (context.logSerializers) {
|
|
467
|
+
loggerOpts.serializers = context.logSerializers
|
|
468
|
+
}
|
|
469
|
+
const childLogger = createChildLogger(context, logger, req, id, loggerOpts)
|
|
470
|
+
// Set initial value; will be re-evaluated after FastifyRequest is constructed if it's a function
|
|
471
|
+
childLogger[kDisableRequestLogging] = disableRequestLoggingFn ? false : disableRequestLogging
|
|
472
|
+
|
|
473
|
+
if (closing === true) {
|
|
474
|
+
/* istanbul ignore next mac, windows */
|
|
475
|
+
if (req.httpVersionMajor !== 2) {
|
|
476
|
+
res.setHeader('Connection', 'close')
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// TODO remove return503OnClosing after Node v18 goes EOL
|
|
480
|
+
/* istanbul ignore else */
|
|
481
|
+
if (return503OnClosing) {
|
|
482
|
+
// On Node v19 we cannot test this behavior as it won't be necessary
|
|
483
|
+
// anymore. It will close all the idle connections before they reach this
|
|
484
|
+
// stage.
|
|
485
|
+
const headers = {
|
|
486
|
+
'Content-Type': 'application/json',
|
|
487
|
+
'Content-Length': '80'
|
|
488
|
+
}
|
|
489
|
+
res.writeHead(503, headers)
|
|
490
|
+
res.end('{"error":"Service Unavailable","message":"Service Unavailable","statusCode":503}')
|
|
491
|
+
childLogger.info({ res: { statusCode: 503 } }, 'request aborted - refusing to accept new requests as server is closing')
|
|
492
|
+
return
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// When server.forceCloseConnections is true, we will collect any requests
|
|
497
|
+
// that have indicated they want persistence so that they can be reaped
|
|
498
|
+
// on server close. Otherwise, the container is a noop container.
|
|
499
|
+
const connHeader = String.prototype.toLowerCase.call(req.headers.connection || '')
|
|
500
|
+
if (connHeader === 'keep-alive') {
|
|
501
|
+
if (keepAliveConnections.has(req.socket) === false) {
|
|
502
|
+
keepAliveConnections.add(req.socket)
|
|
503
|
+
req.socket.on('close', removeTrackedSocket.bind({ keepAliveConnections, socket: req.socket }))
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// we revert the changes in defaultRoute
|
|
508
|
+
if (req.headers[kRequestAcceptVersion] !== undefined) {
|
|
509
|
+
req.headers['accept-version'] = req.headers[kRequestAcceptVersion]
|
|
510
|
+
req.headers[kRequestAcceptVersion] = undefined
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const request = new context.Request(id, params, req, query, childLogger, context)
|
|
514
|
+
const reply = new context.Reply(res, request, childLogger)
|
|
515
|
+
|
|
516
|
+
// Evaluate disableRequestLogging after FastifyRequest is constructed
|
|
517
|
+
// so the caller has access to decorations and customizations
|
|
518
|
+
const resolvedDisableRequestLogging = disableRequestLoggingFn
|
|
519
|
+
? disableRequestLoggingFn(request)
|
|
520
|
+
: disableRequestLogging
|
|
521
|
+
childLogger[kDisableRequestLogging] = resolvedDisableRequestLogging
|
|
522
|
+
|
|
523
|
+
if (resolvedDisableRequestLogging === false) {
|
|
524
|
+
childLogger.info({ req: request }, 'incoming request')
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Handler timeout setup — only when configured (zero overhead otherwise)
|
|
528
|
+
const handlerTimeout = context.handlerTimeout
|
|
529
|
+
if (handlerTimeout > 0) {
|
|
530
|
+
const ac = new AbortController()
|
|
531
|
+
request[kRequestSignal] = ac
|
|
532
|
+
|
|
533
|
+
request[kTimeoutTimer] = setTimeout(() => {
|
|
534
|
+
if (!reply.sent) {
|
|
535
|
+
const err = new FST_ERR_HANDLER_TIMEOUT(handlerTimeout, context.config?.url)
|
|
536
|
+
ac.abort(err)
|
|
537
|
+
reply[kReplyIsError] = true
|
|
538
|
+
reply.send(err)
|
|
539
|
+
}
|
|
540
|
+
}, handlerTimeout)
|
|
541
|
+
|
|
542
|
+
const onAbort = () => {
|
|
543
|
+
if (!ac.signal.aborted) {
|
|
544
|
+
ac.abort()
|
|
545
|
+
}
|
|
546
|
+
clearTimeout(request[kTimeoutTimer])
|
|
547
|
+
}
|
|
548
|
+
req.on('close', onAbort)
|
|
549
|
+
request[kOnAbort] = onAbort
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (hasLogger === true || context.onResponse !== null || handlerTimeout > 0) {
|
|
553
|
+
setupResponseListeners(reply)
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (context.onRequest !== null) {
|
|
557
|
+
onRequestHookRunner(
|
|
558
|
+
context.onRequest,
|
|
559
|
+
request,
|
|
560
|
+
reply,
|
|
561
|
+
runPreParsing
|
|
562
|
+
)
|
|
563
|
+
} else {
|
|
564
|
+
runPreParsing(null, request, reply)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (context.onRequestAbort !== null) {
|
|
568
|
+
req.on('close', () => {
|
|
569
|
+
/* istanbul ignore else */
|
|
570
|
+
if (req.aborted) {
|
|
571
|
+
onRequestAbortHookRunner(
|
|
572
|
+
context.onRequestAbort,
|
|
573
|
+
request,
|
|
574
|
+
handleOnRequestAbortHooksErrors.bind(null, reply)
|
|
575
|
+
)
|
|
576
|
+
}
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (context.onTimeout !== null) {
|
|
581
|
+
if (!request.raw.socket._meta) {
|
|
582
|
+
request.raw.socket.on('timeout', handleTimeout)
|
|
583
|
+
}
|
|
584
|
+
request.raw.socket._meta = { context, request, reply }
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function handleOnRequestAbortHooksErrors (reply, err) {
|
|
590
|
+
if (err) {
|
|
591
|
+
reply.log.error({ err }, 'onRequestAborted hook failed')
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function handleTimeout () {
|
|
596
|
+
const { context, request, reply } = this._meta
|
|
597
|
+
onTimeoutHookRunner(
|
|
598
|
+
context.onTimeout,
|
|
599
|
+
request,
|
|
600
|
+
reply,
|
|
601
|
+
noop
|
|
602
|
+
)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function normalizeAndValidateMethod (method) {
|
|
606
|
+
if (typeof method !== 'string') {
|
|
607
|
+
throw new FST_ERR_ROUTE_METHOD_INVALID()
|
|
608
|
+
}
|
|
609
|
+
method = method.toUpperCase()
|
|
610
|
+
if (!this[kSupportedHTTPMethods].bodyless.has(method) &&
|
|
611
|
+
!this[kSupportedHTTPMethods].bodywith.has(method)) {
|
|
612
|
+
throw new FST_ERR_ROUTE_METHOD_NOT_SUPPORTED(method)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return method
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function validateSchemaBodyOption (method, path, schema) {
|
|
619
|
+
if (this[kSupportedHTTPMethods].bodyless.has(method) && schema?.body) {
|
|
620
|
+
throw new FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED(method, path)
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function validateBodyLimitOption (bodyLimit) {
|
|
625
|
+
if (bodyLimit === undefined) return
|
|
626
|
+
if (!Number.isInteger(bodyLimit) || bodyLimit <= 0) {
|
|
627
|
+
throw new FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT(bodyLimit)
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function validateHandlerTimeoutOption (handlerTimeout) {
|
|
632
|
+
if (handlerTimeout === undefined) return
|
|
633
|
+
if (!Number.isInteger(handlerTimeout) || handlerTimeout <= 0) {
|
|
634
|
+
throw new FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT(handlerTimeout)
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function runPreParsing (err, request, reply) {
|
|
639
|
+
if (reply.sent === true) return
|
|
640
|
+
if (err != null) {
|
|
641
|
+
reply[kReplyIsError] = true
|
|
642
|
+
reply.send(err)
|
|
643
|
+
return
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
request[kRequestPayloadStream] = request.raw
|
|
647
|
+
|
|
648
|
+
if (request[kRouteContext].preParsing !== null) {
|
|
649
|
+
preParsingHookRunner(request[kRouteContext].preParsing, request, reply, handleRequest.bind(request.server))
|
|
650
|
+
} else {
|
|
651
|
+
handleRequest.call(request.server, null, request, reply)
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function buildRouterOptions (options, defaultOptions) {
|
|
656
|
+
const routerOptions = options.routerOptions == null
|
|
657
|
+
? Object.create(null)
|
|
658
|
+
: Object.assign(Object.create(null), options.routerOptions)
|
|
659
|
+
|
|
660
|
+
const usedDeprecatedOptions = routerKeys.filter(key => Object.hasOwn(options, key))
|
|
661
|
+
|
|
662
|
+
if (usedDeprecatedOptions.length > 0) {
|
|
663
|
+
FSTDEP022(usedDeprecatedOptions.join(', '))
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
for (const key of routerKeys) {
|
|
667
|
+
if (!Object.hasOwn(routerOptions, key)) {
|
|
668
|
+
routerOptions[key] = options[key] ?? defaultOptions[key]
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return routerOptions
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Used within the route handler as a `net.Socket.close` event handler.
|
|
677
|
+
* The purpose is to remove a socket from the tracked sockets collection when
|
|
678
|
+
* the socket has naturally timed out.
|
|
679
|
+
*/
|
|
680
|
+
function removeTrackedSocket () {
|
|
681
|
+
this.keepAliveConnections.delete(this.socket)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function noop () { }
|
|
685
|
+
|
|
686
|
+
module.exports = { buildRouting, validateBodyLimitOption, buildRouterOptions }
|