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
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { AsyncResource } = require('node:async_hooks')
|
|
4
|
+
const { FifoMap: Fifo } = require('toad-cache')
|
|
5
|
+
const { parse: secureJsonParse } = require('secure-json-parse')
|
|
6
|
+
const ContentType = require('./content-type')
|
|
7
|
+
const {
|
|
8
|
+
kDefaultJsonParse,
|
|
9
|
+
kContentTypeParser,
|
|
10
|
+
kBodyLimit,
|
|
11
|
+
kRequestPayloadStream,
|
|
12
|
+
kState,
|
|
13
|
+
kTestInternals,
|
|
14
|
+
kReplyIsError,
|
|
15
|
+
kRouteContext
|
|
16
|
+
} = require('./symbols')
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
FST_ERR_CTP_INVALID_TYPE,
|
|
20
|
+
FST_ERR_CTP_EMPTY_TYPE,
|
|
21
|
+
FST_ERR_CTP_ALREADY_PRESENT,
|
|
22
|
+
FST_ERR_CTP_INVALID_HANDLER,
|
|
23
|
+
FST_ERR_CTP_INVALID_PARSE_TYPE,
|
|
24
|
+
FST_ERR_CTP_BODY_TOO_LARGE,
|
|
25
|
+
FST_ERR_CTP_INVALID_MEDIA_TYPE,
|
|
26
|
+
FST_ERR_CTP_INVALID_CONTENT_LENGTH,
|
|
27
|
+
FST_ERR_CTP_EMPTY_JSON_BODY,
|
|
28
|
+
FST_ERR_CTP_INSTANCE_ALREADY_STARTED,
|
|
29
|
+
FST_ERR_CTP_INVALID_JSON_BODY
|
|
30
|
+
} = require('./errors')
|
|
31
|
+
const { FSTSEC001 } = require('./warnings')
|
|
32
|
+
|
|
33
|
+
function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
|
|
34
|
+
this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
|
|
35
|
+
// using a map instead of a plain object to avoid prototype hijack attacks
|
|
36
|
+
this.customParsers = new Map()
|
|
37
|
+
this.customParsers.set('application/json', new Parser(true, false, bodyLimit, this[kDefaultJsonParse]))
|
|
38
|
+
this.customParsers.set('text/plain', new Parser(true, false, bodyLimit, defaultPlainTextParser))
|
|
39
|
+
this.parserList = ['application/json', 'text/plain']
|
|
40
|
+
this.parserRegExpList = []
|
|
41
|
+
this.cache = new Fifo(100)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
|
|
45
|
+
const contentTypeIsString = typeof contentType === 'string'
|
|
46
|
+
|
|
47
|
+
if (contentTypeIsString) {
|
|
48
|
+
contentType = contentType.trim().toLowerCase()
|
|
49
|
+
if (contentType.length === 0) throw new FST_ERR_CTP_EMPTY_TYPE()
|
|
50
|
+
} else if (!(contentType instanceof RegExp)) {
|
|
51
|
+
throw new FST_ERR_CTP_INVALID_TYPE()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof parserFn !== 'function') {
|
|
55
|
+
throw new FST_ERR_CTP_INVALID_HANDLER()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (this.existingParser(contentType)) {
|
|
59
|
+
throw new FST_ERR_CTP_ALREADY_PRESENT(contentType)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (opts.parseAs !== undefined) {
|
|
63
|
+
if (opts.parseAs !== 'string' && opts.parseAs !== 'buffer') {
|
|
64
|
+
throw new FST_ERR_CTP_INVALID_PARSE_TYPE(opts.parseAs)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const parser = new Parser(
|
|
69
|
+
opts.parseAs === 'string',
|
|
70
|
+
opts.parseAs === 'buffer',
|
|
71
|
+
opts.bodyLimit,
|
|
72
|
+
parserFn
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if (contentType === '*') {
|
|
76
|
+
this.customParsers.set('', parser)
|
|
77
|
+
} else {
|
|
78
|
+
if (contentTypeIsString) {
|
|
79
|
+
const ct = new ContentType(contentType)
|
|
80
|
+
if (ct.isValid === false) {
|
|
81
|
+
throw new FST_ERR_CTP_INVALID_TYPE()
|
|
82
|
+
}
|
|
83
|
+
const normalizedContentType = ct.toString()
|
|
84
|
+
this.parserList.unshift(normalizedContentType)
|
|
85
|
+
this.customParsers.set(normalizedContentType, parser)
|
|
86
|
+
} else {
|
|
87
|
+
validateRegExp(contentType)
|
|
88
|
+
this.parserRegExpList.unshift(contentType)
|
|
89
|
+
this.customParsers.set(contentType.toString(), parser)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ContentTypeParser.prototype.hasParser = function (contentType) {
|
|
95
|
+
if (typeof contentType === 'string') {
|
|
96
|
+
contentType = new ContentType(contentType).toString()
|
|
97
|
+
} else {
|
|
98
|
+
if (!(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
|
|
99
|
+
contentType = contentType.toString()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return this.customParsers.has(contentType)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
ContentTypeParser.prototype.existingParser = function (contentType) {
|
|
106
|
+
if (typeof contentType === 'string') {
|
|
107
|
+
const ct = new ContentType(contentType).toString()
|
|
108
|
+
if (contentType === 'application/json' && this.customParsers.has(contentType)) {
|
|
109
|
+
return this.customParsers.get(ct).fn !== this[kDefaultJsonParse]
|
|
110
|
+
}
|
|
111
|
+
if (contentType === 'text/plain' && this.customParsers.has(contentType)) {
|
|
112
|
+
return this.customParsers.get(ct).fn !== defaultPlainTextParser
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return this.hasParser(contentType)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
ContentTypeParser.prototype.getParser = function (contentType) {
|
|
120
|
+
if (typeof contentType === 'string') {
|
|
121
|
+
contentType = new ContentType(contentType)
|
|
122
|
+
}
|
|
123
|
+
const ct = contentType.toString()
|
|
124
|
+
|
|
125
|
+
let parser = this.cache.get(ct)
|
|
126
|
+
if (parser !== undefined) return parser
|
|
127
|
+
parser = this.customParsers.get(ct)
|
|
128
|
+
if (parser !== undefined) {
|
|
129
|
+
this.cache.set(ct, parser)
|
|
130
|
+
return parser
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// We have conflicting desires across our test suite. In some cases, we
|
|
134
|
+
// expect to get a parser by just passing the media-type. In others, we expect
|
|
135
|
+
// to get a parser registered under the media-type while also providing
|
|
136
|
+
// parameters. And in yet others, we expect to register a parser under the
|
|
137
|
+
// media-type and have it apply to any request with a header that starts
|
|
138
|
+
// with that type.
|
|
139
|
+
parser = this.customParsers.get(contentType.mediaType)
|
|
140
|
+
if (parser !== undefined) {
|
|
141
|
+
return parser
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (let j = 0; j !== this.parserRegExpList.length; ++j) {
|
|
145
|
+
const parserRegExp = this.parserRegExpList[j]
|
|
146
|
+
if (parserRegExp.test(ct)) {
|
|
147
|
+
parser = this.customParsers.get(parserRegExp.toString())
|
|
148
|
+
this.cache.set(ct, parser)
|
|
149
|
+
return parser
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return this.customParsers.get('')
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
ContentTypeParser.prototype.removeAll = function () {
|
|
157
|
+
this.customParsers = new Map()
|
|
158
|
+
this.parserRegExpList = []
|
|
159
|
+
this.parserList = []
|
|
160
|
+
this.cache = new Fifo(100)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
ContentTypeParser.prototype.remove = function (contentType) {
|
|
164
|
+
let parsers
|
|
165
|
+
|
|
166
|
+
if (typeof contentType === 'string') {
|
|
167
|
+
contentType = new ContentType(contentType).toString()
|
|
168
|
+
parsers = this.parserList
|
|
169
|
+
} else {
|
|
170
|
+
if (!(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
|
|
171
|
+
contentType = contentType.toString()
|
|
172
|
+
parsers = this.parserRegExpList
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const removed = this.customParsers.delete(contentType)
|
|
176
|
+
const idx = parsers.findIndex(ct => ct.toString() === contentType)
|
|
177
|
+
|
|
178
|
+
if (idx > -1) {
|
|
179
|
+
parsers.splice(idx, 1)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return removed || idx > -1
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
ContentTypeParser.prototype.run = function (contentType, handler, request, reply) {
|
|
186
|
+
const parser = this.getParser(contentType)
|
|
187
|
+
|
|
188
|
+
if (parser === undefined) {
|
|
189
|
+
if (request.is404 === true) {
|
|
190
|
+
handler(request, reply)
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
reply[kReplyIsError] = true
|
|
195
|
+
reply.send(new FST_ERR_CTP_INVALID_MEDIA_TYPE())
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const resource = new AsyncResource('content-type-parser:run', request)
|
|
200
|
+
const done = resource.bind(onDone)
|
|
201
|
+
|
|
202
|
+
if (parser.asString === true || parser.asBuffer === true) {
|
|
203
|
+
rawBody(
|
|
204
|
+
request,
|
|
205
|
+
reply,
|
|
206
|
+
reply[kRouteContext]._parserOptions,
|
|
207
|
+
parser,
|
|
208
|
+
done
|
|
209
|
+
)
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const result = parser.fn(request, request[kRequestPayloadStream], done)
|
|
214
|
+
if (result && typeof result.then === 'function') {
|
|
215
|
+
result.then(body => { done(null, body) }, done)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function onDone (error, body) {
|
|
219
|
+
resource.emitDestroy()
|
|
220
|
+
if (error != null) {
|
|
221
|
+
// We must close the connection as the client may
|
|
222
|
+
// send more data
|
|
223
|
+
reply.header('connection', 'close')
|
|
224
|
+
reply[kReplyIsError] = true
|
|
225
|
+
reply.send(error)
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
request.body = body
|
|
229
|
+
handler(request, reply)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function rawBody (request, reply, options, parser, done) {
|
|
234
|
+
const asString = parser.asString === true
|
|
235
|
+
const limit = options.limit === null ? parser.bodyLimit : options.limit
|
|
236
|
+
const contentLength = Number(request.headers['content-length'])
|
|
237
|
+
|
|
238
|
+
if (contentLength > limit) {
|
|
239
|
+
done(new FST_ERR_CTP_BODY_TOO_LARGE(), undefined)
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
let receivedLength = 0
|
|
244
|
+
let body = asString ? '' : []
|
|
245
|
+
const payload = request[kRequestPayloadStream] || request.raw
|
|
246
|
+
|
|
247
|
+
if (asString) {
|
|
248
|
+
payload.setEncoding('utf8')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
payload.on('data', onData)
|
|
252
|
+
payload.on('end', onEnd)
|
|
253
|
+
payload.on('error', onEnd)
|
|
254
|
+
payload.resume()
|
|
255
|
+
|
|
256
|
+
function onData (chunk) {
|
|
257
|
+
receivedLength += asString ? Buffer.byteLength(chunk) : chunk.length
|
|
258
|
+
const { receivedEncodedLength = 0 } = payload
|
|
259
|
+
// The resulting body length must not exceed bodyLimit (see "zip bomb").
|
|
260
|
+
// The case when encoded length is larger than received length is rather theoretical,
|
|
261
|
+
// unless the stream returned by preParsing hook is broken and reports wrong value.
|
|
262
|
+
if (receivedLength > limit || receivedEncodedLength > limit) {
|
|
263
|
+
payload.removeListener('data', onData)
|
|
264
|
+
payload.removeListener('end', onEnd)
|
|
265
|
+
payload.removeListener('error', onEnd)
|
|
266
|
+
done(new FST_ERR_CTP_BODY_TOO_LARGE(), undefined)
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (asString) {
|
|
271
|
+
body += chunk
|
|
272
|
+
} else {
|
|
273
|
+
body.push(chunk)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function onEnd (err) {
|
|
278
|
+
payload.removeListener('data', onData)
|
|
279
|
+
payload.removeListener('end', onEnd)
|
|
280
|
+
payload.removeListener('error', onEnd)
|
|
281
|
+
|
|
282
|
+
if (err != null) {
|
|
283
|
+
if (!(typeof err.statusCode === 'number' && err.statusCode >= 400)) {
|
|
284
|
+
err.statusCode = 400
|
|
285
|
+
}
|
|
286
|
+
done(err, undefined)
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!Number.isNaN(contentLength) && (payload.receivedEncodedLength || receivedLength) !== contentLength) {
|
|
291
|
+
done(new FST_ERR_CTP_INVALID_CONTENT_LENGTH(), undefined)
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!asString) {
|
|
296
|
+
body = Buffer.concat(body)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const result = parser.fn(request, body, done)
|
|
300
|
+
if (result && typeof result.then === 'function') {
|
|
301
|
+
result.then(body => { done(null, body) }, done)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function getDefaultJsonParser (onProtoPoisoning, onConstructorPoisoning) {
|
|
307
|
+
const parseOptions = { protoAction: onProtoPoisoning, constructorAction: onConstructorPoisoning }
|
|
308
|
+
|
|
309
|
+
return defaultJsonParser
|
|
310
|
+
|
|
311
|
+
function defaultJsonParser (req, body, done) {
|
|
312
|
+
if (body.length === 0) {
|
|
313
|
+
done(new FST_ERR_CTP_EMPTY_JSON_BODY(), undefined)
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
done(null, secureJsonParse(body, parseOptions))
|
|
318
|
+
} catch {
|
|
319
|
+
done(new FST_ERR_CTP_INVALID_JSON_BODY(), undefined)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function defaultPlainTextParser (req, body, done) {
|
|
325
|
+
done(null, body)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function Parser (asString, asBuffer, bodyLimit, fn) {
|
|
329
|
+
this.asString = asString
|
|
330
|
+
this.asBuffer = asBuffer
|
|
331
|
+
this.bodyLimit = bodyLimit
|
|
332
|
+
this.fn = fn
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function buildContentTypeParser (c) {
|
|
336
|
+
const contentTypeParser = new ContentTypeParser()
|
|
337
|
+
contentTypeParser[kDefaultJsonParse] = c[kDefaultJsonParse]
|
|
338
|
+
contentTypeParser.customParsers = new Map(c.customParsers.entries())
|
|
339
|
+
contentTypeParser.parserList = c.parserList.slice()
|
|
340
|
+
contentTypeParser.parserRegExpList = c.parserRegExpList.slice()
|
|
341
|
+
return contentTypeParser
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function addContentTypeParser (contentType, opts, parser) {
|
|
345
|
+
if (this[kState].started) {
|
|
346
|
+
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('addContentTypeParser')
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (typeof opts === 'function') {
|
|
350
|
+
parser = opts
|
|
351
|
+
opts = {}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!opts) opts = {}
|
|
355
|
+
if (!opts.bodyLimit) opts.bodyLimit = this[kBodyLimit]
|
|
356
|
+
|
|
357
|
+
if (Array.isArray(contentType)) {
|
|
358
|
+
contentType.forEach((type) => this[kContentTypeParser].add(type, opts, parser))
|
|
359
|
+
} else {
|
|
360
|
+
this[kContentTypeParser].add(contentType, opts, parser)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return this
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function hasContentTypeParser (contentType) {
|
|
367
|
+
return this[kContentTypeParser].hasParser(contentType)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function removeContentTypeParser (contentType) {
|
|
371
|
+
if (this[kState].started) {
|
|
372
|
+
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('removeContentTypeParser')
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (Array.isArray(contentType)) {
|
|
376
|
+
for (const type of contentType) {
|
|
377
|
+
this[kContentTypeParser].remove(type)
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
this[kContentTypeParser].remove(contentType)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function removeAllContentTypeParsers () {
|
|
385
|
+
if (this[kState].started) {
|
|
386
|
+
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('removeAllContentTypeParsers')
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
this[kContentTypeParser].removeAll()
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function validateRegExp (regexp) {
|
|
393
|
+
// RegExp should either start with ^ or include ;?
|
|
394
|
+
// It can ensure the user is properly detect the essence
|
|
395
|
+
// MIME types.
|
|
396
|
+
if (regexp.source[0] !== '^' && regexp.source.includes(';?') === false) {
|
|
397
|
+
FSTSEC001(regexp.source)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
module.exports = ContentTypeParser
|
|
402
|
+
module.exports.helpers = {
|
|
403
|
+
buildContentTypeParser,
|
|
404
|
+
addContentTypeParser,
|
|
405
|
+
hasContentTypeParser,
|
|
406
|
+
removeContentTypeParser,
|
|
407
|
+
removeAllContentTypeParsers
|
|
408
|
+
}
|
|
409
|
+
module.exports.defaultParsers = {
|
|
410
|
+
getDefaultJsonParser,
|
|
411
|
+
defaultTextParser: defaultPlainTextParser
|
|
412
|
+
}
|
|
413
|
+
module.exports[kTestInternals] = { rawBody }
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* keyValuePairsReg is used to split the parameters list into associated
|
|
5
|
+
* key value pairings. The leading `(?:^|;)\s*` anchor ensures the regex
|
|
6
|
+
* only attempts matches at parameter boundaries, preventing quadratic
|
|
7
|
+
* backtracking on malformed input.
|
|
8
|
+
*
|
|
9
|
+
* @see https://httpwg.org/specs/rfc9110.html#parameter
|
|
10
|
+
* @type {RegExp}
|
|
11
|
+
*/
|
|
12
|
+
const keyValuePairsReg = /(?:^|;)\s*([\w!#$%&'*+.^`|~-]+)=([^;]*)/gm
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* typeNameReg is used to validate that the first part of the media-type
|
|
16
|
+
* does not use disallowed characters. Types must consist solely of
|
|
17
|
+
* characters that match the specified character class. It must terminate
|
|
18
|
+
* with a matching character.
|
|
19
|
+
*
|
|
20
|
+
* @see https://httpwg.org/specs/rfc9110.html#rule.token.separators
|
|
21
|
+
* @type {RegExp}
|
|
22
|
+
*/
|
|
23
|
+
const typeNameReg = /^[\w!#$%&'*+.^`|~-]+$/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* subtypeNameReg is used to validate that the second part of the media-type
|
|
27
|
+
* does not use disallowed characters. Subtypes must consist solely of
|
|
28
|
+
* characters that match the specified character class, and optionally
|
|
29
|
+
* terminated with any amount of whitespace characters. Without the terminating
|
|
30
|
+
* anchor (`$`), the regular expression will match the leading portion of a
|
|
31
|
+
* string instead of the whole string.
|
|
32
|
+
*
|
|
33
|
+
* @see https://httpwg.org/specs/rfc9110.html#rule.token.separators
|
|
34
|
+
* @type {RegExp}
|
|
35
|
+
*/
|
|
36
|
+
const subtypeNameReg = /^[\w!#$%&'*+.^`|~-]+\s*$/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* ContentType parses and represents the value of the content-type header.
|
|
40
|
+
*
|
|
41
|
+
* @see https://httpwg.org/specs/rfc9110.html#media.type
|
|
42
|
+
* @see https://httpwg.org/specs/rfc9110.html#parameter
|
|
43
|
+
*/
|
|
44
|
+
class ContentType {
|
|
45
|
+
#valid = false
|
|
46
|
+
#empty = true
|
|
47
|
+
#type = ''
|
|
48
|
+
#subtype = ''
|
|
49
|
+
#parameters = new Map()
|
|
50
|
+
#string
|
|
51
|
+
|
|
52
|
+
constructor (headerValue) {
|
|
53
|
+
if (headerValue == null || headerValue === '' || headerValue === 'undefined') {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let sepIdx = headerValue.indexOf(';')
|
|
58
|
+
if (sepIdx === -1) {
|
|
59
|
+
// The value is the simplest `type/subtype` variant.
|
|
60
|
+
sepIdx = headerValue.indexOf('/')
|
|
61
|
+
if (sepIdx === -1) {
|
|
62
|
+
// Got a string without the correct `type/subtype` format.
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const type = headerValue.slice(0, sepIdx).trimStart().toLowerCase()
|
|
67
|
+
const subtype = headerValue.slice(sepIdx + 1).trimEnd().toLowerCase()
|
|
68
|
+
|
|
69
|
+
if (
|
|
70
|
+
typeNameReg.test(type) === true &&
|
|
71
|
+
subtypeNameReg.test(subtype) === true
|
|
72
|
+
) {
|
|
73
|
+
this.#valid = true
|
|
74
|
+
this.#empty = false
|
|
75
|
+
this.#type = type
|
|
76
|
+
this.#subtype = subtype
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// We have a `type/subtype; params=list...` header value.
|
|
83
|
+
const mediaType = headerValue.slice(0, sepIdx).toLowerCase()
|
|
84
|
+
const paramsList = headerValue.slice(sepIdx + 1).trim()
|
|
85
|
+
|
|
86
|
+
sepIdx = mediaType.indexOf('/')
|
|
87
|
+
if (sepIdx === -1) {
|
|
88
|
+
// We got an invalid string like `something; params=list...`.
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
const type = mediaType.slice(0, sepIdx).trimStart()
|
|
92
|
+
const subtype = mediaType.slice(sepIdx + 1).trimEnd()
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
typeNameReg.test(type) === false ||
|
|
96
|
+
subtypeNameReg.test(subtype) === false
|
|
97
|
+
) {
|
|
98
|
+
// Some portion of the media-type is using invalid characters. Therefore,
|
|
99
|
+
// the content-type header is invalid.
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
this.#type = type
|
|
103
|
+
this.#subtype = subtype
|
|
104
|
+
this.#valid = true
|
|
105
|
+
this.#empty = false
|
|
106
|
+
|
|
107
|
+
let matches = keyValuePairsReg.exec(paramsList)
|
|
108
|
+
while (matches) {
|
|
109
|
+
const key = matches[1]
|
|
110
|
+
const value = matches[2]
|
|
111
|
+
if (value[0] === '"') {
|
|
112
|
+
if (value.at(-1) !== '"') {
|
|
113
|
+
this.#parameters.set(key, 'invalid quoted string')
|
|
114
|
+
matches = keyValuePairsReg.exec(paramsList)
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
// We should probably verify the value matches a quoted string
|
|
118
|
+
// (https://httpwg.org/specs/rfc9110.html#rule.quoted-string) value.
|
|
119
|
+
// But we are not really doing much with the parameter values, so we
|
|
120
|
+
// are omitting that at this time.
|
|
121
|
+
this.#parameters.set(key, value.slice(1, value.length - 1))
|
|
122
|
+
} else {
|
|
123
|
+
this.#parameters.set(key, value)
|
|
124
|
+
}
|
|
125
|
+
matches = keyValuePairsReg.exec(paramsList)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get [Symbol.toStringTag] () { return 'ContentType' }
|
|
130
|
+
|
|
131
|
+
get isEmpty () { return this.#empty }
|
|
132
|
+
|
|
133
|
+
get isValid () { return this.#valid }
|
|
134
|
+
|
|
135
|
+
get mediaType () { return `${this.#type}/${this.#subtype}` }
|
|
136
|
+
|
|
137
|
+
get type () { return this.#type }
|
|
138
|
+
|
|
139
|
+
get subtype () { return this.#subtype }
|
|
140
|
+
|
|
141
|
+
get parameters () { return this.#parameters }
|
|
142
|
+
|
|
143
|
+
toString () {
|
|
144
|
+
/* c8 ignore next: we don't need to verify the cache */
|
|
145
|
+
if (this.#string) return this.#string
|
|
146
|
+
const parameters = []
|
|
147
|
+
for (const [key, value] of this.#parameters.entries()) {
|
|
148
|
+
parameters.push(`${key}="${value}"`)
|
|
149
|
+
}
|
|
150
|
+
const result = [this.#type, '/', this.#subtype]
|
|
151
|
+
if (parameters.length > 0) {
|
|
152
|
+
result.push('; ')
|
|
153
|
+
result.push(parameters.join('; '))
|
|
154
|
+
}
|
|
155
|
+
this.#string = result.join('')
|
|
156
|
+
return this.#string
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = ContentType
|
package/lib/context.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
kFourOhFourContext,
|
|
5
|
+
kReplySerializerDefault,
|
|
6
|
+
kSchemaErrorFormatter,
|
|
7
|
+
kErrorHandler,
|
|
8
|
+
kChildLoggerFactory,
|
|
9
|
+
kOptions,
|
|
10
|
+
kReply,
|
|
11
|
+
kRequest,
|
|
12
|
+
kBodyLimit,
|
|
13
|
+
kLogLevel,
|
|
14
|
+
kContentTypeParser,
|
|
15
|
+
kRouteByFastify,
|
|
16
|
+
kRequestCacheValidateFns,
|
|
17
|
+
kReplyCacheSerializeFns,
|
|
18
|
+
kHandlerTimeout
|
|
19
|
+
} = require('./symbols.js')
|
|
20
|
+
|
|
21
|
+
// Object that holds the context of every request
|
|
22
|
+
// Every route holds an instance of this object.
|
|
23
|
+
function Context ({
|
|
24
|
+
schema,
|
|
25
|
+
handler,
|
|
26
|
+
config,
|
|
27
|
+
requestIdLogLabel,
|
|
28
|
+
childLoggerFactory,
|
|
29
|
+
errorHandler,
|
|
30
|
+
bodyLimit,
|
|
31
|
+
logLevel,
|
|
32
|
+
logSerializers,
|
|
33
|
+
attachValidation,
|
|
34
|
+
validatorCompiler,
|
|
35
|
+
serializerCompiler,
|
|
36
|
+
replySerializer,
|
|
37
|
+
schemaErrorFormatter,
|
|
38
|
+
exposeHeadRoute,
|
|
39
|
+
prefixTrailingSlash,
|
|
40
|
+
server,
|
|
41
|
+
isFastify,
|
|
42
|
+
handlerTimeout
|
|
43
|
+
}) {
|
|
44
|
+
this.schema = schema
|
|
45
|
+
this.handler = handler
|
|
46
|
+
this.Reply = server[kReply]
|
|
47
|
+
this.Request = server[kRequest]
|
|
48
|
+
this.contentTypeParser = server[kContentTypeParser]
|
|
49
|
+
this.onRequest = null
|
|
50
|
+
this.onSend = null
|
|
51
|
+
this.onError = null
|
|
52
|
+
this.onTimeout = null
|
|
53
|
+
this.preHandler = null
|
|
54
|
+
this.onResponse = null
|
|
55
|
+
this.preSerialization = null
|
|
56
|
+
this.onRequestAbort = null
|
|
57
|
+
this.config = config
|
|
58
|
+
this.errorHandler = errorHandler || server[kErrorHandler]
|
|
59
|
+
this.requestIdLogLabel = requestIdLogLabel || server[kOptions].requestIdLogLabel
|
|
60
|
+
this.childLoggerFactory = childLoggerFactory || server[kChildLoggerFactory]
|
|
61
|
+
this._middie = null
|
|
62
|
+
this._parserOptions = {
|
|
63
|
+
limit: bodyLimit || server[kBodyLimit]
|
|
64
|
+
}
|
|
65
|
+
this.exposeHeadRoute = exposeHeadRoute
|
|
66
|
+
this.prefixTrailingSlash = prefixTrailingSlash
|
|
67
|
+
this.logLevel = logLevel || server[kLogLevel]
|
|
68
|
+
this.logSerializers = logSerializers
|
|
69
|
+
this[kFourOhFourContext] = null
|
|
70
|
+
this.attachValidation = attachValidation
|
|
71
|
+
this[kReplySerializerDefault] = replySerializer
|
|
72
|
+
this.schemaErrorFormatter =
|
|
73
|
+
schemaErrorFormatter ||
|
|
74
|
+
server[kSchemaErrorFormatter] ||
|
|
75
|
+
defaultSchemaErrorFormatter
|
|
76
|
+
this[kRouteByFastify] = isFastify
|
|
77
|
+
|
|
78
|
+
this[kRequestCacheValidateFns] = null
|
|
79
|
+
this[kReplyCacheSerializeFns] = null
|
|
80
|
+
this.validatorCompiler = validatorCompiler || null
|
|
81
|
+
this.serializerCompiler = serializerCompiler || null
|
|
82
|
+
|
|
83
|
+
this.handlerTimeout = handlerTimeout || server[kHandlerTimeout] || 0
|
|
84
|
+
this.server = server
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function defaultSchemaErrorFormatter (errors, dataVar) {
|
|
88
|
+
let text = ''
|
|
89
|
+
const separator = ', '
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i !== errors.length; ++i) {
|
|
92
|
+
const e = errors[i]
|
|
93
|
+
text += dataVar + (e.instancePath || '') + ' ' + e.message + separator
|
|
94
|
+
}
|
|
95
|
+
return new Error(text.slice(0, -separator.length))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = Context
|