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
@@ -0,0 +1,195 @@
1
+ 'use strict'
2
+
3
+ const diagnostics = require('node:diagnostics_channel')
4
+ const ContentType = require('./content-type')
5
+ const wrapThenable = require('./wrap-thenable')
6
+ const { validate: validateSchema } = require('./validation')
7
+ const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
8
+ const { FST_ERR_CTP_INVALID_MEDIA_TYPE } = require('./errors')
9
+ const { setErrorStatusCode } = require('./error-status')
10
+ const {
11
+ kReplyIsError,
12
+ kRouteContext,
13
+ kFourOhFourContext,
14
+ kSupportedHTTPMethods
15
+ } = require('./symbols')
16
+
17
+ const channels = diagnostics.tracingChannel('fastify.request.handler')
18
+
19
+ function handleRequest (err, request, reply) {
20
+ if (reply.sent === true) return
21
+ if (err != null) {
22
+ reply[kReplyIsError] = true
23
+ reply.send(err)
24
+ return
25
+ }
26
+
27
+ const method = request.method
28
+
29
+ if (this[kSupportedHTTPMethods].bodyless.has(method)) {
30
+ handler(request, reply)
31
+ return
32
+ }
33
+
34
+ if (this[kSupportedHTTPMethods].bodywith.has(method)) {
35
+ const headers = request.headers
36
+ const ctHeader = headers['content-type']
37
+
38
+ if (ctHeader === undefined) {
39
+ const contentLength = headers['content-length']
40
+ const transferEncoding = headers['transfer-encoding']
41
+ const isEmptyBody = transferEncoding === undefined &&
42
+ (contentLength === undefined || contentLength === '0')
43
+
44
+ if (isEmptyBody) {
45
+ // Request has no body to parse
46
+ handler(request, reply)
47
+ return
48
+ }
49
+
50
+ request[kRouteContext].contentTypeParser.run('', handler, request, reply)
51
+ return
52
+ }
53
+
54
+ const contentType = new ContentType(ctHeader)
55
+ if (contentType.isValid === false) {
56
+ reply[kReplyIsError] = true
57
+ reply.status(415).send(new FST_ERR_CTP_INVALID_MEDIA_TYPE())
58
+ return
59
+ }
60
+ request[kRouteContext].contentTypeParser.run(contentType.toString(), handler, request, reply)
61
+ return
62
+ }
63
+
64
+ // Return 404 instead of 405 see https://github.com/fastify/fastify/pull/862 for discussion
65
+ handler(request, reply)
66
+ }
67
+
68
+ function handler (request, reply) {
69
+ try {
70
+ if (request[kRouteContext].preValidation !== null) {
71
+ preValidationHookRunner(
72
+ request[kRouteContext].preValidation,
73
+ request,
74
+ reply,
75
+ preValidationCallback
76
+ )
77
+ } else {
78
+ preValidationCallback(null, request, reply)
79
+ }
80
+ } catch (err) {
81
+ preValidationCallback(err, request, reply)
82
+ }
83
+ }
84
+
85
+ function preValidationCallback (err, request, reply) {
86
+ if (reply.sent === true) return
87
+
88
+ if (err != null) {
89
+ reply[kReplyIsError] = true
90
+ reply.send(err)
91
+ return
92
+ }
93
+
94
+ const validationErr = validateSchema(reply[kRouteContext], request)
95
+ const isAsync = (validationErr && typeof validationErr.then === 'function') || false
96
+
97
+ if (isAsync) {
98
+ const cb = validationCompleted.bind(null, request, reply)
99
+ validationErr.then(cb, cb)
100
+ } else {
101
+ validationCompleted(request, reply, validationErr)
102
+ }
103
+ }
104
+
105
+ function validationCompleted (request, reply, validationErr) {
106
+ if (validationErr) {
107
+ if (reply[kRouteContext].attachValidation === false) {
108
+ reply.send(validationErr)
109
+ return
110
+ }
111
+
112
+ reply.request.validationError = validationErr
113
+ }
114
+
115
+ // preHandler hook
116
+ if (request[kRouteContext].preHandler !== null) {
117
+ preHandlerHookRunner(
118
+ request[kRouteContext].preHandler,
119
+ request,
120
+ reply,
121
+ preHandlerCallback
122
+ )
123
+ } else {
124
+ preHandlerCallback(null, request, reply)
125
+ }
126
+ }
127
+
128
+ function preHandlerCallback (err, request, reply) {
129
+ if (reply.sent) return
130
+
131
+ const context = request[kRouteContext]
132
+
133
+ if (!channels.hasSubscribers || context[kFourOhFourContext] === null) {
134
+ preHandlerCallbackInner(err, request, reply)
135
+ } else {
136
+ const store = {
137
+ request,
138
+ reply,
139
+ async: false,
140
+ route: {
141
+ url: context.config.url,
142
+ method: context.config.method
143
+ }
144
+ }
145
+ channels.start.runStores(store, preHandlerCallbackInner, undefined, err, request, reply, store)
146
+ }
147
+ }
148
+
149
+ function preHandlerCallbackInner (err, request, reply, store) {
150
+ const context = request[kRouteContext]
151
+
152
+ try {
153
+ if (err != null) {
154
+ reply[kReplyIsError] = true
155
+ if (store) {
156
+ store.error = err
157
+ // Set status code before publishing so subscribers see the correct value
158
+ setErrorStatusCode(reply, err)
159
+ channels.error.publish(store)
160
+ }
161
+ reply.send(err)
162
+ return
163
+ }
164
+
165
+ let result
166
+
167
+ try {
168
+ result = context.handler(request, reply)
169
+ } catch (err) {
170
+ if (store) {
171
+ store.error = err
172
+ // Set status code before publishing so subscribers see the correct value
173
+ setErrorStatusCode(reply, err)
174
+ channels.error.publish(store)
175
+ }
176
+
177
+ reply[kReplyIsError] = true
178
+ reply.send(err)
179
+ return
180
+ }
181
+
182
+ if (result !== undefined) {
183
+ if (result !== null && typeof result.then === 'function') {
184
+ wrapThenable(result, reply, store)
185
+ } else {
186
+ reply.send(result)
187
+ }
188
+ }
189
+ } finally {
190
+ if (store) channels.end.publish(store)
191
+ }
192
+ }
193
+
194
+ module.exports = handleRequest
195
+ module.exports[Symbol.for('internals')] = { handler, preHandlerCallback }
@@ -0,0 +1,45 @@
1
+ 'use strict'
2
+ function headRouteOnSendHandler (req, reply, payload, done) {
3
+ // If payload is undefined
4
+ if (payload === undefined) {
5
+ reply.header('content-length', '0')
6
+ done(null, null)
7
+ return
8
+ }
9
+
10
+ // node:stream
11
+ if (typeof payload.resume === 'function') {
12
+ payload.on('error', (err) => {
13
+ reply.log.error({ err }, 'Error on Stream found for HEAD route')
14
+ })
15
+ payload.resume()
16
+ done(null, null)
17
+ return
18
+ }
19
+
20
+ // node:stream/web
21
+ if (typeof payload.getReader === 'function') {
22
+ payload.cancel('Stream cancelled by HEAD route').catch((err) => {
23
+ reply.log.error({ err }, 'Error on Stream found for HEAD route')
24
+ })
25
+ done(null, null)
26
+ return
27
+ }
28
+
29
+ const size = '' + Buffer.byteLength(payload)
30
+
31
+ reply.header('content-length', size)
32
+
33
+ done(null, null)
34
+ }
35
+
36
+ function parseHeadOnSendHandlers (onSendHandlers) {
37
+ if (onSendHandlers == null) return headRouteOnSendHandler
38
+ return Array.isArray(onSendHandlers)
39
+ ? [...onSendHandlers, headRouteOnSendHandler]
40
+ : [onSendHandlers, headRouteOnSendHandler]
41
+ }
42
+
43
+ module.exports = {
44
+ parseHeadOnSendHandlers
45
+ }
package/lib/hooks.js ADDED
@@ -0,0 +1,429 @@
1
+ 'use strict'
2
+
3
+ const applicationHooks = [
4
+ 'onRoute',
5
+ 'onRegister',
6
+ 'onReady',
7
+ 'onListen',
8
+ 'preClose',
9
+ 'onClose'
10
+ ]
11
+ const lifecycleHooks = [
12
+ 'onTimeout',
13
+ 'onRequest',
14
+ 'preParsing',
15
+ 'preValidation',
16
+ 'preSerialization',
17
+ 'preHandler',
18
+ 'onSend',
19
+ 'onResponse',
20
+ 'onError',
21
+ 'onRequestAbort'
22
+ ]
23
+ const supportedHooks = lifecycleHooks.concat(applicationHooks)
24
+ const {
25
+ FST_ERR_HOOK_INVALID_TYPE,
26
+ FST_ERR_HOOK_INVALID_HANDLER,
27
+ FST_ERR_SEND_UNDEFINED_ERR,
28
+ FST_ERR_HOOK_TIMEOUT,
29
+ FST_ERR_HOOK_NOT_SUPPORTED,
30
+ AVVIO_ERRORS_MAP,
31
+ appendStackTrace
32
+ } = require('./errors')
33
+
34
+ const {
35
+ kChildren,
36
+ kHooks,
37
+ kRequestPayloadStream
38
+ } = require('./symbols')
39
+
40
+ function Hooks () {
41
+ this.onRequest = []
42
+ this.preParsing = []
43
+ this.preValidation = []
44
+ this.preSerialization = []
45
+ this.preHandler = []
46
+ this.onResponse = []
47
+ this.onSend = []
48
+ this.onError = []
49
+ this.onRoute = []
50
+ this.onRegister = []
51
+ this.onReady = []
52
+ this.onListen = []
53
+ this.onTimeout = []
54
+ this.onRequestAbort = []
55
+ this.preClose = []
56
+ }
57
+
58
+ Hooks.prototype = Object.create(null)
59
+
60
+ Hooks.prototype.validate = function (hook, fn) {
61
+ if (typeof hook !== 'string') throw new FST_ERR_HOOK_INVALID_TYPE()
62
+ if (Array.isArray(this[hook]) === false) {
63
+ throw new FST_ERR_HOOK_NOT_SUPPORTED(hook)
64
+ }
65
+ if (typeof fn !== 'function') throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(fn))
66
+ }
67
+
68
+ Hooks.prototype.add = function (hook, fn) {
69
+ this.validate(hook, fn)
70
+ this[hook].push(fn)
71
+ }
72
+
73
+ function buildHooks (h) {
74
+ const hooks = new Hooks()
75
+ hooks.onRequest = h.onRequest.slice()
76
+ hooks.preParsing = h.preParsing.slice()
77
+ hooks.preValidation = h.preValidation.slice()
78
+ hooks.preSerialization = h.preSerialization.slice()
79
+ hooks.preHandler = h.preHandler.slice()
80
+ hooks.onSend = h.onSend.slice()
81
+ hooks.onResponse = h.onResponse.slice()
82
+ hooks.onError = h.onError.slice()
83
+ hooks.onRoute = h.onRoute.slice()
84
+ hooks.onRegister = h.onRegister.slice()
85
+ hooks.onTimeout = h.onTimeout.slice()
86
+ hooks.onRequestAbort = h.onRequestAbort.slice()
87
+ hooks.onReady = []
88
+ hooks.onListen = []
89
+ hooks.preClose = []
90
+ return hooks
91
+ }
92
+
93
+ function hookRunnerApplication (hookName, boot, server, cb) {
94
+ const hooks = server[kHooks][hookName]
95
+ let i = 0
96
+ let c = 0
97
+
98
+ next()
99
+
100
+ function exit (err) {
101
+ const hookFnName = hooks[i - 1]?.name
102
+ const hookFnFragment = hookFnName ? ` "${hookFnName}"` : ''
103
+
104
+ if (err) {
105
+ if (err.code === 'AVV_ERR_READY_TIMEOUT') {
106
+ err = appendStackTrace(err, new FST_ERR_HOOK_TIMEOUT(hookName, hookFnFragment))
107
+ } else {
108
+ err = AVVIO_ERRORS_MAP[err.code] != null
109
+ ? appendStackTrace(err, new AVVIO_ERRORS_MAP[err.code](err.message))
110
+ : err
111
+ }
112
+
113
+ cb(err)
114
+ return
115
+ }
116
+ cb()
117
+ }
118
+
119
+ function next (err) {
120
+ if (err) {
121
+ exit(err)
122
+ return
123
+ }
124
+
125
+ if (i === hooks.length && c === server[kChildren].length) {
126
+ if (i === 0 && c === 0) { // speed up start
127
+ exit()
128
+ } else {
129
+ // This is the last function executed for every fastify instance
130
+ boot(function manageTimeout (err, done) {
131
+ // this callback is needed by fastify to provide an hook interface without the error
132
+ // as first parameter and managing it on behalf the user
133
+ exit(err)
134
+
135
+ // this callback is needed by avvio to continue the loading of the next `register` plugins
136
+ done(err)
137
+ })
138
+ }
139
+ return
140
+ }
141
+
142
+ if (i === hooks.length && c < server[kChildren].length) {
143
+ const child = server[kChildren][c++]
144
+ hookRunnerApplication(hookName, boot, child, next)
145
+ return
146
+ }
147
+
148
+ boot(wrap(hooks[i++], server))
149
+ next()
150
+ }
151
+
152
+ function wrap (fn, server) {
153
+ return function (err, done) {
154
+ if (err) {
155
+ done(err)
156
+ return
157
+ }
158
+
159
+ if (fn.length === 1) {
160
+ try {
161
+ fn.call(server, done)
162
+ } catch (error) {
163
+ done(error)
164
+ }
165
+ return
166
+ }
167
+
168
+ try {
169
+ const ret = fn.call(server)
170
+ if (ret && typeof ret.then === 'function') {
171
+ ret.then(done, done)
172
+ return
173
+ }
174
+ } catch (error) {
175
+ err = error
176
+ }
177
+
178
+ done(err) // auto done
179
+ }
180
+ }
181
+ }
182
+
183
+ function onListenHookRunner (server) {
184
+ const hooks = server[kHooks].onListen
185
+ const hooksLen = hooks.length
186
+
187
+ let i = 0
188
+ let c = 0
189
+
190
+ next()
191
+
192
+ function next (err) {
193
+ err && server.log.error(err)
194
+
195
+ if (
196
+ i === hooksLen
197
+ ) {
198
+ while (c < server[kChildren].length) {
199
+ const child = server[kChildren][c++]
200
+ onListenHookRunner(child)
201
+ }
202
+ return
203
+ }
204
+
205
+ wrap(hooks[i++], server, next)
206
+ }
207
+
208
+ async function wrap (fn, server, done) {
209
+ if (fn.length === 1) {
210
+ try {
211
+ fn.call(server, done)
212
+ } catch (e) {
213
+ done(e)
214
+ }
215
+ return
216
+ }
217
+ try {
218
+ const ret = fn.call(server)
219
+ if (ret && typeof ret.then === 'function') {
220
+ ret.then(done, done)
221
+ return
222
+ }
223
+ done()
224
+ } catch (error) {
225
+ done(error)
226
+ }
227
+ }
228
+ }
229
+
230
+ function hookRunnerGenerator (iterator) {
231
+ return function hookRunner (functions, request, reply, cb) {
232
+ let i = 0
233
+
234
+ function next (err) {
235
+ if (err || i === functions.length) {
236
+ cb(err, request, reply)
237
+ return
238
+ }
239
+
240
+ let result
241
+ try {
242
+ result = iterator(functions[i++], request, reply, next)
243
+ } catch (error) {
244
+ cb(error, request, reply)
245
+ return
246
+ }
247
+ if (result && typeof result.then === 'function') {
248
+ result.then(handleResolve, handleReject)
249
+ }
250
+ }
251
+
252
+ function handleResolve () {
253
+ next()
254
+ }
255
+
256
+ function handleReject (err) {
257
+ if (!err) {
258
+ err = new FST_ERR_SEND_UNDEFINED_ERR()
259
+ }
260
+
261
+ cb(err, request, reply)
262
+ }
263
+
264
+ next()
265
+ }
266
+ }
267
+
268
+ function onResponseHookIterator (fn, request, reply, next) {
269
+ return fn(request, reply, next)
270
+ }
271
+
272
+ const onResponseHookRunner = hookRunnerGenerator(onResponseHookIterator)
273
+ const preValidationHookRunner = hookRunnerGenerator(hookIterator)
274
+ const preHandlerHookRunner = hookRunnerGenerator(hookIterator)
275
+ const onTimeoutHookRunner = hookRunnerGenerator(hookIterator)
276
+ const onRequestHookRunner = hookRunnerGenerator(hookIterator)
277
+
278
+ function onSendHookRunner (functions, request, reply, payload, cb) {
279
+ let i = 0
280
+
281
+ function next (err, newPayload) {
282
+ if (err) {
283
+ cb(err, request, reply, payload)
284
+ return
285
+ }
286
+
287
+ if (newPayload !== undefined) {
288
+ payload = newPayload
289
+ }
290
+
291
+ if (i === functions.length) {
292
+ cb(null, request, reply, payload)
293
+ return
294
+ }
295
+
296
+ let result
297
+ try {
298
+ result = functions[i++](request, reply, payload, next)
299
+ } catch (error) {
300
+ cb(error, request, reply)
301
+ return
302
+ }
303
+ if (result && typeof result.then === 'function') {
304
+ result.then(handleResolve, handleReject)
305
+ }
306
+ }
307
+
308
+ function handleResolve (newPayload) {
309
+ next(null, newPayload)
310
+ }
311
+
312
+ function handleReject (err) {
313
+ if (!err) {
314
+ err = new FST_ERR_SEND_UNDEFINED_ERR()
315
+ }
316
+
317
+ cb(err, request, reply, payload)
318
+ }
319
+
320
+ next()
321
+ }
322
+
323
+ const preSerializationHookRunner = onSendHookRunner
324
+
325
+ function preParsingHookRunner (functions, request, reply, cb) {
326
+ let i = 0
327
+
328
+ function next (err, newPayload) {
329
+ if (reply.sent) {
330
+ return
331
+ }
332
+
333
+ if (newPayload !== undefined) {
334
+ request[kRequestPayloadStream] = newPayload
335
+ }
336
+
337
+ if (err || i === functions.length) {
338
+ cb(err, request, reply)
339
+ return
340
+ }
341
+
342
+ let result
343
+ try {
344
+ result = functions[i++](request, reply, request[kRequestPayloadStream], next)
345
+ } catch (error) {
346
+ cb(error, request, reply)
347
+ return
348
+ }
349
+
350
+ if (result && typeof result.then === 'function') {
351
+ result.then(handleResolve, handleReject)
352
+ }
353
+ }
354
+
355
+ function handleResolve (newPayload) {
356
+ next(null, newPayload)
357
+ }
358
+
359
+ function handleReject (err) {
360
+ if (!err) {
361
+ err = new FST_ERR_SEND_UNDEFINED_ERR()
362
+ }
363
+
364
+ cb(err, request, reply)
365
+ }
366
+
367
+ next()
368
+ }
369
+
370
+ function onRequestAbortHookRunner (functions, request, cb) {
371
+ let i = 0
372
+
373
+ function next (err) {
374
+ if (err || i === functions.length) {
375
+ cb(err, request)
376
+ return
377
+ }
378
+
379
+ let result
380
+ try {
381
+ result = functions[i++](request, next)
382
+ } catch (error) {
383
+ cb(error, request)
384
+ return
385
+ }
386
+ if (result && typeof result.then === 'function') {
387
+ result.then(handleResolve, handleReject)
388
+ }
389
+ }
390
+
391
+ function handleResolve () {
392
+ next()
393
+ }
394
+
395
+ function handleReject (err) {
396
+ if (!err) {
397
+ err = new FST_ERR_SEND_UNDEFINED_ERR()
398
+ }
399
+
400
+ cb(err, request)
401
+ }
402
+
403
+ next()
404
+ }
405
+
406
+ function hookIterator (fn, request, reply, next) {
407
+ if (reply.sent === true) return undefined
408
+ return fn(request, reply, next)
409
+ }
410
+
411
+ module.exports = {
412
+ Hooks,
413
+ buildHooks,
414
+ hookRunnerGenerator,
415
+ preParsingHookRunner,
416
+ onResponseHookRunner,
417
+ onSendHookRunner,
418
+ preSerializationHookRunner,
419
+ onRequestAbortHookRunner,
420
+ hookIterator,
421
+ hookRunnerApplication,
422
+ onListenHookRunner,
423
+ preHandlerHookRunner,
424
+ preValidationHookRunner,
425
+ onRequestHookRunner,
426
+ onTimeoutHookRunner,
427
+ lifecycleHooks,
428
+ supportedHooks
429
+ }
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+
3
+ const validate = require('./config-validator')
4
+ const deepClone = require('rfdc')({ circles: true, proto: false })
5
+ const { FST_ERR_INIT_OPTS_INVALID } = require('./errors')
6
+
7
+ function validateInitialConfig (options) {
8
+ const opts = deepClone(options)
9
+
10
+ if (!validate(opts)) {
11
+ const error = new FST_ERR_INIT_OPTS_INVALID(JSON.stringify(validate.errors.map(e => e.message)))
12
+ error.errors = validate.errors
13
+ throw error
14
+ }
15
+
16
+ return deepFreezeObject(opts)
17
+ }
18
+
19
+ function deepFreezeObject (object) {
20
+ const properties = Object.getOwnPropertyNames(object)
21
+
22
+ for (const name of properties) {
23
+ const value = object[name]
24
+
25
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
26
+ continue
27
+ }
28
+
29
+ object[name] = value && typeof value === 'object' ? deepFreezeObject(value) : value
30
+ }
31
+
32
+ return Object.freeze(object)
33
+ }
34
+
35
+ module.exports = validateInitialConfig
36
+ module.exports.defaultInitOptions = validate.defaultInitOptions
37
+ module.exports.utils = { deepFreezeObject }