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.
Files changed (354) hide show
  1. package/.borp.yaml +3 -0
  2. package/.markdownlint-cli2.yaml +22 -0
  3. package/.prettierignore +1 -0
  4. package/GOVERNANCE.md +4 -0
  5. package/LICENSE +21 -0
  6. package/PROJECT_CHARTER.md +126 -0
  7. package/README.md +423 -0
  8. package/SECURITY.md +220 -0
  9. package/SPONSORS.md +24 -0
  10. package/build/build-error-serializer.js +35 -0
  11. package/build/build-validation.js +169 -0
  12. package/build/sync-version.js +11 -0
  13. package/docs/Guides/Benchmarking.md +60 -0
  14. package/docs/Guides/Database.md +321 -0
  15. package/docs/Guides/Delay-Accepting-Requests.md +608 -0
  16. package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
  17. package/docs/Guides/Ecosystem.md +726 -0
  18. package/docs/Guides/Fluent-Schema.md +127 -0
  19. package/docs/Guides/Getting-Started.md +620 -0
  20. package/docs/Guides/Index.md +43 -0
  21. package/docs/Guides/Migration-Guide-V3.md +287 -0
  22. package/docs/Guides/Migration-Guide-V4.md +267 -0
  23. package/docs/Guides/Migration-Guide-V5.md +727 -0
  24. package/docs/Guides/Plugins-Guide.md +520 -0
  25. package/docs/Guides/Prototype-Poisoning.md +383 -0
  26. package/docs/Guides/Recommendations.md +378 -0
  27. package/docs/Guides/Serverless.md +604 -0
  28. package/docs/Guides/Style-Guide.md +246 -0
  29. package/docs/Guides/Testing.md +481 -0
  30. package/docs/Guides/Write-Plugin.md +103 -0
  31. package/docs/Guides/Write-Type-Provider.md +34 -0
  32. package/docs/Reference/ContentTypeParser.md +271 -0
  33. package/docs/Reference/Decorators.md +436 -0
  34. package/docs/Reference/Encapsulation.md +194 -0
  35. package/docs/Reference/Errors.md +377 -0
  36. package/docs/Reference/HTTP2.md +94 -0
  37. package/docs/Reference/Hooks.md +958 -0
  38. package/docs/Reference/Index.md +73 -0
  39. package/docs/Reference/LTS.md +86 -0
  40. package/docs/Reference/Lifecycle.md +99 -0
  41. package/docs/Reference/Logging.md +268 -0
  42. package/docs/Reference/Middleware.md +79 -0
  43. package/docs/Reference/Plugins.md +245 -0
  44. package/docs/Reference/Principles.md +73 -0
  45. package/docs/Reference/Reply.md +1001 -0
  46. package/docs/Reference/Request.md +295 -0
  47. package/docs/Reference/Routes.md +802 -0
  48. package/docs/Reference/Server.md +2389 -0
  49. package/docs/Reference/Type-Providers.md +256 -0
  50. package/docs/Reference/TypeScript.md +1729 -0
  51. package/docs/Reference/Validation-and-Serialization.md +1130 -0
  52. package/docs/Reference/Warnings.md +58 -0
  53. package/docs/index.md +24 -0
  54. package/docs/resources/encapsulation_context.drawio +1 -0
  55. package/docs/resources/encapsulation_context.svg +3 -0
  56. package/eslint.config.js +35 -0
  57. package/examples/asyncawait.js +38 -0
  58. package/examples/benchmark/body.json +3 -0
  59. package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
  60. package/examples/benchmark/hooks-benchmark.js +52 -0
  61. package/examples/benchmark/parser.js +47 -0
  62. package/examples/benchmark/simple.js +30 -0
  63. package/examples/benchmark/webstream.js +27 -0
  64. package/examples/hooks.js +91 -0
  65. package/examples/http2.js +39 -0
  66. package/examples/https.js +38 -0
  67. package/examples/parser.js +53 -0
  68. package/examples/plugin.js +12 -0
  69. package/examples/route-prefix.js +38 -0
  70. package/examples/shared-schema.js +38 -0
  71. package/examples/simple-stream.js +20 -0
  72. package/examples/simple.js +32 -0
  73. package/examples/simple.mjs +27 -0
  74. package/examples/typescript-server.ts +79 -0
  75. package/examples/use-plugin.js +29 -0
  76. package/fastify.d.ts +253 -0
  77. package/fastify.js +985 -0
  78. package/integration/server.js +29 -0
  79. package/integration/test.sh +23 -0
  80. package/lib/config-validator.js +1266 -0
  81. package/lib/content-type-parser.js +413 -0
  82. package/lib/content-type.js +160 -0
  83. package/lib/context.js +98 -0
  84. package/lib/decorate.js +152 -0
  85. package/lib/error-handler.js +173 -0
  86. package/lib/error-serializer.js +134 -0
  87. package/lib/error-status.js +14 -0
  88. package/lib/errors.js +516 -0
  89. package/lib/four-oh-four.js +190 -0
  90. package/lib/handle-request.js +195 -0
  91. package/lib/head-route.js +45 -0
  92. package/lib/hooks.js +429 -0
  93. package/lib/initial-config-validation.js +37 -0
  94. package/lib/logger-factory.js +136 -0
  95. package/lib/logger-pino.js +68 -0
  96. package/lib/noop-set.js +10 -0
  97. package/lib/plugin-override.js +90 -0
  98. package/lib/plugin-utils.js +169 -0
  99. package/lib/promise.js +23 -0
  100. package/lib/reply.js +1030 -0
  101. package/lib/req-id-gen-factory.js +52 -0
  102. package/lib/request.js +391 -0
  103. package/lib/route.js +686 -0
  104. package/lib/schema-controller.js +164 -0
  105. package/lib/schemas.js +207 -0
  106. package/lib/server.js +441 -0
  107. package/lib/symbols.js +71 -0
  108. package/lib/validation.js +280 -0
  109. package/lib/warnings.js +57 -0
  110. package/lib/wrap-thenable.js +84 -0
  111. package/package.json +225 -0
  112. package/scripts/validate-ecosystem-links.js +179 -0
  113. package/test/404s.test.js +2035 -0
  114. package/test/500s.test.js +422 -0
  115. package/test/allow-unsafe-regex.test.js +92 -0
  116. package/test/als.test.js +65 -0
  117. package/test/async-await.test.js +705 -0
  118. package/test/async-dispose.test.js +20 -0
  119. package/test/async_hooks.test.js +52 -0
  120. package/test/body-limit.test.js +224 -0
  121. package/test/buffer.test.js +74 -0
  122. package/test/build/error-serializer.test.js +36 -0
  123. package/test/build/version.test.js +14 -0
  124. package/test/build-certificate.js +109 -0
  125. package/test/bundler/README.md +29 -0
  126. package/test/bundler/esbuild/bundler-test.js +32 -0
  127. package/test/bundler/esbuild/package.json +10 -0
  128. package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
  129. package/test/bundler/esbuild/src/index.js +9 -0
  130. package/test/bundler/webpack/bundler-test.js +32 -0
  131. package/test/bundler/webpack/package.json +11 -0
  132. package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
  133. package/test/bundler/webpack/src/index.js +9 -0
  134. package/test/bundler/webpack/webpack.config.js +15 -0
  135. package/test/case-insensitive.test.js +102 -0
  136. package/test/chainable.test.js +40 -0
  137. package/test/child-logger-factory.test.js +128 -0
  138. package/test/client-timeout.test.js +38 -0
  139. package/test/close-pipelining.test.js +78 -0
  140. package/test/close.test.js +706 -0
  141. package/test/conditional-pino.test.js +47 -0
  142. package/test/connection-timeout.test.js +42 -0
  143. package/test/constrained-routes.test.js +1138 -0
  144. package/test/content-length.test.js +174 -0
  145. package/test/content-parser.test.js +739 -0
  146. package/test/content-type.test.js +181 -0
  147. package/test/context-config.test.js +164 -0
  148. package/test/custom-http-server.test.js +118 -0
  149. package/test/custom-parser-async.test.js +59 -0
  150. package/test/custom-parser.0.test.js +701 -0
  151. package/test/custom-parser.1.test.js +266 -0
  152. package/test/custom-parser.2.test.js +91 -0
  153. package/test/custom-parser.3.test.js +208 -0
  154. package/test/custom-parser.4.test.js +218 -0
  155. package/test/custom-parser.5.test.js +130 -0
  156. package/test/custom-querystring-parser.test.js +129 -0
  157. package/test/decorator.test.js +1330 -0
  158. package/test/delete.test.js +344 -0
  159. package/test/diagnostics-channel/404.test.js +49 -0
  160. package/test/diagnostics-channel/async-delay-request.test.js +65 -0
  161. package/test/diagnostics-channel/async-request.test.js +64 -0
  162. package/test/diagnostics-channel/error-before-handler.test.js +35 -0
  163. package/test/diagnostics-channel/error-request.test.js +53 -0
  164. package/test/diagnostics-channel/error-status.test.js +123 -0
  165. package/test/diagnostics-channel/init.test.js +50 -0
  166. package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
  167. package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
  168. package/test/diagnostics-channel/sync-request.test.js +54 -0
  169. package/test/encapsulated-child-logger-factory.test.js +69 -0
  170. package/test/encapsulated-error-handler.test.js +237 -0
  171. package/test/esm/errorCodes.test.mjs +10 -0
  172. package/test/esm/esm.test.mjs +13 -0
  173. package/test/esm/index.test.js +8 -0
  174. package/test/esm/named-exports.mjs +14 -0
  175. package/test/esm/other.mjs +8 -0
  176. package/test/esm/plugin.mjs +8 -0
  177. package/test/fastify-instance.test.js +300 -0
  178. package/test/find-route.test.js +152 -0
  179. package/test/fluent-schema.test.js +209 -0
  180. package/test/genReqId.test.js +426 -0
  181. package/test/handler-context.test.js +45 -0
  182. package/test/handler-timeout.test.js +367 -0
  183. package/test/has-route.test.js +88 -0
  184. package/test/header-overflow.test.js +55 -0
  185. package/test/helper.js +496 -0
  186. package/test/hooks-async.test.js +1099 -0
  187. package/test/hooks.on-listen.test.js +1162 -0
  188. package/test/hooks.on-ready.test.js +421 -0
  189. package/test/hooks.test.js +3578 -0
  190. package/test/http-methods/copy.test.js +35 -0
  191. package/test/http-methods/custom-http-methods.test.js +114 -0
  192. package/test/http-methods/get.test.js +412 -0
  193. package/test/http-methods/head.test.js +263 -0
  194. package/test/http-methods/lock.test.js +108 -0
  195. package/test/http-methods/mkcalendar.test.js +143 -0
  196. package/test/http-methods/mkcol.test.js +35 -0
  197. package/test/http-methods/move.test.js +42 -0
  198. package/test/http-methods/propfind.test.js +136 -0
  199. package/test/http-methods/proppatch.test.js +105 -0
  200. package/test/http-methods/report.test.js +142 -0
  201. package/test/http-methods/search.test.js +233 -0
  202. package/test/http-methods/trace.test.js +21 -0
  203. package/test/http-methods/unlock.test.js +38 -0
  204. package/test/http2/closing.test.js +270 -0
  205. package/test/http2/constraint.test.js +109 -0
  206. package/test/http2/head.test.js +34 -0
  207. package/test/http2/plain.test.js +68 -0
  208. package/test/http2/secure-with-fallback.test.js +113 -0
  209. package/test/http2/secure.test.js +67 -0
  210. package/test/http2/unknown-http-method.test.js +34 -0
  211. package/test/https/custom-https-server.test.js +58 -0
  212. package/test/https/https.test.js +136 -0
  213. package/test/imports.test.js +17 -0
  214. package/test/inject.test.js +502 -0
  215. package/test/input-validation.js +335 -0
  216. package/test/internals/all.test.js +38 -0
  217. package/test/internals/content-type-parser.test.js +111 -0
  218. package/test/internals/context.test.js +31 -0
  219. package/test/internals/decorator.test.js +156 -0
  220. package/test/internals/errors.test.js +982 -0
  221. package/test/internals/handle-request.test.js +270 -0
  222. package/test/internals/hook-runner.test.js +449 -0
  223. package/test/internals/hooks.test.js +96 -0
  224. package/test/internals/initial-config.test.js +383 -0
  225. package/test/internals/logger.test.js +163 -0
  226. package/test/internals/plugin.test.js +170 -0
  227. package/test/internals/promise.test.js +63 -0
  228. package/test/internals/reply-serialize.test.js +714 -0
  229. package/test/internals/reply.test.js +1920 -0
  230. package/test/internals/req-id-gen-factory.test.js +133 -0
  231. package/test/internals/request-validate.test.js +1402 -0
  232. package/test/internals/request.test.js +506 -0
  233. package/test/internals/schema-controller-perf.test.js +40 -0
  234. package/test/internals/server.test.js +91 -0
  235. package/test/internals/validation.test.js +352 -0
  236. package/test/issue-4959.test.js +118 -0
  237. package/test/keep-alive-timeout.test.js +42 -0
  238. package/test/listen.1.test.js +154 -0
  239. package/test/listen.2.test.js +113 -0
  240. package/test/listen.3.test.js +83 -0
  241. package/test/listen.4.test.js +168 -0
  242. package/test/listen.5.test.js +122 -0
  243. package/test/logger/instantiation.test.js +341 -0
  244. package/test/logger/logger-test-utils.js +47 -0
  245. package/test/logger/logging.test.js +460 -0
  246. package/test/logger/options.test.js +579 -0
  247. package/test/logger/request.test.js +292 -0
  248. package/test/logger/response.test.js +183 -0
  249. package/test/logger/tap-parallel-not-ok +0 -0
  250. package/test/max-requests-per-socket.test.js +113 -0
  251. package/test/middleware.test.js +37 -0
  252. package/test/noop-set.test.js +19 -0
  253. package/test/nullable-validation.test.js +187 -0
  254. package/test/options.error-handler.test.js +5 -0
  255. package/test/options.test.js +5 -0
  256. package/test/output-validation.test.js +140 -0
  257. package/test/patch.error-handler.test.js +5 -0
  258. package/test/patch.test.js +5 -0
  259. package/test/plugin.1.test.js +230 -0
  260. package/test/plugin.2.test.js +314 -0
  261. package/test/plugin.3.test.js +287 -0
  262. package/test/plugin.4.test.js +504 -0
  263. package/test/plugin.helper.js +8 -0
  264. package/test/plugin.name.display.js +10 -0
  265. package/test/post-empty-body.test.js +38 -0
  266. package/test/pretty-print.test.js +366 -0
  267. package/test/promises.test.js +125 -0
  268. package/test/proto-poisoning.test.js +145 -0
  269. package/test/put.error-handler.test.js +5 -0
  270. package/test/put.test.js +5 -0
  271. package/test/register.test.js +184 -0
  272. package/test/reply-code.test.js +148 -0
  273. package/test/reply-early-hints.test.js +100 -0
  274. package/test/reply-error.test.js +815 -0
  275. package/test/reply-trailers.test.js +445 -0
  276. package/test/reply-web-stream-locked.test.js +37 -0
  277. package/test/request-error.test.js +624 -0
  278. package/test/request-header-host.test.js +339 -0
  279. package/test/request-id.test.js +118 -0
  280. package/test/request-timeout.test.js +53 -0
  281. package/test/route-hooks.test.js +635 -0
  282. package/test/route-prefix.test.js +904 -0
  283. package/test/route-shorthand.test.js +48 -0
  284. package/test/route.1.test.js +259 -0
  285. package/test/route.2.test.js +100 -0
  286. package/test/route.3.test.js +213 -0
  287. package/test/route.4.test.js +127 -0
  288. package/test/route.5.test.js +211 -0
  289. package/test/route.6.test.js +306 -0
  290. package/test/route.7.test.js +406 -0
  291. package/test/route.8.test.js +225 -0
  292. package/test/router-options.test.js +1108 -0
  293. package/test/same-shape.test.js +124 -0
  294. package/test/schema-examples.test.js +661 -0
  295. package/test/schema-feature.test.js +2198 -0
  296. package/test/schema-serialization.test.js +1171 -0
  297. package/test/schema-special-usage.test.js +1348 -0
  298. package/test/schema-validation.test.js +1572 -0
  299. package/test/scripts/validate-ecosystem-links.test.js +339 -0
  300. package/test/serialize-response.test.js +186 -0
  301. package/test/server.test.js +347 -0
  302. package/test/set-error-handler.test.js +69 -0
  303. package/test/skip-reply-send.test.js +317 -0
  304. package/test/stream-serializers.test.js +40 -0
  305. package/test/stream.1.test.js +94 -0
  306. package/test/stream.2.test.js +129 -0
  307. package/test/stream.3.test.js +198 -0
  308. package/test/stream.4.test.js +176 -0
  309. package/test/stream.5.test.js +188 -0
  310. package/test/sync-routes.test.js +32 -0
  311. package/test/throw.test.js +359 -0
  312. package/test/toolkit.js +63 -0
  313. package/test/trust-proxy.test.js +162 -0
  314. package/test/type-provider.test.js +22 -0
  315. package/test/types/content-type-parser.test-d.ts +72 -0
  316. package/test/types/decorate-request-reply.test-d.ts +18 -0
  317. package/test/types/dummy-plugin.ts +9 -0
  318. package/test/types/errors.test-d.ts +90 -0
  319. package/test/types/fastify.test-d.ts +352 -0
  320. package/test/types/hooks.test-d.ts +550 -0
  321. package/test/types/import.ts +2 -0
  322. package/test/types/instance.test-d.ts +588 -0
  323. package/test/types/logger.test-d.ts +277 -0
  324. package/test/types/plugin.test-d.ts +97 -0
  325. package/test/types/register.test-d.ts +237 -0
  326. package/test/types/reply.test-d.ts +254 -0
  327. package/test/types/request.test-d.ts +188 -0
  328. package/test/types/route.test-d.ts +553 -0
  329. package/test/types/schema.test-d.ts +135 -0
  330. package/test/types/serverFactory.test-d.ts +37 -0
  331. package/test/types/type-provider.test-d.ts +1213 -0
  332. package/test/types/using.test-d.ts +17 -0
  333. package/test/upgrade.test.js +52 -0
  334. package/test/url-rewriting.test.js +122 -0
  335. package/test/use-semicolon-delimiter.test.js +168 -0
  336. package/test/validation-error-handling.test.js +900 -0
  337. package/test/versioned-routes.test.js +603 -0
  338. package/test/web-api.test.js +616 -0
  339. package/test/wrap-thenable.test.js +30 -0
  340. package/types/content-type-parser.d.ts +75 -0
  341. package/types/context.d.ts +22 -0
  342. package/types/errors.d.ts +92 -0
  343. package/types/hooks.d.ts +875 -0
  344. package/types/instance.d.ts +609 -0
  345. package/types/logger.d.ts +107 -0
  346. package/types/plugin.d.ts +44 -0
  347. package/types/register.d.ts +42 -0
  348. package/types/reply.d.ts +81 -0
  349. package/types/request.d.ts +95 -0
  350. package/types/route.d.ts +199 -0
  351. package/types/schema.d.ts +61 -0
  352. package/types/server-factory.d.ts +19 -0
  353. package/types/type-provider.d.ts +130 -0
  354. 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 }