summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock507
-rw-r--r--Cargo.toml31
-rw-r--r--src/certs.rs115
-rw-r--r--src/config.rs246
-rw-r--r--src/deser_helpers.rs87
-rw-r--r--src/generation.rs71
-rw-r--r--src/main.rs179
-rw-r--r--src/modules/accesslog.rs18
-rw-r--r--src/modules/auth/basic.rs21
-rw-r--r--src/modules/auth/cookie.rs32
-rw-r--r--src/modules/auth/openid.rs26
-rw-r--r--src/modules/cache.rs21
-rw-r--r--src/modules/cgi.rs9
-rw-r--r--src/modules/debug.rs7
-rw-r--r--src/modules/delay.rs22
-rw-r--r--src/modules/error.rs8
-rw-r--r--src/modules/fallback.rs19
-rw-r--r--src/modules/file.rs8
-rw-r--r--src/modules/files.rs12
-rw-r--r--src/modules/headers.rs21
-rw-r--r--src/modules/hosts.rs23
-rw-r--r--src/modules/inspect.rs19
-rw-r--r--src/modules/limits.rs21
-rw-r--r--src/modules/loadbalance.rs23
-rw-r--r--src/modules/log.rs18
-rw-r--r--src/modules/mod.rs7
-rw-r--r--src/modules/paths.rs17
-rw-r--r--src/modules/proxy.rs8
-rw-r--r--src/modules/ratelimit.rs17
-rw-r--r--src/modules/redirect.rs6
-rw-r--r--src/modules/semaphore.rs19
-rw-r--r--src/modules/switch.rs23
-rw-r--r--src/modules/upgrade_insecure.rs21
33 files changed, 935 insertions, 747 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 086c5fa..ab5b6df 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,21 +3,6 @@
version = 4
[[package]]
-name = "addr2line"
-version = "0.24.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-
-[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -55,9 +40,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
-version = "1.1.3"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
@@ -114,15 +99,15 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.99"
+version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "argon2"
-version = "0.6.0-rc.1"
+version = "0.6.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d911686206fdd816a61ed5226535997149b0fc7726e37fee46f407c9ff82ed87"
+checksum = "e1a213fe583d472f454ae47407edc78848bebd950493528b1d4f7327a7dc335f"
dependencies = [
"base64ct",
"blake2",
@@ -188,21 +173,6 @@ dependencies = [
]
[[package]]
-name = "backtrace"
-version = "0.3.75"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
-dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
- "windows-targets 0.52.6",
-]
-
-[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -236,17 +206,17 @@ dependencies = [
[[package]]
name = "bitflags"
-version = "2.9.4"
+version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
+checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "blake2"
-version = "0.11.0-rc.2"
+version = "0.11.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1edac47499deef695d9431bf241c75ea29f4cf3dcb78d39e19b31515e4ad3b08"
+checksum = "679065eb2b85a078ace42411e657bef3a6afe93a40d1b9cb04e39ca303cc3f36"
dependencies = [
- "digest 0.11.0-rc.1",
+ "digest 0.11.0-rc.4",
]
[[package]]
@@ -281,9 +251,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
-version = "1.2.37"
+version = "1.2.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44"
+checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -308,9 +278,9 @@ dependencies = [
[[package]]
name = "cfg-if"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
@@ -340,6 +310,46 @@ dependencies = [
]
[[package]]
+name = "clap"
+version = "4.5.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
+
+[[package]]
name = "cmake"
version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -408,9 +418,9 @@ dependencies = [
[[package]]
name = "crypto-common"
-version = "0.2.0-rc.4"
+version = "0.2.0-rc.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a8235645834fbc6832939736ce2f2d08192652269e11010a6240f61b908a1c6"
+checksum = "919bd05924682a5480aec713596b9e2aabed3a0a6022fab6847f85a99e5f190a"
dependencies = [
"hybrid-array",
]
@@ -436,13 +446,13 @@ dependencies = [
[[package]]
name = "digest"
-version = "0.11.0-rc.1"
+version = "0.11.0-rc.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a4aae35a0fcbe22ff1be50fe96df72002d5a4a6fb4aae9193cf2da0daa36da2"
+checksum = "ea390c940e465846d64775e55e3115d5dc934acb953de6f6e6360bc232fe2bf7"
dependencies = [
"block-buffer 0.11.0-rc.5",
"const-oid",
- "crypto-common 0.2.0-rc.4",
+ "crypto-common 0.2.0-rc.5",
"subtle",
]
@@ -493,7 +503,7 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18c1ddb9231d8554c2d6bdf4cfaabf0c59251658c68b6c95cd52dd0c513a912a"
dependencies = [
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"libm",
"rand",
"siphasher",
@@ -507,9 +517,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
-version = "0.1.1"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
+checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "fnv"
@@ -631,31 +641,25 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
- "wasi 0.11.1+wasi-snapshot-preview1",
+ "wasi",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
-version = "0.3.3"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
- "wasi 0.14.5+wasi-0.2.4",
+ "wasip2",
"wasm-bindgen",
]
[[package]]
-name = "gimli"
-version = "0.31.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
-
-[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -671,6 +675,7 @@ dependencies = [
"async-stream",
"base64",
"bytes",
+ "clap",
"env_logger",
"futures",
"futures-util",
@@ -702,7 +707,7 @@ dependencies = [
"serde_json",
"serde_yml",
"sha2",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"tokio",
"tokio-rustls",
"tokio-util",
@@ -759,9 +764,9 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.15.5"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
name = "headers"
@@ -788,6 +793,12 @@ dependencies = [
]
[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -859,9 +870,9 @@ dependencies = [
[[package]]
name = "hyper"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
+checksum = "1744436df46f0bde35af3eda22aeaba453aada65d8f1c171cd8a5f59030bd69f"
dependencies = [
"atomic-waker",
"bytes",
@@ -882,9 +893,9 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.16"
+version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
+checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
dependencies = [
"bytes",
"futures-core",
@@ -897,9 +908,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.11.1"
+version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921"
+checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown",
@@ -937,17 +948,6 @@ dependencies = [
]
[[package]]
-name = "io-uring"
-version = "0.7.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
-dependencies = [
- "bitflags",
- "cfg-if",
- "libc",
-]
-
-[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1020,15 +1020,15 @@ version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"libc",
]
[[package]]
name = "js-sys"
-version = "0.3.78"
+version = "0.3.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738"
+checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -1036,18 +1036,18 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.175"
+version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
+checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libloading"
-version = "0.8.8"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
+checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
- "windows-targets 0.53.3",
+ "windows-link",
]
[[package]]
@@ -1068,11 +1068,10 @@ dependencies = [
[[package]]
name = "lock_api"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
- "autocfg",
"scopeguard",
]
@@ -1110,9 +1109,9 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.7.5"
+version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "mime"
@@ -1137,23 +1136,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
-name = "miniz_oxide"
-version = "0.8.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
-dependencies = [
- "adler2",
-]
-
-[[package]]
name = "mio"
-version = "1.0.4"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
+checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
dependencies = [
"libc",
- "wasi 0.11.1+wasi-snapshot-preview1",
- "windows-sys 0.59.0",
+ "wasi",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1177,15 +1167,6 @@ dependencies = [
]
[[package]]
-name = "object"
-version = "0.36.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1211,9 +1192,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "parking_lot"
-version = "0.12.4"
+version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -1221,25 +1202,25 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.11"
+version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
- "windows-targets 0.52.6",
+ "windows-link",
]
[[package]]
name = "password-hash"
-version = "0.6.0-rc.1"
+version = "0.6.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ee14c44aa1c04c22c4d4532c4fa2cdd5b6d31c2514a5898530d889fc2fc2737"
+checksum = "a7d47a2d1aee5a339aa6c740d9128211a8a3d2bdf06a13e01b3f8a0b5c49b9db"
dependencies = [
"base64ct",
- "rand_core 0.9.3",
+ "rand_core 0.10.0-rc-2",
"subtle",
]
@@ -1329,9 +1310,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.101"
+version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
+checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
@@ -1351,7 +1332,7 @@ dependencies = [
"rustc-hash",
"rustls",
"socket2",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"tokio",
"tracing",
"web-time",
@@ -1365,7 +1346,7 @@ checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
dependencies = [
"bytes",
"fastbloom",
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"lru-slab",
"rand",
"ring",
@@ -1374,7 +1355,7 @@ dependencies = [
"rustls-pki-types",
"rustls-platform-verifier",
"slab",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"tinyvec",
"tracing",
"web-time",
@@ -1396,9 +1377,9 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.40"
+version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
@@ -1444,10 +1425,16 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
]
[[package]]
+name = "rand_core"
+version = "0.10.0-rc-2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "104a23e4e8b77312a823b6b5613edbac78397e2f34320bc7ac4277013ec4478e"
+
+[[package]]
name = "rand_distr"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1459,18 +1446,18 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.17"
+version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
-version = "1.11.2"
+version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
+checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
@@ -1480,9 +1467,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.10"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
@@ -1491,9 +1478,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.8.6"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "ring"
@@ -1510,12 +1497,6 @@ dependencies = [
]
[[package]]
-name = "rustc-demangle"
-version = "0.1.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
-
-[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1523,9 +1504,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustls"
-version = "0.23.31"
+version = "0.23.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
+checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
dependencies = [
"aws-lc-rs",
"log",
@@ -1560,9 +1541,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
-version = "1.12.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
+checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
dependencies = [
"web-time",
"zeroize",
@@ -1597,9 +1578,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
-version = "0.103.5"
+version = "0.103.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8"
+checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
dependencies = [
"aws-lc-rs",
"ring",
@@ -1608,6 +1589,12 @@ dependencies = [
]
[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1628,7 +1615,7 @@ version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
dependencies = [
- "windows-sys 0.61.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1662,9 +1649,9 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.220"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ceecad4c782e936ac90ecfd6b56532322e3262b14320abf30ce89a92ffdbfe22"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
@@ -1672,18 +1659,18 @@ dependencies = [
[[package]]
name = "serde_core"
-version = "1.0.220"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddba47394f3b862d6ff6efdbd26ca4673e3566a307880a0ffb98f274bbe0ec32"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.220"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60e1f3b1761e96def5ec6d04a6e7421c0404fa3cf5c0155f1e2848fae3d8cc08"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -1692,14 +1679,15 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.143"
+version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
+checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
+ "serde_core",
]
[[package]]
@@ -1730,13 +1718,13 @@ dependencies = [
[[package]]
name = "sha2"
-version = "0.11.0-rc.2"
+version = "0.11.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924"
+checksum = "19d43dc0354d88b791216bb5c1bfbb60c0814460cc653ae0ebd71f286d0bd927"
dependencies = [
"cfg-if",
"cpufeatures",
- "digest 0.11.0-rc.1",
+ "digest 0.11.0-rc.4",
]
[[package]]
@@ -1774,15 +1762,21 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
-version = "0.6.0"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
+checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1790,9 +1784,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
-version = "2.0.106"
+version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
+checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [
"proc-macro2",
"quote",
@@ -1810,11 +1804,11 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.16"
+version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
+checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
- "thiserror-impl 2.0.16",
+ "thiserror-impl 2.0.17",
]
[[package]]
@@ -1830,9 +1824,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
-version = "2.0.16"
+version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
+checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
@@ -1856,29 +1850,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.47.1"
+version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
+checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
dependencies = [
- "backtrace",
"bytes",
- "io-uring",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
- "slab",
"socket2",
"tokio-macros",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
-version = "2.5.0"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
+checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
@@ -1887,9 +1878,9 @@ dependencies = [
[[package]]
name = "tokio-rustls"
-version = "0.26.2"
+version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
dependencies = [
"rustls",
"tokio",
@@ -1897,9 +1888,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.16"
+version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
+checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
dependencies = [
"bytes",
"futures-core",
@@ -1936,9 +1927,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "typenum"
-version = "1.18.0"
+version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicase"
@@ -1948,9 +1939,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
-version = "1.0.19"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "universal-hash"
@@ -2016,54 +2007,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
-name = "wasi"
-version = "0.14.5+wasi-0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4"
-dependencies = [
- "wasip2",
-]
-
-[[package]]
name = "wasip2"
-version = "1.0.0+wasi-0.2.4"
+version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24"
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
-version = "0.2.101"
+version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
+checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
dependencies = [
"cfg-if",
"once_cell",
+ "rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.101"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
-dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.101"
+version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
+checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2071,22 +2040,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.101"
+version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
+checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
dependencies = [
+ "bumpalo",
"proc-macro2",
"quote",
"syn",
- "wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.101"
+version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
+checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
dependencies = [
"unicode-ident",
]
@@ -2112,9 +2081,9 @@ dependencies = [
[[package]]
name = "webpki-roots"
-version = "1.0.2"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
+checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
dependencies = [
"rustls-pki-types",
]
@@ -2125,20 +2094,14 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys 0.61.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "windows-link"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
-
-[[package]]
-name = "windows-link"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
@@ -2173,16 +2136,16 @@ version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
- "windows-targets 0.53.3",
+ "windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
-version = "0.61.0"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
- "windows-link 0.2.0",
+ "windows-link",
]
[[package]]
@@ -2218,19 +2181,19 @@ dependencies = [
[[package]]
name = "windows-targets"
-version = "0.53.3"
+version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
- "windows-link 0.1.3",
- "windows_aarch64_gnullvm 0.53.0",
- "windows_aarch64_msvc 0.53.0",
- "windows_i686_gnu 0.53.0",
- "windows_i686_gnullvm 0.53.0",
- "windows_i686_msvc 0.53.0",
- "windows_x86_64_gnu 0.53.0",
- "windows_x86_64_gnullvm 0.53.0",
- "windows_x86_64_msvc 0.53.0",
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
]
[[package]]
@@ -2247,9 +2210,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.53.0"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
@@ -2265,9 +2228,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.53.0"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
@@ -2283,9 +2246,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
-version = "0.53.0"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
@@ -2295,9 +2258,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
-version = "0.53.0"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
@@ -2313,9 +2276,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
-version = "0.53.0"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
@@ -2331,9 +2294,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.53.0"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -2349,9 +2312,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.53.0"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
@@ -2367,15 +2330,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.53.0"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "wit-bindgen"
-version = "0.45.1"
+version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36"
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "zerocopy"
@@ -2399,6 +2362,6 @@ dependencies = [
[[package]]
name = "zeroize"
-version = "1.8.1"
+version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
diff --git a/Cargo.toml b/Cargo.toml
index d1fe787..5a8ebaf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,11 +5,11 @@ edition = "2021"
[dependencies]
# HTTP
-hyper = { version = "1.7.0", features = ["server", "client", "http1", "http2"] }
+hyper = { version = "1.8.0", features = ["server", "client", "http1", "http2"] }
quinn = "0.11.9"
h3 = "0.0.8"
h3-quinn = "0.0.10"
-hyper-util = { version = "0.1.16", features = [
+hyper-util = { version = "0.1.17", features = [
"server-auto",
"server",
"http1",
@@ -26,25 +26,26 @@ hex = "0.4.3"
# TLS
rustls-pemfile = "2.2.0"
-rustls = { version = "0.23.31", default-features = false, features = ["ring"] }
-tokio-rustls = "0.26.2"
-rustls-webpki = "0.103.5"
-webpki-roots = "1.0.2"
+rustls = { version = "0.23.35", default-features = false, features = ["ring"] }
+tokio-rustls = "0.26.4"
+rustls-webpki = "0.103.8"
+webpki-roots = "1.0.4"
ring = "0.17.14"
# Async stuff
-tokio = { version = "1.47.1", features = ["full"] }
-tokio-util = { version = "0.7.16", features = ["io"] }
+tokio = { version = "1.48.0", features = ["full"] }
+tokio-util = { version = "0.7.17", features = ["io"] }
futures-util = "0.3.31"
futures = "0.3.31"
async-stream = "0.3.6"
pin-project = "1.1.10"
# Config
-serde = { version = "1.0.220", features = ["derive"] }
+serde = { version = "1.0.228", features = ["derive"] }
serde_yml = "0.0.12"
-serde_json = "1.0.143"
+serde_json = "1.0.145"
inotify = "0.11.0"
+clap = { version = "4.5.51", features = ["derive"] }
# Logging
env_logger = "0.11.8"
@@ -57,14 +58,14 @@ mime_guess = "2.0.5"
# Crypto for authentificating clients
aes-gcm-siv = "0.11.1"
-argon2 = "0.6.0-rc.1"
-sha2 = "0.11.0-rc.2"
+argon2 = "0.6.0-rc.2"
+sha2 = "0.11.0-rc.3"
rand = "0.9.2"
rand_distr = "0.5.1"
# Other helpers and stuff
bytes = "1.10.1"
-anyhow = "1.0.99"
-thiserror = "2.0.16"
-regex = "1.11.2"
+anyhow = "1.0.100"
+thiserror = "2.0.17"
+regex = "1.12.2"
users = "0.11.0"
diff --git a/src/certs.rs b/src/certs.rs
index e75d5e0..950bc88 100644
--- a/src/certs.rs
+++ b/src/certs.rs
@@ -7,14 +7,13 @@ use anyhow::{anyhow, Context, Result};
use log::debug;
use rustls::{
crypto::CryptoProvider,
- pki_types::{CertificateDer, PrivateKeyDer},
server::{ClientHello, ResolvesServerCert},
sign::CertifiedKey,
};
use std::{
collections::HashMap,
- fs::File,
- io::BufReader,
+ fs::read_to_string,
+ io::Cursor,
path::{Path, PathBuf},
sync::Arc,
};
@@ -28,6 +27,12 @@ pub struct CertPool {
fallback: Option<Arc<CertifiedKey>>,
}
+pub struct CertPackage {
+ pub name: String,
+ pub cert: String,
+ pub key: String,
+}
+
impl Default for CertPool {
fn default() -> Self {
Self {
@@ -39,59 +44,68 @@ impl Default for CertPool {
}
}
-impl CertPool {
- pub fn load(roots: &[PathBuf], fallback: Option<PathBuf>) -> Result<Self> {
- let mut s = Self::default();
+impl CertPackage {
+ fn create_from(path: &Path) -> Result<Option<CertPackage>> {
+ let keypath = path.join("privkey.pem");
+ let certpath = if path.join("fullchain.pem").exists() {
+ path.join("fullchain.pem")
+ } else {
+ path.join("cert.pem")
+ };
+ Ok(if keypath.exists() && certpath.exists() {
+ debug!("creating cert package at {path:?}");
+ Some(CertPackage {
+ name: path.to_string_lossy().to_string(),
+ cert: read_to_string(certpath)?,
+ key: read_to_string(keypath)?,
+ })
+ } else {
+ None
+ })
+ }
+
+ pub fn create_from_recursive(roots: &[PathBuf]) -> Result<Vec<CertPackage>> {
+ let mut out = Vec::new();
for r in roots {
- s.load_recursive(r)?;
- }
- if let Some(path) = fallback {
- let keypath = path.join("privkey.pem");
- let certpath = if path.join("fullchain.pem").exists() {
- path.join("fullchain.pem")
- } else {
- path.join("cert.pem")
- };
- let certs = load_certs(&certpath)?;
- let key = load_private_key(&keypath)?;
- let skey = s.provider.key_provider.load_private_key(key)?;
- let ck = CertifiedKey::new(certs.clone(), skey.clone());
- s.fallback = Some(Arc::new(ck))
+ Self::create_package_recursion(r, &mut out)?;
}
- Ok(s)
+ Ok(out)
}
- fn load_recursive(&mut self, path: &Path) -> Result<()> {
+ fn create_package_recursion(path: &Path, out: &mut Vec<CertPackage>) -> Result<()> {
if !path.is_dir() {
return Ok(());
}
for e in path.read_dir()? {
let p = e?.path();
if p.is_dir() {
- self.load_recursive(&p)?;
+ Self::create_package_recursion(&p, out)?;
}
}
- let keypath = path.join("privkey.pem");
- let certpath = if path.join("fullchain.pem").exists() {
- path.join("fullchain.pem")
- } else {
- path.join("cert.pem")
- };
- if keypath.exists() && certpath.exists() {
- let certs = load_certs(&certpath)?;
- let key = load_private_key(&keypath)?;
- let skey = self.provider.key_provider.load_private_key(key)?;
- for c in &certs {
- let eec = EndEntityCert::try_from(c).unwrap();
- for name in eec.valid_dns_names() {
- let ck = CertifiedKey::new(certs.clone(), skey.clone());
- if let Some(name) = name.strip_prefix("*.") {
- debug!("loaded wildcard key for {name:?}");
- self.wildcards.insert(name.to_owned(), Arc::new(ck));
- } else {
- debug!("loaded key for {name:?}");
- self.domains.insert(name.to_owned(), Arc::new(ck));
- }
+ out.extend(Self::create_from(path)?);
+ Ok(())
+ }
+}
+
+impl CertPool {
+ pub fn add_package(&mut self, cp: CertPackage) -> Result<()> {
+ let certs = rustls_pemfile::certs(&mut Cursor::new(cp.cert))
+ .try_collect::<Vec<_>>()
+ .context("parsing tls certs")?;
+ let key = rustls_pemfile::private_key(&mut Cursor::new(cp.key))
+ .context("parsing tls private key")?
+ .ok_or(anyhow!("private key missing"))?;
+ let skey = self.provider.key_provider.load_private_key(key)?;
+ for c in &certs {
+ let eec = EndEntityCert::try_from(c).unwrap();
+ for name in eec.valid_dns_names() {
+ let ck = CertifiedKey::new(certs.clone(), skey.clone());
+ if let Some(name) = name.strip_prefix("*.") {
+ debug!("loaded wildcard key for {name:?}");
+ self.wildcards.insert(name.to_owned(), Arc::new(ck));
+ } else {
+ debug!("loaded key for {name:?}");
+ self.domains.insert(name.to_owned(), Arc::new(ck));
}
}
}
@@ -116,16 +130,3 @@ impl ResolvesServerCert for CertPool {
)
}
}
-
-fn load_certs(path: &Path) -> Result<Vec<CertificateDer<'static>>> {
- let mut reader = BufReader::new(File::open(path).context("reading tls certs")?);
- let certs = rustls_pemfile::certs(&mut reader)
- .try_collect::<Vec<_>>()
- .context("parsing tls certs")?;
- Ok(certs)
-}
-fn load_private_key(path: &Path) -> Result<PrivateKeyDer<'static>> {
- let mut reader = BufReader::new(File::open(path).context("reading tls private key")?);
- let keys = rustls_pemfile::private_key(&mut reader).context("parsing tls private key")?;
- keys.ok_or(anyhow!("no private key found"))
-}
diff --git a/src/config.rs b/src/config.rs
index 112e86f..13a8019 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -4,27 +4,19 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use crate::{
- modules::{Node, NodeKind},
- State,
+ certs::CertPackage,
+ deser_helpers::{seq_or_not, string_or_seq},
+ modules::{Node, MODULES},
};
-use anyhow::Context;
-use inotify::{Inotify, WatchMask};
-use log::{error, info};
+use anyhow::{anyhow, bail, Context, Result};
use rand::random;
-use serde::{
- de::{value, Error, SeqAccess, Visitor},
- Deserialize, Deserializer, Serialize,
-};
-use serde_yml::value::TaggedValue;
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
+use serde_yml::Value;
use std::{
- collections::BTreeMap,
- fmt,
fs::read_to_string,
- marker::PhantomData,
net::SocketAddr,
- ops::Deref,
path::{Path, PathBuf},
- sync::{Arc, RwLock},
+ sync::Arc,
};
#[derive(Deserialize)]
@@ -39,7 +31,7 @@ pub struct Config {
pub limits: Limits,
#[serde(default)]
pub source_ip_from_header: bool,
- pub handler: DynNode,
+ pub handler: DynNodeConfig,
#[serde(default)]
pub disable_server_header: bool,
}
@@ -81,124 +73,70 @@ pub struct HttpsConfig {
pub disable_h1: bool,
}
-// try deser Vec<T> but fall back to deser T and putting that in Vec
-pub fn seq_or_not<'de, D, V: Deserialize<'de>>(des: D) -> Result<Vec<V>, D::Error>
-where
- D: Deserializer<'de>,
-{
- struct SeqOrNot<V>(PhantomData<V>);
- impl<'de, V: Deserialize<'de>> Visitor<'de> for SeqOrNot<V> {
- type Value = Vec<V>;
+pub struct ConfigPackage {
+ pub config: String,
+ pub certificates: Vec<CertPackage>, // name, fullchain, key
+}
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("a sequence or not a sequence")
- }
- fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
- where
- A: serde::de::EnumAccess<'de>,
- {
- Ok(vec![V::deserialize(value::EnumAccessDeserializer::new(
- data,
- ))?])
- }
- fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
- where
- A: serde::de::MapAccess<'de>,
- {
- Ok(vec![V::deserialize(value::MapAccessDeserializer::new(
- map,
- ))?])
- }
- fn visit_str<E>(self, val: &str) -> Result<Vec<V>, E>
- where
- E: Error,
- {
- Ok(vec![V::deserialize(value::StrDeserializer::new(val))?])
- }
+pub type DynNode = Arc<dyn Node>;
- fn visit_seq<A>(self, val: A) -> Result<Vec<V>, A::Error>
- where
- A: SeqAccess<'de>,
- {
- Vec::<V>::deserialize(value::SeqAccessDeserializer::new(val))
- }
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct DynNodeConfig(serde_yml::Value);
+impl DynNodeConfig {
+ pub fn parse<T: DeserializeOwned>(self) -> serde_yml::Result<T> {
+ serde_yml::from_value(self.0)
}
- des.deserialize_any(SeqOrNot::<V>(PhantomData))
}
-// fall back to expecting a single string and putting that in a 1-length vector
-fn string_or_seq<'de, D>(des: D) -> Result<Vec<SocketAddr>, D::Error>
-where
- D: Deserializer<'de>,
-{
- struct StringOrList;
- impl<'de> Visitor<'de> for StringOrList {
- type Value = Vec<SocketAddr>;
-
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("sequence or list")
- }
-
- fn visit_str<E>(self, val: &str) -> Result<Vec<SocketAddr>, E>
- where
- E: Error,
- {
- let addr = SocketAddr::deserialize(value::StrDeserializer::new(val))?;
- Ok(vec![addr])
- }
-
- fn visit_seq<A>(self, val: A) -> Result<Vec<SocketAddr>, A::Error>
- where
- A: SeqAccess<'de>,
- {
- Vec::<SocketAddr>::deserialize(value::SeqAccessDeserializer::new(val))
- }
+pub struct InstContext {
+ config: DynNodeConfig,
+}
+impl InstContext {
+ pub fn config(&self) -> DynNodeConfig {
+ self.config.clone()
+ }
+ pub fn instanciate_child(&self, config: DynNodeConfig) -> Result<DynNode> {
+ instanciate_handler(config)
}
-
- des.deserialize_any(StringOrList)
}
-pub static NODE_KINDS: RwLock<BTreeMap<String, &'static dyn NodeKind>> =
- RwLock::new(BTreeMap::new());
-
-#[derive(Clone)]
-pub struct DynNode(Arc<dyn Node>);
-
-impl<'de> Deserialize<'de> for DynNode {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- let tv = TaggedValue::deserialize(deserializer)?;
- let s = tv.tag.to_string();
- let s = s.strip_prefix("!").unwrap_or(s.as_str());
- let inst = NODE_KINDS
- .read()
- .unwrap()
- .get(s)
- .ok_or(serde::de::Error::unknown_variant(s, &[]))?
- .instanciate(tv.value)
- .map_err(|e| {
- let x = format!("instanciating module {s:?}: {e:?}");
- serde::de::Error::custom(e.context(x))
- })?;
-
- Ok(Self(inst))
+impl Config {
+ pub fn new(raw: &str) -> Result<Self> {
+ Ok(serde_yml::from_str(&raw).context("parsing config YAML")?)
}
-}
-impl Deref for DynNode {
- type Target = dyn Node;
- fn deref(&self) -> &Self::Target {
- self.0.as_ref()
+ pub fn create_handler(&self) -> Result<DynNode> {
+ instanciate_handler(self.handler.clone())
}
}
-impl Config {
- pub fn load(path: &Path) -> anyhow::Result<Config> {
- info!("loading config from {path:?}");
- let raw = read_to_string(path).context("reading config file")?;
- let config: Config = serde_yml::from_str(&raw).context("during parsing")?;
- Ok(config)
+fn instanciate_handler(config: DynNodeConfig) -> Result<DynNode> {
+ let Value::Tagged(tv) = config.0 else {
+ bail!("handler is not a tagged value")
+ };
+ let kind = tv.tag.string;
+ let cons = MODULES
+ .iter()
+ .find(|m| m.name() == kind)
+ .ok_or(anyhow!("unknown module name {kind:?}"))?;
+
+ cons.instanciate(InstContext {
+ config: DynNodeConfig(tv.value),
+ })
+}
+
+impl ConfigPackage {
+ pub fn new(entry: &Path) -> Result<Self> {
+ let config = read_to_string(entry).context("reading main config file")?;
+ let config_parsed: Config = serde_yml::from_str(&config).context("parsing config YAML")?;
+ let certificates = config_parsed
+ .https
+ .map(|h| CertPackage::create_from_recursive(&h.cert_path))
+ .transpose()?
+ .unwrap_or_default();
+ Ok(Self {
+ config,
+ certificates,
+ })
}
}
@@ -213,36 +151,36 @@ impl Default for Limits {
}
}
-pub fn setup_file_watch(config_path: PathBuf, state: Arc<State>) {
- std::thread::spawn(move || {
- let mut inotify = Inotify::init().unwrap();
- inotify
- .watches()
- .add(
- config_path.parent().unwrap(),
- WatchMask::MODIFY | WatchMask::CREATE | WatchMask::DELETE | WatchMask::MOVED_TO,
- )
- .unwrap();
- let mut buffer = [0u8; 4096];
- loop {
- let events = inotify
- .read_events_blocking(&mut buffer)
- .expect("Failed to read inotify events");
+// pub fn setup_file_watch(config_path: PathBuf, state: Arc<State>) {
+// std::thread::spawn(move || {
+// let mut inotify = Inotify::init().unwrap();
+// inotify
+// .watches()
+// .add(
+// config_path.parent().unwrap(),
+// WatchMask::MODIFY | WatchMask::CREATE | WatchMask::DELETE | WatchMask::MOVED_TO,
+// )
+// .unwrap();
+// let mut buffer = [0u8; 4096];
+// loop {
+// let events = inotify
+// .read_events_blocking(&mut buffer)
+// .expect("Failed to read inotify events");
- for event in events {
- if event.name == config_path.file_name() {
- if config_path.metadata().map(|m| m.len()).unwrap_or_default() == 0 {
- continue;
- }
- match Config::load(&config_path) {
- Ok(conf) => {
- let mut r = state.config.blocking_write();
- *r = Arc::new(conf)
- }
- Err(e) => error!("config has errors: {e:?}"),
- }
- }
- }
- }
- });
-}
+// for event in events {
+// if event.name == config_path.file_name() {
+// if config_path.metadata().map(|m| m.len()).unwrap_or_default() == 0 {
+// continue;
+// }
+// match Config::load(&config_path) {
+// Ok(conf) => {
+// let mut r = state.config.blocking_write();
+// *r = Arc::new(conf)
+// }
+// Err(e) => error!("config has errors: {e:?}"),
+// }
+// }
+// }
+// }
+// });
+// }
diff --git a/src/deser_helpers.rs b/src/deser_helpers.rs
new file mode 100644
index 0000000..693d67f
--- /dev/null
+++ b/src/deser_helpers.rs
@@ -0,0 +1,87 @@
+/*
+ This file is part of gnix (https://codeberg.org/metamuffin/gnix)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2025 metamuffin <metamuffin.org>
+*/
+use serde::{
+ de::{value, Error, SeqAccess, Visitor},
+ Deserialize, Deserializer,
+};
+use std::{fmt, marker::PhantomData, net::SocketAddr};
+
+// try deser Vec<T> but fall back to deser T and putting that in Vec
+pub fn seq_or_not<'de, D, V: Deserialize<'de>>(des: D) -> Result<Vec<V>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ struct SeqOrNot<V>(PhantomData<V>);
+ impl<'de, V: Deserialize<'de>> Visitor<'de> for SeqOrNot<V> {
+ type Value = Vec<V>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a sequence or not a sequence")
+ }
+ fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::EnumAccess<'de>,
+ {
+ Ok(vec![V::deserialize(value::EnumAccessDeserializer::new(
+ data,
+ ))?])
+ }
+ fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::MapAccess<'de>,
+ {
+ Ok(vec![V::deserialize(value::MapAccessDeserializer::new(
+ map,
+ ))?])
+ }
+ fn visit_str<E>(self, val: &str) -> Result<Vec<V>, E>
+ where
+ E: Error,
+ {
+ Ok(vec![V::deserialize(value::StrDeserializer::new(val))?])
+ }
+
+ fn visit_seq<A>(self, val: A) -> Result<Vec<V>, A::Error>
+ where
+ A: SeqAccess<'de>,
+ {
+ Vec::<V>::deserialize(value::SeqAccessDeserializer::new(val))
+ }
+ }
+ des.deserialize_any(SeqOrNot::<V>(PhantomData))
+}
+
+// fall back to expecting a single string and putting that in a 1-length vector
+pub fn string_or_seq<'de, D>(des: D) -> Result<Vec<SocketAddr>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ struct StringOrList;
+ impl<'de> Visitor<'de> for StringOrList {
+ type Value = Vec<SocketAddr>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("sequence or list")
+ }
+
+ fn visit_str<E>(self, val: &str) -> Result<Vec<SocketAddr>, E>
+ where
+ E: Error,
+ {
+ let addr = SocketAddr::deserialize(value::StrDeserializer::new(val))?;
+ Ok(vec![addr])
+ }
+
+ fn visit_seq<A>(self, val: A) -> Result<Vec<SocketAddr>, A::Error>
+ where
+ A: SeqAccess<'de>,
+ {
+ Vec::<SocketAddr>::deserialize(value::SeqAccessDeserializer::new(val))
+ }
+ }
+
+ des.deserialize_any(StringOrList)
+}
diff --git a/src/generation.rs b/src/generation.rs
new file mode 100644
index 0000000..73874c5
--- /dev/null
+++ b/src/generation.rs
@@ -0,0 +1,71 @@
+/*
+ This file is part of gnix (https://codeberg.org/metamuffin/gnix)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2025 metamuffin <metamuffin.org>
+*/
+use crate::{
+ certs::{CertPackage, CertPool},
+ config::{Config, ConfigPackage, DynNode},
+};
+use anyhow::{anyhow, Context, Result};
+use quinn::crypto::rustls::QuicServerConfig;
+use std::sync::Arc;
+use tokio_rustls::TlsAcceptor;
+
+pub struct Generation {
+ pub config: Config,
+ pub handler: DynNode,
+ pub tls_acceptor: Option<Arc<TlsAcceptor>>,
+ pub quic_config: Option<quinn::ServerConfig>,
+}
+
+impl Generation {
+ pub fn new(cp: ConfigPackage) -> Result<Self> {
+ let config = Config::new(&cp.config)?;
+ let handler = config.create_handler()?;
+ let (tls_acceptor, quic_config) = setup_tls(&config, cp.certificates)?;
+ Ok(Generation {
+ config,
+ handler,
+ tls_acceptor,
+ quic_config,
+ })
+ }
+}
+
+fn setup_tls(
+ config: &Config,
+ certs: Vec<CertPackage>,
+) -> Result<(Option<Arc<TlsAcceptor>>, Option<quinn::ServerConfig>)> {
+ let Some(https_config) = &config.https else {
+ return Ok((None, None));
+ };
+ let mut resolver = CertPool::default();
+ for cert in certs {
+ let err = anyhow!("loading cert {:?}", cert.name);
+ resolver.add_package(cert).context(err)?;
+ }
+ let resolver = Arc::new(resolver);
+
+ let mut h12_config = rustls::ServerConfig::builder()
+ .with_no_client_auth()
+ .with_cert_resolver(resolver.clone());
+ let mut h3_config = rustls::ServerConfig::builder()
+ .with_no_client_auth()
+ .with_cert_resolver(resolver.clone());
+
+ if !https_config.disable_h1 {
+ h12_config.alpn_protocols.push(b"http/1.1".to_vec());
+ }
+ if !https_config.disable_h2 {
+ h12_config.alpn_protocols.push(b"h2".to_vec());
+ }
+ h3_config.alpn_protocols.push(b"h3".to_vec());
+
+ let h3_config = Arc::new(QuicServerConfig::try_from(h3_config)?);
+ let quic_config = quinn::ServerConfig::with_crypto(h3_config.clone());
+
+ let tls_acceptor = Arc::new(TlsAcceptor::from(Arc::new(h12_config)));
+
+ Ok((Some(tls_acceptor), Some(quic_config)))
+}
diff --git a/src/main.rs b/src/main.rs
index 00e4e5b..3787527 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,22 +7,23 @@
try_trait_v2,
slice_split_once,
iterator_try_collect,
- path_add_extension,
never_type,
string_from_utf8_lossy_owned
)]
pub mod certs;
pub mod config;
+pub mod deser_helpers;
pub mod error;
+pub mod generation;
pub mod h3_support;
pub mod modules;
+use crate::{config::ConfigPackage, generation::Generation};
use aes_gcm_siv::{aead::generic_array::GenericArray, Aes256GcmSiv, KeyInit};
use anyhow::{Context, Result};
use bytes::Bytes;
-use certs::CertPool;
-use config::{setup_file_watch, Config, NODE_KINDS};
+use clap::Parser;
use error::ServiceError;
use futures::future::try_join_all;
use h3::server::RequestStream;
@@ -39,10 +40,8 @@ use hyper::{
};
use hyper_util::rt::{TokioExecutor, TokioIo};
use log::{debug, error, info, warn, LevelFilter};
-use modules::{NodeContext, MODULES};
-use quinn::crypto::rustls::QuicServerConfig;
+use modules::NodeContext;
use std::{
- collections::HashMap,
net::{IpAddr, SocketAddr},
path::PathBuf,
process::exit,
@@ -50,26 +49,32 @@ use std::{
sync::Arc,
};
use tokio::{
- fs::File,
- io::BufWriter,
net::TcpListener,
signal::ctrl_c,
spawn,
sync::{RwLock, Semaphore},
};
-use tokio_rustls::TlsAcceptor;
pub struct State {
pub crypto_key: Aes256GcmSiv,
- pub config: RwLock<Arc<Config>>,
- pub access_logs: RwLock<HashMap<String, BufWriter<File>>>,
+ pub generation: RwLock<Arc<Generation>>,
pub l_incoming: Semaphore,
pub l_outgoing: Semaphore,
pub l_incoming_h3: Semaphore,
}
+#[derive(Parser)]
+struct Args {
+ #[arg(long)]
+ setuid: Option<String>,
+ /// YAML config file
+ config: PathBuf,
+}
+
#[tokio::main]
async fn main() -> anyhow::Result<()> {
+ let args = Args::parse();
+
env_logger::Builder::new()
.filter_level(LevelFilter::Info)
.parse_env("LOG")
@@ -79,39 +84,21 @@ async fn main() -> anyhow::Result<()> {
.install_default()
.unwrap();
- NODE_KINDS
- .write()
- .unwrap()
- .extend(MODULES.iter().map(|m| (m.name().to_owned(), *m)));
-
- let Some(config_path) = std::env::args().nth(1) else {
- eprintln!("error: first argument is expected to be the configuration file");
- exit(1)
- };
- let config_path = PathBuf::from_str(&config_path)
- .unwrap()
- .canonicalize()
- .unwrap();
-
- let config = match Config::load(&config_path) {
- Ok(c) => c,
- Err(e) => {
- eprintln!("error {e:?}");
- exit(1);
- }
- };
+ let config_package = ConfigPackage::new(&args.config)?;
+ let generation = Generation::new(config_package)?;
let state = Arc::new(State {
- crypto_key: aes_gcm_siv::Aes256GcmSiv::new(GenericArray::from_slice(&config.private_key)),
- l_incoming: Semaphore::new(config.limits.max_incoming_connections),
- l_incoming_h3: Semaphore::new(config.limits.max_incoming_connections_h3),
- l_outgoing: Semaphore::new(config.limits.max_outgoing_connections),
- config: RwLock::new(Arc::new(config)),
- access_logs: Default::default(),
+ crypto_key: aes_gcm_siv::Aes256GcmSiv::new(GenericArray::from_slice(
+ &generation.config.private_key,
+ )),
+ l_incoming: Semaphore::new(generation.config.limits.max_incoming_connections),
+ l_incoming_h3: Semaphore::new(generation.config.limits.max_incoming_connections_h3),
+ l_outgoing: Semaphore::new(generation.config.limits.max_outgoing_connections),
+ generation: RwLock::new(Arc::new(generation)),
});
- if state.config.read().await.watch_config {
- setup_file_watch(config_path, state.clone());
- }
+ // if state.config.read().await.watch_config {
+ // setup_file_watch(config_path, state.clone());
+ // }
{
let state = state.clone();
@@ -146,18 +133,27 @@ async fn main() -> anyhow::Result<()> {
}
async fn serve_http(state: Arc<State>) -> Result<()> {
- let config = state.config.read().await.clone();
- let http_config = match &config.http {
- Some(n) => n,
- None => return Ok(()),
+ let Some(bind_addrs) = state
+ .generation
+ .read()
+ .await
+ .config
+ .http
+ .as_ref()
+ .map(|h| h.bind.clone())
+ else {
+ return Ok(()); // http disabled
};
- let listen_futs: Result<Vec<()>> = try_join_all(http_config.bind.iter().map(|e| async {
- let l = TcpListener::bind(*e).await?;
- let listen_addr = l.local_addr()?;
- info!("HTTP listener bound to {}/tcp", l.local_addr().unwrap());
+ let listen_futs: Result<Vec<()>> = try_join_all(bind_addrs.iter().map(|e| async {
+ let listener = TcpListener::bind(*e).await?;
+ let listen_addr = listener.local_addr()?;
+ info!(
+ "HTTP listener bound to {}/tcp",
+ listener.local_addr().unwrap()
+ );
loop {
- let (stream, addr) = l.accept().await.context("accepting connection")?;
+ let (stream, addr) = listener.accept().await.context("accepting connection")?;
debug!("connection from {addr}");
let stream = TokioIo::new(stream);
let state = state.clone();
@@ -172,36 +168,33 @@ async fn serve_http(state: Arc<State>) -> Result<()> {
}
async fn serve_https(state: Arc<State>) -> Result<()> {
- let config = state.config.read().await.clone();
- let https_config = match &config.https {
- Some(n) => n,
- None => return Ok(()),
- };
- let tls_config = {
- let certs = CertPool::load(&https_config.cert_path, https_config.cert_fallback.clone())?;
- let mut cfg = rustls::ServerConfig::builder()
- .with_no_client_auth()
- .with_cert_resolver(Arc::new(certs));
- if !https_config.disable_h1 {
- cfg.alpn_protocols.push(b"http/1.1".to_vec());
- }
- if !https_config.disable_h2 {
- cfg.alpn_protocols.push(b"h2".to_vec());
- }
- Arc::new(cfg)
+ let Some(bind_addrs) = state
+ .generation
+ .read()
+ .await
+ .config
+ .https
+ .as_ref()
+ .map(|h| h.bind.clone())
+ else {
+ return Ok(()); // https disabled
};
- let tls_acceptor = Arc::new(TlsAcceptor::from(tls_config));
- let listen_futs: Result<Vec<()>> = try_join_all(https_config.bind.iter().map(|e| async {
- let l = TcpListener::bind(*e).await?;
- let listen_addr = l.local_addr()?;
+
+ let listen_futs: Result<Vec<()>> = try_join_all(bind_addrs.iter().map(|e| async {
+ let listener = TcpListener::bind(*e).await?;
+ let listen_addr = listener.local_addr()?;
info!(
"HTTPS (h1+h2) listener bound to {}/tcp",
- l.local_addr().unwrap()
+ listener.local_addr().unwrap()
);
loop {
- let (stream, addr) = l.accept().await.context("accepting connection")?;
+ let (stream, addr) = listener.accept().await.context("accepting connection")?;
+ let generation = state.generation.read().await.clone();
+ let Some(tls_acceptor) = generation.tls_acceptor.clone() else {
+ warn!("HTTPS (h1+h2) listener is missing TLS configuration");
+ break;
+ };
let state = state.clone();
- let tls_acceptor = tls_acceptor.clone();
tokio::task::spawn(async move {
debug!("connection from {addr}");
match tls_acceptor.accept(stream).await {
@@ -212,6 +205,11 @@ async fn serve_https(state: Arc<State>) -> Result<()> {
};
});
}
+ info!(
+ "HTTPS (h1+h2) listener for {} shutting down",
+ listener.local_addr().unwrap()
+ );
+ Ok(())
}))
.await;
listen_futs?;
@@ -250,8 +248,8 @@ pub async fn serve_stream<T: Unpin + Send + 'static + hyper::rt::Read + hyper::r
}
async fn serve_h3(state: Arc<State>) -> Result<()> {
- let config = state.config.read().await.clone();
- let https_config = match &config.https {
+ let generation = state.generation.read().await.clone();
+ let https_config = match &generation.config.https {
Some(n) => n,
None => return Ok(()),
};
@@ -259,22 +257,19 @@ async fn serve_h3(state: Arc<State>) -> Result<()> {
return Ok(());
}
let bind_addrs = https_config.bind.clone();
- let certs = CertPool::load(&https_config.cert_path, https_config.cert_fallback.clone())?;
- let mut cfg = rustls::ServerConfig::builder()
- .with_no_client_auth()
- .with_cert_resolver(Arc::new(certs));
- cfg.alpn_protocols = vec![b"h3".to_vec()];
- let cfg = Arc::new(QuicServerConfig::try_from(cfg)?);
try_join_all(bind_addrs.iter().map(|listen_addr| async {
- let cfg = quinn::ServerConfig::with_crypto(cfg.clone());
- let endpoint = quinn::Endpoint::server(cfg, *listen_addr)?;
+ let Some(quic_config) = generation.quic_config.clone() else {
+ warn!("HTTPS (h3) listener is missing TLS configuration");
+ return Ok(());
+ };
+ let endpoint = quinn::Endpoint::server(quic_config, *listen_addr)?;
let listen_addr = *listen_addr;
info!("HTTPS (h3) listener bound to {listen_addr}/udp");
while let Some(conn) = endpoint.accept().await {
let state = state.clone();
- let config = config.clone();
- tokio::spawn(serve_h3_stream(conn, config, state, listen_addr));
+ let generation = generation.clone();
+ tokio::spawn(serve_h3_stream(conn, generation, state, listen_addr));
}
Ok::<_, anyhow::Error>(())
}))
@@ -284,7 +279,7 @@ async fn serve_h3(state: Arc<State>) -> Result<()> {
async fn serve_h3_stream(
conn: quinn::Incoming,
- config: Arc<Config>,
+ generation: Arc<Generation>,
state: Arc<State>,
listen_addr: SocketAddr,
) {
@@ -308,7 +303,7 @@ async fn serve_h3_stream(
Err(e) => return warn!("h3 accept failed {e}"),
};
debug!("h3 stream from {addr}");
- let max_par_requests = Semaphore::new(config.limits.max_requests_per_connnection);
+ let max_par_requests = Semaphore::new(generation.config.limits.max_requests_per_connnection);
loop {
match conn.accept().await {
Ok(Some(x)) => {
@@ -405,7 +400,7 @@ async fn service(
secure: bool,
listen_addr: SocketAddr,
) -> Result<hyper::Response<BoxBody<bytes::Bytes, ServiceError>>, ServiceError> {
- let config = state.config.read().await.clone();
+ let generation = state.generation.read().await.clone();
// move uri authority used in HTTP/2 to Host header field
{
let uri = request.uri_mut();
@@ -420,7 +415,7 @@ async fn service(
}
}
- if config.source_ip_from_header {
+ if generation.config.source_ip_from_header {
if let Some(x) = request.headers_mut().remove("x-real-ip") {
addr = SocketAddr::new(
IpAddr::from_str(x.to_str()?).map_err(|_| ServiceError::InvalidHeader)?,
@@ -443,9 +438,9 @@ async fn service(
secure,
listen_addr,
};
- let mut resp = config.handler.handle(&mut context, request).await?;
+ let mut resp = generation.handler.handle(&mut context, request).await?;
- if !config.disable_server_header {
+ if !generation.config.disable_server_header {
let server_header = resp.headers().get(SERVER).cloned();
resp.headers_mut().insert(
SERVER,
diff --git a/src/modules/accesslog.rs b/src/modules/accesslog.rs
index c41d4fb..5b91949 100644
--- a/src/modules/accesslog.rs
+++ b/src/modules/accesslog.rs
@@ -4,7 +4,12 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
+use anyhow::Result;
use futures::Future;
use log::error;
use serde::Deserialize;
@@ -24,11 +29,12 @@ struct AccessLogConfig {
flush: bool,
#[serde(default)]
reject_on_fail: bool,
- next: DynNode,
+ next: DynNodeConfig,
}
struct AccessLog {
config: AccessLogConfig,
+ next: DynNode,
file: RwLock<Option<BufWriter<File>>>,
}
@@ -36,9 +42,11 @@ impl NodeKind for AccessLogKind {
fn name(&self) -> &'static str {
"access_log"
}
- fn instanciate(&self, config: serde_yml::Value) -> anyhow::Result<Arc<dyn Node>> {
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: AccessLogConfig = ic.config().parse()?;
Ok(Arc::new(AccessLog {
- config: serde_yml::from_value::<AccessLogConfig>(config)?,
+ next: ic.instanciate_child(config.next.clone())?,
+ config,
file: Default::default(),
}))
}
@@ -81,7 +89,7 @@ impl Node for AccessLog {
error!("failed to write log: {e:?}")
}
- self.config.next.handle(context, request).await
+ self.next.handle(context, request).await
})
}
}
diff --git a/src/modules/auth/basic.rs b/src/modules/auth/basic.rs
index 4b10a47..b8b3e52 100644
--- a/src/modules/auth/basic.rs
+++ b/src/modules/auth/basic.rs
@@ -4,10 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use crate::{
- config::DynNode,
+ config::{DynNode, DynNodeConfig},
error::ServiceError,
- modules::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse},
+ modules::{InstContext, Node, NodeContext, NodeKind, NodeRequest, NodeResponse},
};
+use anyhow::Result;
use base64::Engine;
use futures::Future;
use http_body_util::{combinators::BoxBody, BodyExt};
@@ -17,7 +18,6 @@ use hyper::{
};
use log::debug;
use serde::Deserialize;
-use serde_yml::Value;
use std::{pin::Pin, sync::Arc};
use super::Credentials;
@@ -27,19 +27,24 @@ impl NodeKind for HttpBasicAuthKind {
fn name(&self) -> &'static str {
"http_basic_auth"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<HttpBasicAuth>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: HttpBasicAuth<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(HttpBasicAuth {
+ next: ic.instanciate_child(config.next)?,
+ realm: config.realm,
+ users: config.users,
+ }))
}
}
#[derive(Deserialize)]
-pub struct HttpBasicAuth {
+pub struct HttpBasicAuth<N> {
realm: String,
users: Credentials,
- next: DynNode,
+ next: N,
}
-impl Node for HttpBasicAuth {
+impl Node for HttpBasicAuth<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
diff --git a/src/modules/auth/cookie.rs b/src/modules/auth/cookie.rs
index c0935f7..81fc50a 100644
--- a/src/modules/auth/cookie.rs
+++ b/src/modules/auth/cookie.rs
@@ -3,15 +3,17 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
+use super::Credentials;
use crate::{
- config::{return_true, DynNode},
+ config::{return_true, DynNode, DynNodeConfig},
error::ServiceError,
- modules::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse},
+ modules::{InstContext, Node, NodeContext, NodeKind, NodeRequest, NodeResponse},
};
use aes_gcm_siv::{
aead::{Aead, Payload},
Nonce,
};
+use anyhow::Result;
use base64::Engine;
use bytes::Bytes;
use futures::Future;
@@ -25,33 +27,37 @@ use log::debug;
use percent_encoding::{percent_decode_str, percent_encode, NON_ALPHANUMERIC};
use rand::random;
use serde::Deserialize;
-use serde_yml::Value;
use std::fmt::Write;
use std::{pin::Pin, sync::Arc, time::SystemTime};
-use super::Credentials;
-
pub struct CookieAuthKind;
impl NodeKind for CookieAuthKind {
fn name(&self) -> &'static str {
"cookie_auth"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<CookieAuth>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<Arc<dyn Node>> {
+ let config: CookieAuth<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(CookieAuth {
+ expire: config.expire,
+ secure: config.secure,
+ users: config.users,
+ fail: ic.instanciate_child(config.fail)?,
+ next: ic.instanciate_child(config.next)?,
+ }))
}
}
#[derive(Deserialize)]
-pub struct CookieAuth {
+pub struct CookieAuth<N> {
users: Credentials,
expire: Option<u64>,
#[serde(default = "return_true")]
secure: bool,
- next: DynNode,
- fail: DynNode,
+ next: N,
+ fail: N,
}
-impl Node for CookieAuth {
+impl Node for CookieAuth<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
@@ -160,7 +166,7 @@ impl Node for CookieAuth {
fn apply_login_success_headers(
context: &mut NodeContext,
- node: &CookieAuth,
+ node: &CookieAuth<DynNode>,
username: &str,
r: &mut Response<BoxBody<Bytes, ServiceError>>,
) {
@@ -205,7 +211,7 @@ fn apply_login_success_headers(
fn login_success_response(
context: &mut NodeContext,
- node: &CookieAuth,
+ node: &CookieAuth<DynNode>,
referrer: Option<HeaderValue>,
username: &str,
) -> Response<BoxBody<Bytes, ServiceError>> {
diff --git a/src/modules/auth/openid.rs b/src/modules/auth/openid.rs
index 0b5aea7..175fa22 100644
--- a/src/modules/auth/openid.rs
+++ b/src/modules/auth/openid.rs
@@ -4,14 +4,15 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use crate::{
- config::DynNode,
+ config::{DynNode, DynNodeConfig},
error::ServiceError,
- modules::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse},
+ modules::{InstContext, Node, NodeContext, NodeKind, NodeRequest, NodeResponse},
};
use aes_gcm_siv::{
aead::{Aead, Payload},
Nonce,
};
+use anyhow::Result;
use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine};
use bytes::Buf;
use futures::Future;
@@ -31,7 +32,6 @@ use percent_encoding::{
use rand::random;
use rustls::{pki_types::ServerName, RootCertStore};
use serde::Deserialize;
-use serde_yml::Value;
use sha2::{Digest, Sha256};
use std::{collections::HashSet, io::Read, pin::Pin, str::FromStr, sync::Arc, time::SystemTime};
use tokio::net::TcpStream;
@@ -41,13 +41,23 @@ impl NodeKind for OpenIDAuthKind {
fn name(&self) -> &'static str {
"openid_auth"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<OpenIDAuth>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: OpenIDAuth<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(OpenIDAuth {
+ authorize_endpoint: config.authorize_endpoint,
+ authorized_emails: config.authorized_emails,
+ client_id: config.client_id,
+ client_secret: config.client_secret,
+ next: ic.instanciate_child(config.next)?,
+ salt: config.salt,
+ scope: config.scope,
+ token_endpoint: config.token_endpoint,
+ }))
}
}
#[derive(Deserialize)]
-pub struct OpenIDAuth {
+pub struct OpenIDAuth<N> {
salt: String,
client_id: String,
client_secret: String,
@@ -55,10 +65,10 @@ pub struct OpenIDAuth {
token_endpoint: String,
scope: String,
authorized_emails: HashSet<String>,
- next: DynNode,
+ next: N,
}
-impl Node for OpenIDAuth {
+impl Node for OpenIDAuth<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
diff --git a/src/modules/cache.rs b/src/modules/cache.rs
index 34db902..548c076 100644
--- a/src/modules/cache.rs
+++ b/src/modules/cache.rs
@@ -15,7 +15,11 @@
//! - on disk (redb? filesystem?)
//! - external db?
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use bytes::Bytes;
use headers::{CacheControl, HeaderMapExt};
@@ -23,7 +27,6 @@ use http::Response;
use http_body_util::{BodyExt, Full};
use log::debug;
use serde::Deserialize;
-use serde_yml::Value;
use sha2::{Digest, Sha256};
use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc, time::Duration};
use tokio::sync::RwLock;
@@ -32,21 +35,23 @@ pub struct CacheKind;
#[derive(Deserialize)]
struct CacheConfig {
- next: DynNode,
+ next: DynNodeConfig,
}
struct Cache {
- entries: RwLock<HashMap<[u8; 32], Response<Bytes>>>,
config: CacheConfig,
+ next: DynNode,
+ entries: RwLock<HashMap<[u8; 32], Response<Bytes>>>,
}
impl NodeKind for CacheKind {
fn name(&self) -> &'static str {
"cache"
}
- fn instanciate(&self, config: Value) -> Result<Arc<dyn Node>> {
- let config = serde_yml::from_value::<CacheConfig>(config)?;
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: CacheConfig = ic.config().parse()?;
Ok(Arc::new(Cache {
+ next: ic.instanciate_child(config.next.clone())?,
config,
entries: HashMap::new().into(),
}))
@@ -62,7 +67,7 @@ impl Node for Cache {
let allow_cache = request.method().is_safe();
if !allow_cache {
- return self.config.next.handle(context, request).await;
+ return self.next.handle(context, request).await;
}
// not very fast
@@ -94,7 +99,7 @@ impl Node for Cache {
}
debug!("miss");
- let response = self.config.next.handle(context, request).await?;
+ let response = self.next.handle(context, request).await?;
let cache_control = response
.headers()
diff --git a/src/modules/cgi.rs b/src/modules/cgi.rs
index 121bcac..b687398 100644
--- a/src/modules/cgi.rs
+++ b/src/modules/cgi.rs
@@ -4,7 +4,7 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::error::ServiceError;
+use crate::{error::ServiceError, modules::InstContext};
use anyhow::{anyhow, Result};
use futures::TryStreamExt;
use http_body_util::{combinators::BoxBody, BodyExt, StreamBody};
@@ -14,7 +14,6 @@ use hyper::{
Response, StatusCode,
};
use serde::Deserialize;
-use serde_yml::Value;
use std::{
collections::BTreeMap, future::Future, io::ErrorKind, path::PathBuf, pin::Pin, process::Stdio,
str::FromStr, sync::Arc,
@@ -48,10 +47,8 @@ impl NodeKind for CgiKind {
fn name(&self) -> &'static str {
"cgi"
}
- fn instanciate(&self, config: Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(Cgi::new(serde_yml::from_value::<CgiConfig>(
- config,
- )?)?))
+ fn instanciate(&self, ic: InstContext) -> Result<Arc<dyn Node>> {
+ Ok(Arc::new(Cgi::new(ic.config().parse()?)?))
}
}
impl Cgi {
diff --git a/src/modules/debug.rs b/src/modules/debug.rs
index 5802881..f9cad9a 100644
--- a/src/modules/debug.rs
+++ b/src/modules/debug.rs
@@ -4,12 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::error::ServiceError;
+use crate::{error::ServiceError, modules::InstContext};
use futures::Future;
use http::{header::CONTENT_TYPE, HeaderValue, Response};
use http_body_util::BodyExt;
use serde::Deserialize;
-use serde_yml::Value;
use std::{pin::Pin, sync::Arc};
pub struct DebugKind;
@@ -21,8 +20,8 @@ impl NodeKind for DebugKind {
fn name(&self) -> &'static str {
"debug"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Debug>(config)?))
+ fn instanciate(&self, _ic: InstContext) -> anyhow::Result<Arc<dyn Node>> {
+ Ok(Arc::new(Debug))
}
}
diff --git a/src/modules/delay.rs b/src/modules/delay.rs
index 31036fe..59f6a71 100644
--- a/src/modules/delay.rs
+++ b/src/modules/delay.rs
@@ -4,32 +4,40 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use rand_distr::Distribution;
use serde::Deserialize;
-use serde_yml::Value;
use std::{future::Future, pin::Pin, sync::Arc, time::Duration};
use tokio::time::sleep;
pub struct DelayKind;
#[derive(Deserialize)]
-struct Delay {
+struct Delay<N> {
duration: u64,
stdev: Option<u64>,
- next: DynNode,
+ next: N,
}
impl NodeKind for DelayKind {
fn name(&self) -> &'static str {
"delay"
}
- fn instanciate(&self, config: Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Delay>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: Delay<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(Delay {
+ duration: config.duration,
+ stdev: config.stdev,
+ next: ic.instanciate_child(config.next)?,
+ }))
}
}
-impl Node for Delay {
+impl Node for Delay<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
diff --git a/src/modules/error.rs b/src/modules/error.rs
index 6e895d0..4ec5c6b 100644
--- a/src/modules/error.rs
+++ b/src/modules/error.rs
@@ -4,10 +4,10 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::error::ServiceError;
+use crate::{config::DynNode, error::ServiceError, modules::InstContext};
+use anyhow::Result;
use futures::Future;
use serde::Deserialize;
-use serde_yml::Value;
use std::{pin::Pin, sync::Arc};
pub struct ErrorKind;
@@ -20,8 +20,8 @@ impl NodeKind for ErrorKind {
fn name(&self) -> &'static str {
"error"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Error>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ Ok(Arc::new(ic.config().parse::<Error>()?))
}
}
diff --git a/src/modules/fallback.rs b/src/modules/fallback.rs
index ef3d741..5b518bd 100644
--- a/src/modules/fallback.rs
+++ b/src/modules/fallback.rs
@@ -4,25 +4,32 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use http::Request;
use http_body_util::{combinators::BoxBody, BodyExt, Full};
-use serde::Deserialize;
-use serde_yml::Value;
use std::{future::Future, pin::Pin, sync::Arc};
pub struct FallbackKind;
-#[derive(Deserialize)]
struct Fallback(Vec<DynNode>);
impl NodeKind for FallbackKind {
fn name(&self) -> &'static str {
"fallback"
}
- fn instanciate(&self, config: Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Fallback>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: Vec<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(Fallback(
+ config
+ .into_iter()
+ .map(|c| ic.instanciate_child(c))
+ .collect::<Result<_>>()?,
+ )))
}
}
diff --git a/src/modules/file.rs b/src/modules/file.rs
index 0a03179..d6273aa 100644
--- a/src/modules/file.rs
+++ b/src/modules/file.rs
@@ -4,7 +4,8 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::error::ServiceError;
+use crate::{config::DynNode, error::ServiceError, modules::InstContext};
+use anyhow::Result;
use futures::Future;
use http_body_util::{combinators::BoxBody, BodyExt};
use hyper::{
@@ -12,7 +13,6 @@ use hyper::{
Response,
};
use serde::Deserialize;
-use serde_yml::Value;
use std::{fs::read_to_string, path::PathBuf, pin::Pin, sync::Arc};
pub struct FileKind;
@@ -34,8 +34,8 @@ impl NodeKind for FileKind {
fn name(&self) -> &'static str {
"file"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- let conf = serde_yml::from_value::<FileConfig>(config)?;
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let conf = ic.config().parse::<FileConfig>()?;
Ok(Arc::new(File {
content: conf
.content
diff --git a/src/modules/files.rs b/src/modules/files.rs
index 94dab8c..1d2706c 100644
--- a/src/modules/files.rs
+++ b/src/modules/files.rs
@@ -4,7 +4,12 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::return_true, ServiceError};
+use crate::{
+ config::{return_true, DynNode},
+ modules::InstContext,
+ ServiceError,
+};
+use anyhow::Result;
use bytes::{Bytes, BytesMut};
use futures::Future;
use futures_util::{future, future::Either, ready, stream, FutureExt, Stream, StreamExt};
@@ -24,7 +29,6 @@ use hyper::{
use log::debug;
use percent_encoding::percent_decode_str;
use serde::Deserialize;
-use serde_yml::Value;
use std::{
fs::Metadata,
io,
@@ -69,8 +73,8 @@ impl NodeKind for FilesKind {
fn name(&self) -> &'static str {
"files"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Files>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ Ok(Arc::new(ic.config().parse::<Files>()?))
}
}
diff --git a/src/modules/headers.rs b/src/modules/headers.rs
index f5b081f..5ca0663 100644
--- a/src/modules/headers.rs
+++ b/src/modules/headers.rs
@@ -4,7 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use futures::Future;
use hyper::{
@@ -17,24 +21,29 @@ use std::{collections::BTreeMap, pin::Pin, str::FromStr, sync::Arc};
pub struct HeadersKind;
#[derive(Deserialize)]
-pub struct Headers {
+pub struct Headers<N> {
#[serde(default)]
request: HeaderMapWrap,
#[serde(default)]
response: HeaderMapWrap,
- next: DynNode,
+ next: N,
}
impl NodeKind for HeadersKind {
fn name(&self) -> &'static str {
"headers"
}
- fn instanciate(&self, config: serde_yml::Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Headers>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<Arc<dyn Node>> {
+ let config: Headers<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(Headers {
+ request: config.request,
+ response: config.response,
+ next: ic.instanciate_child(config.next)?,
+ }))
}
}
-impl Node for Headers {
+impl Node for Headers<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
diff --git a/src/modules/hosts.rs b/src/modules/hosts.rs
index e747e64..f486738 100644
--- a/src/modules/hosts.rs
+++ b/src/modules/hosts.rs
@@ -4,27 +4,38 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
+use anyhow::Result;
use futures::Future;
use hyper::header::HOST;
use serde::Deserialize;
-use serde_yml::Value;
use std::{collections::HashMap, pin::Pin, sync::Arc};
#[derive(Deserialize)]
#[serde(transparent)]
-struct Hosts(HashMap<String, DynNode>);
+struct Hosts<N>(HashMap<String, N>);
pub struct HostsKind;
impl NodeKind for HostsKind {
fn name(&self) -> &'static str {
"hosts"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Hosts>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: Hosts<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(Hosts(
+ config
+ .0
+ .into_iter()
+ .map(|(k, v)| anyhow::Ok((k, ic.instanciate_child(v)?)))
+ .collect::<Result<_>>()?,
+ )))
}
}
-impl Node for Hosts {
+impl Node for Hosts<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
diff --git a/src/modules/inspect.rs b/src/modules/inspect.rs
index c9b3abc..a0c68e6 100644
--- a/src/modules/inspect.rs
+++ b/src/modules/inspect.rs
@@ -4,7 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use futures::Future;
use log::debug;
@@ -14,20 +18,23 @@ use std::{pin::Pin, sync::Arc};
pub struct InspectKind;
#[derive(Deserialize)]
-pub struct Inspect {
- next: DynNode,
+pub struct Inspect<N> {
+ next: N,
}
impl NodeKind for InspectKind {
fn name(&self) -> &'static str {
"inspect"
}
- fn instanciate(&self, config: serde_yml::Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Inspect>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<Arc<dyn Node>> {
+ let config: Inspect<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(Inspect {
+ next: ic.instanciate_child(config.next)?,
+ }))
}
}
-impl Node for Inspect {
+impl Node for Inspect<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
diff --git a/src/modules/limits.rs b/src/modules/limits.rs
index dcfc508..4f8e85e 100644
--- a/src/modules/limits.rs
+++ b/src/modules/limits.rs
@@ -4,7 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use bytes::Bytes;
use futures::{Future, StreamExt};
@@ -21,12 +25,12 @@ use tokio::time::sleep_until;
pub struct LimitsKind;
#[derive(Deserialize)]
-pub struct Limits {
+pub struct Limits<N> {
#[serde(default)]
request: LimitParam,
#[serde(default)]
response: LimitParam,
- next: DynNode,
+ next: N,
}
#[derive(Debug, Clone, Deserialize, Default)]
@@ -40,12 +44,17 @@ impl NodeKind for LimitsKind {
fn name(&self) -> &'static str {
"limits"
}
- fn instanciate(&self, config: serde_yml::Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Limits>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: Limits<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(Limits {
+ request: config.request,
+ response: config.response,
+ next: ic.instanciate_child(config.next)?,
+ }))
}
}
-impl Node for Limits {
+impl Node for Limits<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
diff --git a/src/modules/loadbalance.rs b/src/modules/loadbalance.rs
index 8a93e69..1b09c86 100644
--- a/src/modules/loadbalance.rs
+++ b/src/modules/loadbalance.rs
@@ -8,10 +8,13 @@
//! Given a set of handlers, the handler that is the least busy will handle the next request.
//! Current implementation does not scale well for many handlers.
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use serde::Deserialize;
-use serde_yml::Value;
use std::{
future::Future,
pin::Pin,
@@ -24,22 +27,26 @@ use std::{
pub struct LoadBalanceKind;
#[derive(Deserialize)]
-struct LoadBalanceConfig(Vec<DynNode>);
+struct LoadBalanceConfig(Vec<DynNodeConfig>);
struct LoadBalance {
load: Vec<AtomicUsize>,
- config: LoadBalanceConfig,
+ nodes: Vec<DynNode>,
}
impl NodeKind for LoadBalanceKind {
fn name(&self) -> &'static str {
"loadbalance"
}
- fn instanciate(&self, config: Value) -> Result<Arc<dyn Node>> {
- let config = serde_yml::from_value::<LoadBalanceConfig>(config)?;
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: LoadBalanceConfig = ic.config().parse()?;
Ok(Arc::new(LoadBalance {
load: config.0.iter().map(|_| AtomicUsize::new(0)).collect(),
- config,
+ nodes: config
+ .0
+ .into_iter()
+ .map(|c| ic.instanciate_child(c))
+ .collect::<Result<_>>()?,
}))
}
}
@@ -59,7 +66,7 @@ impl Node for LoadBalance {
.ok_or(ServiceError::CustomStatic("zero routes to balance load"))?;
self.load[index].fetch_add(1, Ordering::Relaxed);
- let resp = self.config.0[index].handle(context, request).await;
+ let resp = self.nodes[index].handle(context, request).await;
self.load[index].fetch_sub(1, Ordering::Relaxed);
resp
})
diff --git a/src/modules/log.rs b/src/modules/log.rs
index 9efa3ad..e1a6205 100644
--- a/src/modules/log.rs
+++ b/src/modules/log.rs
@@ -4,7 +4,12 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
+use anyhow::Result;
use futures::Future;
use http::{
header::{HOST, USER_AGENT},
@@ -39,11 +44,12 @@ struct LogConfig {
format: Format,
#[serde(default)]
flush: bool,
- next: DynNode,
+ next: DynNodeConfig,
}
struct Log {
config: LogConfig,
+ next: DynNode,
file: RwLock<Option<(BufWriter<File>, Instant)>>,
}
#[derive(Deserialize, Default)]
@@ -71,9 +77,11 @@ impl NodeKind for LogKind {
fn name(&self) -> &'static str {
"log"
}
- fn instanciate(&self, config: serde_yml::Value) -> anyhow::Result<Arc<dyn Node>> {
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: LogConfig = ic.config().parse()?;
Ok(Arc::new(Log {
- config: serde_yml::from_value::<LogConfig>(config)?,
+ next: ic.instanciate_child(config.next.clone())?,
+ config,
file: Default::default(),
}))
}
@@ -257,7 +265,7 @@ impl Node for Log {
}
}
- self.config.next.handle(context, request).await
+ self.next.handle(context, request).await
})
}
}
diff --git a/src/modules/mod.rs b/src/modules/mod.rs
index b267a04..308fe4f 100644
--- a/src/modules/mod.rs
+++ b/src/modules/mod.rs
@@ -3,13 +3,13 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::error::ServiceError;
use crate::State;
+use crate::{config::DynNode, error::ServiceError};
+use anyhow::Result;
use bytes::Bytes;
use futures::Future;
use http_body_util::combinators::BoxBody;
use hyper::{Request, Response};
-use serde_yml::Value;
use std::{net::SocketAddr, pin::Pin, sync::Arc};
mod accesslog;
@@ -36,6 +36,7 @@ mod semaphore;
mod switch;
mod upgrade_insecure;
+pub use crate::config::InstContext;
pub type NodeRequest = Request<BoxBody<Bytes, ServiceError>>;
pub type NodeResponse = Response<BoxBody<Bytes, ServiceError>>;
@@ -76,7 +77,7 @@ pub struct NodeContext {
pub trait NodeKind: Send + Sync + 'static {
fn name(&self) -> &'static str;
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>>;
+ fn instanciate(&self, context: InstContext) -> Result<DynNode>;
}
pub trait Node: Send + Sync + 'static {
fn handle<'a>(
diff --git a/src/modules/paths.rs b/src/modules/paths.rs
index 7131664..8818502 100644
--- a/src/modules/paths.rs
+++ b/src/modules/paths.rs
@@ -4,7 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use futures::Future;
use http::Uri;
use regex::{Regex, RegexSet};
@@ -15,7 +19,6 @@ use serde::{
},
Deserialize,
};
-use serde_yml::Value;
use std::{collections::BTreeMap, marker::PhantomData, pin::Pin, sync::Arc};
pub struct PathsKind;
@@ -30,14 +33,14 @@ impl NodeKind for PathsKind {
fn name(&self) -> &'static str {
"paths"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- let routes = serde_yml::from_value::<SeqOrMap<DynNode>>(config)?.0;
+ fn instanciate(&self, ic: InstContext) -> anyhow::Result<Arc<dyn Node>> {
+ let routes = ic.config().parse::<SeqOrMap<DynNodeConfig>>()?.0;
let mut handlers = Vec::new();
let mut patterns = Vec::new();
let mut extractors = Vec::new();
- for (k, v) in routes {
- let pattern = format!("^{k}$");
- handlers.push(v);
+ for (pattern, config) in routes {
+ let pattern = format!("^{pattern}$");
+ handlers.push(ic.instanciate_child(config)?);
extractors.push(Regex::new(&pattern)?);
patterns.push(pattern);
}
diff --git a/src/modules/proxy.rs b/src/modules/proxy.rs
index aecd775..252e482 100644
--- a/src/modules/proxy.rs
+++ b/src/modules/proxy.rs
@@ -4,7 +4,8 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::ServiceError;
+use crate::{config::DynNode, modules::InstContext, ServiceError};
+use anyhow::Result;
use futures::Future;
use http::{Response, Version};
use http_body_util::BodyExt;
@@ -12,7 +13,6 @@ use hyper::{body::Incoming, http::HeaderValue, upgrade::OnUpgrade, StatusCode};
use hyper_util::rt::TokioIo;
use log::{debug, warn};
use serde::Deserialize;
-use serde_yml::Value;
use std::{
fmt::Display,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
@@ -53,8 +53,8 @@ impl NodeKind for ProxyKind {
fn name(&self) -> &'static str {
"proxy"
}
- fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Proxy>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ Ok(Arc::new(ic.config().parse::<Proxy>()?))
}
}
diff --git a/src/modules/ratelimit.rs b/src/modules/ratelimit.rs
index ce5ac68..34a8bca 100644
--- a/src/modules/ratelimit.rs
+++ b/src/modules/ratelimit.rs
@@ -4,7 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{cgi::set_cgi_variables, Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use futures::Future;
use http::{header::RETRY_AFTER, HeaderValue, Response, StatusCode};
@@ -26,7 +30,7 @@ pub struct RatelimitKind;
#[derive(Deserialize)]
pub struct RatelimitConfig {
- next: DynNode,
+ next: DynNodeConfig,
#[serde(default)]
identity: IdentityMode,
#[serde(default = "default_max_identities")]
@@ -62,6 +66,7 @@ enum LimitMode {
pub struct Ratelimit {
state: Arc<Mutex<HashMap<u64, IdentityState>>>,
config: RatelimitConfig,
+ next: DynNode,
}
struct IdentityState {
@@ -73,10 +78,12 @@ impl NodeKind for RatelimitKind {
fn name(&self) -> &'static str {
"ratelimit"
}
- fn instanciate(&self, config: serde_yml::Value) -> Result<Arc<dyn Node>> {
+ fn instanciate(&self, ic: InstContext) -> Result<Arc<dyn Node>> {
+ let config: RatelimitConfig = ic.config().parse()?;
Ok(Arc::new(Ratelimit {
state: Arc::new(Mutex::new(HashMap::new())),
- config: serde_yml::from_value::<RatelimitConfig>(config)?,
+ next: ic.instanciate_child(config.next.clone())?,
+ config,
}))
}
}
@@ -164,7 +171,7 @@ impl Node for Ratelimit {
}
}
- self.config.next.handle(context, request).await
+ self.next.handle(context, request).await
})
}
}
diff --git a/src/modules/redirect.rs b/src/modules/redirect.rs
index ef9115b..5163013 100644
--- a/src/modules/redirect.rs
+++ b/src/modules/redirect.rs
@@ -4,7 +4,7 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::error::ServiceError;
+use crate::{error::ServiceError, modules::InstContext};
use anyhow::Result;
use futures::Future;
use http_body_util::BodyExt;
@@ -21,8 +21,8 @@ impl NodeKind for RedirectKind {
fn name(&self) -> &'static str {
"redirect"
}
- fn instanciate(&self, config: serde_yml::Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Redirect>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<Arc<dyn Node>> {
+ Ok(Arc::new(ic.config().parse::<Redirect>()?))
}
}
diff --git a/src/modules/semaphore.rs b/src/modules/semaphore.rs
index 7aa8283..8fed518 100644
--- a/src/modules/semaphore.rs
+++ b/src/modules/semaphore.rs
@@ -4,10 +4,13 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use serde::Deserialize;
-use serde_yml::Value;
use std::{future::Future, pin::Pin, sync::Arc};
use tokio::sync::Semaphore;
@@ -16,23 +19,23 @@ pub struct SemaphoreKind;
#[derive(Deserialize)]
struct SemaphoreConfig {
permits: usize,
- next: DynNode,
+ next: DynNodeConfig,
}
struct SemaphoreState {
- config: SemaphoreConfig,
semaphore: Semaphore,
+ next: DynNode,
}
impl NodeKind for SemaphoreKind {
fn name(&self) -> &'static str {
"semaphore"
}
- fn instanciate(&self, config: Value) -> Result<Arc<dyn Node>> {
- let config = serde_yml::from_value::<SemaphoreConfig>(config)?;
+ fn instanciate(&self, ic: InstContext) -> Result<Arc<dyn Node>> {
+ let config = ic.config().parse::<SemaphoreConfig>()?;
Ok(Arc::new(SemaphoreState {
semaphore: Semaphore::new(config.permits),
- config,
+ next: ic.instanciate_child(config.next)?,
}))
}
}
@@ -44,7 +47,7 @@ impl Node for SemaphoreState {
) -> Pin<Box<dyn Future<Output = Result<NodeResponse, ServiceError>> + Send + Sync + 'a>> {
Box::pin(async move {
let _permit = self.semaphore.acquire().await;
- let resp = self.config.next.handle(context, request).await?;
+ let resp = self.next.handle(context, request).await?;
drop(_permit);
Ok(resp)
})
diff --git a/src/modules/switch.rs b/src/modules/switch.rs
index 0cc013c..c42216e 100644
--- a/src/modules/switch.rs
+++ b/src/modules/switch.rs
@@ -4,7 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use futures::Future;
use headers::{HeaderMapExt, Upgrade};
@@ -16,22 +20,27 @@ use std::{pin::Pin, sync::Arc};
pub struct SwitchKind;
#[derive(Deserialize)]
-pub struct Switch {
+pub struct Switch<N> {
condition: Condition,
- case_true: DynNode,
- case_false: DynNode,
+ case_true: N,
+ case_false: N,
}
impl NodeKind for SwitchKind {
fn name(&self) -> &'static str {
"switch"
}
- fn instanciate(&self, config: serde_yml::Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<Switch>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config: Switch<DynNodeConfig> = ic.config().parse()?;
+ Ok(Arc::new(Switch {
+ condition: config.condition,
+ case_true: ic.instanciate_child(config.case_true)?,
+ case_false: ic.instanciate_child(config.case_false)?,
+ }))
}
}
-impl Node for Switch {
+impl Node for Switch<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
diff --git a/src/modules/upgrade_insecure.rs b/src/modules/upgrade_insecure.rs
index 37fc04e..c06caa7 100644
--- a/src/modules/upgrade_insecure.rs
+++ b/src/modules/upgrade_insecure.rs
@@ -4,7 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse};
-use crate::{config::DynNode, error::ServiceError};
+use crate::{
+ config::{DynNode, DynNodeConfig},
+ error::ServiceError,
+ modules::InstContext,
+};
use anyhow::Result;
use futures::Future;
use http::{
@@ -18,18 +22,23 @@ use std::{pin::Pin, str::FromStr, sync::Arc};
pub struct UpgradeInsecureKind;
#[derive(Deserialize)]
-pub struct UpgradeInsecure([DynNode; 1]);
+pub struct UpgradeInsecure<N> {
+ next: N,
+}
impl NodeKind for UpgradeInsecureKind {
fn name(&self) -> &'static str {
"upgrade_insecure"
}
- fn instanciate(&self, config: serde_yml::Value) -> Result<Arc<dyn Node>> {
- Ok(Arc::new(serde_yml::from_value::<UpgradeInsecure>(config)?))
+ fn instanciate(&self, ic: InstContext) -> Result<DynNode> {
+ let config = ic.config().parse::<UpgradeInsecure<DynNodeConfig>>()?;
+ Ok(Arc::new(UpgradeInsecure::<DynNode> {
+ next: ic.instanciate_child(config.next)?,
+ }))
}
}
-impl Node for UpgradeInsecure {
+impl Node for UpgradeInsecure<DynNode> {
fn handle<'a>(
&'a self,
context: &'a mut NodeContext,
@@ -67,7 +76,7 @@ impl Node for UpgradeInsecure {
);
return Ok(resp);
}
- self.0[0].handle(context, request).await
+ self.next.handle(context, request).await
})
}
}