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.
- package/.borp.yaml +3 -0
- package/.markdownlint-cli2.yaml +22 -0
- package/.prettierignore +1 -0
- package/GOVERNANCE.md +4 -0
- package/LICENSE +21 -0
- package/PROJECT_CHARTER.md +126 -0
- package/README.md +423 -0
- package/SECURITY.md +220 -0
- package/SPONSORS.md +24 -0
- package/build/build-error-serializer.js +35 -0
- package/build/build-validation.js +169 -0
- package/build/sync-version.js +11 -0
- package/docs/Guides/Benchmarking.md +60 -0
- package/docs/Guides/Database.md +321 -0
- package/docs/Guides/Delay-Accepting-Requests.md +608 -0
- package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
- package/docs/Guides/Ecosystem.md +726 -0
- package/docs/Guides/Fluent-Schema.md +127 -0
- package/docs/Guides/Getting-Started.md +620 -0
- package/docs/Guides/Index.md +43 -0
- package/docs/Guides/Migration-Guide-V3.md +287 -0
- package/docs/Guides/Migration-Guide-V4.md +267 -0
- package/docs/Guides/Migration-Guide-V5.md +727 -0
- package/docs/Guides/Plugins-Guide.md +520 -0
- package/docs/Guides/Prototype-Poisoning.md +383 -0
- package/docs/Guides/Recommendations.md +378 -0
- package/docs/Guides/Serverless.md +604 -0
- package/docs/Guides/Style-Guide.md +246 -0
- package/docs/Guides/Testing.md +481 -0
- package/docs/Guides/Write-Plugin.md +103 -0
- package/docs/Guides/Write-Type-Provider.md +34 -0
- package/docs/Reference/ContentTypeParser.md +271 -0
- package/docs/Reference/Decorators.md +436 -0
- package/docs/Reference/Encapsulation.md +194 -0
- package/docs/Reference/Errors.md +377 -0
- package/docs/Reference/HTTP2.md +94 -0
- package/docs/Reference/Hooks.md +958 -0
- package/docs/Reference/Index.md +73 -0
- package/docs/Reference/LTS.md +86 -0
- package/docs/Reference/Lifecycle.md +99 -0
- package/docs/Reference/Logging.md +268 -0
- package/docs/Reference/Middleware.md +79 -0
- package/docs/Reference/Plugins.md +245 -0
- package/docs/Reference/Principles.md +73 -0
- package/docs/Reference/Reply.md +1001 -0
- package/docs/Reference/Request.md +295 -0
- package/docs/Reference/Routes.md +802 -0
- package/docs/Reference/Server.md +2389 -0
- package/docs/Reference/Type-Providers.md +256 -0
- package/docs/Reference/TypeScript.md +1729 -0
- package/docs/Reference/Validation-and-Serialization.md +1130 -0
- package/docs/Reference/Warnings.md +58 -0
- package/docs/index.md +24 -0
- package/docs/resources/encapsulation_context.drawio +1 -0
- package/docs/resources/encapsulation_context.svg +3 -0
- package/eslint.config.js +35 -0
- package/examples/asyncawait.js +38 -0
- package/examples/benchmark/body.json +3 -0
- package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
- package/examples/benchmark/hooks-benchmark.js +52 -0
- package/examples/benchmark/parser.js +47 -0
- package/examples/benchmark/simple.js +30 -0
- package/examples/benchmark/webstream.js +27 -0
- package/examples/hooks.js +91 -0
- package/examples/http2.js +39 -0
- package/examples/https.js +38 -0
- package/examples/parser.js +53 -0
- package/examples/plugin.js +12 -0
- package/examples/route-prefix.js +38 -0
- package/examples/shared-schema.js +38 -0
- package/examples/simple-stream.js +20 -0
- package/examples/simple.js +32 -0
- package/examples/simple.mjs +27 -0
- package/examples/typescript-server.ts +79 -0
- package/examples/use-plugin.js +29 -0
- package/fastify.d.ts +253 -0
- package/fastify.js +985 -0
- package/integration/server.js +29 -0
- package/integration/test.sh +23 -0
- package/lib/config-validator.js +1266 -0
- package/lib/content-type-parser.js +413 -0
- package/lib/content-type.js +160 -0
- package/lib/context.js +98 -0
- package/lib/decorate.js +152 -0
- package/lib/error-handler.js +173 -0
- package/lib/error-serializer.js +134 -0
- package/lib/error-status.js +14 -0
- package/lib/errors.js +516 -0
- package/lib/four-oh-four.js +190 -0
- package/lib/handle-request.js +195 -0
- package/lib/head-route.js +45 -0
- package/lib/hooks.js +429 -0
- package/lib/initial-config-validation.js +37 -0
- package/lib/logger-factory.js +136 -0
- package/lib/logger-pino.js +68 -0
- package/lib/noop-set.js +10 -0
- package/lib/plugin-override.js +90 -0
- package/lib/plugin-utils.js +169 -0
- package/lib/promise.js +23 -0
- package/lib/reply.js +1030 -0
- package/lib/req-id-gen-factory.js +52 -0
- package/lib/request.js +391 -0
- package/lib/route.js +686 -0
- package/lib/schema-controller.js +164 -0
- package/lib/schemas.js +207 -0
- package/lib/server.js +441 -0
- package/lib/symbols.js +71 -0
- package/lib/validation.js +280 -0
- package/lib/warnings.js +57 -0
- package/lib/wrap-thenable.js +84 -0
- package/package.json +225 -0
- package/scripts/validate-ecosystem-links.js +179 -0
- package/test/404s.test.js +2035 -0
- package/test/500s.test.js +422 -0
- package/test/allow-unsafe-regex.test.js +92 -0
- package/test/als.test.js +65 -0
- package/test/async-await.test.js +705 -0
- package/test/async-dispose.test.js +20 -0
- package/test/async_hooks.test.js +52 -0
- package/test/body-limit.test.js +224 -0
- package/test/buffer.test.js +74 -0
- package/test/build/error-serializer.test.js +36 -0
- package/test/build/version.test.js +14 -0
- package/test/build-certificate.js +109 -0
- package/test/bundler/README.md +29 -0
- package/test/bundler/esbuild/bundler-test.js +32 -0
- package/test/bundler/esbuild/package.json +10 -0
- package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
- package/test/bundler/esbuild/src/index.js +9 -0
- package/test/bundler/webpack/bundler-test.js +32 -0
- package/test/bundler/webpack/package.json +11 -0
- package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
- package/test/bundler/webpack/src/index.js +9 -0
- package/test/bundler/webpack/webpack.config.js +15 -0
- package/test/case-insensitive.test.js +102 -0
- package/test/chainable.test.js +40 -0
- package/test/child-logger-factory.test.js +128 -0
- package/test/client-timeout.test.js +38 -0
- package/test/close-pipelining.test.js +78 -0
- package/test/close.test.js +706 -0
- package/test/conditional-pino.test.js +47 -0
- package/test/connection-timeout.test.js +42 -0
- package/test/constrained-routes.test.js +1138 -0
- package/test/content-length.test.js +174 -0
- package/test/content-parser.test.js +739 -0
- package/test/content-type.test.js +181 -0
- package/test/context-config.test.js +164 -0
- package/test/custom-http-server.test.js +118 -0
- package/test/custom-parser-async.test.js +59 -0
- package/test/custom-parser.0.test.js +701 -0
- package/test/custom-parser.1.test.js +266 -0
- package/test/custom-parser.2.test.js +91 -0
- package/test/custom-parser.3.test.js +208 -0
- package/test/custom-parser.4.test.js +218 -0
- package/test/custom-parser.5.test.js +130 -0
- package/test/custom-querystring-parser.test.js +129 -0
- package/test/decorator.test.js +1330 -0
- package/test/delete.test.js +344 -0
- package/test/diagnostics-channel/404.test.js +49 -0
- package/test/diagnostics-channel/async-delay-request.test.js +65 -0
- package/test/diagnostics-channel/async-request.test.js +64 -0
- package/test/diagnostics-channel/error-before-handler.test.js +35 -0
- package/test/diagnostics-channel/error-request.test.js +53 -0
- package/test/diagnostics-channel/error-status.test.js +123 -0
- package/test/diagnostics-channel/init.test.js +50 -0
- package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
- package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
- package/test/diagnostics-channel/sync-request.test.js +54 -0
- package/test/encapsulated-child-logger-factory.test.js +69 -0
- package/test/encapsulated-error-handler.test.js +237 -0
- package/test/esm/errorCodes.test.mjs +10 -0
- package/test/esm/esm.test.mjs +13 -0
- package/test/esm/index.test.js +8 -0
- package/test/esm/named-exports.mjs +14 -0
- package/test/esm/other.mjs +8 -0
- package/test/esm/plugin.mjs +8 -0
- package/test/fastify-instance.test.js +300 -0
- package/test/find-route.test.js +152 -0
- package/test/fluent-schema.test.js +209 -0
- package/test/genReqId.test.js +426 -0
- package/test/handler-context.test.js +45 -0
- package/test/handler-timeout.test.js +367 -0
- package/test/has-route.test.js +88 -0
- package/test/header-overflow.test.js +55 -0
- package/test/helper.js +496 -0
- package/test/hooks-async.test.js +1099 -0
- package/test/hooks.on-listen.test.js +1162 -0
- package/test/hooks.on-ready.test.js +421 -0
- package/test/hooks.test.js +3578 -0
- package/test/http-methods/copy.test.js +35 -0
- package/test/http-methods/custom-http-methods.test.js +114 -0
- package/test/http-methods/get.test.js +412 -0
- package/test/http-methods/head.test.js +263 -0
- package/test/http-methods/lock.test.js +108 -0
- package/test/http-methods/mkcalendar.test.js +143 -0
- package/test/http-methods/mkcol.test.js +35 -0
- package/test/http-methods/move.test.js +42 -0
- package/test/http-methods/propfind.test.js +136 -0
- package/test/http-methods/proppatch.test.js +105 -0
- package/test/http-methods/report.test.js +142 -0
- package/test/http-methods/search.test.js +233 -0
- package/test/http-methods/trace.test.js +21 -0
- package/test/http-methods/unlock.test.js +38 -0
- package/test/http2/closing.test.js +270 -0
- package/test/http2/constraint.test.js +109 -0
- package/test/http2/head.test.js +34 -0
- package/test/http2/plain.test.js +68 -0
- package/test/http2/secure-with-fallback.test.js +113 -0
- package/test/http2/secure.test.js +67 -0
- package/test/http2/unknown-http-method.test.js +34 -0
- package/test/https/custom-https-server.test.js +58 -0
- package/test/https/https.test.js +136 -0
- package/test/imports.test.js +17 -0
- package/test/inject.test.js +502 -0
- package/test/input-validation.js +335 -0
- package/test/internals/all.test.js +38 -0
- package/test/internals/content-type-parser.test.js +111 -0
- package/test/internals/context.test.js +31 -0
- package/test/internals/decorator.test.js +156 -0
- package/test/internals/errors.test.js +982 -0
- package/test/internals/handle-request.test.js +270 -0
- package/test/internals/hook-runner.test.js +449 -0
- package/test/internals/hooks.test.js +96 -0
- package/test/internals/initial-config.test.js +383 -0
- package/test/internals/logger.test.js +163 -0
- package/test/internals/plugin.test.js +170 -0
- package/test/internals/promise.test.js +63 -0
- package/test/internals/reply-serialize.test.js +714 -0
- package/test/internals/reply.test.js +1920 -0
- package/test/internals/req-id-gen-factory.test.js +133 -0
- package/test/internals/request-validate.test.js +1402 -0
- package/test/internals/request.test.js +506 -0
- package/test/internals/schema-controller-perf.test.js +40 -0
- package/test/internals/server.test.js +91 -0
- package/test/internals/validation.test.js +352 -0
- package/test/issue-4959.test.js +118 -0
- package/test/keep-alive-timeout.test.js +42 -0
- package/test/listen.1.test.js +154 -0
- package/test/listen.2.test.js +113 -0
- package/test/listen.3.test.js +83 -0
- package/test/listen.4.test.js +168 -0
- package/test/listen.5.test.js +122 -0
- package/test/logger/instantiation.test.js +341 -0
- package/test/logger/logger-test-utils.js +47 -0
- package/test/logger/logging.test.js +460 -0
- package/test/logger/options.test.js +579 -0
- package/test/logger/request.test.js +292 -0
- package/test/logger/response.test.js +183 -0
- package/test/logger/tap-parallel-not-ok +0 -0
- package/test/max-requests-per-socket.test.js +113 -0
- package/test/middleware.test.js +37 -0
- package/test/noop-set.test.js +19 -0
- package/test/nullable-validation.test.js +187 -0
- package/test/options.error-handler.test.js +5 -0
- package/test/options.test.js +5 -0
- package/test/output-validation.test.js +140 -0
- package/test/patch.error-handler.test.js +5 -0
- package/test/patch.test.js +5 -0
- package/test/plugin.1.test.js +230 -0
- package/test/plugin.2.test.js +314 -0
- package/test/plugin.3.test.js +287 -0
- package/test/plugin.4.test.js +504 -0
- package/test/plugin.helper.js +8 -0
- package/test/plugin.name.display.js +10 -0
- package/test/post-empty-body.test.js +38 -0
- package/test/pretty-print.test.js +366 -0
- package/test/promises.test.js +125 -0
- package/test/proto-poisoning.test.js +145 -0
- package/test/put.error-handler.test.js +5 -0
- package/test/put.test.js +5 -0
- package/test/register.test.js +184 -0
- package/test/reply-code.test.js +148 -0
- package/test/reply-early-hints.test.js +100 -0
- package/test/reply-error.test.js +815 -0
- package/test/reply-trailers.test.js +445 -0
- package/test/reply-web-stream-locked.test.js +37 -0
- package/test/request-error.test.js +624 -0
- package/test/request-header-host.test.js +339 -0
- package/test/request-id.test.js +118 -0
- package/test/request-timeout.test.js +53 -0
- package/test/route-hooks.test.js +635 -0
- package/test/route-prefix.test.js +904 -0
- package/test/route-shorthand.test.js +48 -0
- package/test/route.1.test.js +259 -0
- package/test/route.2.test.js +100 -0
- package/test/route.3.test.js +213 -0
- package/test/route.4.test.js +127 -0
- package/test/route.5.test.js +211 -0
- package/test/route.6.test.js +306 -0
- package/test/route.7.test.js +406 -0
- package/test/route.8.test.js +225 -0
- package/test/router-options.test.js +1108 -0
- package/test/same-shape.test.js +124 -0
- package/test/schema-examples.test.js +661 -0
- package/test/schema-feature.test.js +2198 -0
- package/test/schema-serialization.test.js +1171 -0
- package/test/schema-special-usage.test.js +1348 -0
- package/test/schema-validation.test.js +1572 -0
- package/test/scripts/validate-ecosystem-links.test.js +339 -0
- package/test/serialize-response.test.js +186 -0
- package/test/server.test.js +347 -0
- package/test/set-error-handler.test.js +69 -0
- package/test/skip-reply-send.test.js +317 -0
- package/test/stream-serializers.test.js +40 -0
- package/test/stream.1.test.js +94 -0
- package/test/stream.2.test.js +129 -0
- package/test/stream.3.test.js +198 -0
- package/test/stream.4.test.js +176 -0
- package/test/stream.5.test.js +188 -0
- package/test/sync-routes.test.js +32 -0
- package/test/throw.test.js +359 -0
- package/test/toolkit.js +63 -0
- package/test/trust-proxy.test.js +162 -0
- package/test/type-provider.test.js +22 -0
- package/test/types/content-type-parser.test-d.ts +72 -0
- package/test/types/decorate-request-reply.test-d.ts +18 -0
- package/test/types/dummy-plugin.ts +9 -0
- package/test/types/errors.test-d.ts +90 -0
- package/test/types/fastify.test-d.ts +352 -0
- package/test/types/hooks.test-d.ts +550 -0
- package/test/types/import.ts +2 -0
- package/test/types/instance.test-d.ts +588 -0
- package/test/types/logger.test-d.ts +277 -0
- package/test/types/plugin.test-d.ts +97 -0
- package/test/types/register.test-d.ts +237 -0
- package/test/types/reply.test-d.ts +254 -0
- package/test/types/request.test-d.ts +188 -0
- package/test/types/route.test-d.ts +553 -0
- package/test/types/schema.test-d.ts +135 -0
- package/test/types/serverFactory.test-d.ts +37 -0
- package/test/types/type-provider.test-d.ts +1213 -0
- package/test/types/using.test-d.ts +17 -0
- package/test/upgrade.test.js +52 -0
- package/test/url-rewriting.test.js +122 -0
- package/test/use-semicolon-delimiter.test.js +168 -0
- package/test/validation-error-handling.test.js +900 -0
- package/test/versioned-routes.test.js +603 -0
- package/test/web-api.test.js +616 -0
- package/test/wrap-thenable.test.js +30 -0
- package/types/content-type-parser.d.ts +75 -0
- package/types/context.d.ts +22 -0
- package/types/errors.d.ts +92 -0
- package/types/hooks.d.ts +875 -0
- package/types/instance.d.ts +609 -0
- package/types/logger.d.ts +107 -0
- package/types/plugin.d.ts +44 -0
- package/types/register.d.ts +42 -0
- package/types/reply.d.ts +81 -0
- package/types/request.d.ts +95 -0
- package/types/route.d.ts +199 -0
- package/types/schema.d.ts +61 -0
- package/types/server-factory.d.ts +19 -0
- package/types/type-provider.d.ts +130 -0
- 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
|