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/server.js ADDED
@@ -0,0 +1,441 @@
1
+ 'use strict'
2
+
3
+ const http = require('node:http')
4
+ const https = require('node:https')
5
+ const http2 = require('node:http2')
6
+ const dns = require('node:dns')
7
+ const os = require('node:os')
8
+
9
+ const { kState, kOptions, kServerBindings, kHttp2ServerSessions } = require('./symbols')
10
+ const { FSTWRN003 } = require('./warnings')
11
+ const { onListenHookRunner } = require('./hooks')
12
+ const {
13
+ FST_ERR_REOPENED_CLOSE_SERVER,
14
+ FST_ERR_REOPENED_SERVER,
15
+ FST_ERR_LISTEN_OPTIONS_INVALID,
16
+ FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE
17
+ } = require('./errors')
18
+ const noopSet = require('./noop-set')
19
+ const PonyPromise = require('./promise')
20
+
21
+ module.exports.createServer = createServer
22
+
23
+ function defaultResolveServerListeningText (address) {
24
+ return `Server listening at ${address}`
25
+ }
26
+
27
+ function createServer (options, httpHandler) {
28
+ const server = getServerInstance(options, httpHandler)
29
+
30
+ // `this` is the Fastify object
31
+ function listen (
32
+ listenOptions = { port: 0, host: 'localhost' },
33
+ cb = undefined
34
+ ) {
35
+ if (typeof cb === 'function') {
36
+ if (cb.constructor.name === 'AsyncFunction') {
37
+ FSTWRN003('listen method')
38
+ }
39
+
40
+ listenOptions.cb = cb
41
+ }
42
+ if (listenOptions.signal) {
43
+ if (typeof listenOptions.signal.on !== 'function' && typeof listenOptions.signal.addEventListener !== 'function') {
44
+ throw new FST_ERR_LISTEN_OPTIONS_INVALID('Invalid options.signal')
45
+ }
46
+
47
+ // copy the current signal state
48
+ this[kState].aborted = listenOptions.signal.aborted
49
+
50
+ if (this[kState].aborted) {
51
+ return this.close()
52
+ } else {
53
+ const onAborted = () => {
54
+ this[kState].aborted = true
55
+ this.close()
56
+ }
57
+ listenOptions.signal.addEventListener('abort', onAborted, { once: true })
58
+ }
59
+ }
60
+
61
+ // If we have a path specified, don't default host to 'localhost' so we don't end up listening
62
+ // on both path and host
63
+ // See https://github.com/fastify/fastify/issues/4007
64
+ let host
65
+ if (listenOptions.path == null) {
66
+ host = listenOptions.host ?? 'localhost'
67
+ } else {
68
+ host = listenOptions.host
69
+ }
70
+ if (!Object.hasOwn(listenOptions, 'host') ||
71
+ listenOptions.host == null) {
72
+ listenOptions.host = host
73
+ }
74
+ if (host === 'localhost') {
75
+ listenOptions.cb = (err, address) => {
76
+ if (err) {
77
+ // the server did not start
78
+ cb(err, address)
79
+ return
80
+ }
81
+
82
+ multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
83
+ this[kState].listening = true
84
+ cb(null, address)
85
+ onListenHookRunner(this)
86
+ })
87
+ }
88
+ } else {
89
+ listenOptions.cb = (err, address) => {
90
+ // the server did not start
91
+ if (err) {
92
+ cb(err, address)
93
+ return
94
+ }
95
+ this[kState].listening = true
96
+ cb(null, address)
97
+ onListenHookRunner(this)
98
+ }
99
+ }
100
+
101
+ // https://github.com/nodejs/node/issues/9390
102
+ // If listening to 'localhost', listen to both 127.0.0.1 or ::1 if they are available.
103
+ // If listening to 127.0.0.1, only listen to 127.0.0.1.
104
+ // If listening to ::1, only listen to ::1.
105
+
106
+ if (cb === undefined) {
107
+ const listening = listenPromise.call(this, server, listenOptions)
108
+ return listening.then(address => {
109
+ const { promise, resolve } = PonyPromise.withResolvers()
110
+ if (host === 'localhost') {
111
+ multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
112
+ this[kState].listening = true
113
+ resolve(address)
114
+ onListenHookRunner(this)
115
+ })
116
+ } else {
117
+ resolve(address)
118
+ onListenHookRunner(this)
119
+ }
120
+ return promise
121
+ })
122
+ }
123
+
124
+ this.ready(listenCallback.call(this, server, listenOptions))
125
+ }
126
+
127
+ const serverHasCloseAllConnections = typeof server.closeAllConnections === 'function'
128
+ const serverHasCloseIdleConnections = typeof server.closeIdleConnections === 'function'
129
+ const serverHasCloseHttp2Sessions = typeof server.closeHttp2Sessions === 'function'
130
+
131
+ let forceCloseConnections = options.forceCloseConnections
132
+ if (forceCloseConnections === 'idle' && !serverHasCloseIdleConnections) {
133
+ throw new FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE()
134
+ } else if (typeof forceCloseConnections !== 'boolean') {
135
+ /* istanbul ignore next: only one branch can be valid in a given Node.js version */
136
+ forceCloseConnections = serverHasCloseIdleConnections ? 'idle' : false
137
+ }
138
+
139
+ const keepAliveConnections = !serverHasCloseAllConnections && forceCloseConnections === true ? new Set() : noopSet()
140
+
141
+ return {
142
+ server,
143
+ listen,
144
+ forceCloseConnections,
145
+ serverHasCloseAllConnections,
146
+ serverHasCloseHttp2Sessions,
147
+ keepAliveConnections
148
+ }
149
+ }
150
+
151
+ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, onListen) {
152
+ // the main server is started, we need to start the secondary servers
153
+ this[kState].listening = false
154
+
155
+ // let's check if we need to bind additional addresses
156
+ dns.lookup(listenOptions.host, { all: true }, (dnsErr, addresses) => {
157
+ if (dnsErr || this[kState].aborted) {
158
+ // not blocking the main server listening
159
+ // this.log.warn('dns.lookup error:', dnsErr)
160
+ onListen()
161
+ return
162
+ }
163
+
164
+ const isMainServerListening = mainServer.listening && serverOpts.serverFactory
165
+
166
+ let binding = 0
167
+ let bound = 0
168
+ if (!isMainServerListening) {
169
+ const primaryAddress = mainServer.address()
170
+ for (const adr of addresses) {
171
+ if (adr.address !== primaryAddress.address) {
172
+ binding++
173
+ const secondaryOpts = Object.assign({}, listenOptions, {
174
+ host: adr.address,
175
+ port: primaryAddress.port,
176
+ cb: (_ignoreErr) => {
177
+ bound++
178
+
179
+ if (!_ignoreErr) {
180
+ this[kServerBindings].push(secondaryServer)
181
+ }
182
+
183
+ if (bound === binding) {
184
+ // regardless of the error, we are done
185
+ onListen()
186
+ }
187
+ }
188
+ })
189
+
190
+ const secondaryServer = getServerInstance(serverOpts, httpHandler)
191
+ const closeSecondary = () => {
192
+ // To avoid falling into situations where the close of the
193
+ // secondary server is triggered before the preClose hook
194
+ // is done running, we better wait until the main server is closed.
195
+ // No new TCP connections are accepted
196
+ // We swallow any error from the secondary server
197
+ secondaryServer.close(() => {})
198
+ if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections === true) {
199
+ secondaryServer.closeAllConnections()
200
+ }
201
+ if (typeof secondaryServer.closeHttp2Sessions === 'function') {
202
+ secondaryServer.closeHttp2Sessions()
203
+ }
204
+ }
205
+
206
+ secondaryServer.on('upgrade', mainServer.emit.bind(mainServer, 'upgrade'))
207
+ mainServer.on('unref', closeSecondary)
208
+ mainServer.on('close', closeSecondary)
209
+ mainServer.on('error', closeSecondary)
210
+ this[kState].listening = false
211
+ listenCallback.call(this, secondaryServer, secondaryOpts)()
212
+ }
213
+ }
214
+ }
215
+ // no extra bindings are necessary
216
+ if (binding === 0) {
217
+ onListen()
218
+ return
219
+ }
220
+
221
+ // in test files we are using unref so we need to propagate the unref event
222
+ // to the secondary servers. It is valid only when the user is
223
+ // listening on localhost
224
+ const originUnref = mainServer.unref
225
+ mainServer.unref = function () {
226
+ originUnref.call(mainServer)
227
+ mainServer.emit('unref')
228
+ }
229
+ })
230
+ }
231
+
232
+ function listenCallback (server, listenOptions) {
233
+ const wrap = (err) => {
234
+ server.removeListener('error', wrap)
235
+ server.removeListener('listening', wrap)
236
+ if (!err) {
237
+ const address = logServerAddress.call(
238
+ this,
239
+ server,
240
+ listenOptions.listenTextResolver || defaultResolveServerListeningText
241
+ )
242
+ listenOptions.cb(null, address)
243
+ } else {
244
+ this[kState].listening = false
245
+ listenOptions.cb(err, null)
246
+ }
247
+ }
248
+
249
+ return (err) => {
250
+ if (err != null) return listenOptions.cb(err)
251
+
252
+ if (this[kState].listening && this[kState].closing) {
253
+ return listenOptions.cb(new FST_ERR_REOPENED_CLOSE_SERVER(), null)
254
+ }
255
+ if (this[kState].listening) {
256
+ return listenOptions.cb(new FST_ERR_REOPENED_SERVER(), null)
257
+ }
258
+
259
+ server.once('error', wrap)
260
+ if (!this[kState].closing) {
261
+ server.once('listening', wrap)
262
+ server.listen(listenOptions)
263
+ this[kState].listening = true
264
+ }
265
+ }
266
+ }
267
+
268
+ function listenPromise (server, listenOptions) {
269
+ if (this[kState].listening && this[kState].closing) {
270
+ return Promise.reject(new FST_ERR_REOPENED_CLOSE_SERVER())
271
+ }
272
+ if (this[kState].listening) {
273
+ return Promise.reject(new FST_ERR_REOPENED_SERVER())
274
+ }
275
+
276
+ return this.ready().then(() => {
277
+ // skip listen when aborted during ready
278
+ if (this[kState].aborted) return
279
+
280
+ const { promise, resolve, reject } = PonyPromise.withResolvers()
281
+
282
+ const errEventHandler = (err) => {
283
+ cleanup()
284
+ this[kState].listening = false
285
+ reject(err)
286
+ }
287
+ const listeningEventHandler = () => {
288
+ cleanup()
289
+ this[kState].listening = true
290
+ resolve(logServerAddress.call(
291
+ this,
292
+ server,
293
+ listenOptions.listenTextResolver || defaultResolveServerListeningText
294
+ ))
295
+ }
296
+ function cleanup () {
297
+ server.removeListener('error', errEventHandler)
298
+ server.removeListener('listening', listeningEventHandler)
299
+ }
300
+ server.once('error', errEventHandler)
301
+ server.once('listening', listeningEventHandler)
302
+
303
+ server.listen(listenOptions)
304
+
305
+ return promise
306
+ })
307
+ }
308
+
309
+ function getServerInstance (options, httpHandler) {
310
+ if (options.serverFactory) {
311
+ // User provided server instance
312
+ return options.serverFactory(httpHandler, options)
313
+ }
314
+
315
+ // We have accepted true as a valid way to init https but node requires an options obj
316
+ const httpsOptions = options.https === true ? {} : options.https
317
+
318
+ if (options.http2) {
319
+ const server = typeof httpsOptions === 'object' ? http2.createSecureServer(httpsOptions, httpHandler) : http2.createServer(options.http, httpHandler)
320
+ server.on('session', (session) => session.setTimeout(options.http2SessionTimeout, () => {
321
+ session.close()
322
+ }))
323
+
324
+ // This is only needed for Node.js versions < 24.0.0 since Node.js added native
325
+ // closeAllSessions() on server.close() support for HTTP/2 servers in v24.0.0
326
+ if (options.forceCloseConnections === true) {
327
+ server.closeHttp2Sessions = createCloseHttp2SessionsByHttp2Server(server)
328
+ }
329
+
330
+ server.setTimeout(options.connectionTimeout)
331
+
332
+ return server
333
+ }
334
+
335
+ // HTTP1 server instance
336
+ const server = httpsOptions
337
+ ? https.createServer(httpsOptions, httpHandler)
338
+ : http.createServer(options.http, httpHandler)
339
+ server.keepAliveTimeout = options.keepAliveTimeout
340
+ server.requestTimeout = options.requestTimeout
341
+ server.setTimeout(options.connectionTimeout)
342
+ // We treat zero as null(node default) so we do not pass zero to the server instance
343
+ if (options.maxRequestsPerSocket > 0) {
344
+ server.maxRequestsPerSocket = options.maxRequestsPerSocket
345
+ }
346
+
347
+ return server
348
+ }
349
+
350
+ /**
351
+ * Inspects the provided `server.address` object and returns a
352
+ * normalized list of IP address strings. Normalization in this
353
+ * case refers to mapping wildcard `0.0.0.0` to the list of IP
354
+ * addresses the wildcard refers to.
355
+ *
356
+ * @see https://nodejs.org/docs/latest/api/net.html#serveraddress
357
+ *
358
+ * @param {object} A server address object as described in the
359
+ * linked docs.
360
+ *
361
+ * @returns {string[]}
362
+ */
363
+ function getAddresses (address) {
364
+ if (address.address === '0.0.0.0') {
365
+ return Object.values(os.networkInterfaces()).flatMap((iface) => {
366
+ return iface.filter((iface) => iface.family === 'IPv4')
367
+ }).sort((iface) => {
368
+ /* c8 ignore next 2 */
369
+ // Order the interfaces so that internal ones come first
370
+ return iface.internal ? -1 : 1
371
+ }).map((iface) => { return iface.address })
372
+ }
373
+ return [address.address]
374
+ }
375
+
376
+ function logServerAddress (server, listenTextResolver) {
377
+ let addresses
378
+ const isUnixSocket = typeof server.address() === 'string'
379
+ if (!isUnixSocket) {
380
+ if (server.address().address.indexOf(':') === -1) {
381
+ // IPv4
382
+ addresses = getAddresses(server.address()).map((address) => address + ':' + server.address().port)
383
+ } else {
384
+ // IPv6
385
+ addresses = ['[' + server.address().address + ']:' + server.address().port]
386
+ }
387
+
388
+ addresses = addresses.map((address) => ('http' + (this[kOptions].https ? 's' : '') + '://') + address)
389
+ } else {
390
+ addresses = [server.address()]
391
+ }
392
+
393
+ for (const address of addresses) {
394
+ this.log.info(listenTextResolver(address))
395
+ }
396
+ return addresses[0]
397
+ }
398
+
399
+ /**
400
+ * @param {http2.Http2Server} http2Server
401
+ * @returns {() => void}
402
+ */
403
+ function createCloseHttp2SessionsByHttp2Server (http2Server) {
404
+ /**
405
+ * @type {Set<http2.Http2Session>}
406
+ */
407
+ http2Server[kHttp2ServerSessions] = new Set()
408
+
409
+ http2Server.on('session', function (session) {
410
+ session.once('connect', function () {
411
+ http2Server[kHttp2ServerSessions].add(session)
412
+ })
413
+
414
+ session.once('close', function () {
415
+ http2Server[kHttp2ServerSessions].delete(session)
416
+ })
417
+
418
+ session.once('frameError', function (type, code, streamId) {
419
+ if (streamId === 0) {
420
+ // The stream ID is 0, which means that the error is related to the session itself.
421
+ // If the event is not associated with a stream, the Http2Session will be shut down immediately
422
+ http2Server[kHttp2ServerSessions].delete(session)
423
+ }
424
+ })
425
+
426
+ session.once('goaway', function () {
427
+ // The Http2Session instance will be shut down automatically when the 'goaway' event is emitted.
428
+ http2Server[kHttp2ServerSessions].delete(session)
429
+ })
430
+ })
431
+
432
+ return function closeHttp2Sessions () {
433
+ if (http2Server[kHttp2ServerSessions].size === 0) {
434
+ return
435
+ }
436
+
437
+ for (const session of http2Server[kHttp2ServerSessions]) {
438
+ session.close()
439
+ }
440
+ }
441
+ }
package/lib/symbols.js ADDED
@@ -0,0 +1,71 @@
1
+ 'use strict'
2
+
3
+ const keys = {
4
+ kAvvioBoot: Symbol('fastify.avvioBoot'),
5
+ kChildren: Symbol('fastify.children'),
6
+ kServerBindings: Symbol('fastify.serverBindings'),
7
+ kBodyLimit: Symbol('fastify.bodyLimit'),
8
+ kSupportedHTTPMethods: Symbol('fastify.acceptedHTTPMethods'),
9
+ kRoutePrefix: Symbol('fastify.routePrefix'),
10
+ kLogLevel: Symbol('fastify.logLevel'),
11
+ kLogSerializers: Symbol('fastify.logSerializers'),
12
+ kHooks: Symbol('fastify.hooks'),
13
+ kContentTypeParser: Symbol('fastify.contentTypeParser'),
14
+ kState: Symbol('fastify.state'),
15
+ kOptions: Symbol('fastify.options'),
16
+ kDisableRequestLogging: Symbol('fastify.disableRequestLogging'),
17
+ kPluginNameChain: Symbol('fastify.pluginNameChain'),
18
+ kRouteContext: Symbol('fastify.context'),
19
+ kGenReqId: Symbol('fastify.genReqId'),
20
+ kHttp2ServerSessions: Symbol('fastify.http2ServerSessions'),
21
+ // Schema
22
+ kSchemaController: Symbol('fastify.schemaController'),
23
+ kSchemaHeaders: Symbol('headers-schema'),
24
+ kSchemaParams: Symbol('params-schema'),
25
+ kSchemaQuerystring: Symbol('querystring-schema'),
26
+ kSchemaBody: Symbol('body-schema'),
27
+ kSchemaResponse: Symbol('response-schema'),
28
+ kSchemaErrorFormatter: Symbol('fastify.schemaErrorFormatter'),
29
+ kSchemaVisited: Symbol('fastify.schemas.visited'),
30
+ // Request
31
+ kRequest: Symbol('fastify.Request'),
32
+ kRequestPayloadStream: Symbol('fastify.RequestPayloadStream'),
33
+ kRequestAcceptVersion: Symbol('fastify.RequestAcceptVersion'),
34
+ kRequestCacheValidateFns: Symbol('fastify.request.cache.validateFns'),
35
+ kRequestOriginalUrl: Symbol('fastify.request.originalUrl'),
36
+ kRequestSignal: Symbol('fastify.request.signal'),
37
+ kHandlerTimeout: Symbol('fastify.handlerTimeout'),
38
+ kTimeoutTimer: Symbol('fastify.request.timeoutTimer'),
39
+ kOnAbort: Symbol('fastify.request.onAbort'),
40
+ // 404
41
+ kFourOhFour: Symbol('fastify.404'),
42
+ kCanSetNotFoundHandler: Symbol('fastify.canSetNotFoundHandler'),
43
+ kFourOhFourLevelInstance: Symbol('fastify.404LogLevelInstance'),
44
+ kFourOhFourContext: Symbol('fastify.404ContextKey'),
45
+ kDefaultJsonParse: Symbol('fastify.defaultJSONParse'),
46
+ // Reply
47
+ kReply: Symbol('fastify.Reply'),
48
+ kReplySerializer: Symbol('fastify.reply.serializer'),
49
+ kReplyIsError: Symbol('fastify.reply.isError'),
50
+ kReplyHeaders: Symbol('fastify.reply.headers'),
51
+ kReplyTrailers: Symbol('fastify.reply.trailers'),
52
+ kReplyHasStatusCode: Symbol('fastify.reply.hasStatusCode'),
53
+ kReplyHijacked: Symbol('fastify.reply.hijacked'),
54
+ kReplyStartTime: Symbol('fastify.reply.startTime'),
55
+ kReplyNextErrorHandler: Symbol('fastify.reply.nextErrorHandler'),
56
+ kReplyEndTime: Symbol('fastify.reply.endTime'),
57
+ kReplyErrorHandlerCalled: Symbol('fastify.reply.errorHandlerCalled'),
58
+ kReplyIsRunningOnErrorHook: Symbol('fastify.reply.isRunningOnErrorHook'),
59
+ kReplySerializerDefault: Symbol('fastify.replySerializerDefault'),
60
+ kReplyCacheSerializeFns: Symbol('fastify.reply.cache.serializeFns'),
61
+ // This symbol is only meant to be used for fastify tests and should not be used for any other purpose
62
+ kTestInternals: Symbol('fastify.testInternals'),
63
+ kErrorHandler: Symbol('fastify.errorHandler'),
64
+ kErrorHandlerAlreadySet: Symbol('fastify.errorHandlerAlreadySet'),
65
+ kChildLoggerFactory: Symbol('fastify.childLoggerFactory'),
66
+ kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
67
+ kKeepAliveConnections: Symbol('fastify.keepAliveConnections'),
68
+ kRouteByFastify: Symbol('fastify.routeByFastify')
69
+ }
70
+
71
+ module.exports = keys