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,706 @@
1
+ 'use strict'
2
+
3
+ const net = require('node:net')
4
+ const http = require('node:http')
5
+ const { test } = require('node:test')
6
+ const Fastify = require('..')
7
+ const { Client } = require('undici')
8
+ const split = require('split2')
9
+ const { sleep } = require('./helper')
10
+
11
+ test('close callback', (t, testDone) => {
12
+ t.plan(7)
13
+ const fastify = Fastify()
14
+ fastify.addHook('onClose', onClose)
15
+ function onClose (instance, done) {
16
+ t.assert.ok(typeof fastify === typeof this)
17
+ t.assert.ok(typeof fastify === typeof instance)
18
+ t.assert.strictEqual(fastify, this)
19
+ t.assert.strictEqual(fastify, instance)
20
+ done()
21
+ }
22
+
23
+ fastify.listen({ port: 0 }, err => {
24
+ t.assert.ifError(err)
25
+
26
+ fastify.close((err) => {
27
+ t.assert.ifError(err)
28
+ t.assert.ok('close callback')
29
+ testDone()
30
+ })
31
+ })
32
+ })
33
+
34
+ test('inside register', (t, done) => {
35
+ t.plan(5)
36
+ const fastify = Fastify()
37
+ fastify.register(function (f, opts, done) {
38
+ f.addHook('onClose', onClose)
39
+ function onClose (instance, done) {
40
+ t.assert.ok(instance.prototype === fastify.prototype)
41
+ t.assert.strictEqual(instance, f)
42
+ done()
43
+ }
44
+
45
+ done()
46
+ })
47
+
48
+ fastify.listen({ port: 0 }, err => {
49
+ t.assert.ifError(err)
50
+
51
+ fastify.close((err) => {
52
+ t.assert.ifError(err)
53
+ t.assert.ok('close callback')
54
+ done()
55
+ })
56
+ })
57
+ })
58
+
59
+ test('close order', (t, done) => {
60
+ t.plan(5)
61
+ const fastify = Fastify()
62
+ const order = [1, 2, 3]
63
+
64
+ fastify.register(function (f, opts, done) {
65
+ f.addHook('onClose', (instance, done) => {
66
+ t.assert.strictEqual(order.shift(), 1)
67
+ done()
68
+ })
69
+
70
+ done()
71
+ })
72
+
73
+ fastify.addHook('onClose', (instance, done) => {
74
+ t.assert.strictEqual(order.shift(), 2)
75
+ done()
76
+ })
77
+
78
+ fastify.listen({ port: 0 }, err => {
79
+ t.assert.ifError(err)
80
+
81
+ fastify.close((err) => {
82
+ t.assert.ifError(err)
83
+ t.assert.strictEqual(order.shift(), 3)
84
+ done()
85
+ })
86
+ })
87
+ })
88
+
89
+ test('close order - async', async t => {
90
+ t.plan(3)
91
+ const fastify = Fastify()
92
+ const order = [1, 2, 3]
93
+
94
+ fastify.register(function (f, opts, done) {
95
+ f.addHook('onClose', async instance => {
96
+ t.assert.strictEqual(order.shift(), 1)
97
+ })
98
+
99
+ done()
100
+ })
101
+
102
+ fastify.addHook('onClose', () => {
103
+ t.assert.strictEqual(order.shift(), 2)
104
+ })
105
+
106
+ await fastify.listen({ port: 0 })
107
+ await fastify.close()
108
+
109
+ t.assert.strictEqual(order.shift(), 3)
110
+ })
111
+
112
+ test('should not throw an error if the server is not listening', (t, done) => {
113
+ t.plan(2)
114
+ const fastify = Fastify()
115
+ fastify.addHook('onClose', onClose)
116
+ function onClose (instance, done) {
117
+ t.assert.ok(instance.prototype === fastify.prototype)
118
+ done()
119
+ }
120
+
121
+ fastify.close((err) => {
122
+ t.assert.ifError(err)
123
+ done()
124
+ })
125
+ })
126
+
127
+ test('onClose should keep the context', (t, done) => {
128
+ t.plan(4)
129
+ const fastify = Fastify()
130
+ fastify.register(plugin)
131
+
132
+ function plugin (instance, opts, done) {
133
+ instance.decorate('test', true)
134
+ instance.addHook('onClose', onClose)
135
+ t.assert.ok(instance.prototype === fastify.prototype)
136
+
137
+ function onClose (i, done) {
138
+ t.assert.ok(i.test)
139
+ t.assert.strictEqual(i, instance)
140
+ done()
141
+ }
142
+
143
+ done()
144
+ }
145
+
146
+ fastify.close((err) => {
147
+ t.assert.ifError(err)
148
+ done()
149
+ })
150
+ })
151
+
152
+ test('Should return error while closing (promise) - injection', (t, done) => {
153
+ t.plan(4)
154
+ const fastify = Fastify()
155
+
156
+ fastify.addHook('onClose', (instance, done) => { done() })
157
+
158
+ fastify.get('/', (req, reply) => {
159
+ reply.send({ hello: 'world' })
160
+ })
161
+
162
+ fastify.inject({
163
+ method: 'GET',
164
+ url: '/'
165
+ }, (err, res) => {
166
+ t.assert.ifError(err)
167
+ t.assert.strictEqual(res.statusCode, 200)
168
+ fastify.close()
169
+
170
+ process.nextTick(() => {
171
+ fastify.inject({
172
+ method: 'GET',
173
+ url: '/'
174
+ }).catch(err => {
175
+ t.assert.ok(err)
176
+ t.assert.strictEqual(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
177
+ done()
178
+ })
179
+ }, 100)
180
+ })
181
+ })
182
+
183
+ test('Should return error while closing (callback) - injection', (t, done) => {
184
+ t.plan(4)
185
+ const fastify = Fastify()
186
+
187
+ fastify.addHook('onClose', (instance, done) => {
188
+ setTimeout(done, 150)
189
+ })
190
+
191
+ fastify.get('/', (req, reply) => {
192
+ reply.send({ hello: 'world' })
193
+ })
194
+
195
+ fastify.inject({
196
+ method: 'GET',
197
+ url: '/'
198
+ }, (err, res) => {
199
+ t.assert.ifError(err)
200
+ t.assert.strictEqual(res.statusCode, 200)
201
+ fastify.close()
202
+
203
+ setTimeout(() => {
204
+ fastify.inject({
205
+ method: 'GET',
206
+ url: '/'
207
+ }, (err, res) => {
208
+ t.assert.ok(err)
209
+ t.assert.strictEqual(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
210
+ done()
211
+ })
212
+ }, 100)
213
+ })
214
+ })
215
+
216
+ test('Current opened connection should NOT continue to work after closing and return "connection: close" header - return503OnClosing: false', (t, done) => {
217
+ t.plan(4)
218
+ const fastify = Fastify({
219
+ return503OnClosing: false,
220
+ forceCloseConnections: false
221
+ })
222
+
223
+ fastify.get('/', (req, reply) => {
224
+ fastify.close()
225
+ reply.send({ hello: 'world' })
226
+ })
227
+
228
+ fastify.listen({ port: 0 }, err => {
229
+ t.assert.ifError(err)
230
+
231
+ const port = fastify.server.address().port
232
+ const client = net.createConnection({ port }, () => {
233
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
234
+
235
+ client.on('error', function () {
236
+ // Depending on the Operating System
237
+ // the socket could error or not.
238
+ // However, it will always be closed.
239
+ })
240
+
241
+ client.on('close', function () {
242
+ t.assert.ok(true)
243
+ done()
244
+ })
245
+
246
+ client.once('data', data => {
247
+ t.assert.match(data.toString(), /Connection:\s*keep-alive/i)
248
+ t.assert.match(data.toString(), /200 OK/i)
249
+
250
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
251
+ })
252
+ })
253
+ })
254
+ })
255
+
256
+ test('Current opened connection should not accept new incoming connections', (t, done) => {
257
+ t.plan(3)
258
+ const fastify = Fastify({ forceCloseConnections: false })
259
+ fastify.get('/', (req, reply) => {
260
+ fastify.close()
261
+ setTimeout(() => {
262
+ reply.send({ hello: 'world' })
263
+ }, 250)
264
+ })
265
+
266
+ fastify.listen({ port: 0 }, async err => {
267
+ t.assert.ifError(err)
268
+ const instance = new Client('http://localhost:' + fastify.server.address().port)
269
+ let response = await instance.request({ path: '/', method: 'GET' })
270
+ t.assert.strictEqual(response.statusCode, 200)
271
+
272
+ response = await instance.request({ path: '/', method: 'GET' })
273
+ t.assert.strictEqual(response.statusCode, 503)
274
+
275
+ done()
276
+ })
277
+ })
278
+
279
+ test('rejected incoming connections should be logged', (t, done) => {
280
+ t.plan(2)
281
+ const stream = split(JSON.parse)
282
+ const fastify = Fastify({
283
+ forceCloseConnections: false,
284
+ logger: {
285
+ stream,
286
+ level: 'info'
287
+ }
288
+ })
289
+
290
+ const messages = []
291
+ stream.on('data', message => {
292
+ messages.push(message)
293
+ })
294
+ fastify.get('/', (req, reply) => {
295
+ fastify.close()
296
+ setTimeout(() => {
297
+ reply.send({ hello: 'world' })
298
+ }, 250)
299
+ })
300
+
301
+ fastify.listen({ port: 0 }, err => {
302
+ t.assert.ifError(err)
303
+ const instance = new Client('http://localhost:' + fastify.server.address().port)
304
+ // initial request to trigger close
305
+ instance.request({ path: '/', method: 'GET' })
306
+ // subsequent request should be rejected
307
+ instance.request({ path: '/', method: 'GET' }).then(() => {
308
+ t.assert.ok(messages.find(message => message.msg.includes('request aborted')))
309
+ done()
310
+ })
311
+ })
312
+ })
313
+
314
+ test('Cannot be reopened the closed server without listen callback', async t => {
315
+ t.plan(2)
316
+ const fastify = Fastify()
317
+
318
+ await fastify.listen({ port: 0 })
319
+ await fastify.close()
320
+
321
+ try {
322
+ await fastify.listen({ port: 0 })
323
+ } catch (err) {
324
+ t.assert.ok(err)
325
+ t.assert.strictEqual(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
326
+ }
327
+ })
328
+
329
+ test('Cannot be reopened the closed server has listen callback', async t => {
330
+ t.plan(2)
331
+ const fastify = Fastify()
332
+
333
+ await fastify.listen({ port: 0 })
334
+ await fastify.close()
335
+
336
+ await new Promise((resolve, reject) => {
337
+ fastify.listen({ port: 0 }, err => {
338
+ reject(err)
339
+ })
340
+ }).catch(err => {
341
+ t.assert.strictEqual(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
342
+ t.assert.ok(err)
343
+ })
344
+ })
345
+
346
+ const server = http.createServer()
347
+ const noSupport = typeof server.closeAllConnections !== 'function'
348
+
349
+ test('shutsdown while keep-alive connections are active (non-async, native)', { skip: noSupport }, (t, done) => {
350
+ t.plan(5)
351
+
352
+ const timeoutTime = 2 * 60 * 1000
353
+ const fastify = Fastify({ forceCloseConnections: true })
354
+
355
+ fastify.server.setTimeout(timeoutTime)
356
+ fastify.server.keepAliveTimeout = timeoutTime
357
+
358
+ fastify.get('/', (req, reply) => {
359
+ reply.send({ hello: 'world' })
360
+ })
361
+
362
+ fastify.listen({ port: 0 }, (err, address) => {
363
+ t.assert.ifError(err)
364
+
365
+ const client = new Client(
366
+ 'http://localhost:' + fastify.server.address().port,
367
+ { keepAliveTimeout: 1 * 60 * 1000 }
368
+ )
369
+ client.request({ path: '/', method: 'GET' }, (err, response) => {
370
+ t.assert.ifError(err)
371
+ t.assert.strictEqual(client.closed, false)
372
+
373
+ fastify.close((err) => {
374
+ t.assert.ifError(err)
375
+
376
+ // Due to the nature of the way we reap these keep-alive connections,
377
+ // there hasn't been enough time before the server fully closed in order
378
+ // for the client to have seen the socket get destroyed. The mere fact
379
+ // that we have reached this callback is enough indication that the
380
+ // feature being tested works as designed.
381
+ t.assert.strictEqual(client.closed, false)
382
+ done()
383
+ })
384
+ })
385
+ })
386
+ })
387
+
388
+ test('shutsdown while keep-alive connections are active (non-async, idle, native)', { skip: noSupport }, (t, done) => {
389
+ t.plan(5)
390
+
391
+ const timeoutTime = 2 * 60 * 1000
392
+ const fastify = Fastify({ forceCloseConnections: 'idle' })
393
+
394
+ fastify.server.setTimeout(timeoutTime)
395
+ fastify.server.keepAliveTimeout = timeoutTime
396
+
397
+ fastify.get('/', (req, reply) => {
398
+ reply.send({ hello: 'world' })
399
+ })
400
+
401
+ fastify.listen({ port: 0 }, (err, address) => {
402
+ t.assert.ifError(err)
403
+
404
+ const client = new Client(
405
+ 'http://localhost:' + fastify.server.address().port,
406
+ { keepAliveTimeout: 1 * 60 * 1000 }
407
+ )
408
+ client.request({ path: '/', method: 'GET' }, (err, response) => {
409
+ t.assert.ifError(err)
410
+ t.assert.strictEqual(client.closed, false)
411
+
412
+ fastify.close((err) => {
413
+ t.assert.ifError(err)
414
+
415
+ // Due to the nature of the way we reap these keep-alive connections,
416
+ // there hasn't been enough time before the server fully closed in order
417
+ // for the client to have seen the socket get destroyed. The mere fact
418
+ // that we have reached this callback is enough indication that the
419
+ // feature being tested works as designed.
420
+ t.assert.strictEqual(client.closed, false)
421
+
422
+ done()
423
+ })
424
+ })
425
+ })
426
+ })
427
+
428
+ test('triggers on-close hook in the right order with multiple bindings', async t => {
429
+ const expectedOrder = [1, 2, 3]
430
+ const order = []
431
+ const fastify = Fastify()
432
+
433
+ t.plan(1)
434
+
435
+ // Follows LIFO
436
+ fastify.addHook('onClose', () => {
437
+ order.push(2)
438
+ })
439
+
440
+ fastify.addHook('onClose', () => {
441
+ order.push(1)
442
+ })
443
+
444
+ await fastify.listen({ port: 0 })
445
+
446
+ await new Promise((resolve, reject) => {
447
+ setTimeout(() => {
448
+ fastify.close(err => {
449
+ order.push(3)
450
+ t.assert.deepEqual(order, expectedOrder)
451
+
452
+ if (err) t.assert.ifError(err)
453
+ else resolve()
454
+ })
455
+ }, 2000)
456
+ })
457
+ })
458
+
459
+ test('triggers on-close hook in the right order with multiple bindings (forceCloseConnections - idle)', { skip: noSupport }, async t => {
460
+ const expectedPayload = { hello: 'world' }
461
+ const timeoutTime = 2 * 60 * 1000
462
+ const expectedOrder = [1, 2]
463
+ const order = []
464
+ const fastify = Fastify({ forceCloseConnections: 'idle' })
465
+
466
+ fastify.server.setTimeout(timeoutTime)
467
+ fastify.server.keepAliveTimeout = timeoutTime
468
+
469
+ fastify.get('/', async (req, reply) => {
470
+ await new Promise((resolve) => {
471
+ setTimeout(resolve, 1000)
472
+ })
473
+
474
+ return expectedPayload
475
+ })
476
+
477
+ fastify.addHook('onClose', () => {
478
+ order.push(1)
479
+ })
480
+
481
+ await fastify.listen({ port: 0 })
482
+ const addresses = fastify.addresses()
483
+ const testPlan = (addresses.length * 2) + 1
484
+
485
+ t.plan(testPlan)
486
+
487
+ for (const addr of addresses) {
488
+ const { family, address, port } = addr
489
+ const host = family === 'IPv6' ? `[${address}]` : address
490
+ const client = new Client(`http://${host}:${port}`, {
491
+ keepAliveTimeout: 1 * 60 * 1000
492
+ })
493
+
494
+ client.request({ path: '/', method: 'GET' })
495
+ .then((res) => res.body.json(), err => t.assert.ifError(err))
496
+ .then(json => {
497
+ t.assert.deepEqual(json, expectedPayload, 'should payload match')
498
+ t.assert.ok(!client.closed, 'should client not be closed')
499
+ }, err => t.assert.ifError(err))
500
+ }
501
+
502
+ await new Promise((resolve, reject) => {
503
+ setTimeout(() => {
504
+ fastify.close(err => {
505
+ order.push(2)
506
+ t.assert.deepEqual(order, expectedOrder)
507
+
508
+ if (err) t.assert.ifError(err)
509
+ else resolve()
510
+ })
511
+ }, 2000)
512
+ })
513
+ })
514
+
515
+ test('triggers on-close hook in the right order with multiple bindings (forceCloseConnections - true)', { skip: noSupport }, async t => {
516
+ const expectedPayload = { hello: 'world' }
517
+ const timeoutTime = 2 * 60 * 1000
518
+ const expectedOrder = [1, 2]
519
+ const order = []
520
+ const fastify = Fastify({ forceCloseConnections: true })
521
+
522
+ fastify.server.setTimeout(timeoutTime)
523
+ fastify.server.keepAliveTimeout = timeoutTime
524
+
525
+ fastify.get('/', async (req, reply) => {
526
+ await new Promise((resolve) => {
527
+ setTimeout(resolve, 1000)
528
+ })
529
+
530
+ return expectedPayload
531
+ })
532
+
533
+ fastify.addHook('onClose', () => {
534
+ order.push(1)
535
+ })
536
+
537
+ await fastify.listen({ port: 0 })
538
+ const addresses = fastify.addresses()
539
+ const testPlan = (addresses.length * 2) + 1
540
+
541
+ t.plan(testPlan)
542
+
543
+ for (const addr of addresses) {
544
+ const { family, address, port } = addr
545
+ const host = family === 'IPv6' ? `[${address}]` : address
546
+ const client = new Client(`http://${host}:${port}`, {
547
+ keepAliveTimeout: 1 * 60 * 1000
548
+ })
549
+
550
+ client.request({ path: '/', method: 'GET' })
551
+ .then((res) => res.body.json(), err => t.assert.ifError(err))
552
+ .then(json => {
553
+ t.assert.deepEqual(json, expectedPayload, 'should payload match')
554
+ t.assert.ok(!client.closed, 'should client not be closed')
555
+ }, err => t.assert.ifError(err))
556
+ }
557
+
558
+ await new Promise((resolve, reject) => {
559
+ setTimeout(() => {
560
+ fastify.close(err => {
561
+ order.push(2)
562
+ t.assert.deepEqual(order, expectedOrder)
563
+
564
+ if (err) t.assert.ifError(err)
565
+ else resolve()
566
+ })
567
+ }, 2000)
568
+ })
569
+ })
570
+
571
+ test('shutsdown while keep-alive connections are active (non-async, custom)', (t, done) => {
572
+ t.plan(5)
573
+
574
+ const timeoutTime = 2 * 60 * 1000
575
+ const fastify = Fastify({
576
+ forceCloseConnections: true,
577
+ serverFactory (handler) {
578
+ const server = http.createServer(handler)
579
+
580
+ server.closeAllConnections = null
581
+
582
+ return server
583
+ }
584
+ })
585
+
586
+ fastify.server.setTimeout(timeoutTime)
587
+ fastify.server.keepAliveTimeout = timeoutTime
588
+
589
+ fastify.get('/', (req, reply) => {
590
+ reply.send({ hello: 'world' })
591
+ })
592
+
593
+ fastify.listen({ port: 0 }, (err, address) => {
594
+ t.assert.ifError(err)
595
+
596
+ const client = new Client(
597
+ 'http://localhost:' + fastify.server.address().port,
598
+ { keepAliveTimeout: 1 * 60 * 1000 }
599
+ )
600
+ client.request({ path: '/', method: 'GET' }, (err, response) => {
601
+ t.assert.ifError(err)
602
+ t.assert.strictEqual(client.closed, false)
603
+
604
+ fastify.close((err) => {
605
+ t.assert.ifError(err)
606
+
607
+ // Due to the nature of the way we reap these keep-alive connections,
608
+ // there hasn't been enough time before the server fully closed in order
609
+ // for the client to have seen the socket get destroyed. The mere fact
610
+ // that we have reached this callback is enough indication that the
611
+ // feature being tested works as designed.
612
+ t.assert.strictEqual(client.closed, false)
613
+
614
+ done()
615
+ })
616
+ })
617
+ })
618
+ })
619
+
620
+ test('preClose callback', (t, done) => {
621
+ t.plan(5)
622
+ const fastify = Fastify()
623
+ fastify.addHook('onClose', onClose)
624
+ let preCloseCalled = false
625
+ function onClose (instance, done) {
626
+ t.assert.strictEqual(preCloseCalled, true)
627
+ done()
628
+ }
629
+ fastify.addHook('preClose', preClose)
630
+
631
+ function preClose (done) {
632
+ t.assert.ok(typeof this === typeof fastify)
633
+ preCloseCalled = true
634
+ done()
635
+ }
636
+
637
+ fastify.listen({ port: 0 }, err => {
638
+ t.assert.ifError(err)
639
+
640
+ fastify.close((err) => {
641
+ t.assert.ifError(err)
642
+ t.assert.ok('close callback')
643
+ done()
644
+ })
645
+ })
646
+ })
647
+
648
+ test('preClose async', async t => {
649
+ t.plan(2)
650
+ const fastify = Fastify()
651
+ fastify.addHook('onClose', onClose)
652
+ let preCloseCalled = false
653
+ async function onClose () {
654
+ t.assert.strictEqual(preCloseCalled, true)
655
+ }
656
+ fastify.addHook('preClose', preClose)
657
+
658
+ async function preClose () {
659
+ preCloseCalled = true
660
+ t.assert.ok(typeof this === typeof fastify)
661
+ }
662
+
663
+ await fastify.listen({ port: 0 })
664
+
665
+ await fastify.close()
666
+ })
667
+
668
+ test('preClose execution order', (t, done) => {
669
+ t.plan(4)
670
+ const fastify = Fastify()
671
+ const order = []
672
+ fastify.addHook('onClose', onClose)
673
+ function onClose (instance, done) {
674
+ t.assert.deepStrictEqual(order, [1, 2, 3])
675
+ done()
676
+ }
677
+
678
+ fastify.addHook('preClose', (done) => {
679
+ setTimeout(function () {
680
+ order.push(1)
681
+ done()
682
+ }, 200)
683
+ })
684
+
685
+ fastify.addHook('preClose', async () => {
686
+ await sleep(100)
687
+ order.push(2)
688
+ })
689
+
690
+ fastify.addHook('preClose', (done) => {
691
+ setTimeout(function () {
692
+ order.push(3)
693
+ done()
694
+ }, 100)
695
+ })
696
+
697
+ fastify.listen({ port: 0 }, err => {
698
+ t.assert.ifError(err)
699
+
700
+ fastify.close((err) => {
701
+ t.assert.ifError(err)
702
+ t.assert.ok('close callback')
703
+ done()
704
+ })
705
+ })
706
+ })