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,270 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const http2 = require('node:http2')
6
+ const { promisify } = require('node:util')
7
+ const connect = promisify(http2.connect)
8
+ const { once } = require('node:events')
9
+ const { buildCertificate } = require('../build-certificate')
10
+ const { getServerUrl } = require('../helper')
11
+ const { kHttp2ServerSessions } = require('../../lib/symbols')
12
+
13
+ test.before(buildCertificate)
14
+
15
+ const isNode24OrGreater = Number(process.versions.node.split('.')[0]) >= 24
16
+
17
+ test('http/2 request while fastify closing Node <24', { skip: isNode24OrGreater }, (t, done) => {
18
+ const fastify = Fastify({
19
+ http2: true
20
+ })
21
+ t.assert.ok('http2 successfully loaded')
22
+
23
+ fastify.get('/', () => Promise.resolve({}))
24
+
25
+ t.after(() => { fastify.close() })
26
+ fastify.listen({ port: 0 }, err => {
27
+ t.assert.ifError(err)
28
+
29
+ const url = getServerUrl(fastify)
30
+ const session = http2.connect(url, function () {
31
+ this.request({
32
+ ':method': 'GET',
33
+ ':path': '/'
34
+ }).on('response', headers => {
35
+ t.assert.strictEqual(headers[':status'], 503)
36
+ done()
37
+ this.destroy()
38
+ }).on('error', () => {
39
+ // Nothing to do here,
40
+ // we are not interested in this error that might
41
+ // happen or not
42
+ })
43
+ session.on('error', () => {
44
+ // Nothing to do here,
45
+ // we are not interested in this error that might
46
+ // happen or not
47
+ done()
48
+ })
49
+ fastify.close()
50
+ })
51
+ })
52
+ })
53
+
54
+ test('http/2 request while fastify closing Node >=24', { skip: !isNode24OrGreater }, (t, done) => {
55
+ const fastify = Fastify({
56
+ http2: true
57
+ })
58
+ t.assert.ok('http2 successfully loaded')
59
+
60
+ fastify.get('/', () => Promise.resolve({}))
61
+
62
+ t.after(() => { fastify.close() })
63
+ fastify.listen({ port: 0 }, err => {
64
+ t.assert.ifError(err)
65
+
66
+ const url = getServerUrl(fastify)
67
+ const session = http2.connect(url, function () {
68
+ session.on('error', () => {
69
+ // Nothing to do here,
70
+ // we are not interested in this error that might
71
+ // happen or not
72
+ })
73
+ session.on('close', () => {
74
+ done()
75
+ })
76
+ fastify.close()
77
+ })
78
+ })
79
+ })
80
+
81
+ test('http/2 request while fastify closing - return503OnClosing: false', { skip: isNode24OrGreater }, (t, done) => {
82
+ const fastify = Fastify({
83
+ http2: true,
84
+ return503OnClosing: false
85
+ })
86
+
87
+ t.after(() => { fastify.close() })
88
+
89
+ fastify.get('/', () => Promise.resolve({}))
90
+
91
+ fastify.listen({ port: 0 }, err => {
92
+ t.assert.ifError(err)
93
+ const url = getServerUrl(fastify)
94
+ const session = http2.connect(url, function () {
95
+ this.request({
96
+ ':method': 'GET',
97
+ ':path': '/'
98
+ }).on('response', headers => {
99
+ t.assert.strictEqual(headers[':status'], 200)
100
+ done()
101
+ this.destroy()
102
+ }).on('error', () => {
103
+ // Nothing to do here,
104
+ // we are not interested in this error that might
105
+ // happen or not
106
+ })
107
+ fastify.close()
108
+ })
109
+ session.on('error', () => {
110
+ // Nothing to do here,
111
+ // we are not interested in this error that might
112
+ // happen or not
113
+ done()
114
+ })
115
+ })
116
+ })
117
+
118
+ test('http/2 closes successfully with async await', async t => {
119
+ const fastify = Fastify({
120
+ http2SessionTimeout: 100,
121
+ http2: true
122
+ })
123
+
124
+ await fastify.listen({ port: 0 })
125
+
126
+ const url = getServerUrl(fastify)
127
+ const session = await connect(url)
128
+ // An error might or might not happen, as it's OS dependent.
129
+ session.on('error', () => {})
130
+ await fastify.close()
131
+ })
132
+
133
+ test('https/2 closes successfully with async await', async t => {
134
+ const fastify = Fastify({
135
+ http2SessionTimeout: 100,
136
+ http2: true,
137
+ https: {
138
+ key: global.context.key,
139
+ cert: global.context.cert
140
+ }
141
+ })
142
+
143
+ await fastify.listen({ port: 0 })
144
+
145
+ const url = getServerUrl(fastify)
146
+ const session = await connect(url)
147
+ // An error might or might not happen, as it's OS dependent.
148
+ session.on('error', () => {})
149
+ await fastify.close()
150
+ })
151
+
152
+ test('http/2 server side session emits a timeout event', async t => {
153
+ let _resolve
154
+ const p = new Promise((resolve) => { _resolve = resolve })
155
+
156
+ const fastify = Fastify({
157
+ http2SessionTimeout: 100,
158
+ http2: true
159
+ })
160
+
161
+ fastify.get('/', async (req) => {
162
+ req.raw.stream.session.on('timeout', () => _resolve())
163
+ return {}
164
+ })
165
+
166
+ await fastify.listen({ port: 0 })
167
+
168
+ const url = getServerUrl(fastify)
169
+ const session = await connect(url)
170
+ const req = session.request({
171
+ ':method': 'GET',
172
+ ':path': '/'
173
+ }).end()
174
+
175
+ const [headers] = await once(req, 'response')
176
+ t.assert.strictEqual(headers[':status'], 200)
177
+ req.resume()
178
+
179
+ // An error might or might not happen, as it's OS dependent.
180
+ session.on('error', () => {})
181
+ await p
182
+ await fastify.close()
183
+ })
184
+
185
+ test('http/2 sessions closed after closing server', async t => {
186
+ t.plan(1)
187
+ const fastify = Fastify({
188
+ http2: true,
189
+ http2SessionTimeout: 100
190
+ })
191
+ await fastify.listen()
192
+ const url = getServerUrl(fastify)
193
+ const waitSessionConnect = once(fastify.server, 'session')
194
+ const session = http2.connect(url)
195
+ await once(session, 'connect')
196
+ await waitSessionConnect
197
+ const waitSessionClosed = once(session, 'close')
198
+ await fastify.close()
199
+ await waitSessionClosed
200
+ t.assert.strictEqual(session.closed, true)
201
+ })
202
+
203
+ test('http/2 sessions should be closed when setting forceClosedConnections to true', async t => {
204
+ t.plan(2)
205
+ const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
206
+ fastify.get('/', () => 'hello world')
207
+ await fastify.listen()
208
+ const client = await connect(getServerUrl(fastify))
209
+ const req = client.request({
210
+ [http2.HTTP2_HEADER_PATH]: '/',
211
+ [http2.HTTP2_HEADER_METHOD]: 'GET'
212
+ })
213
+ await once(req, 'response')
214
+ fastify.close()
215
+ const r2 = client.request({
216
+ [http2.HTTP2_HEADER_PATH]: '/',
217
+ [http2.TTP2_HEADER_METHOD]: 'GET'
218
+ })
219
+ r2.on('error', (err) => {
220
+ t.assert.strictEqual(err.toString(), 'Error [ERR_HTTP2_STREAM_ERROR]: Stream closed with error code NGHTTP2_REFUSED_STREAM')
221
+ })
222
+ await once(r2, 'error')
223
+ r2.end()
224
+ t.assert.strictEqual(client.closed, true)
225
+ client.destroy()
226
+ })
227
+
228
+ test('http/2 sessions should be removed from server[kHttp2ServerSessions] Set on goaway', async t => {
229
+ t.plan(2)
230
+ const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
231
+ await fastify.listen()
232
+ const waitSession = once(fastify.server, 'session')
233
+ const client = http2.connect(getServerUrl(fastify))
234
+ const [session] = await waitSession
235
+ const waitGoaway = once(session, 'goaway')
236
+ t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
237
+ client.goaway()
238
+ await waitGoaway
239
+ t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 0)
240
+ client.destroy()
241
+ await fastify.close()
242
+ })
243
+
244
+ test('http/2 sessions should be removed from server[kHttp2ServerSessions] Set on frameError', async t => {
245
+ t.plan(2)
246
+ const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
247
+ await fastify.listen()
248
+ const waitSession = once(fastify.server, 'session')
249
+ const client = http2.connect(getServerUrl(fastify))
250
+ const [session] = await waitSession
251
+ t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
252
+ session.emit('frameError', 0, 0, 0)
253
+ t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 0)
254
+ client.destroy()
255
+ await fastify.close()
256
+ })
257
+
258
+ test('http/2 sessions should not be removed from server[kHttp2ServerSessions] from Set if stream id passed on frameError', async t => {
259
+ t.plan(2)
260
+ const fastify = Fastify({ http2: true, http2SessionTimeout: 100, forceCloseConnections: true })
261
+ await fastify.listen()
262
+ const waitSession = once(fastify.server, 'session')
263
+ const client = http2.connect(getServerUrl(fastify))
264
+ const [session] = await waitSession
265
+ t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
266
+ session.emit('frameError', 0, 0, 1)
267
+ t.assert.strictEqual(fastify.server[kHttp2ServerSessions].size, 1)
268
+ client.destroy()
269
+ await fastify.close()
270
+ })
@@ -0,0 +1,109 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const h2url = require('h2url')
6
+
7
+ const alpha = { res: 'alpha' }
8
+ const beta = { res: 'beta' }
9
+
10
+ const { buildCertificate } = require('../build-certificate')
11
+ test.before(buildCertificate)
12
+
13
+ test('A route supports host constraints under http2 protocol and secure connection', async (t) => {
14
+ t.plan(5)
15
+
16
+ let fastify
17
+ try {
18
+ fastify = Fastify({
19
+ http2: true,
20
+ https: {
21
+ key: global.context.key,
22
+ cert: global.context.cert
23
+ }
24
+ })
25
+ t.assert.ok(true, 'Key/cert successfully loaded')
26
+ } catch (e) {
27
+ t.assert.fail('Key/cert loading failed')
28
+ }
29
+
30
+ const constrain = 'fastify.dev'
31
+
32
+ fastify.route({
33
+ method: 'GET',
34
+ url: '/',
35
+ handler: function (_, reply) {
36
+ reply.code(200).send(alpha)
37
+ }
38
+ })
39
+ fastify.route({
40
+ method: 'GET',
41
+ url: '/beta',
42
+ constraints: { host: constrain },
43
+ handler: function (_, reply) {
44
+ reply.code(200).send(beta)
45
+ }
46
+ })
47
+ fastify.route({
48
+ method: 'GET',
49
+ url: '/hostname_port',
50
+ constraints: { host: constrain },
51
+ handler: function (req, reply) {
52
+ reply.code(200).send({ ...beta, hostname: req.hostname })
53
+ }
54
+ })
55
+ t.after(() => { fastify.close() })
56
+
57
+ await fastify.listen({ port: 0 })
58
+
59
+ await t.test('https get request - no constrain', async (t) => {
60
+ t.plan(3)
61
+
62
+ const url = `https://localhost:${fastify.server.address().port}`
63
+ const res = await h2url.concat({ url })
64
+
65
+ t.assert.strictEqual(res.headers[':status'], 200)
66
+ t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(alpha).length)
67
+ t.assert.deepStrictEqual(JSON.parse(res.body), alpha)
68
+ })
69
+
70
+ await t.test('https get request - constrain', async (t) => {
71
+ t.plan(3)
72
+
73
+ const url = `https://localhost:${fastify.server.address().port}/beta`
74
+ const res = await h2url.concat({
75
+ url,
76
+ headers: {
77
+ ':authority': constrain
78
+ }
79
+ })
80
+
81
+ t.assert.strictEqual(res.headers[':status'], 200)
82
+ t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(beta).length)
83
+ t.assert.deepStrictEqual(JSON.parse(res.body), beta)
84
+ })
85
+
86
+ await t.test('https get request - constrain - not found', async (t) => {
87
+ t.plan(1)
88
+
89
+ const url = `https://localhost:${fastify.server.address().port}/beta`
90
+ const res = await h2url.concat({
91
+ url
92
+ })
93
+
94
+ t.assert.strictEqual(res.headers[':status'], 404)
95
+ })
96
+ await t.test('https get request - constrain - verify hostname and port from request', async (t) => {
97
+ t.plan(1)
98
+
99
+ const url = `https://localhost:${fastify.server.address().port}/hostname_port`
100
+ const res = await h2url.concat({
101
+ url,
102
+ headers: {
103
+ ':authority': constrain
104
+ }
105
+ })
106
+ const body = JSON.parse(res.body)
107
+ t.assert.strictEqual(body.hostname, constrain)
108
+ })
109
+ })
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const h2url = require('h2url')
6
+ const msg = { hello: 'world' }
7
+
8
+ test('http2 HEAD test', async (t) => {
9
+ let fastify
10
+ try {
11
+ fastify = Fastify({
12
+ http2: true
13
+ })
14
+ t.assert.ok(true, 'http2 successfully loaded')
15
+ } catch (e) {
16
+ t.assert.fail('http2 loading failed')
17
+ }
18
+
19
+ fastify.all('/', function (req, reply) {
20
+ reply.code(200).send(msg)
21
+ })
22
+ t.after(() => { fastify.close() })
23
+
24
+ await fastify.listen({ port: 0 })
25
+
26
+ await t.test('http HEAD request', async (t) => {
27
+ t.plan(1)
28
+
29
+ const url = `http://localhost:${fastify.server.address().port}`
30
+ const res = await h2url.concat({ url, method: 'HEAD' })
31
+
32
+ t.assert.strictEqual(res.headers[':status'], 200)
33
+ })
34
+ })
@@ -0,0 +1,68 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const h2url = require('h2url')
6
+ const msg = { hello: 'world' }
7
+
8
+ test('http2 plain test', async t => {
9
+ let fastify
10
+ try {
11
+ fastify = Fastify({
12
+ http2: true
13
+ })
14
+ t.assert.ok(true, 'http2 successfully loaded')
15
+ } catch (e) {
16
+ t.assert.fail('http2 loading failed')
17
+ }
18
+
19
+ fastify.get('/', function (req, reply) {
20
+ reply.code(200).send(msg)
21
+ })
22
+
23
+ fastify.get('/host', function (req, reply) {
24
+ reply.code(200).send(req.host)
25
+ })
26
+
27
+ fastify.get('/hostname_port', function (req, reply) {
28
+ reply.code(200).send({ hostname: req.hostname, port: req.port })
29
+ })
30
+
31
+ t.after(() => { fastify.close() })
32
+
33
+ await fastify.listen({ port: 0 })
34
+
35
+ await t.test('http get request', async (t) => {
36
+ t.plan(3)
37
+
38
+ const url = `http://localhost:${fastify.server.address().port}`
39
+ const res = await h2url.concat({ url })
40
+
41
+ t.assert.strictEqual(res.headers[':status'], 200)
42
+ t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
43
+
44
+ t.assert.deepStrictEqual(JSON.parse(res.body), msg)
45
+ })
46
+
47
+ await t.test('http host', async (t) => {
48
+ t.plan(1)
49
+
50
+ const host = `localhost:${fastify.server.address().port}`
51
+
52
+ const url = `http://${host}/host`
53
+ const res = await h2url.concat({ url })
54
+
55
+ t.assert.strictEqual(res.body, host)
56
+ })
57
+ await t.test('http hostname and port', async (t) => {
58
+ t.plan(2)
59
+
60
+ const host = `localhost:${fastify.server.address().port}`
61
+
62
+ const url = `http://${host}/hostname_port`
63
+ const res = await h2url.concat({ url })
64
+
65
+ t.assert.strictEqual(JSON.parse(res.body).hostname, host.split(':')[0])
66
+ t.assert.strictEqual(JSON.parse(res.body).port, parseInt(host.split(':')[1]))
67
+ })
68
+ })
@@ -0,0 +1,113 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const h2url = require('h2url')
6
+ const msg = { hello: 'world' }
7
+
8
+ const { buildCertificate } = require('../build-certificate')
9
+ const { Agent } = require('undici')
10
+ test.before(buildCertificate)
11
+
12
+ test('secure with fallback', async (t) => {
13
+ t.plan(6)
14
+
15
+ let fastify
16
+ try {
17
+ fastify = Fastify({
18
+ http2: true,
19
+ https: {
20
+ allowHTTP1: true,
21
+ key: global.context.key,
22
+ cert: global.context.cert
23
+ }
24
+ })
25
+ t.assert.ok(true, 'Key/cert successfully loaded')
26
+ } catch (e) {
27
+ t.assert.fail('Key/cert loading failed')
28
+ }
29
+
30
+ fastify.get('/', function (req, reply) {
31
+ reply.code(200).send(msg)
32
+ })
33
+
34
+ fastify.post('/', function (req, reply) {
35
+ reply.code(200).send(req.body)
36
+ })
37
+
38
+ fastify.get('/error', async function (req, reply) {
39
+ throw new Error('kaboom')
40
+ })
41
+
42
+ t.after(() => fastify.close())
43
+
44
+ const fastifyServer = await fastify.listen({ port: 0 })
45
+
46
+ await t.test('https get error', async (t) => {
47
+ t.plan(1)
48
+
49
+ const url = `${fastifyServer}/error`
50
+ const res = await h2url.concat({ url })
51
+
52
+ t.assert.strictEqual(res.headers[':status'], 500)
53
+ })
54
+
55
+ await t.test('https post', async (t) => {
56
+ t.plan(2)
57
+
58
+ const res = await h2url.concat({
59
+ url: fastifyServer,
60
+ method: 'POST',
61
+ body: JSON.stringify({ hello: 'http2' }),
62
+ headers: {
63
+ 'content-type': 'application/json'
64
+ }
65
+ })
66
+
67
+ t.assert.strictEqual(res.headers[':status'], 200)
68
+ t.assert.deepStrictEqual(JSON.parse(res.body), { hello: 'http2' })
69
+ })
70
+
71
+ await t.test('https get request', async (t) => {
72
+ t.plan(3)
73
+
74
+ const res = await h2url.concat({ url: fastifyServer })
75
+
76
+ t.assert.strictEqual(res.headers[':status'], 200)
77
+ t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
78
+ t.assert.deepStrictEqual(JSON.parse(res.body), msg)
79
+ })
80
+
81
+ await t.test('http1 get request', async t => {
82
+ t.plan(4)
83
+
84
+ const result = await fetch(fastifyServer, {
85
+ dispatcher: new Agent({
86
+ connect: {
87
+ rejectUnauthorized: false
88
+ }
89
+ })
90
+ })
91
+
92
+ const body = await result.text()
93
+ t.assert.ok(result.ok)
94
+ t.assert.strictEqual(result.status, 200)
95
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
96
+ t.assert.deepStrictEqual(JSON.parse(body), msg)
97
+ })
98
+
99
+ await t.test('http1 get error', async t => {
100
+ t.plan(2)
101
+
102
+ const result = await fetch(`${fastifyServer}/error`, {
103
+ dispatcher: new Agent({
104
+ connect: {
105
+ rejectUnauthorized: false
106
+ }
107
+ })
108
+ })
109
+
110
+ t.assert.ok(!result.ok)
111
+ t.assert.strictEqual(result.status, 500)
112
+ })
113
+ })
@@ -0,0 +1,67 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const h2url = require('h2url')
6
+ const msg = { hello: 'world' }
7
+
8
+ const { buildCertificate } = require('../build-certificate')
9
+ test.before(buildCertificate)
10
+
11
+ test('secure', async (t) => {
12
+ t.plan(4)
13
+
14
+ let fastify
15
+ try {
16
+ fastify = Fastify({
17
+ http2: true,
18
+ https: {
19
+ key: global.context.key,
20
+ cert: global.context.cert
21
+ }
22
+ })
23
+ t.assert.ok(true, 'Key/cert successfully loaded')
24
+ } catch (e) {
25
+ t.assert.fail('Key/cert loading failed')
26
+ }
27
+
28
+ fastify.get('/', function (req, reply) {
29
+ reply.code(200).send(msg)
30
+ })
31
+ fastify.get('/proto', function (req, reply) {
32
+ reply.code(200).send({ proto: req.protocol })
33
+ })
34
+ fastify.get('/hostname_port', function (req, reply) {
35
+ reply.code(200).send({ hostname: req.hostname, port: req.port })
36
+ })
37
+
38
+ t.after(() => { fastify.close() })
39
+ await fastify.listen({ port: 0 })
40
+
41
+ await t.test('https get request', async (t) => {
42
+ t.plan(3)
43
+
44
+ const url = `https://localhost:${fastify.server.address().port}`
45
+ const res = await h2url.concat({ url })
46
+
47
+ t.assert.strictEqual(res.headers[':status'], 200)
48
+ t.assert.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
49
+ t.assert.deepStrictEqual(JSON.parse(res.body), msg)
50
+ })
51
+
52
+ await t.test('https get request without trust proxy - protocol', async (t) => {
53
+ t.plan(2)
54
+
55
+ const url = `https://localhost:${fastify.server.address().port}/proto`
56
+ t.assert.deepStrictEqual(JSON.parse((await h2url.concat({ url })).body), { proto: 'https' })
57
+ t.assert.deepStrictEqual(JSON.parse((await h2url.concat({ url, headers: { 'X-Forwarded-Proto': 'lorem' } })).body), { proto: 'https' })
58
+ })
59
+ await t.test('https get request - test hostname and port', async (t) => {
60
+ t.plan(2)
61
+
62
+ const url = `https://localhost:${fastify.server.address().port}/hostname_port`
63
+ const parsedbody = JSON.parse((await h2url.concat({ url })).body)
64
+ t.assert.strictEqual(parsedbody.hostname, 'localhost')
65
+ t.assert.strictEqual(parsedbody.port, fastify.server.address().port)
66
+ })
67
+ })
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const h2url = require('h2url')
6
+ const msg = { hello: 'world' }
7
+
8
+ test('http2 unknown http method', async t => {
9
+ const fastify = Fastify({
10
+ http2: true
11
+ })
12
+
13
+ fastify.get('/', function (req, reply) {
14
+ reply.code(200).send(msg)
15
+ })
16
+
17
+ t.after(() => { fastify.close() })
18
+ await fastify.listen({ port: 0 })
19
+
20
+ await t.test('http UNKNOWN_METHOD request', async (t) => {
21
+ t.plan(2)
22
+
23
+ const url = `http://localhost:${fastify.server.address().port}`
24
+ const res = await h2url.concat({ url, method: 'UNKNOWN_METHOD' })
25
+
26
+ t.assert.strictEqual(res.headers[':status'], 404)
27
+ t.assert.deepStrictEqual(JSON.parse(res.body), {
28
+ statusCode: 404,
29
+ code: 'FST_ERR_NOT_FOUND',
30
+ error: 'Not Found',
31
+ message: 'Not Found'
32
+ })
33
+ })
34
+ })