v0.31.2
This commit is contained in:
1 parent
24af7478b8
commit
0facfe6f5c
44 files changed
+898
-321
No files matched your search
@@ -2,7 +2,7 @@
|
||||
|
||||
Read README.md, ROADMAP.md and docs/TEST_REPORT.md before making changes.
|
||||
The project name is RustProxy; the Cargo package/binary remains `rust-proxy` for
|
||||
compatibility. Current source version: 0.31.1.
|
||||
compatibility. Current source version: 0.31.2.
|
||||
|
||||
## Delivery
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
> Operator-facing release history. Historical engineering notes are kept in
|
||||
> [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md).
|
||||
|
||||
## 0.31.2
|
||||
|
||||
- Refresh core runtime, security, TLS certificate generation, database, Swagger UI and monitoring libraries for current stable versions. Keep persisted data, cache format, configuration, panel behavior, HTTP/3 and proxy routing compatible.
|
||||
- Move the build toolchain baseline to Rust 1.99. This dependency refresh must pass compilation, runtime and integration checks before production deployment.
|
||||
|
||||
## 0.31.1
|
||||
|
||||
- Make mobile and reverse-proxied panel reloads reliable by loading the dashboard JavaScript as one ordered resource, waiting for its initialization before fetching settings, and showing a recoverable error if essential resources are unavailable. Preserve the loading indicator and preferences.
|
||||
|
||||
Generated
+322
-106
@@ -23,13 +23,28 @@ version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
||||
|
||||
[[package]]
|
||||
name = "alloc-no-stdlib"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2fb6cfd47bf496ff64095c20eaba0c201404ee38714d4142fcfa1dc334fcc7a"
|
||||
|
||||
[[package]]
|
||||
name = "alloc-stdlib"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-no-stdlib 2.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloc-stdlib"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5c1865780388bfa186411ab5f247819487fc4864c6e9c3106611fa347586e1"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib 3.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -105,16 +120,55 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
checksum = "134c52ddac6d63c576bef8168db10c83c49c26444ecbc68060fef078925a901c"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures 0.2.17",
|
||||
"cpufeatures 0.3.1",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8"
|
||||
dependencies = [
|
||||
"asn1-rs-derive",
|
||||
"asn1-rs-impl",
|
||||
"displaydoc",
|
||||
"nom",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
"thiserror 2.0.21",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-derive"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
"synstructure 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-impl"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.48"
|
||||
@@ -155,7 +209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"futures-util",
|
||||
@@ -207,6 +261,12 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
@@ -231,6 +291,15 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.2"
|
||||
@@ -239,11 +308,11 @@ checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
checksum = "5b5d4d889834ee8ecfc0f8426ad30faf7cdcb10f741a8e6d7224d95325479f6f"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -255,15 +324,35 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "8.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor",
|
||||
"alloc-no-stdlib 2.0.4",
|
||||
"alloc-stdlib 0.2.4",
|
||||
"brotli-decompressor 5.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "9.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8b851b75c23ca7873623d612fe49bd1989aeb03d08fb9432187eb253d3d4c6b"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib 3.0.0",
|
||||
"alloc-stdlib 0.3.0",
|
||||
"brotli-decompressor 6.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -272,8 +361,18 @@ version = "5.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"alloc-no-stdlib 2.0.4",
|
||||
"alloc-stdlib 0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli-decompressor"
|
||||
version = "6.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "941cd9bd4ddab83cb46fa5a2d428f1c857b24ac78cb876cf7beb710840934bd7"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib 3.0.0",
|
||||
"alloc-stdlib 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -389,6 +488,12 @@ version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486"
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
@@ -401,7 +506,7 @@ version = "0.4.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef16c47ba2797aa6a909cc37d39911f3a6743811fe7408ac0b0cc0276b656e9"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"brotli 8.0.4",
|
||||
"compression-core",
|
||||
"flate2",
|
||||
"memchr",
|
||||
@@ -482,12 +587,44 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctutils"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574"
|
||||
dependencies = [
|
||||
"cmov",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06"
|
||||
|
||||
[[package]]
|
||||
name = "der-parser"
|
||||
version = "10.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"displaydoc",
|
||||
"nom",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.8"
|
||||
@@ -500,9 +637,19 @@ version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common 0.1.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.1",
|
||||
"crypto-common 0.2.2",
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -582,9 +729,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -599,9 +746,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
@@ -781,9 +928,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
@@ -798,11 +945,11 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.10.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||
checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.5",
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -819,9 +966,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hpx-h3"
|
||||
version = "2.5.19"
|
||||
version = "2.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4b773a1c31de56cc61c87de6d1db73ed838c1d23d8fad454b302a38ae178ad7"
|
||||
checksum = "f137abcec3be4197580d1429894c2c33dc438a6d830500dd11bf0a1b958472ff"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fastrand",
|
||||
@@ -879,10 +1026,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.11.1"
|
||||
name = "hybrid-array"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43"
|
||||
checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -1288,12 +1444,31 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -1322,6 +1497,15 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oid-registry"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
@@ -1365,22 +1549,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
"getrandom 0.4.3",
|
||||
"phc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.6"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
|
||||
checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.23.1",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
@@ -1390,6 +1573,17 @@ version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "phc"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"ctutils",
|
||||
"getrandom 0.4.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
@@ -1534,24 +1728,13 @@ version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
@@ -1566,16 +1749,6 @@ dependencies = [
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
@@ -1586,15 +1759,6 @@ dependencies = [
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
@@ -1621,14 +1785,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rcgen"
|
||||
version = "0.13.2"
|
||||
version = "0.14.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
|
||||
checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8"
|
||||
dependencies = [
|
||||
"pem",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"time",
|
||||
"x509-parser",
|
||||
"yasna",
|
||||
]
|
||||
|
||||
@@ -1716,14 +1881,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-proxy"
|
||||
version = "0.31.1"
|
||||
version = "0.31.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"argon2",
|
||||
"axum",
|
||||
"base64",
|
||||
"brotli",
|
||||
"base64 0.22.1",
|
||||
"brotli 9.0.0",
|
||||
"bytes",
|
||||
"clap",
|
||||
"flate2",
|
||||
@@ -1741,7 +1906,7 @@ dependencies = [
|
||||
"libc",
|
||||
"metrics-process",
|
||||
"quinn",
|
||||
"rand 0.8.8",
|
||||
"rand 0.10.3",
|
||||
"rcgen",
|
||||
"regex",
|
||||
"ring",
|
||||
@@ -1776,6 +1941,15 @@ version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
||||
|
||||
[[package]]
|
||||
name = "rusticata-macros"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.5"
|
||||
@@ -1925,7 +2099,7 @@ checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1936,7 +2110,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2009,9 +2183,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
|
||||
checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71"
|
||||
dependencies = [
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
@@ -2020,12 +2194,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
|
||||
checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"cfg-if",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
@@ -2034,12 +2209,11 @@ dependencies = [
|
||||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashbrown 0.15.5",
|
||||
"hashbrown 0.16.1",
|
||||
"hashlink",
|
||||
"indexmap",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"sha2",
|
||||
@@ -2053,9 +2227,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d"
|
||||
checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2066,15 +2240,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros-core"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
|
||||
checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"heck",
|
||||
"hex",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
@@ -2089,12 +2263,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-sqlite"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
|
||||
checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"flume",
|
||||
"form_urlencoded",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
@@ -2104,7 +2279,6 @@ dependencies = [
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_urlencoded",
|
||||
"sqlx-core",
|
||||
"thiserror 2.0.21",
|
||||
"tracing",
|
||||
@@ -2157,6 +2331,17 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.14.0"
|
||||
@@ -2170,9 +2355,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.38.4"
|
||||
version = "0.39.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f"
|
||||
checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"memchr",
|
||||
@@ -2271,6 +2456,7 @@ dependencies = [
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2279,6 +2465,16 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.4"
|
||||
@@ -2297,9 +2493,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.53.1"
|
||||
version = "1.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
|
||||
checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@@ -2387,9 +2583,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.11"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
|
||||
checksum = "08a05a66a4fdd61cbbe0a1d755ffe0ca6aba159dd4820936a0ff8a8278245b9c"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"bitflags",
|
||||
@@ -2397,6 +2593,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -2562,9 +2759,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
@@ -2875,11 +3072,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
|
||||
|
||||
[[package]]
|
||||
name = "yasna"
|
||||
version = "0.5.2"
|
||||
name = "x509-parser"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
|
||||
checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"data-encoding",
|
||||
"der-parser",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"oid-registry",
|
||||
"ring",
|
||||
"rusticata-macros",
|
||||
"thiserror 2.0.21",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yasna"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -2903,7 +3119,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"synstructure",
|
||||
"synstructure 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2944,7 +3160,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.6",
|
||||
"synstructure",
|
||||
"synstructure 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+13
-13
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "rust-proxy"
|
||||
version = "0.31.1"
|
||||
version = "0.31.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
rust-version = "1.99"
|
||||
description = "RustProxy - independent reverse proxy with local administration"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
argon2 = { version = "0.5.3", features = ["std"] }
|
||||
argon2 = "0.6"
|
||||
sha2 = "0.10"
|
||||
ipnet = "2.11"
|
||||
rpassword = "7.4"
|
||||
@@ -15,7 +15,7 @@ zeroize = "1.8"
|
||||
anyhow = "1.0"
|
||||
libc = "0.2"
|
||||
arc-swap = "1.7"
|
||||
axum = { version = "0.8", default-features = false, features = ["http1", "http2", "json", "query", "tokio", "ws"] }
|
||||
axum = { version = "0.8.9", default-features = false, features = ["http1", "http2", "json", "query", "tokio", "ws"] }
|
||||
bytes = "1.10"
|
||||
clap = { version = "4.5", features = ["derive", "env"] }
|
||||
futures-util = "0.3"
|
||||
@@ -23,15 +23,15 @@ fs2 = "0.4"
|
||||
http = "1.3"
|
||||
http-body = "1.0"
|
||||
http-body-util = "0.1"
|
||||
hyper = { version = "1.6", features = ["client", "server", "http1", "http2"] }
|
||||
hyper = { version = "1.12.0", features = ["client", "server", "http1", "http2"] }
|
||||
hyper-util = { version = "0.1", features = ["client-legacy", "server-auto", "http1", "http2", "tokio"] }
|
||||
rand = "0.8"
|
||||
rand = "0.10"
|
||||
regex = "1.11"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite"] }
|
||||
sqlx = { version = "0.9", default-features = false, features = ["runtime-tokio", "sqlite"] }
|
||||
subtle = "2.6"
|
||||
tokio = { version = "1.44", features = ["macros", "rt-multi-thread", "net", "signal", "time", "sync", "fs", "io-util", "process"] }
|
||||
tokio = { version = "1.53.2", features = ["macros", "rt-multi-thread", "net", "signal", "time", "sync", "fs", "io-util", "process"] }
|
||||
tokio-util = { version = "0.7", features = ["rt", "io"] }
|
||||
tower = { version = "0.5", features = ["util"] }
|
||||
tracing = "0.1"
|
||||
@@ -46,19 +46,19 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["ring",
|
||||
hyper-rustls = { version = "0.27", default-features = false, features = ["ring", "http1", "http2", "tls12"] }
|
||||
rustls-pemfile = "2.2"
|
||||
webpki-roots = "1.0"
|
||||
rcgen = { version = "0.13.2", default-features = false, features = ["crypto", "pem", "ring"] }
|
||||
rcgen = { version = "0.14.10", default-features = false, features = ["crypto", "pem", "ring"] }
|
||||
|
||||
httpdate = "1.0"
|
||||
tower-http = { version = "0.6.8", default-features = false, features = ["compression-gzip", "compression-br", "compression-zstd", "compression-deflate"] }
|
||||
tower-http = { version = "0.7.1", default-features = false, features = ["compression-gzip", "compression-br", "compression-zstd", "compression-deflate"] }
|
||||
flate2 = "1.1"
|
||||
brotli = "8"
|
||||
brotli = "9"
|
||||
zstd = "0.14"
|
||||
|
||||
h3 = { package = "hpx-h3", version = "=2.5.19", features = ["quinn"] }
|
||||
h3 = { package = "hpx-h3", version = "=2.5.20", features = ["quinn"] }
|
||||
quinn = { version = "0.11.12", default-features = false, features = ["runtime-tokio", "rustls-ring"] }
|
||||
|
||||
metrics-process = { version = "=2.4.3", default-features = false, features = ["dummy"] }
|
||||
sysinfo = { version = "=0.38.4", default-features = false, features = ["network", "system"] }
|
||||
sysinfo = { version = "0.39", default-features = false, features = ["network", "system"] }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "macos", target_os = "windows"))'.dependencies]
|
||||
systemstat = "=0.2.7"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM rust:1.88-trixie AS build
|
||||
FROM rust:1.99-trixie AS build
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
RUN if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi \
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# RustProxy 0.31.1
|
||||
# RustProxy 0.31.2
|
||||
|
||||
**0.31.0 panel/cache additions:** browser-negotiated Zstd/Brotli/gzip for HTML, JS and CSS (Settings → Administration); optional HTTP BAN/PURGE restricted to explicit IP/CIDR allowlist (Settings → Cache, disabled by default); System information Refresh button beside the API badge. See [Settings](docs/SETTINGS.md) and [Cache](docs/CACHE.md).
|
||||
|
||||
Independent Rust reverse proxy and static Web Server with a dashboard, SQLite configuration,
|
||||
streaming HTTP/TLS/WebSocket forwarding, raw TCP/UDP stream hosts, IP-bound vhosts and backend pools.
|
||||
|
||||
**0.31.1 release source.**
|
||||
**0.31.2 dependency-refresh candidate source (Cargo verification required).**
|
||||
|
||||
## Layer-4 streams
|
||||
|
||||
@@ -13,7 +13,18 @@ Raw TCP/UDP forwarding has its own **Streams** page in the primary navigation, a
|
||||
|
||||
Named HTTP/HTTPS listener bind addresses use the same chip-style editor as vhost hostnames, including multi-address listeners.
|
||||
|
||||
## What changed in 0.31.1
|
||||
## What changed in 0.31.2
|
||||
|
||||
- Refresh Rust dependencies to current stable API families, including SQLx 0.9, Rand 0.10, Argon2 0.6, rcgen 0.14, Tower HTTP 0.7, Brotli 9 and Sysinfo 0.39. Retain SQLite schema 9, serialized settings, TLS/QUIC behavior and administration contracts.
|
||||
- Raise the minimum compiler version to Rust 1.99 for current dependency requirements, including Docker and RouterOS build images.
|
||||
- **Release gate open:** the updated dependency graph must be resolved with Cargo; the bundled lockfile is a prior-resolution snapshot and must be refreshed and tested before release. The source package is not production-verified.
|
||||
- **Complete the dependency upgrade on a Rust 1.99+ build host** with `bash scripts/resolve_dependencies_0312.sh` (registry and the pinned Swagger UI asset must be reachable, or supply an audited local archive). This calls `cargo update`, verifies the refreshed lockfile and runs formatting, clippy, build and tests. Review the lockfile diff and fix any Rust compiler errors before publishing.
|
||||
|
||||
## Previously in 0.31.1
|
||||
|
||||
- Improve mobile panel startup via an ordered JavaScript resource, versioned URLs and retry of temporary config failures.
|
||||
|
||||
## Earlier panel improvements
|
||||
|
||||
- Improved mobile panel loading behind reverse proxies: one ordered JavaScript resource, versioned assets and retry of temporary configuration errors.
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# RustProxy roadmap
|
||||
|
||||
## 0.31.2 - Dependency updates
|
||||
|
||||
- [x] Prepare stable dependency-family and code API migrations, preserving existing wire/configuration/SQLite formats.
|
||||
- [ ] Resolve and commit an actual Cargo.lock using Rust 1.99; run format, clippy, tests and release builds.
|
||||
- [ ] Confirm HTTP/3/QUIC, TLS, SQLite migration/restore, auth, compression, panel and cache BAN/PURGE in real integration tests.
|
||||
|
||||
## 0.31.1 - Mobile panel loading stability
|
||||
|
||||
- [x] Load dashboard modules as one ordered JavaScript resource and wait for document/module readiness before session initialization.
|
||||
|
||||
+39
-37
@@ -1,18 +1,18 @@
|
||||
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
|
||||
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
|
||||
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
|
||||
a414a328c21b7c4f81e5448a452258d8069ce40243a41e4e06b8950386494b96 AGENTS.md
|
||||
15a02c3476a51deade17d25b56c45068ebe50e5f127680f387c98777a8854c5b CHANGELOG.md
|
||||
e95030916a57faaa3fceb509f4aeec167b789022703f2e0cc09afaa0a071c89b Cargo.lock
|
||||
a1aba61dca0fa20436ce0651baf6c59fe2602d2e9b4ee48b6900ba0351d7c208 Cargo.toml
|
||||
75b008f237b2226c62a7721b7901018ce83bfc2726f047a83a17c66fcb799f3b Dockerfile
|
||||
3a0ee0e90ee5a75cfbc6b5497546861ae56b4fc5f343a5d83f722cdddbd4b8e1 README.md
|
||||
d1914a8afe65d73620bafb6cb4d9b13206ebde5674f79da77fab5cec6bbe81c7 ROADMAP.md
|
||||
047139f3c7c40778001602e77ef69302e3bef8f495d48d780953c97c401fa2d4 AGENTS.md
|
||||
9b42590541917e8d92fa26a5831b9dd43503f179337f8ed6c3a4d7d1de84d3b7 CHANGELOG.md
|
||||
50238f33805987e7301f9ed06a8fd51b74b0e01330dbbae42c3a2fd8bd0d8e86 Cargo.lock
|
||||
c6cc53538b89420b82526bc766d73a5fc93a82017f8fdb4bf767335dd613c90a Cargo.toml
|
||||
25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile
|
||||
f3ad7c8b4b450bcb32a7c68f90e868726154b3f1ea88c7d9ba5173850334d24e README.md
|
||||
e2187e12e611638f85bae7808947b46a5de3c12db234ec217767ac8aefd1919a ROADMAP.md
|
||||
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
|
||||
f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs
|
||||
ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md
|
||||
4834c278430ddf3bf842995735195368f1ac2c3b5c1d749df4f9238caf1e2b25 deploy/rustproxy.service
|
||||
90c5b561079142ff094330066a6a551d7b0e1705012613341ec3defb7b3dc199 dev.sh
|
||||
31d018c9ef676d397bafa66c5099a57bc41ee56bbb272df59bc521574d920c82 dev.sh
|
||||
5f51efe621f45512a466fa7f2656867c6502a135ca040f4bfeec06a6919b39b6 docker-agent/README.md
|
||||
7fee7d51a7deb67e73e53a0bdff3aa4630aea9bab9b23a0f9b8001b2169892b1 docker-agent/certificate.go
|
||||
5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example
|
||||
@@ -28,7 +28,7 @@ e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/n
|
||||
875faa3969df9409d79f050a84b4183496f557edda3d34aaec19fe556cf8c471 docs/ACME_PROVIDERS.md
|
||||
ec15163f5f51c9212003b7edd1fc380321311c2496fbaa9b45c838281b9af475 docs/ACME_RFC2136.md
|
||||
8ded74f422426606238fa9876b49277a7bc56ddd96062a836cdd6ddbf78bd335 docs/API.md
|
||||
f054206a09bed81018708608cdeecbfd09018ee7cec9d3abc038f8a37d47b815 docs/API_ACCESS.md
|
||||
6ed12684b970fb7173fc9d1ad1da8ecce784eb38b29b7e6e631309fb25446807 docs/API_ACCESS.md
|
||||
4c5288c9ed427cfb919bd023ed9a321dcf6d64bc4cad3e1768393356c5d835ba docs/ARCHITECTURE.md
|
||||
e9e2327684a4ae2d1ca4a0b3fb67d0ff83b5a150d38d4de4cfad2c5d3102eab6 docs/CACHE.md
|
||||
9113f3abbfe81bafc4855d8680df8aa2a381ecc358cc3e1a0f78b6b756421117 docs/CACHE_DIAGNOSTICS.md
|
||||
@@ -65,7 +65,7 @@ a2bb837cdd3559d58158880c1f832badf64035376a3623050bb4e40c2bca104c docs/ROADMAP_H
|
||||
007537ee971463072b2482dfa4fdcf1955f837ced0b711d4eef3b6267d16d4f6 docs/SECURITY_AUDIT_0.31.0.md
|
||||
2cd9dadc49a5474e4ab06d5c01fa38c27cab436578cb9c9f327a37b3406d2e13 docs/SETTINGS.md
|
||||
3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md
|
||||
14ef0d7935c0ff9a1557ca3a8cac97274be7e2b8e0fd68ea6cce87372989865d docs/TEST_REPORT.md
|
||||
8ce2502e64a159a23eadebafdc9f0d65d384e8099eb63368748ee7e0269e299d docs/TEST_REPORT.md
|
||||
cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md
|
||||
751c9b13b47d0959a8575d6e82bdecba988b950a374ba0b084fbba08b4b65ebe docs/TRANSFORM.md
|
||||
660687fc961f71aae2bdf71595aa47a336fa782f4e78ceee8c1d1e63e46393ee docs/TRANSPORT.md
|
||||
@@ -131,7 +131,7 @@ fde541cd76bc3fa2b6765822bf04baf9f9ea3e4b6e185e76ff467270ddbd945e install-debian
|
||||
2d4c859aae5ba357a6d30024c8487ae8f59f01b192711f13f9c2f8fee0f48b50 integrations/opnsense-acme/README.md
|
||||
c73b814a42bb4ff703441dbd177ee625055eeb115f9b5bb0c2150aede573a159 integrations/opnsense-acme/opnsense-rustproxy-cert-deploy.sh
|
||||
45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 integrations/routeros-oci-build/.env.routeros.example
|
||||
cdb15e23f04b4a330b7ab8869eb38da72b3c27729fa7fcf164e3d9e8e893a651 integrations/routeros-oci-build/Dockerfile.routeros
|
||||
11aa102de1089ecc375f20c4bd5420fcfd3b2ef23fe0c8bdf04953fcfaf40ef0 integrations/routeros-oci-build/Dockerfile.routeros
|
||||
7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e integrations/routeros-oci-build/Dockerfile.routeros.dockerignore
|
||||
cb01fdef286f38b442dd9252c23c4f8e44eb72e9ecdd99adb926e8e9f6318bb1 integrations/routeros-oci-build/README.md
|
||||
96563a83e87cfb33e238c2a970ce36b1a1b51e91318d09b123c3755002ba2646 integrations/routeros-oci-build/build-routeros-oci.sh
|
||||
@@ -156,7 +156,7 @@ ee50f38c037765fab742e888d4b9fc4d874c31c785c6064d4eca3d2c4d24d944 nagios/check_r
|
||||
6e036f736a2b40feb78eec17bf34871771524190f283591d1c89e48e6d2271e6 nagios/check_rustproxy_queues
|
||||
7458b2d74495e6fb1c1edeea4d5a793705476431dadb60627187111f91441e13 rust-toolchain.toml
|
||||
2145ad17ae7a823756371ceab6bdbeb332211df93193ffb4e486f7a80c4f29ae scripts/README.md
|
||||
2044e6d979712457d6ebc3483efa029cc6de22416ee85898d950300015bd815d scripts/build/swagger_assets.py
|
||||
a1f55872201b5178f8af5e2beee6b3a68e37026831e41426ee62a8c15c1cf333 scripts/build/swagger_assets.py
|
||||
e4d15aa6aa864d04b9b30aca33826b833632af02ca643f8b23e219a5ea1298dd scripts/deploy/deploy.py
|
||||
cc268d9d3dd125c9cbff2522fe3137a561fb4fcbd5ee461d70ef2e3a8241cdb1 scripts/deploy/deploy_core.py
|
||||
8c7da13e95bcddd2bd1b4f1b22ade1c5ac1ecf54448601daa8416905d4239751 scripts/deploy/deploy_runtime.py
|
||||
@@ -164,6 +164,7 @@ a641bd35400c56f3b68b0a92e97b602d06090f03972ca8f01fa744e90dd97403 scripts/deploy
|
||||
2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migration/migrate_npm.py
|
||||
a459d95bbb0076f33ade8b273b813308167f9d8d45242b309a2d18320ca0093c scripts/migration/migrate_npm_api.py
|
||||
31ccad3ec3c880119f3b7002f0c6652f8b43634edad8ab3763cb0e4497d0bb1f scripts/migration/migrate_npm_core.py
|
||||
308ceeca51ddfb43db8be8286d5286aff1a44979f84af87d9346fb4176499c0c scripts/resolve_dependencies_0312.sh
|
||||
20124173452b4514bad8850c1637569a9a191882478451e549bd16b432605fc1 scripts/runtime/acme_provider.py
|
||||
e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/runtime/acme_rfc2136.py
|
||||
22e880e98c7b9d700c7a4c02b9b1d543e5dbd1f4ecdca42813a1083165b32a19 scripts/version.py
|
||||
@@ -188,14 +189,14 @@ bc9eb32f0d2785bac122d5eff44742cd24306c503daa065564383d46416b2211 src/admin/acce
|
||||
d7aa68a78a4cb5a61342d92c1f3209af230d0f9ca3fea1115f67078372e6f37c src/admin/file_logs_io.rs
|
||||
899e87a73bfe46c4078a46f9e1eb1c17b384472e8a30d2bab5486872b43fc809 src/admin/logging_api.rs
|
||||
58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs
|
||||
2f7e9ff687147cbf751f88828479e970727594d7c21726b1470e8c59ea40289e src/admin/openapi.json
|
||||
8c58aec76463fc512ee87a8cceff7b905488c6f70185c055c496d235badea6d7 src/admin/openapi.json
|
||||
0d1a5324e8ad32f5ab0861b722663f50f066e256bc5fd5dcced6a63f2bd399d1 src/admin/operations.rs
|
||||
2b0ec6df0ed9e1bdc5c9e4ff1758b59dd14abc150f98365ac3edcf4672908e48 src/admin/panel_assets.rs
|
||||
75bbedd54fdabb2b489642a642ee7c6533f69cdacb85e251763a56ba11174d5c src/admin/panel_content.rs
|
||||
7c113cd9d8642a808c73ebee24e695f7a318995e39dc2b81e747d2b2246e277f src/admin/preferences.rs
|
||||
d9e90166d2199152585a5208feedfc1dd1395dc32ddbd2a094ae4a1aee45e115 src/admin/preferences_columns.rs
|
||||
9baf3c0ec4c543bda51605930ac57d6425c5e03e06d29ff755a7ce3ff611aeaf src/admin/public.rs
|
||||
5135f743f628103deda9f50629065ca343f7a5bf872dcd3cc0033ac2971e361b src/admin/public/tests.rs
|
||||
c9f952bec99dfe1d5a60998a38bd177f82f54de567230413e0c322ee561eba7c src/admin/public/tests.rs
|
||||
6125419b099cf4d330767bc07424d52a2b400f273d31084c058c43a72a6427b7 src/admin/refresh_preferences.rs
|
||||
f99655cf19b04b1eb7fb49d12bfd2ba1ef2c095b81e80ef46cfddfacefc91936 src/admin/router.rs
|
||||
8708427263cd4caf8454b9115e89acb09172483a090c6b0bd43811dd9e52c7f6 src/admin/session.rs
|
||||
@@ -207,7 +208,7 @@ cadd7dca29b9aad8c77c7bc23dd249ea37a15adfa4d636a8ef00273a91ce35dc src/admin/tail
|
||||
5df98246c46135a176b4b8b705c92fd328719e82eb7342f088256abb976d5ae7 src/admin/tests/part_01.rs
|
||||
6f8c1e30babb2cc4cb7accf4bfa28338651a469b16341f6e7eaec881dd9abf2b src/admin/tests/part_02.rs
|
||||
34cf08cafb63cf7a8a6336e5c81fc99abfce8669662e387a3702c3a532be8056 src/admin/tests/part_03.rs
|
||||
a1e14bfad1ba42045421fb761e0cc490cc30530409b22416db7e727548c06b93 src/admin/tests/part_04.rs
|
||||
2137a53991c9337988545c4472b4ffcb9a111cac9c460ebbec7e51c0be8f5630 src/admin/tests/part_04.rs
|
||||
5f0062abc42a3b81c79088dc0824ab4c81a22c6ea765e77d2527d07d13bffd58 src/admin/tests/part_05.rs
|
||||
311c4ad16b3ad5b295a339241afd4ca8ea0d3c7f54f609c27518b4de1b4a4b71 src/admin/tests/part_06.rs
|
||||
5fa006413fd93529ac9aaa4ffb28bf6b0b88d0a55a0bf0c49576db4a06fe9fc3 src/admin/tests/part_07.rs
|
||||
@@ -255,28 +256,28 @@ a6e7bbff4fd2d50ecf759ed28161a672d7b483af2b35c9f912746a7a06a9f476 src/admin/web/
|
||||
f2689283cfe43d6aae911b0df778a69335089be230fbfc6de5dcce1801c38427 src/admin/web/vhosts/render.js
|
||||
91e3417fba7e8f59023cd64813befccc8341741cd4aa53dfa1ec68a03c7f4632 src/admin/web/web-server-profiles.js
|
||||
de3326d4fae254eb0b8c190f02e762f4094dcba86bfdcb24c5fb472676ebcc4b src/admin/web/web-server.js
|
||||
98c00d99822f9be54fc58afb6d4b577901712effd2c5eddce014312681864625 src/auth/files.rs
|
||||
76b0d92e4360e06ae97f87c0f6ddde4962c44efdeec9727e5b9a720598302891 src/auth/files.rs
|
||||
b2f948bcc3dbd24952c7b1b02d41d06757f49f0c38a78caf51f8f8a687d8480d src/auth/files/tests.rs
|
||||
97b5a060f24a102bbaa2f406426e985236c0d719b8038db210c25b34fcd8e923 src/auth/mod.rs
|
||||
105cb934956457a68adc14f2c64569d9d1d40b8da0a7908a9a59fc2153a939c9 src/auth/mod.rs
|
||||
c96a87e82ff0c1c9ce3d0cd5135903bb53af78d1c4fb6ebfe5fea503bf5644ea src/auth/store.rs
|
||||
745226cabb007819370fc6aa680995a03a8a13fa057dacfab0fcc21d0c43bbe4 src/auth/tests.rs
|
||||
09c3a622a7d2169e49e3369ea5f0f816eab2f717eaf1675c625d518c8f34d367 src/backup.rs
|
||||
ebce56a154c57bf20d6618de161359c7c49392d7d24e8d23428b7d78a7a060c1 src/backup/archive.rs
|
||||
76aa2afe9e7302be39b3512f30d0448d9134300f8ae930d64ada40cf3ad4a8a3 src/backup/archive.rs
|
||||
732cd4f9ed38ae69f8bbae61b1db8baaa6f86b07ca21c41f24a206a30f0a96fc src/backup/ownership.rs
|
||||
076350506215faec3cfe7d9de4e60baf29672cc22a41eabe601dcc510c257799 src/backup/snapshot.rs
|
||||
a00571b409d305af86ab2a57a25434b5ab58706772df440d017fc1fec097f7b4 src/backup/tests.rs
|
||||
b2ce83a48736c082e6b820e4e363cca7a5018f3ae2a1ce5adfd6af1a03d62598 src/backup/validation.rs
|
||||
f5afae218f3a6f91bc2f1f5a90094630ce5b7ca574de61807b5a317b13937ec2 src/backup/validation.rs
|
||||
426a946b2c9e244f09f6cbc5f9664b8ad0737529a9bb5a3d549d27196a1f8043 src/balance.rs
|
||||
3d4b3986f54bed99212063b6d135d40a202d556a3e4d253e24c74be4cb9e8ebc src/balance/health.rs
|
||||
a1e5d43f5417d71f3974812cb539c0932f206e2290090917f067494f3587fc72 src/balance/lease_runtime.rs
|
||||
a57f352aad3b5a9a6debb59625864808f0a0ff7b11db83b47d6825a5932ca16b src/balance/model.rs
|
||||
7628aa331c9dbb02650392afeb65838cc23e32939c002d5d0488a8f3b8c11bd4 src/balance/pool_core.rs
|
||||
46d739f14b510b35aaccf18fb9f21593c92d8b8d3ea47828271574df5ed008e5 src/balance/model.rs
|
||||
2a6b20ffc2b326cb2380beb0805b52181306d3b9cc57330d67930000535c03f9 src/balance/pool_core.rs
|
||||
477132dd381e23f1be197c32ff58a0925f41d3e6aa63fe77c10973cefb2b76f6 src/balance/pool_selection.rs
|
||||
519cb798040ac9a65d9ebe43ae11ef729bd78c5c4a5c756490458add4b6fdc24 src/balance/pool_view.rs
|
||||
7d94d1bd4eb698e0588b822d93e56e3dc5594c46edac4aa9713f148893ebe6d5 src/balance/tests.rs
|
||||
81141aedaec495183b88a4f6362a71f56ee14cbfa83d71fce51d8abed8df3f5d src/balance/tests/part_01.rs
|
||||
490b38a8b1472dbd9fcb823ec0ed0e92498fb9528b982bcee340c8579e89ae36 src/balance/tests/part_02.rs
|
||||
ccb3e1988d6ae5825450e894eea896d53ae9fac150d0175ea5e742413e55105a src/basic_auth.rs
|
||||
5049d2bd01a9c866f3bad2288d8d594df96bba6045dfe15cd22b4173562b7388 src/basic_auth.rs
|
||||
9a8858b7ff461a4d961ba701ca3ed6eaec15c3f7f05eef8e0e30edc63617605a src/basic_auth/tests.rs
|
||||
7def03ace07dc38a4806277d75b6ce673d33ddaaf1c4855fa0a3372be1cf9489 src/cache/cold.rs
|
||||
a9eeb93519aae2de7a1282296ced91d2eb8307cc966f8fbf6085af0fd21b0ad2 src/cache/cold/cleanup.rs
|
||||
@@ -309,7 +310,7 @@ d426d341a7678f8ec70406d86052c97d0f49804bbcd8402951103d61a735b9da src/cache/poli
|
||||
7e2bcfbb2b57732cdc704d9df7a9298fb80e735e37984431bc936630e41e7f8a src/cache/range.rs
|
||||
7ad96b3baae36ec49c90d03b380e749944fb3d54abb045c90db3a35f6b89b1c1 src/cache/tests.rs
|
||||
13dab7b7a587cfc146826a92ab94ec9e04f7e500b7a84e678d77226829844923 src/cache/tests/cold_tier_tests.rs
|
||||
b4f609ee900c556b613136bbd3fce11c242a415d277ac3d25bdcb72275b66832 src/cache/tests/cold_tier_tests/part_01.rs
|
||||
9cd6a8407888c8f9cbf7652774186d1ca588d2d78308750e8aa1316b4e3e5cb9 src/cache/tests/cold_tier_tests/part_01.rs
|
||||
f43aa6c231f2c85ee3390dddab74aab2eea2a3f874acb35f145c23866266cb52 src/cache/tests/cold_tier_tests/part_02.rs
|
||||
22cb9bf20a0d25bee30c1e2213dfe8278ad7d0d5d294275d47d4a6ff9ac646ab src/cache/tests/dashboard_tests.rs
|
||||
d7a02eb7bad37e6a5524600aabb5e60ccf50e26c7a487e2806891c9d8acd48f3 src/cache/tests/http_invalidation_tests.rs
|
||||
@@ -411,9 +412,9 @@ c438066616527a3108f2ad53e3698ae9b2a80961714394cdd0a450c92f99d992 src/proxy/head
|
||||
cc682f0d5754e0b806a4eb5950e4cc00611aca5ce4668414fddef1d692489805 src/proxy/tests/authority_tests.rs
|
||||
d134b0eb617f82f9971b3f3461379ea37c57bd0079bf54748d0c434b2ef1b475 src/proxy/tests/http_invalidation_tests.rs
|
||||
e80c7fe44abb7607c64ea02d56542753dc25290b3bcf4c4d45b75f2db6ca90d0 src/proxy/types.rs
|
||||
26afb4dcda99a54abdd85f7950ecac6d608eca78067323f695a43382d21fa2fb src/proxy/upstream_helpers.rs
|
||||
55c44a5d0c6588beea67439d62e3c9f57041e4d07cb1f8cf293efd856ceb8eff src/proxy/upstream_helpers.rs
|
||||
69f775f6746b4c6d729dc10961dbef54168846f4ce9cf7fe453ab5a2b879c9b8 src/proxy/websocket.rs
|
||||
016a60b2471edd3df9bbe9f92a9cebea11047178b83ff0e3b2351c12814a4939 src/proxy/websocket/handshake.rs
|
||||
d866e7e72dee6ff54c2418c34e9899cbcd7e4e027d9cbe2b625044dc9062e4ee src/proxy/websocket/handshake.rs
|
||||
7e9c4d4c207badabf90630223964bb2ad641f06d4149843cab751f192b8b4adf src/proxy/websocket/tests.rs
|
||||
a44421714aab37f7ceda19e3ec2512c645d2a3ce5dcb3da5bc7a0d87a442d6a8 src/proxy/websocket/tunnel.rs
|
||||
15fabd38576b61f650c039acdbf7ac5a5bd24330062475b94315285b2dc258b6 src/proxy/websocket/validation.rs
|
||||
@@ -428,7 +429,7 @@ d0939e1b0829c25356a0b67ecb3a178c74936dd07baa15fee24eb32544db6cad src/runtime.rs
|
||||
e90df3fff8a25465425b099ae358d19085809e56d948ba47e01e53f8b93d5dd0 src/runtime/tests.rs
|
||||
0046525317ffbd5d3b34845656b0f58d9d31f38b3d8acd71e90d94bd7cdfb8ab src/runtime/types.rs
|
||||
88bc546fdda20d3f1f8f5e7d7872ede1fa334b8bb5102c729bd72f56e3fb94b9 src/runtime/updates.rs
|
||||
b4c17b304166592e63c81884076589fbaf725c3f57043f8924c46167b061ac5b src/secret_store.rs
|
||||
b5062082d7fc7c9b08533283e1f0a841f85a42917b6ebf135760b04023884d9a src/secret_store.rs
|
||||
6dca8da072a3bd610704a391bb6e25c9042971f4ebc60edb47665ce1d74cbc58 src/secret_store/tests.rs
|
||||
6fa1d8c352bce7a16ff0208bc4cdbe80404715df0aff4ed26910a5df57cbbcd4 src/security.rs
|
||||
0e5fc2aff856bf6fd959cba12b5ddd1e3b3cbeb0fbaadde906c811cdfafdaae7 src/security/tests.rs
|
||||
@@ -451,12 +452,12 @@ d9123c623e2a97d09e41edaa29993b4b2c8cba39a3abdf2b48a205aa9a9c2939 src/system_sta
|
||||
7467d83066bb6986d8c9103597f7a7617ac677579ed46d573c573ff6ca57bbd9 src/tinyauth.rs
|
||||
0e1d87554db55e724f30c41ec4a5c80bdb8fc9c8cba8700ac509864d4abf8871 src/tinyauth/tests.rs
|
||||
1084cccd9de28acd6cb0f0813de7c0953910f51726f73f4acdb2e3ada4593976 src/tls.rs
|
||||
5919db3296523dd0b7a8e0cccbe44266500a6a5183a7a08cc195d7f9847886dc src/tls/certificates.rs
|
||||
4972a436f8878dec5f9eb8ff17eb82670b0bfd9e94e821c516575479f5fc1919 src/tls/certificates.rs
|
||||
436575144aa352498a4696f12c2258bef3a898f3f50e6fbc6d67c3b93afa099f src/tls/external_paths.rs
|
||||
3e6947dde167f82a5efc407ccf9bd4c9f5b26cc8633d1418a10284bc1a1503d4 src/tls/server_config.rs
|
||||
893d1077fc0d9a08598596a1d3f4bde7019357fac27ebe13f52c11a4859a1d25 src/tls/tests.rs
|
||||
e0ab65406911d4b95cb26594df65779a88d9011d5b11adb22c1a28831cda3826 src/tls/tests/acme.rs
|
||||
cfab6b61f0d121a15d6f78943091c770cb5a2a023d68d66be3c0228f9c8adf9c src/tls/tests/external_paths.rs
|
||||
31c0f51a3a0bab433f438c3ba1b1cdaf3c63fae6086e7d99b3464a537f75a5e1 src/tls/tests.rs
|
||||
2af2164ecdf2b7fd9eef7c6e358dec053dfac9aedaa501dbb75df742d18638b2 src/tls/tests/acme.rs
|
||||
93b359e9defd62506729bc90e56c96783e2f14dd477dd67f337ca83149da7168 src/tls/tests/external_paths.rs
|
||||
10c4a187e3aeb0c72a8c8968ea0cc8bfae4e3cb2f3d834ebd3da1056c4171ec5 src/tls/types.rs
|
||||
c2f802d95da4332fb8480e83f0c7502334457b3408141c1c4516c9ba320a0463 src/traffic.rs
|
||||
36301bcbd20b557dd3d58e77a9175f3581947f577c0bb0b972899cec6786b03f src/traffic/tests.rs
|
||||
@@ -509,7 +510,7 @@ c5cb561b0f276e8082f904b9446fa5885f6b3c0608f25ea2ef425061e9f7f6b4 src/web_server
|
||||
518856a221161430cb0ab3e411fa81cab07e1a6e52edd470f026d25c5307943e src/web_server/serve.rs
|
||||
63d71a336187779e9cbc1cc6afa065c5fa9472835a8c2cc15dbf4527279d0c90 src/web_server/tests.rs
|
||||
9426ad19fd6234e944d6e122a995d2cf8ebe4177d27e42fd157f64d05abd1e23 src/web_server/tests/part_01.rs
|
||||
6f0b68d732fa0d1385744ef7ac1b36d558f9532f9ef1dea44d3dcc47d149840a src/web_server/tests/part_02.rs
|
||||
5591a0b994d4392265799f881790d15abd8b6263b5475a84766455d66572bfcf src/web_server/tests/part_02.rs
|
||||
8f9a0e2b0a69a1f2b8cf7505a654606150da4a53bc8c09d1d6c4b8751185b134 src/web_server/tests/part_03.rs
|
||||
b25b6e0082b5153272119c7faba9d6f2ccdd288ce5c066526431c56954c66c4c src/web_server/tests/part_04.rs
|
||||
f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json
|
||||
@@ -522,23 +523,24 @@ b04362ad082a32c9ccc4cf38c46f44d71347adecf7aba3224b3af3ea4872e3d0 tools/README.m
|
||||
fd082812411cff7afb71b2289a6f1a469b8d3c8a7cfdcdd9e567a2a4baf81be4 tools/checks/admin_surface_audit_0310.py
|
||||
412255d383fcc8306c6086784fb416d9814e3ec790e440a0927431ca615f3411 tools/checks/backend_cache_0297_ui_test.mjs
|
||||
2a493dbce2991a23d807c01952f58608a364f541560836b4cb385e75e108e9e7 tools/checks/backend_health_0300_ui_test.mjs
|
||||
daa718b6a3b519234cbb3614bd321f5b4b4ebd704bc9fb1c1dc18ba742759203 tools/checks/check_source.py
|
||||
4bb77602254ff8ed1673b6d31518c77ae40c9b9c44e85ac436b93a300115ca18 tools/checks/check_source.py
|
||||
0b583ee59dcccc35b15ef4e211d7e6546a8aaa7b0286ceab03c0b0dc084c1e07 tools/checks/dashboard_boot_0311_ui_test.mjs
|
||||
5f798699502c7a18e7b45aa84bdcf7ad0ccdb77f11cf2003527c97f0391c7abe tools/checks/dashboard_startup_0310_ui_test.mjs
|
||||
7428c63a80be49c57b6aad15738679175bad24c4271964cf01a67805ec22c0eb tools/checks/docs_check.py
|
||||
3a5703cd3e0e511e0ea8da0c7802ccdc5cf53289f9b9169b9cf96ea5209b4a57 tools/checks/dependency_upgrade_0312.py
|
||||
5d4fa0b66ae05241236551c07c900ad0c437bd10e34406b17a8a002188caebc3 tools/checks/docs_check.py
|
||||
e33a9cc51d58bc92a637c0e6a1c88cbd24ba4605be55748212e296c3c35a4060 tools/checks/high_traffic_memory_model.py
|
||||
4f7821e7e25b09ce251d25a0b3fb4d8ce8396c36a5ed971c2637aeb325425909 tools/checks/langs_check.py
|
||||
f8f87ba891338b565249d02c3cf5f02cec44e606b4205de99fcbea1eb2c24d57 tools/checks/memory_contracts.py
|
||||
5b7901747d879e12a39ed82b6efc513bcdddd15d508b5828210d2c83398fb6a4 tools/checks/panel_cache_http_0310_ui_test.mjs
|
||||
274019cdcdccff5a5c7f76107daf096d111df1834b7280ccc3da9c564b65a04e tools/checks/preflight.py
|
||||
8c5c64b681de7f72f01b414f2c77d1f9a44c13b47972d8252a0d392fd71c6ab4 tools/checks/preflight.py
|
||||
3485681d369b5f486dfc2d14660f19b4098df670bafd9a296cc9dabe68003a51 tools/checks/refresh_0310_ui_test.mjs
|
||||
c8b9216936300dac4ebb582a70efe2286124f0d14e501a6c22fc10cc2caaddbb tools/checks/release_smoke.py
|
||||
db468bc0ad68704e70a15d7dda894d1913db1e2ce94e04a84b20c7dadfaa80a6 tools/checks/release_smoke.py
|
||||
f69845bc26ec33160fc756953991cf32aa8ad6a7fb4a131caa2093db91e783ef tools/checks/rust_module_contracts.py
|
||||
ecfe0bb21a7549225a19b0846bb6de8c99cbfbbf5a706d33cf84db2c7146abed tools/checks/rust_source.py
|
||||
adf84a983e0dbba228e47d71ec2d5c9964f7ca9989d5139de58f96ee961b035a tools/checks/security_contracts.py
|
||||
23b9250245e6bec5ef728c73cee4c88f64924afc3597f8a9b58a1924235df98e tools/checks/security_dependency_contracts.py
|
||||
5c1d428cbda5f08b8d6847732672e45e9da96b545337334e473986e119197de2 tools/checks/source_module_contracts.py
|
||||
b78240c3ccead8c023609fc71560be7ce2f2aa5d31c78059a24cfe4b0412d9c5 tools/checks/system_info_0301_ui_test.mjs
|
||||
49c1fe96340d97c326943826dd8b3331b884c19f4a9631beaa820f01a12108e0 tools/checks/system_info_0301_ui_test.mjs
|
||||
543a488951ea6ec8fe06e04f168ccc4d4f2af3cfc281c9a728ab7bd87377fa98 tools/checks/system_info_no_subprocess_contracts.py
|
||||
614ed0c775e32ec5bfb4ee75aaa90206a10da0f43659747a7ec76fd93e33767a tools/checks/version_contracts.py
|
||||
2020e171be1a044f7ca2ab83af7604a014c36fe4ff04791d0620ccbf1bf8e000 tools/checks/vhost_origin_errors_0297_ui_test.mjs
|
||||
@@ -547,7 +549,7 @@ b78240c3ccead8c023609fc71560be7ce2f2aa5d31c78059a24cfe4b0412d9c5 tools/checks/s
|
||||
0398892be30fe416a00578915c588f360fd93244e3af77ece834e560e547d1c8 tools/checks/web_server_contracts.py
|
||||
35f76060e585a96c623ef83bd5f771969f5be16a0e1ae626a3092d7458b84e84 tools/checks/web_service_ui_test.mjs
|
||||
adaa1ed9e5958739df40190055c780c80d0a8e270ec8dc105ed3fa2dbb8fb74f tools/checks/www_redirect_ui_test.mjs
|
||||
c8bf1c3ef876b4f87840414f5de19c183309550572437cca7ba1fe02df496462 tools/dev/memory_load.py
|
||||
05382366fd801a17f8d7c1166fd4e64b8aa70e465cb69121519b239ebf8ed55f tools/dev/memory_load.py
|
||||
d1418f8e574a7390fd45a2aa879bdf5720b773162d65e295ab0350506aa5220a tools/dev/mock_auth.py
|
||||
2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 tools/dev/mock_server.py
|
||||
538b5f6cfa738355a729940ee3d92e7ab3dc25ca69ae330ddaa08f831775b8ba tools/dev/test_compression.py
|
||||
|
||||
@@ -101,6 +101,7 @@ if [[ "$mode" == "test" ]]; then
|
||||
python3 tools/checks/security_contracts.py
|
||||
python3 tools/checks/admin_surface_audit_0310.py
|
||||
python3 tools/checks/security_dependency_contracts.py
|
||||
python3 tools/checks/dependency_upgrade_0312.py
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
node tools/checks/web_service_ui_test.mjs
|
||||
node tools/checks/backend_cache_0297_ui_test.mjs
|
||||
|
||||
+4
-4
@@ -132,7 +132,7 @@ promise to erase browser/OS screenshots or data intentionally downloaded by an a
|
||||
## First build / offline assets (fixed in 0.7.2)
|
||||
|
||||
No `SWAGGER_UI_DOWNLOAD_URL` environment variable or `.cargo/config.toml` is required.
|
||||
The default is Swagger UI **5.32.11**, selected by `scripts/build/swagger_assets.py` and
|
||||
The default is Swagger UI **5.33.1**, selected by `scripts/build/swagger_assets.py` and
|
||||
called from this application's `build.rs`. The former `utoipa-swagger-ui` adapter
|
||||
is removed: there is no separate dependency build silently choosing its older
|
||||
historical browser default. Direct Cargo builds using `--manifest-path` use the
|
||||
@@ -160,10 +160,10 @@ release binaries**, served behind the existing admin guard. No runtime download,
|
||||
CDN, public asset endpoint or alternate authentication mechanism was introduced.
|
||||
|
||||
An offline or GitHub-restricted build can use a reviewed local copy of the same
|
||||
GitHub **tag archive**, with its original `swagger-ui-5.32.11/` directory structure:
|
||||
GitHub **tag archive**, with its original `swagger-ui-5.33.1/` directory structure:
|
||||
|
||||
```bash
|
||||
export SWAGGER_UI_DOWNLOAD_URL=file:///srv/build-assets/swagger-ui-5.32.11.zip
|
||||
export SWAGGER_UI_DOWNLOAD_URL=file:///srv/build-assets/swagger-ui-5.33.1.zip
|
||||
# Optional: require the archive digest independently verified by your build team.
|
||||
# export SWAGGER_UI_ARCHIVE_SHA256=<64-hex-digit-verified-digest>
|
||||
bash dev.sh check
|
||||
@@ -237,4 +237,4 @@ unchanged. No configuration schema migration is needed for existing header rules
|
||||
- Swagger UI configuration: https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
|
||||
- Cargo configuration discovery: https://doc.rust-lang.org/cargo/reference/config.html
|
||||
- Cargo build scripts: https://doc.rust-lang.org/cargo/reference/build-scripts.html
|
||||
- Pinned browser release: https://github.com/swagger-api/swagger-ui/tree/v5.32.11
|
||||
- Pinned browser release: https://github.com/swagger-api/swagger-ui/tree/v5.33.1
|
||||
+13
-1
@@ -1,4 +1,16 @@
|
||||
# Verification report - RustProxy 0.31.1
|
||||
# Verification report - RustProxy 0.31.2
|
||||
|
||||
## 0.31.2 — stable library upgrade candidate (2026-10-10)
|
||||
|
||||
- **Source basis:** shipped RustProxy 0.31.1 archive, without local Git history; no confirmation of current remote `master`.
|
||||
- **Changed dependency branches:** Rust 1.99 (manifest and Docker builders), SQLx 0.9, Argon2 0.6, Rand 0.10, rcgen 0.14, Tower HTTP 0.7, Brotli 9 and sysinfo 0.39; HTTP/3 hpx-h3 is pinned to reviewed 2.5.20. Existing SemVer-compatible crates remain range-defined for a full `cargo update`. Embedded Swagger UI is pinned to upstream 5.33.1.
|
||||
- **Code adaptations:** secure RNG API, Argon2 PHC generation/verification and TLS certificate signing key representation. No intended changes to JSON schema, SQLite schema 9, cache format, credential storage, TLS policy, or proxy traffic processing.
|
||||
- **Executed locally:** `./dev.sh test` passed all offline code/API/SQLite/upgrade/security/translation/UI/Node/documentation checks; then exited 127 because Rust/Cargo is absent. `cd docker-agent && go test ./...` passed. `python3 tools/checks/dependency_upgrade_0312.py` reports PENDING for the old lockfile. Python compilation and Bash syntax checks passed. This does not prove a Rust build.
|
||||
- **CRITICAL:** This sandbox has no Rust/Cargo/rustfmt and cannot reach the crates registry. The existing `Cargo.lock` carries the previous dependency graph: only the root package version has been updated, with all third-party resolved entries left untouched. It is **NOT the resolved lockfile for 0.31.2** and `--locked` is expected to reject it until `cargo update` runs. Do not treat this source package as a built or production-accepted release. Do not fake checksums or mark compilation/tests as passing.
|
||||
- **Required on a Rust 1.99 build host:** run `cargo update`, review and commit the newly resolved `Cargo.lock`, `cargo metadata --locked`, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `./dev.sh check`, `./dev.sh test`, `./dev.sh release`, plus `cargo audit` when installed. Fix all compilation/API errors revealed by real Cargo before approving a release.
|
||||
- **Runtime regression gate:** real H1/H2/h2c/H3 QUIC, WebSocket, TLS and certificates, SQLite upgrade/backup/restore with preexisting data, sessions/Basic Auth/API security, SSE and panel on Safari behind reverse proxy, cache HOT/COLD and BAN/PURGE, and zstd/br/gzip negotiation. Compare load/RSS and avoid overwriting production data.
|
||||
|
||||
# Historical verification records (0.31.1 and earlier)
|
||||
|
||||
## 0.31.1 — mobile/reverse-proxy dashboard startup (2026-10-10)
|
||||
|
||||
|
||||
Executable → Regular
+94
-4
@@ -27,6 +27,9 @@ Environment:
|
||||
RUSTPROXY_ALLOW_REMOTE_ADMIN Set to 1 only to permit plain-HTTP non-loopback admin on first install
|
||||
RUSTPROXY_PREFIX Managed install prefix (default /opt/rustproxy)
|
||||
RUSTPROXY_BUILD_JOBS Cargo jobs for low-memory builds (default 1)
|
||||
|
||||
The bootstrap installs/updates the builder Rust toolchain only when required
|
||||
by Cargo.toml (and respects rust-toolchain.toml, if present).
|
||||
USAGE
|
||||
exit 0
|
||||
;;
|
||||
@@ -101,11 +104,94 @@ run_builder() {
|
||||
runuser -u "$builder" -- env HOME="$builder_home" PATH="$builder_home/.cargo/bin:/usr/local/bin:/usr/bin:/bin" "$@"
|
||||
}
|
||||
|
||||
if ! run_builder bash -lc 'command -v cargo >/dev/null 2>&1'; then
|
||||
run_builder bash -lc "wget -qO- --https-only --secure-protocol=TLSv1_2 https://sh.rustup.rs | sh -s -- -y --profile minimal"
|
||||
run_builder git clone --depth 1 "$REPO_URL" "$work/RustProxy"
|
||||
|
||||
# The builder, not root, owns the toolchain. Never update a system-wide
|
||||
# apt-managed compiler or change the user's default rustup toolchain.
|
||||
# rustup is needed even when an older system-installed cargo exists.
|
||||
if ! run_builder bash -c 'command -v rustup >/dev/null 2>&1'; then
|
||||
echo "Installing rustup for build user: $builder"
|
||||
run_builder env RUSTUP_INIT_SKIP_PATH_CHECK=yes bash -c \
|
||||
'set -o pipefail; wget -qO- --https-only --secure-protocol=TLSv1_2 https://sh.rustup.rs | sh -s -- -y --profile minimal --no-modify-path --default-toolchain none'
|
||||
fi
|
||||
|
||||
run_builder git clone --depth 1 "$REPO_URL" "$work/RustProxy"
|
||||
# Read the actual requirements from the checked-out sources, not a hardcoded
|
||||
# Rust version. The project may inherit rust-version from [workspace.package].
|
||||
# The channel from rust-toolchain.toml (or legacy rust-toolchain) takes precedence.
|
||||
rust_settings=$(python3 - "$work/RustProxy" <<'PY_RUST'
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
import tomllib
|
||||
|
||||
repo = pathlib.Path(sys.argv[1])
|
||||
with (repo / "Cargo.toml").open("rb") as f:
|
||||
manifest = tomllib.load(f)
|
||||
minimum = manifest.get("package", {}).get("rust-version")
|
||||
if isinstance(minimum, dict) and minimum.get("workspace"):
|
||||
minimum = manifest.get("workspace", {}).get("package", {}).get("rust-version")
|
||||
if not isinstance(minimum, str) or not re.fullmatch(r"[0-9]+\.[0-9]+(?:\.[0-9]+)?", minimum):
|
||||
sys.exit("Cargo.toml must declare a valid package rust-version for safe automatic toolchain updates")
|
||||
|
||||
channel = "stable"
|
||||
toolchain_toml = repo / "rust-toolchain.toml"
|
||||
toolchain_legacy = repo / "rust-toolchain"
|
||||
if toolchain_toml.exists():
|
||||
with toolchain_toml.open("rb") as f:
|
||||
channel = tomllib.load(f).get("toolchain", {}).get("channel", "")
|
||||
elif toolchain_legacy.exists():
|
||||
channel = toolchain_legacy.read_text(encoding="utf-8").strip()
|
||||
if not isinstance(channel, str) or not re.fullmatch(
|
||||
r"(?:stable|beta|nightly|[0-9]+\.[0-9]+(?:\.[0-9]+)?(?:-[A-Za-z0-9_\-]+)?)", channel
|
||||
):
|
||||
sys.exit(f"Unsupported rust-toolchain channel: {channel!r}")
|
||||
print(f"{minimum}|{channel}")
|
||||
PY_RUST
|
||||
)
|
||||
required_rust=${rust_settings%%|*}
|
||||
build_toolchain=${rust_settings#*|}
|
||||
|
||||
# Compare numerically (1.100 > 1.99); do not use lexicographic ordering.
|
||||
rust_version_at_least() {
|
||||
python3 - "$1" "$2" <<'PY_VERSION'
|
||||
import sys
|
||||
|
||||
def version(value):
|
||||
parts = value.split(".")
|
||||
if len(parts) not in (2, 3) or not all(part.isdecimal() for part in parts):
|
||||
sys.exit(2)
|
||||
return tuple(map(int, (parts + ["0"] * (3 - len(parts)))))
|
||||
|
||||
sys.exit(0 if version(sys.argv[1]) >= version(sys.argv[2]) else 1)
|
||||
PY_VERSION
|
||||
}
|
||||
|
||||
# rustup run is a non-updating check: it fails when the channel is missing.
|
||||
installed_rust=$(run_builder rustup run "$build_toolchain" rustc --version 2>/dev/null | awk '$1 == "rustc" { print $2; exit }' || true)
|
||||
if [[ -z "$installed_rust" ]] || ! rust_version_at_least "$installed_rust" "$required_rust"; then
|
||||
echo "Rust toolchain '$build_toolchain' needs installation/update (installed: ${installed_rust:-none}; required: >= $required_rust)."
|
||||
case "$build_toolchain" in
|
||||
stable|beta|nightly)
|
||||
run_builder rustup update "$build_toolchain"
|
||||
;;
|
||||
*)
|
||||
# A fixed toolchain must be respected. Do not silently override a pin
|
||||
# that is older than the manifest's minimum supported Rust version.
|
||||
if [[ -n "$installed_rust" ]]; then
|
||||
echo "Pinned toolchain '$build_toolchain' is older than rust-version $required_rust; fix rust-toolchain.toml in the repository." >&2
|
||||
exit 1
|
||||
fi
|
||||
run_builder rustup toolchain install "$build_toolchain" --profile minimal
|
||||
;;
|
||||
esac
|
||||
installed_rust=$(run_builder rustup run "$build_toolchain" rustc --version | awk '$1 == "rustc" { print $2; exit }')
|
||||
fi
|
||||
if ! rust_version_at_least "$installed_rust" "$required_rust"; then
|
||||
echo "Toolchain '$build_toolchain' provides Rust $installed_rust, but Cargo.toml requires $required_rust. Aborting before installation/update." >&2
|
||||
exit 1
|
||||
fi
|
||||
run_builder rustup run "$build_toolchain" cargo --version
|
||||
echo "Building with Rust $installed_rust ($build_toolchain); required >= $required_rust."
|
||||
|
||||
# Keep source builds reliable on small Debian VPSes. The repository release
|
||||
# profile may use LTO/codegen settings that can make rustc exceed RAM limits.
|
||||
@@ -119,7 +205,11 @@ if [[ "$ALLOW_REMOTE_ADMIN" != "0" && "$ALLOW_REMOTE_ADMIN" != "1" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_builder bash -lc "cd '$work/RustProxy' && CARGO_BUILD_JOBS='$BUILD_JOBS' CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_CODEGEN_UNITS=8 cargo build --release --locked"
|
||||
run_builder env RUSTUP_TOOLCHAIN="$build_toolchain" \
|
||||
CARGO_BUILD_JOBS="$BUILD_JOBS" \
|
||||
CARGO_PROFILE_RELEASE_LTO=false \
|
||||
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=8 \
|
||||
bash -c 'cd "$1" && exec cargo build --release --locked' _ "$work/RustProxy"
|
||||
|
||||
cd "$work/RustProxy"
|
||||
if [[ "$MODE" == install ]]; then
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM rust:1.88-trixie AS build
|
||||
FROM rust:1.99-trixie AS build
|
||||
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
|
||||
@@ -22,7 +22,7 @@ from urllib.request import HTTPRedirectHandler, Request, build_opener, url2pathn
|
||||
import zipfile
|
||||
import zlib
|
||||
|
||||
VERSION = "5.32.11"
|
||||
VERSION = "5.33.1"
|
||||
DOWNLOAD_URL = f"https://github.com/swagger-api/swagger-ui/archive/refs/tags/v{VERSION}.zip"
|
||||
PREFIX = f"swagger-ui-{VERSION}/"
|
||||
MAX_ARCHIVE = 64 * 1024 * 1024
|
||||
@@ -32,7 +32,7 @@ ASSETS = {
|
||||
}
|
||||
LICENSE_MEMBERS = ("LICENSE", "NOTICE", "dist/swagger-ui-bundle.js.LICENSE.txt")
|
||||
HELP = ("Install Python 3.10+ and allow HTTPS access to github.com/codeload.github.com, "
|
||||
"or set SWAGGER_UI_DOWNLOAD_URL=file:///absolute/path/swagger-ui-5.32.11.zip "
|
||||
"or set SWAGGER_UI_DOWNLOAD_URL=file:///absolute/path/swagger-ui-5.33.1.zip "
|
||||
"to a reviewed copy of the same tag archive. See docs/API_ACCESS.md.")
|
||||
|
||||
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run on a clean Rust 1.99+ build machine with registry and Swagger asset access.
|
||||
set -euo pipefail
|
||||
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
if ! command -v cargo >/dev/null 2>&1 || ! command -v rustc >/dev/null 2>&1; then
|
||||
printf 'Rust 1.99+ and Cargo are required; no lockfile was regenerated.\n' >&2
|
||||
exit 127
|
||||
fi
|
||||
python3 tools/checks/preflight.py
|
||||
if [[ "$(python3 scripts/version.py)" != '0.31.2' ]]; then
|
||||
printf 'Expected RustProxy 0.31.2 sources.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
printf 'Toolchain: '; rustc --version
|
||||
cargo update
|
||||
python3 tools/checks/dependency_upgrade_0312.py --require-lock
|
||||
cargo metadata --locked --format-version 1 --no-deps >/dev/null
|
||||
cargo fmt --all -- --check
|
||||
cargo clippy --locked --all-targets -- -D warnings
|
||||
./dev.sh check
|
||||
./dev.sh test
|
||||
./dev.sh release
|
||||
if cargo audit --version >/dev/null 2>&1; then
|
||||
cargo audit
|
||||
else
|
||||
printf 'NOTICE: cargo-audit is unavailable; install it and run cargo audit before release.\n' >&2
|
||||
fi
|
||||
printf 'Source and locked Rust build complete. Still required: live H1/H2/H3, auth/TLS, SQLite, cache, UI and performance acceptance.\n'
|
||||
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "RustProxy Administration API",
|
||||
"version": "0.31.1",
|
||||
"version": "0.31.2",
|
||||
"description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust."
|
||||
},
|
||||
"servers": [
|
||||
@@ -10893,4 +10893,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
use flate2::read::GzDecoder;
|
||||
use http::header;
|
||||
use http_body_util::BodyExt;
|
||||
use std::io::Read;
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
let first_body = json!({
|
||||
"certificate_id": "opnsense-cert",
|
||||
"cert_pem": first.cert.pem(),
|
||||
"key_pem": first.key_pair.serialize_pem(),
|
||||
"key_pem": first.signing_key.serialize_pem(),
|
||||
})
|
||||
.to_string();
|
||||
|
||||
@@ -326,7 +326,7 @@
|
||||
let second_body = json!({
|
||||
"certificate_id": "opnsense-cert",
|
||||
"cert_pem": second.cert.pem(),
|
||||
"key_pem": second.key_pair.serialize_pem(),
|
||||
"key_pem": second.signing_key.serialize_pem(),
|
||||
})
|
||||
.to_string();
|
||||
let response = router.oneshot(deploy(second_body)).await.unwrap();
|
||||
|
||||
@@ -46,14 +46,16 @@
|
||||
interception.append(toggle, codesLabel, hint); parent.append(interception);
|
||||
const errors = errorsEditor(parent, values.errors);
|
||||
parent.append(label("p", "pagesSafetyHint", "fine"));
|
||||
return { read: () => {
|
||||
const selectedTokens = enabled.checked ? statuses.value.split(/[\s,]+/).filter(Boolean) : [];
|
||||
const selected = selectedTokens.map(Number);
|
||||
if (enabled.checked && (!selected.length || selectedTokens.some(token => !/^\d{3}$/.test(token)) || selected.length > codes.length || new Set(selected).size !== selected.length || selected.some(code => !codes.includes(code)))) {
|
||||
throw new Error(t("vhostInterceptOriginInvalid"));
|
||||
return {
|
||||
read: () => {
|
||||
const selectedTokens = enabled.checked ? statuses.value.split(/[\s,]+/).filter(Boolean) : [];
|
||||
const selected = selectedTokens.map(Number);
|
||||
if (enabled.checked && (!selected.length || selectedTokens.some(token => !/^\d{3}$/.test(token)) || selected.length > codes.length || new Set(selected).size !== selected.length || selected.some(code => !codes.includes(code)))) {
|
||||
throw new Error(t("vhostInterceptOriginInvalid"));
|
||||
}
|
||||
return { response: response.read(), fallback: fallback.read(), errors: errors.read(), intercept_statuses: selected };
|
||||
}
|
||||
return { response: response.read(), fallback: fallback.read(), errors: errors.read(), intercept_statuses: selected };
|
||||
} };
|
||||
};
|
||||
}
|
||||
let global = null;
|
||||
function render() { if (!envelope || settingsDirty.has("pages-form")) return; const values = envelope.config.pages || {}, parent = $("global-pages-fields"); parent.replaceChildren(); const ip = pageEditor(parent, "pageDirectIp", values.direct_ip), unknown = pageEditor(parent, "pageUnknownHost", values.unknown_host), errors = errorsEditor(parent, values.errors); global = { read: () => ({ direct_ip: ip.read(), unknown_host: unknown.read(), errors: errors.read() }) }; }
|
||||
|
||||
@@ -214,7 +214,7 @@ curl --fail --show-error --request POST \
|
||||
$("admin-certificate").replaceChildren(generated, ...(envelope.config.tls?.certificates || []).map(cert => { const o = document.createElement("option"); o.value = cert.id; o.textContent = cert.id; return o; })); $("admin-certificate").value = current;
|
||||
$("admin-hosts").value = (admin.allowed_hosts || []).join("\n");
|
||||
$("admin-session-days").value = Number(admin.session_days || 30);
|
||||
for (const codec of ["zstd", "br", "gzip"]) $( `admin-compression-${codec}` ).checked = admin.panel_compression?.[codec] !== false;
|
||||
for (const codec of ["zstd", "br", "gzip"]) $(`admin-compression-${codec}`).checked = admin.panel_compression?.[codec] !== false;
|
||||
}
|
||||
const listenLocked = !!adminStatus?.listen_locked;
|
||||
$("admin-listen").disabled = listenLocked; $("admin-listen-lock-note").hidden = !listenLocked;
|
||||
@@ -300,7 +300,7 @@ curl --fail --show-error --request POST \
|
||||
}, "prometheus");
|
||||
bindSettingsForm("administration-form", next => {
|
||||
const locked = !!latestStatus?.administration?.listen_locked;
|
||||
next.administration = { listen: locked ? null : $("admin-listen").value.trim(), tls: $("admin-tls").checked, certificate_id: $("admin-tls").checked ? ($("admin-certificate").value || null) : null, allowed_hosts: $("admin-hosts").value.split(/[\n,]+/).map(v => v.trim()).filter(Boolean), session_days: Number($("admin-session-days").value), panel_compression: Object.fromEntries(["zstd", "br", "gzip"].map(codec => [codec, $( `admin-compression-${codec}` ).checked])) };
|
||||
next.administration = { listen: locked ? null : $("admin-listen").value.trim(), tls: $("admin-tls").checked, certificate_id: $("admin-tls").checked ? ($("admin-certificate").value || null) : null, allowed_hosts: $("admin-hosts").value.split(/[\n,]+/).map(v => v.trim()).filter(Boolean), session_days: Number($("admin-session-days").value), panel_compression: Object.fromEntries(["zstd", "br", "gzip"].map(codec => [codec, $(`admin-compression-${codec}`).checked])) };
|
||||
}, "administration");
|
||||
bindSettingsForm("tls-policy-form", next => {
|
||||
if (!$("tls-policy-12").checked && !$("tls-policy-13").checked) throw fieldError($("tls-policy-12"), t("tlsPolicyVersionRequired"));
|
||||
|
||||
+93
-25
@@ -252,15 +252,21 @@ html.app-startup-failed .app-startup-recovery {
|
||||
}
|
||||
|
||||
@keyframes app-startup-spin {
|
||||
to { transform: rotate(360deg) }
|
||||
to {
|
||||
transform: rotate(360deg)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes app-startup-reveal {
|
||||
to { visibility: visible }
|
||||
to {
|
||||
visibility: visible
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.app-startup-spinner { animation: none }
|
||||
.app-startup-spinner {
|
||||
animation: none
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -5416,7 +5422,9 @@ dialog .window-heading {
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.vh-www-pair-directions { grid-template-columns: minmax(0, 1fr) }
|
||||
.vh-www-pair-directions {
|
||||
grid-template-columns: minmax(0, 1fr)
|
||||
}
|
||||
}
|
||||
|
||||
.vh-www-choice {
|
||||
@@ -7701,9 +7709,17 @@ body[data-density="compact"] .page-footer {
|
||||
}
|
||||
|
||||
/* The legend keys must use the same theme/preset palette as the SVG strokes. */
|
||||
.stat-chart .stat-chart-legend .legend-h1 i { background: var(--traffic-h1); }
|
||||
.stat-chart .stat-chart-legend .legend-h2 i { background: var(--traffic-h2); }
|
||||
.stat-chart .stat-chart-legend .legend-h3 i { background: var(--traffic-h3); }
|
||||
.stat-chart .stat-chart-legend .legend-h1 i {
|
||||
background: var(--traffic-h1);
|
||||
}
|
||||
|
||||
.stat-chart .stat-chart-legend .legend-h2 i {
|
||||
background: var(--traffic-h2);
|
||||
}
|
||||
|
||||
.stat-chart .stat-chart-legend .legend-h3 i {
|
||||
background: var(--traffic-h3);
|
||||
}
|
||||
|
||||
.stat-chart .stat-chart-legend b {
|
||||
font-family: var(--mono);
|
||||
@@ -9946,6 +9962,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
|
||||
.listener-active-summary {
|
||||
margin-top: 10px;
|
||||
padding: 10px 12px;
|
||||
@@ -10002,7 +10019,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
margin-top: 10px
|
||||
}
|
||||
|
||||
.location-header-editor .vh-header-rule-head > button {
|
||||
.location-header-editor .vh-header-rule-head>button {
|
||||
margin-left: auto;
|
||||
flex: 0 0 auto
|
||||
}
|
||||
@@ -10013,7 +10030,6 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
}
|
||||
}
|
||||
|
||||
/* 0.29.7: PHP-FPM local/profile toggle rows with accessible info popovers. */
|
||||
.php-switch-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -10023,6 +10039,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
min-height: 30px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.php-switch-row .php-switch-label {
|
||||
display: inline-flex !important;
|
||||
flex: 0 1 auto;
|
||||
@@ -10032,14 +10049,26 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
margin: 0 !important;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.php-switch-row .php-switch-label input[type="checkbox"] {
|
||||
position: static;
|
||||
flex: 0 0 var(--ui-check-size);
|
||||
margin: 0;
|
||||
}
|
||||
.php-switch-row .php-switch-label span { min-width: 0; }
|
||||
.php-switch-row .php-field-info { flex: 0 0 auto; }
|
||||
.php-switch-row .php-field-info .metric-info-popover { right: auto; left: 0; }
|
||||
|
||||
.php-switch-row .php-switch-label span {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.php-switch-row .php-field-info {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.php-switch-row .php-field-info .metric-info-popover {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.php-reference pre {
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
@@ -10049,6 +10078,7 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
background: var(--panel2);
|
||||
font: 12px/1.6 var(--mono);
|
||||
}
|
||||
|
||||
.php-profile-help-divider {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
@@ -10056,12 +10086,27 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
border: 0;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
.php-origin-errors { grid-column: 1 / -1; display: grid; gap: 8px; padding: 10px 0; border-top: 1px solid var(--line); }
|
||||
.php-origin-errors > label { max-width: 430px; }
|
||||
.php-origin-errors > .check { margin: 0 !important; }
|
||||
.php-origin-errors .fine { margin: 0; }
|
||||
|
||||
/* Keep the Overview refresh action on the same row as the page title. */
|
||||
.php-origin-errors {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding: 10px 0;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.php-origin-errors>label {
|
||||
max-width: 430px;
|
||||
}
|
||||
|
||||
.php-origin-errors>.check {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.php-origin-errors .fine {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-intro-copy,
|
||||
.page-intro-title-row {
|
||||
min-width: 0;
|
||||
@@ -10090,30 +10135,47 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
font-size: clamp(18px, 5vw, 24px);
|
||||
letter-spacing: -.6px
|
||||
}
|
||||
|
||||
.page-intro-title-row .overview-refresh {
|
||||
padding: 7px 9px;
|
||||
font-size: 11px
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep the system API indicator next to its action, aligned as one unit. */
|
||||
.system-info-heading-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; }
|
||||
.system-info-heading-actions .pill { white-space: nowrap; }
|
||||
.system-info-heading-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.system-info-heading-actions .pill {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Contextual, keyboard-accessible curl examples next to the cache allowlist. */
|
||||
.cache-maintenance-help {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
}
|
||||
.cache-maintenance-help .subheading { margin: 0; }
|
||||
.cache-maintenance-info { position: static; flex-shrink: 0; }
|
||||
|
||||
.cache-maintenance-help .subheading {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cache-maintenance-info {
|
||||
position: static;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cache-maintenance-info .metric-info-popover {
|
||||
max-height: min(60vh, 420px);
|
||||
overflow-y: auto;
|
||||
width: min(440px, calc(100vw - 56px));
|
||||
}
|
||||
|
||||
.cache-maintenance-info .metric-info-popover code {
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
@@ -10122,5 +10184,11 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
font-size: 11px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.cache-maintenance-info .metric-info-popover .fine + code { margin-top: 6px; }
|
||||
.cache-maintenance-info .metric-info-popover code + .fine { margin-top: 8px; }
|
||||
|
||||
.cache-maintenance-info .metric-info-popover .fine+code {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.cache-maintenance-info .metric-info-popover code+.fine {
|
||||
margin-top: 8px;
|
||||
}
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
//! Local filesystem authority. The process owner can initialize or recover the sole admin.
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use fs2::FileExt;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{rngs::SysRng, TryRng};
|
||||
use std::{
|
||||
fs::{self, File, OpenOptions},
|
||||
io::Write,
|
||||
@@ -86,7 +86,9 @@ pub fn load_or_create_setup_token(dir: &Path) -> Result<String> {
|
||||
return load_setup_token(dir);
|
||||
}
|
||||
let mut bytes = [0u8; 32];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
SysRng
|
||||
.try_fill_bytes(&mut bytes)
|
||||
.expect("secure operating system random source unavailable");
|
||||
let token: String = bytes.iter().map(|b| format!("{b:02x}")).collect();
|
||||
let mut file = match private_options().create_new(true).open(&path) {
|
||||
Ok(file) => file,
|
||||
|
||||
+9
-9
@@ -5,7 +5,7 @@ mod store;
|
||||
use crate::{db::Store, runtime::ApiError};
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use argon2::{
|
||||
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||
password_hash::{phc::PasswordHash, PasswordHasher, PasswordVerifier},
|
||||
Algorithm, Argon2, Params, Version,
|
||||
};
|
||||
pub use files::{
|
||||
@@ -13,7 +13,7 @@ pub use files::{
|
||||
prepare_directory, try_lock_directory,
|
||||
};
|
||||
use http::{header::COOKIE, HeaderMap, StatusCode};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{rngs::SysRng, TryRng};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
@@ -56,7 +56,9 @@ pub fn now() -> i64 {
|
||||
}
|
||||
pub fn random_secret() -> String {
|
||||
let mut bytes = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
SysRng
|
||||
.try_fill_bytes(&mut bytes)
|
||||
.expect("secure operating system random source unavailable");
|
||||
bytes.iter().map(|b| format!("{b:02x}")).collect()
|
||||
}
|
||||
pub fn secret_hash(secret: &str) -> String {
|
||||
@@ -118,17 +120,15 @@ pub fn validate_password(value: &str) -> Result<()> {
|
||||
}
|
||||
pub(crate) fn hash_sync(password: &str) -> Result<String> {
|
||||
let params = Params::new(19_456, 2, 1, None).context("Argon2 parameters")?;
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
Ok(Argon2::new(Algorithm::Argon2id, Version::V0x13, params)
|
||||
.hash_password(password.as_bytes(), &salt)?
|
||||
.hash_password(password.as_bytes())?
|
||||
.to_string())
|
||||
}
|
||||
fn verify_sync(password: &str, encoded: &str) -> bool {
|
||||
PasswordHash::new(encoded).ok().is_some_and(|hash| {
|
||||
Argon2::default()
|
||||
.verify_password(password.as_bytes(), &hash)
|
||||
PasswordHash::new(encoded).is_ok()
|
||||
&& Argon2::default()
|
||||
.verify_password(password.as_bytes(), encoded)
|
||||
.is_ok()
|
||||
})
|
||||
}
|
||||
pub async fn hash_password_offline(password: String) -> Result<String> {
|
||||
validate_password(&password)?;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{bail, ensure, Context, Result};
|
||||
use argon2::{Algorithm, Argon2, Params, Version};
|
||||
use base64::Engine as _;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{rngs::SysRng, TryRng};
|
||||
use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -231,8 +231,8 @@ fn derive_key(password: &str, salt: &[u8]) -> Result<Zeroizing<[u8; 32]>> {
|
||||
fn new_crypto(password: &str) -> Result<CryptoContext> {
|
||||
let mut salt = [0u8; 16];
|
||||
let mut nonce_prefix = [0u8; 4];
|
||||
OsRng.fill_bytes(&mut salt);
|
||||
OsRng.fill_bytes(&mut nonce_prefix);
|
||||
SysRng.try_fill_bytes(&mut salt).expect("secure operating system random source unavailable");
|
||||
SysRng.try_fill_bytes(&mut nonce_prefix).expect("secure operating system random source unavailable");
|
||||
let header = CryptoHeader {
|
||||
format_version: FORMAT_VERSION,
|
||||
kdf: "Argon2id".into(),
|
||||
@@ -395,7 +395,7 @@ fn decrypt_archive(input: &Path, output: &Path, password: &str) -> Result<Zeroiz
|
||||
fn seal_master_key(master: &[u8], backup_key: &[u8]) -> Result<Vec<u8>> {
|
||||
ensure!(master.len() == 32, "invalid secret store master key");
|
||||
let mut nonce_bytes = [0u8; 12];
|
||||
OsRng.fill_bytes(&mut nonce_bytes);
|
||||
SysRng.try_fill_bytes(&mut nonce_bytes).expect("secure operating system random source unavailable");
|
||||
let cipher = cipher(backup_key)?;
|
||||
let mut encrypted = Zeroizing::new(master.to_vec());
|
||||
cipher
|
||||
|
||||
@@ -426,6 +426,6 @@ pub async fn restore(archive: &Path, data_dir: &Path, password: &str) -> Result<
|
||||
|
||||
pub fn generated_password() -> String {
|
||||
let mut bytes = [0u8; 32];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
SysRng.try_fill_bytes(&mut bytes).expect("secure operating system random source unavailable");
|
||||
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use crate::{config::CompiledBackend, vhost::PoolMember};
|
||||
use anyhow::{ensure, Result};
|
||||
use axum::body::Body;
|
||||
use http::{HeaderMap, HeaderValue, Request};
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use rand::{distr::Alphanumeric, RngExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -218,7 +218,10 @@ impl Default for Settings {
|
||||
impl Settings {
|
||||
/// Resolve active health per member; a disabled monitor never probes.
|
||||
pub fn health_for(&self, member: &PoolMember) -> HealthCheck {
|
||||
let mut health = member.health_check.clone().unwrap_or_else(|| self.health_check.clone());
|
||||
let mut health = member
|
||||
.health_check
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.health_check.clone());
|
||||
if health.host.is_none() && health.mode == HealthMode::Http {
|
||||
health.host = self.health_check.host.clone();
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ impl Pool {
|
||||
state: Arc::new(BackendState::new(settings.health_for(&definition).mode)),
|
||||
definition,
|
||||
backend,
|
||||
token: rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
token: rand::rng()
|
||||
.sample_iter(Alphanumeric)
|
||||
.take(32)
|
||||
.map(char::from)
|
||||
.collect(),
|
||||
@@ -53,14 +53,16 @@ impl Pool {
|
||||
};
|
||||
let same_origin = serde_json::to_vec(&member.definition.backend()).ok()
|
||||
== serde_json::to_vec(&old.definition.backend()).ok();
|
||||
let same_health =
|
||||
serde_json::to_vec(&(
|
||||
self.settings.health_for(&member.definition),
|
||||
self.settings.passive_for(&member.definition),
|
||||
)).ok() == serde_json::to_vec(&(
|
||||
let same_health = serde_json::to_vec(&(
|
||||
self.settings.health_for(&member.definition),
|
||||
self.settings.passive_for(&member.definition),
|
||||
))
|
||||
.ok()
|
||||
== serde_json::to_vec(&(
|
||||
previous.settings.health_for(&old.definition),
|
||||
previous.settings.passive_for(&old.definition),
|
||||
)).ok();
|
||||
))
|
||||
.ok();
|
||||
let same_clients = member.backend.client.reusable_with(&old.backend.client)
|
||||
&& member
|
||||
.backend
|
||||
|
||||
+7
-6
@@ -2,7 +2,7 @@
|
||||
//! ACLs run first; credentials are never cached with content or written to logs.
|
||||
use anyhow::{ensure, Result};
|
||||
use argon2::{
|
||||
password_hash::{PasswordHash, PasswordVerifier},
|
||||
password_hash::{phc::PasswordHash, PasswordVerifier},
|
||||
Argon2,
|
||||
};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
@@ -88,8 +88,10 @@ impl Settings {
|
||||
);
|
||||
let hash = PasswordHash::new(&user.password_hash)
|
||||
.map_err(|_| anyhow::anyhow!("basic.invalid_hash"))?;
|
||||
// Match the former 16-character minimum for the PHC-encoded salt.
|
||||
// phc 0.6 Salt::len() now measures decoded bytes instead.
|
||||
ensure!(
|
||||
hash.salt.is_some_and(|s| s.as_str().len() >= 16)
|
||||
hash.salt.is_some_and(|s| s.to_salt_string().len() >= 16)
|
||||
&& hash.hash.is_some_and(|h| h.len() == 32),
|
||||
"basic.invalid_hash"
|
||||
);
|
||||
@@ -221,11 +223,10 @@ impl Compiled {
|
||||
.map_err(|_| denied(StatusCode::SERVICE_UNAVAILABLE, None))?;
|
||||
let verified = tokio::task::spawn_blocking(move || {
|
||||
let _permit = permit;
|
||||
PasswordHash::new(&encoded).ok().is_some_and(|hash| {
|
||||
Argon2::default()
|
||||
.verify_password(password.as_bytes(), &hash)
|
||||
PasswordHash::new(&encoded).is_ok()
|
||||
&& Argon2::default()
|
||||
.verify_password(password.as_bytes(), encoded.as_str())
|
||||
.is_ok()
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
|
||||
+22
-16
@@ -256,23 +256,29 @@ async fn cold_restart_preserves_transformed_validators_stale_metadata_and_no_tra
|
||||
restart_shutdown.clone(),
|
||||
);
|
||||
assert_eq!(restarted.view().cold_entries, 2);
|
||||
{
|
||||
// COLD entries intentionally keep validators and headers on disk to bound RAM.
|
||||
// Inspect them through the same verified metadata loader used on COLD hits.
|
||||
let (key, transformed) = {
|
||||
let cold = restarted.cold.lock().unwrap();
|
||||
let transformed = cold
|
||||
.entries
|
||||
.values()
|
||||
.find(|entry| entry.path == "/transformed.json")
|
||||
.expect("transformed cold entry");
|
||||
assert_eq!(transformed.stale_while_revalidate, 30);
|
||||
assert_eq!(transformed.stale_if_error, 90);
|
||||
assert_eq!(transformed.origin_etag.as_ref().unwrap(), "\"origin-v1\"");
|
||||
assert_eq!(
|
||||
transformed.origin_last_modified.as_ref().unwrap(),
|
||||
"Mon, 26 Jan 2026 23:11:40 GMT"
|
||||
);
|
||||
assert_eq!(transformed.headers["etag"], "\"rustproxy-representation\"");
|
||||
assert!(transformed.transformed);
|
||||
}
|
||||
cold.entries
|
||||
.iter()
|
||||
.find(|(_, entry)| entry.path == "/transformed.json")
|
||||
.map(|(key, entry)| (*key, entry.clone()))
|
||||
.expect("transformed cold entry")
|
||||
};
|
||||
assert_eq!(transformed.stale_while_revalidate, 30);
|
||||
assert_eq!(transformed.stale_if_error, 90);
|
||||
assert!(transformed.has_validator);
|
||||
assert!(transformed.transformed);
|
||||
let metadata = read_cold_metadata(&key, &transformed)
|
||||
.await
|
||||
.expect("persisted transformed metadata");
|
||||
assert_eq!(metadata.origin_etag.as_ref().unwrap(), "\"origin-v1\"");
|
||||
assert_eq!(
|
||||
metadata.origin_last_modified.as_ref().unwrap(),
|
||||
"Mon, 26 Jan 2026 23:11:40 GMT"
|
||||
);
|
||||
assert_eq!(metadata.headers["etag"], "\"rustproxy-representation\"");
|
||||
|
||||
let transformed_hit = restarted
|
||||
.get_tiered(&transformed_ticket, &settings)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
fn request_id_hash(sequence: u64) -> String {
|
||||
use rand::RngCore;
|
||||
use rand::TryRng;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
let mut nonce = [0u8; 32];
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.fill_bytes(&mut nonce);
|
||||
let mut rng = rand::rngs::SysRng;
|
||||
rng.try_fill_bytes(&mut nonce)
|
||||
.expect("secure operating system random source unavailable");
|
||||
let mut hash = Sha256::new();
|
||||
hash.update(sequence.to_be_bytes());
|
||||
hash.update(nonce);
|
||||
@@ -13,9 +14,18 @@ fn request_id_hash(sequence: u64) -> String {
|
||||
|
||||
fn should_replace_error_page(response: &Response, minimal_welcome: bool) -> bool {
|
||||
!minimal_welcome
|
||||
&& response.extensions().get::<ErrorFallbackResponse>().is_none()
|
||||
&& response.extensions().get::<crate::pages::OriginProvidedResponse>().is_none()
|
||||
&& response.extensions().get::<crate::pages::PageServed>().is_none()
|
||||
&& response
|
||||
.extensions()
|
||||
.get::<ErrorFallbackResponse>()
|
||||
.is_none()
|
||||
&& response
|
||||
.extensions()
|
||||
.get::<crate::pages::OriginProvidedResponse>()
|
||||
.is_none()
|
||||
&& response
|
||||
.extensions()
|
||||
.get::<crate::pages::PageServed>()
|
||||
.is_none()
|
||||
&& response
|
||||
.extensions()
|
||||
.get::<crate::tinyauth::AuthenticationFailure>()
|
||||
@@ -298,14 +308,19 @@ async fn observe(State(state): State<Arc<App>>, mut request: Request, next: Next
|
||||
let global_compression = &snapshot.envelope.config.compression;
|
||||
let mut compression = global_compression.clone();
|
||||
if let Some(frontend) = frontend_context.as_ref() {
|
||||
compression = frontend.compression.resolve(global_compression, &compression);
|
||||
compression = frontend
|
||||
.compression
|
||||
.resolve(global_compression, &compression);
|
||||
}
|
||||
if let Some(route) = effective_route_context {
|
||||
if let crate::config::CompiledSource::WebServer(service) = &route.source {
|
||||
compression = service.compression.resolve(global_compression, &compression);
|
||||
compression = service
|
||||
.compression
|
||||
.resolve(global_compression, &compression);
|
||||
}
|
||||
}
|
||||
response = crate::compression::encode(response, &original_headers, &method, &compression).await;
|
||||
response =
|
||||
crate::compression::encode(response, &original_headers, &method, &compression).await;
|
||||
if let Some(encoding) = response
|
||||
.extensions()
|
||||
.get::<crate::compression::AppliedEncoding>()
|
||||
|
||||
@@ -13,7 +13,7 @@ use http_body::{Body as HttpBody, Frame, SizeHint};
|
||||
use http_body_util::BodyExt;
|
||||
use hyper::upgrade::{OnUpgrade, Upgraded};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{rngs::SysRng, TryRng};
|
||||
use serde::Serialize;
|
||||
use sha1::{Digest, Sha1};
|
||||
use std::{
|
||||
@@ -186,7 +186,7 @@ pub fn validate_h3(request: &Request) -> Result<Handshake> {
|
||||
"WebSocket version must be 13"
|
||||
);
|
||||
let mut nonce = [0u8; 16];
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
SysRng.try_fill_bytes(&mut nonce).expect("secure operating system random source unavailable");
|
||||
let key = STANDARD.encode(nonce);
|
||||
Ok(Handshake {
|
||||
accept: accept_for_key(&key),
|
||||
@@ -229,7 +229,7 @@ pub fn validate_h2(request: &Request) -> Result<Handshake> {
|
||||
"WebSocket version must be 13"
|
||||
);
|
||||
let mut nonce = [0u8; 16];
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
SysRng.try_fill_bytes(&mut nonce).expect("secure operating system random source unavailable");
|
||||
let key = STANDARD.encode(nonce);
|
||||
Ok(Handshake {
|
||||
accept: accept_for_key(&key),
|
||||
|
||||
+35
-11
@@ -1,7 +1,7 @@
|
||||
//! Central encrypted secret storage for provider/ACME credentials.
|
||||
//! Plaintext exists only for the duration of an explicit read/write operation.
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{rngs::SysRng, TryRng};
|
||||
use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};
|
||||
use serde::Serialize;
|
||||
use sqlx::Row;
|
||||
@@ -59,7 +59,10 @@ fn read_key(path: &Path) -> Result<Zeroizing<[u8; KEY_BYTES]>> {
|
||||
metadata.is_file() && !metadata.file_type().is_symlink(),
|
||||
"secret store master key must be a regular file"
|
||||
);
|
||||
ensure!(metadata.len() == KEY_BYTES as u64, "invalid secret store master key size");
|
||||
ensure!(
|
||||
metadata.len() == KEY_BYTES as u64,
|
||||
"invalid secret store master key size"
|
||||
);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
@@ -79,7 +82,10 @@ fn read_key(path: &Path) -> Result<Zeroizing<[u8; KEY_BYTES]>> {
|
||||
let mut file = options.open(path)?;
|
||||
file.read_exact(bytes.as_mut())?;
|
||||
let mut extra = [0u8; 1];
|
||||
ensure!(file.read(&mut extra)? == 0, "invalid secret store master key size");
|
||||
ensure!(
|
||||
file.read(&mut extra)? == 0,
|
||||
"invalid secret store master key size"
|
||||
);
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
@@ -92,7 +98,9 @@ pub fn initialize(data_dir: &Path) -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
let mut key = Zeroizing::new([0u8; KEY_BYTES]);
|
||||
OsRng.fill_bytes(key.as_mut());
|
||||
SysRng
|
||||
.try_fill_bytes(key.as_mut())
|
||||
.expect("secure operating system random source unavailable");
|
||||
let mut options = OpenOptions::new();
|
||||
options.write(true).create_new(true);
|
||||
#[cfg(unix)]
|
||||
@@ -158,13 +166,18 @@ pub async fn put(
|
||||
metadata: serde_json::Value,
|
||||
) -> Result<()> {
|
||||
validate_identity(secret_id, provider)?;
|
||||
ensure!(!plaintext.is_empty() && plaintext.len() <= MAX_SECRET_BYTES, "invalid secret size");
|
||||
ensure!(
|
||||
!plaintext.is_empty() && plaintext.len() <= MAX_SECRET_BYTES,
|
||||
"invalid secret size"
|
||||
);
|
||||
let metadata = serde_json::to_string(&metadata)?;
|
||||
ensure!(metadata.len() <= 4096, "secret metadata is too large");
|
||||
initialize(data_dir)?;
|
||||
let cipher = key(data_dir)?;
|
||||
let mut nonce = [0u8; NONCE_BYTES];
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
SysRng
|
||||
.try_fill_bytes(&mut nonce)
|
||||
.expect("secure operating system random source unavailable");
|
||||
let mut ciphertext = Zeroizing::new(plaintext.to_vec());
|
||||
cipher
|
||||
.seal_in_place_append_tag(
|
||||
@@ -212,12 +225,16 @@ pub async fn get(
|
||||
}
|
||||
validate_identity(secret_id, &provider)?;
|
||||
let version: i64 = row.try_get("encryption_version")?;
|
||||
ensure!(version == ENCRYPTION_VERSION, "unsupported secret encryption version");
|
||||
ensure!(
|
||||
version == ENCRYPTION_VERSION,
|
||||
"unsupported secret encryption version"
|
||||
);
|
||||
let nonce: Vec<u8> = row.try_get("nonce")?;
|
||||
ensure!(nonce.len() == NONCE_BYTES, "invalid secret nonce");
|
||||
let mut ciphertext = Zeroizing::new(row.try_get::<Vec<u8>, _>("ciphertext")?);
|
||||
ensure!(
|
||||
ciphertext.len() >= AES_256_GCM.tag_len() && ciphertext.len() <= MAX_SECRET_BYTES + AES_256_GCM.tag_len(),
|
||||
ciphertext.len() >= AES_256_GCM.tag_len()
|
||||
&& ciphertext.len() <= MAX_SECRET_BYTES + AES_256_GCM.tag_len(),
|
||||
"invalid encrypted secret size"
|
||||
);
|
||||
let mut nonce_bytes = [0u8; NONCE_BYTES];
|
||||
@@ -287,7 +304,10 @@ pub async fn migrate_acme_credentials(
|
||||
return Ok(0);
|
||||
}
|
||||
let root_meta = fs::symlink_metadata(&root)?;
|
||||
ensure!(root_meta.is_dir() && !root_meta.file_type().is_symlink(), "invalid ACME directory");
|
||||
ensure!(
|
||||
root_meta.is_dir() && !root_meta.file_type().is_symlink(),
|
||||
"invalid ACME directory"
|
||||
);
|
||||
let mut migrated = 0usize;
|
||||
for entry in fs::read_dir(&root)? {
|
||||
let entry = entry?;
|
||||
@@ -301,7 +321,10 @@ pub async fn migrate_acme_credentials(
|
||||
continue;
|
||||
}
|
||||
let profile: serde_json::Value = serde_json::from_slice(&fs::read(&profile_path)?)?;
|
||||
let provider = profile.get("provider").and_then(serde_json::Value::as_str).unwrap_or("");
|
||||
let provider = profile
|
||||
.get("provider")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.unwrap_or("");
|
||||
let certificate_id = profile
|
||||
.get("certificate_id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
@@ -341,7 +364,8 @@ pub async fn migrate_acme_credentials(
|
||||
)
|
||||
.await?;
|
||||
plaintext.zeroize();
|
||||
fs::remove_file(&credential_path).context("legacy ACME credential was encrypted but could not be removed")?;
|
||||
fs::remove_file(&credential_path)
|
||||
.context("legacy ACME credential was encrypted but could not be removed")?;
|
||||
migrated += 1;
|
||||
}
|
||||
Ok(migrated)
|
||||
|
||||
@@ -80,8 +80,8 @@ fn generate_locked(directory: &Path, name: &str, hosts: Vec<String>) -> Result<(
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let rcgen::CertifiedKey { cert, key_pair } = rcgen::generate_simple_self_signed(hosts)?;
|
||||
let key = Zeroizing::new(key_pair.serialize_pem());
|
||||
let rcgen::CertifiedKey { cert, signing_key } = rcgen::generate_simple_self_signed(hosts)?;
|
||||
let key = Zeroizing::new(signing_key.serialize_pem());
|
||||
store_pair(directory, name, cert.pem().as_bytes(), key.as_bytes())
|
||||
}
|
||||
pub fn initialize_default(directory: &Path) -> Result<()> {
|
||||
|
||||
+5
-5
@@ -77,7 +77,7 @@ mod tests {
|
||||
dir.path(),
|
||||
"shared-cert",
|
||||
generated.cert.pem().as_bytes(),
|
||||
generated.key_pair.serialize_pem().as_bytes(),
|
||||
generated.signing_key.serialize_pem().as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(cert_file.starts_with("external-shared-cert-"));
|
||||
@@ -100,7 +100,7 @@ mod tests {
|
||||
let generated =
|
||||
rcgen::generate_simple_self_signed(vec!["reconcile.example.test".into()]).unwrap();
|
||||
let certificate = generated.cert.pem();
|
||||
let key = generated.key_pair.serialize_pem();
|
||||
let key = generated.signing_key.serialize_pem();
|
||||
let (cert_file, key_file, _) = stage_external_pair(
|
||||
dir.path(),
|
||||
"reconcile-cert",
|
||||
@@ -281,7 +281,7 @@ mod tests {
|
||||
let b = rcgen::generate_simple_self_signed(vec!["b.localhost".into()]).unwrap();
|
||||
assert!(parse_pair(
|
||||
a.cert.pem().as_bytes(),
|
||||
b.key_pair.serialize_pem().as_bytes()
|
||||
b.signing_key.serialize_pem().as_bytes()
|
||||
)
|
||||
.is_err());
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
@@ -338,7 +338,7 @@ mod tests {
|
||||
assert!(upload(
|
||||
dir.path(),
|
||||
pair.cert.pem().as_bytes(),
|
||||
pair.key_pair.serialize_pem().as_bytes(),
|
||||
pair.signing_key.serialize_pem().as_bytes(),
|
||||
hosts,
|
||||
)
|
||||
.is_ok());
|
||||
@@ -353,7 +353,7 @@ mod tests {
|
||||
])
|
||||
.unwrap();
|
||||
let pem = pair.cert.pem();
|
||||
let key = pair.key_pair.serialize_pem();
|
||||
let key = pair.signing_key.serialize_pem();
|
||||
assert!(upload(
|
||||
dir.path(),
|
||||
pem.as_bytes(),
|
||||
|
||||
@@ -10,7 +10,7 @@ mod acme_tests {
|
||||
dir.path(),
|
||||
"acme-fixture",
|
||||
cert.cert.pem().as_bytes(),
|
||||
cert.key_pair.serialize_pem().as_bytes(),
|
||||
cert.signing_key.serialize_pem().as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
let requested = vec!["example.org".into(), "*.example.org".into()];
|
||||
@@ -62,7 +62,7 @@ mod acme_tests {
|
||||
dir.path(),
|
||||
"acme-many",
|
||||
cert.cert.pem().as_bytes(),
|
||||
cert.key_pair.serialize_pem().as_bytes(),
|
||||
cert.signing_key.serialize_pem().as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(validate_acme(
|
||||
|
||||
@@ -15,7 +15,7 @@ fn external_absolute_paths_follow_certbot_style_symlinks_on_each_reload() {
|
||||
|
||||
let first = rcgen::generate_simple_self_signed(vec!["example.test".into()]).unwrap();
|
||||
fs::write(archive.join("fullchain1.pem"), first.cert.pem()).unwrap();
|
||||
fs::write(archive.join("privkey1.pem"), first.key_pair.serialize_pem()).unwrap();
|
||||
fs::write(archive.join("privkey1.pem"), first.signing_key.serialize_pem()).unwrap();
|
||||
fs::set_permissions(archive.join("privkey1.pem"), fs::Permissions::from_mode(0o650)).unwrap();
|
||||
symlink("../../archive/example.test/fullchain1.pem", live.join("fullchain.pem")).unwrap();
|
||||
symlink("../../archive/example.test/privkey1.pem", live.join("privkey.pem")).unwrap();
|
||||
@@ -47,7 +47,7 @@ fn external_absolute_paths_follow_certbot_style_symlinks_on_each_reload() {
|
||||
])
|
||||
.unwrap();
|
||||
fs::write(archive.join("fullchain2.pem"), second.cert.pem()).unwrap();
|
||||
fs::write(archive.join("privkey2.pem"), second.key_pair.serialize_pem()).unwrap();
|
||||
fs::write(archive.join("privkey2.pem"), second.signing_key.serialize_pem()).unwrap();
|
||||
fs::set_permissions(archive.join("privkey2.pem"), fs::Permissions::from_mode(0o640)).unwrap();
|
||||
fs::remove_file(live.join("fullchain.pem")).unwrap();
|
||||
fs::remove_file(live.join("privkey.pem")).unwrap();
|
||||
|
||||
@@ -40,7 +40,7 @@ async fn php_fpm_gzip_response_finishes_after_fastcgi_eof() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
std::fs::write(tmp.path().join("index.php"), b"<?php echo 'placeholder';").unwrap();
|
||||
let address = fastcgi_fixture(expected).await;
|
||||
let configured = Service {
|
||||
let mut configured = Service {
|
||||
id: "php-compression-regression".into(),
|
||||
name: "PHP compression regression".into(),
|
||||
document_root: tmp.path().to_path_buf(),
|
||||
|
||||
@@ -24,22 +24,22 @@ def read_docker_agent_source() -> str:
|
||||
|
||||
preflight(root)
|
||||
manifest = tomllib.loads((root / "Cargo.toml").read_text())
|
||||
assert manifest["package"]["version"] == "0.31.1"
|
||||
assert manifest["package"]["version"] == "0.31.2"
|
||||
assert manifest["dependencies"]["quinn"]["version"] == "0.11.12"
|
||||
h3dep = manifest["dependencies"]["h3"]
|
||||
assert h3dep["package"] == "hpx-h3" and h3dep["version"] == "=2.5.19" and "quinn" in h3dep["features"]
|
||||
assert h3dep["package"] == "hpx-h3" and h3dep["version"] == "=2.5.20" and "quinn" in h3dep["features"]
|
||||
assert "h3-quinn" not in manifest["dependencies"]
|
||||
lock_path = root / "Cargo.lock"
|
||||
if lock_path.exists():
|
||||
lock = lock_path.read_text()
|
||||
assert 'name = "rust-proxy"\nversion = "0.31.1"' in lock
|
||||
assert 'name = "rust-proxy"\nversion = "0.31.2"' in lock
|
||||
assert 'name = "systemstat"' in lock and 'name = "metrics-process"' in lock and 'name = "sysinfo"' in lock, "Cargo.lock must be resolver-generated after adding cross-platform statistics libraries"
|
||||
for release_doc, marker in {
|
||||
"README.md": "# RustProxy 0.31.1",
|
||||
"CHANGELOG.md": "## 0.31.1",
|
||||
"ROADMAP.md": "## 0.31.1",
|
||||
"AGENTS.md": "Current source version: 0.31.1.",
|
||||
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.31.1",
|
||||
"README.md": "# RustProxy 0.31.2",
|
||||
"CHANGELOG.md": "## 0.31.2",
|
||||
"ROADMAP.md": "## 0.31.2",
|
||||
"AGENTS.md": "Current source version: 0.31.2.",
|
||||
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.31.2",
|
||||
}.items():
|
||||
assert marker in (root / release_doc).read_text(), f"stale release identity in {release_doc}"
|
||||
assert tomllib.loads((root / "rust-toolchain.toml").read_text())["toolchain"]["channel"] == "stable"
|
||||
@@ -285,7 +285,7 @@ assert 'Opt-in health decisions based on ordinary user traffic. Defaults to disa
|
||||
assert '"force_ssl"' in openapi and '"forward_client_ip"' in openapi
|
||||
assert '"backend_header"' in openapi and '"backend_timing_header"' in openapi
|
||||
assert '"StreamMetrics"' in openapi and '"streams"' in openapi
|
||||
for marker in ['"version": "0.31.1"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']:
|
||||
for marker in ['"version": "0.31.2"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']:
|
||||
assert marker in openapi, marker
|
||||
for marker in ['"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
|
||||
assert marker in openapi, marker
|
||||
@@ -1362,7 +1362,7 @@ metrics_dep = manifest["dependencies"]["metrics-process"]
|
||||
assert metrics_dep["version"] == "=2.4.3" and metrics_dep["default-features"] is False
|
||||
assert metrics_dep["features"] == ["dummy"]
|
||||
sysinfo_dep = manifest["dependencies"]["sysinfo"]
|
||||
assert sysinfo_dep["version"] == "=0.38.4" and sysinfo_dep["default-features"] is False
|
||||
assert sysinfo_dep["version"] == "0.39" and sysinfo_dep["default-features"] is False
|
||||
assert set(sysinfo_dep["features"]) == {"network", "system"}
|
||||
for marker_0241 in [
|
||||
"systemstat::{Platform, System}", "metrics_process::collector::collect",
|
||||
@@ -1396,7 +1396,7 @@ assert '"error_log" => {' not in admin_0241 and 'request("/api/config/error_log"
|
||||
assert '"error_log"' in openapi and 'logging, error_log, forwarding' not in openapi
|
||||
assert 'logging_section_persists_error_log_settings_atomically' in admin_0241 and 'error_log_is_not_a_standalone_configuration_section' in admin_0241
|
||||
assert 'location.pathname === "/logging"' in settings_js_0241
|
||||
assert '"RuntimeInformation"' in openapi and '"version": "0.31.1"' in openapi
|
||||
assert '"RuntimeInformation"' in openapi and '"version": "0.31.2"' in openapi
|
||||
|
||||
# 0.24.1 compiler and hostname-cardinality regressions.
|
||||
tls_0241 = rust_source(root, "src/tls.rs")
|
||||
@@ -1635,5 +1635,5 @@ for marker_0282 in [
|
||||
assert 'Zewnętrzne ścieżki plików' in (root / 'langs/pl_pl.json').read_text()
|
||||
assert 'setfacl -m u:rustproxy:--x /etc/ssl/' in (root / 'docs/V0.28.2_IMPLEMENTATION.md').read_text()
|
||||
|
||||
print("PASS: 0.31.1 manifest/package, shared system-statistics/runtime metadata, Settings logging, retained H2, external-packaging/Grafana/Prometheus/404-host/custom-location/path/cache/dashboard/Nagios UI/source contracts, stopped legacy-renewal removal, session/admin/transport contracts, deterministic package source and embedded SQLite schema-v9 migrations.")
|
||||
print("PASS: 0.31.2 manifest/package, shared system-statistics/runtime metadata, Settings logging, retained H2, external-packaging/Grafana/Prometheus/404-host/custom-location/path/cache/dashboard/Nagios UI/source contracts, stopped legacy-renewal removal, session/admin/transport contracts, deterministic package source and embedded SQLite schema-v9 migrations.")
|
||||
print("NOT VERIFIED: Rust parsing, types, compilation, linking or runtime behavior. Run ./dev.sh test.")
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check the 0.31.2 dependency/source migration; optionally require a resolved lock.
|
||||
|
||||
This is not a substitute for rustc, cargo metadata or runtime tests.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import re
|
||||
import tomllib
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--require-lock', action='store_true', help='Require the post-upgrade Cargo.lock produced by cargo update')
|
||||
args = parser.parse_args()
|
||||
manifest = tomllib.loads((ROOT / 'Cargo.toml').read_text())
|
||||
assert manifest['package']['version'] == '0.31.2'
|
||||
assert manifest['package']['rust-version'] == '1.99'
|
||||
deps = manifest['dependencies']
|
||||
expected_requirements = {
|
||||
'argon2': '0.6', 'rand': '0.10', 'sqlx': '0.9', 'rcgen': '0.14.10',
|
||||
'tower-http': '0.7.1', 'brotli': '9', 'sysinfo': '0.39',
|
||||
'h3': '=2.5.20', 'tokio': '1.53.2', 'hyper': '1.12.0',
|
||||
}
|
||||
for name, version in expected_requirements.items():
|
||||
entry = deps[name]
|
||||
found = entry if isinstance(entry, str) else entry['version']
|
||||
assert found == version, f'{name} requirement changed: {found!r} != {version!r}'
|
||||
assert deps['h3']['package'] == 'hpx-h3'
|
||||
assert deps['h3']['features'] == ['quinn']
|
||||
assert deps['sqlx']['default-features'] is False
|
||||
assert deps['sqlx']['features'] == ['runtime-tokio', 'sqlite']
|
||||
assert set(deps['sysinfo']['features']) == {'network','system'}
|
||||
assert set(deps['rcgen']['features']) == {'crypto', 'pem', 'ring'}
|
||||
assert 'tls12' in deps['rustls']['features'] and 'ring' in deps['rustls']['features']
|
||||
assert 'tls12' in deps['tokio-rustls']['features']
|
||||
for target in ('Dockerfile', 'integrations/routeros-oci-build/Dockerfile.routeros'):
|
||||
assert 'FROM rust:1.99-trixie AS build' in (ROOT / target).read_text(), target
|
||||
files = [p for p in (ROOT / 'src').rglob('*.rs')]
|
||||
sources = '\n'.join(p.read_text() for p in files)
|
||||
for old in ('rngs::OsRng', 'rand::thread_rng()', 'SaltString::generate(',
|
||||
'distributions::Alphanumeric', '.key_pair.serialize_pem()',
|
||||
'hash_password(password.as_bytes(), &salt)'):
|
||||
assert old not in sources, f'obsolete dependency API: {old}'
|
||||
for new in ('SysRng.try_fill_bytes(', 'phc::PasswordHash',
|
||||
'hash_password(password.as_bytes())', 'verify_password(password.as_bytes()',
|
||||
'signing_key.serialize_pem()', 'rand::rng()'):
|
||||
assert new in sources, f'missing dependency migration: {new}'
|
||||
assert 'VERSION = "5.33.1"' in (ROOT / 'scripts/build/swagger_assets.py').read_text()
|
||||
lock = tomllib.loads((ROOT / 'Cargo.lock').read_text())
|
||||
installed: dict[str,set[str]] = {}
|
||||
for item in lock['package']:
|
||||
installed.setdefault(item['name'], set()).add(item['version'])
|
||||
latest_required = {
|
||||
'argon2': '0.6.0', 'rand': '0.10.3', 'sqlx': '0.9.0',
|
||||
'rcgen': '0.14.10', 'tower-http': '0.7.1', 'brotli': '9.0.0',
|
||||
'sysinfo': '0.39.6', 'hpx-h3': '2.5.20', 'hyper': '1.12.0',
|
||||
'tokio': '1.53.2',
|
||||
}
|
||||
def compatible_update(package: str, installed_version: str, minimum: str) -> bool:
|
||||
candidate = tuple(int(x) for x in installed_version.split('.')[:3])
|
||||
baseline = tuple(int(x) for x in minimum.split('.')[:3])
|
||||
if package == 'hpx-h3':
|
||||
return candidate == baseline # Intentionally exact-pinned for the QUIC integration.
|
||||
if candidate[0] != baseline[0]:
|
||||
return False
|
||||
if baseline[0] == 0 and candidate[1] != baseline[1]:
|
||||
return False
|
||||
return candidate >= baseline
|
||||
|
||||
missing = {name: version for name,version in latest_required.items()
|
||||
if not any(compatible_update(name, candidate, version)
|
||||
for candidate in installed.get(name, set()))}
|
||||
if missing:
|
||||
details = ', '.join(f'{name} {version}' for name,version in missing.items())
|
||||
if args.require_lock:
|
||||
raise SystemExit(f'FAIL: Cargo.lock not resolved to current deps: {details}. Run cargo update first.')
|
||||
print(f'PENDING: lockfile still requires cargo update ({details})')
|
||||
else:
|
||||
print('PASS: pinned key packages found in post-upgrade lock; cargo --locked verification is still required')
|
||||
print('PASS: 0.31.2 source dependency APIs, manifest, TLS features, toolchain images and Swagger pin (static only)')
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rust-independent documentation/Reader consistency checks for the 0.31.1 release."""
|
||||
"""Rust-independent documentation/Reader consistency checks for the 0.31.2 release."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
from rust_source import rust_source
|
||||
@@ -65,7 +65,7 @@ assert '"/docs/content.json"' in admin
|
||||
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
roadmap = (ROOT / "ROADMAP.md").read_text(encoding="utf-8")
|
||||
changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8")
|
||||
assert readme.startswith("# RustProxy 0.31.1")
|
||||
assert readme.startswith("# RustProxy 0.31.2")
|
||||
assert "docs/WEB_SERVER.md" in readme and "docs/README.md" in readme
|
||||
assert readme.count("## What changed in ") == 1, "README duplicates historical release notes; use CHANGELOG/archive"
|
||||
assert "## Release history" in readme and "docs/ROADMAP_HISTORY.md" in readme
|
||||
|
||||
@@ -75,10 +75,10 @@ def check(root: Path, repair_known: bool = False) -> list[str]:
|
||||
raise ValueError(f"Conflicting Rust modules: {flat.relative_to(root)} and {module.relative_to(root)}")
|
||||
for path in ["src/auth/mod.rs", "src/auth/store.rs", "src/backup/snapshot.rs", "src/upstream.rs", "src/tls.rs", "src/config/internal_routing.rs", "src/proxy/websocket.rs", "src/proxy/error_fallback.rs", "src/cache/mod.rs", "src/cache/model.rs", "src/cache/lifecycle.rs", "src/cache/cold.rs", "src/cache/dashboard.rs", "src/cache/tickets.rs", "src/cache/lookup.rs", "src/cache/policy.rs", "src/cache/fill.rs", "src/cache/tests.rs", "src/settings.rs", "src/compression.rs", "src/diagnostics.rs", "src/traffic.rs", "src/system_stats/mod.rs", "src/system_stats/linux.rs", "src/vhost.rs", "src/vhost/error_fallback.rs", "src/web_server/mod.rs", "src/web_server/config.rs", "src/web_server/filesystem.rs", "src/web_server/security.rs", "src/web_server/response.rs", "src/web_server/request.rs", "src/web_server/routing.rs", "src/web_server/fastcgi.rs", "src/web_server/htaccess.rs", "src/web_server/serve.rs", "src/web_server/tests.rs", "src/balance.rs", "src/security.rs", "src/admin/web/vhosts/core.js", "src/admin/web/vhosts/health.js", "src/admin/web/vhosts/editor.js", "src/admin/web/vhosts/render.js", "src/admin/web/web-server.js", "src/admin/web/streams.js", "src/admin/web/host404.js", "src/admin/web/app/core.js", "src/admin/web/app/preferences.js", "src/admin/web/app/config-history.js", "src/admin/web/app/runtime.js", "src/admin/web/app/settings.js", "src/admin/web/management.js", "src/admin/web/drafts.js", "src/pages.rs", "tools/dev/mock_server.py", "tools/dev/mock_auth.py", "src/forwarding.rs", "src/tinyauth.rs", "src/http3.rs", "src/stream.rs", "src/admin/web/integrations.js", "src/admin/web/traffic-panel.js", "src/prometheus.rs", "src/admin/web/settings.js", "src/admin/web/docker.js", "src/docker.rs", "docker-agent/main.go", "docker-agent/model.go", "docker-agent/docker.go", "docker-agent/network.go", "docker-agent/certificate.go", "docker-agent/server.go", "docker-agent/main_test.go", "docker-agent/rustproxy-docker-agent.service", "docker-agent/install.sh", "docs/DOCKER_DISCOVERY.md", "docs/WEB_SERVER.md", "docs/PHP_FPM.md", "docs/README.md", "docs/TODO.md", "docs/V0.25.5_AUDIT.md", "docs/V0.25.4_AUDIT.md", "docs/V0.25.3_AUDIT.md", "docs/V0.25.2_AUDIT.md", "docs/CHANGELOG_TECHNICAL_ARCHIVE.md", "docs/ROADMAP_HISTORY.md", "tools/checks/docs_check.py", "tools/checks/web_server_contracts.py", "tools/checks/rust_source.py", "tools/checks/rust_module_contracts.py", "tools/checks/source_module_contracts.py", "scripts/runtime/acme_provider.py", "src/basic_auth.rs", "scripts/deploy/deploy.py", "scripts/deploy/deploy_core.py", "scripts/deploy/deploy_runtime.py", "scripts/migration/migrate_npm.py", "scripts/migration/migrate_npm_core.py", "scripts/migration/migrate_npm_api.py", "docs/NPM_MIGRATION.md", "docs/STREAMS.md", "install.sh", "install-debian13.sh", "update.sh", "build.rs", "scripts/build/swagger_assets.py", "src/admin/documentation.rs", "src/admin/openapi.json", "langs/en_US.json", "langs/pl_pl.json", "Dockerfile", ".dockerignore", "docs/CONTAINER.md", "docs/PROMETHEUS.md", "docs/NOT_FOUND_HOSTS.md", "grafana/rustproxy-overview.json"]:
|
||||
if not (root / path).is_file():
|
||||
raise ValueError(f"Incomplete RustProxy 0.31.1 source tree: missing {path}")
|
||||
raise ValueError(f"Incomplete RustProxy 0.31.2 source tree: missing {path}")
|
||||
text = (root / "src/lib.rs").read_text(encoding="utf-8")
|
||||
if '#[path = "auth/mod.rs"]' not in text:
|
||||
raise ValueError("src/lib.rs is not the 0.31.1 entry point; extract the complete package, not individual files.")
|
||||
raise ValueError("src/lib.rs is not the 0.31.2 entry point; extract the complete package, not individual files.")
|
||||
return messages
|
||||
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ def upgrade_smoke(tmp: Path) -> None:
|
||||
assert backup.is_dir()
|
||||
assert (backup / "src/obsolete.rs").is_file()
|
||||
assert not (target / "src/obsolete.rs").exists()
|
||||
assert 'version = "0.31.1"' in (target / "Cargo.toml").read_text(encoding="utf-8")
|
||||
assert 'version = "0.31.2"' in (target / "Cargo.toml").read_text(encoding="utf-8")
|
||||
assert (target / "data/keep.txt").read_text(encoding="utf-8") == "preserve\n"
|
||||
assert (target / ".env").read_text(encoding="utf-8") == "RUST_PROXY_DEMO=0\n"
|
||||
assert (target / "langs/site.json").is_file()
|
||||
@@ -146,7 +146,7 @@ def upgrade_smoke(tmp: Path) -> None:
|
||||
assert (target / "docker-agent/certificate.go").is_file()
|
||||
assert (target / "docker-agent/server.go").is_file()
|
||||
manifest = json.loads((backup / "upgrade.json").read_text(encoding="utf-8"))
|
||||
assert manifest["to_version"] == "0.31.1"
|
||||
assert manifest["to_version"] == "0.31.2"
|
||||
assert manifest["removed_obsolete_runtime_state"] is True
|
||||
assert not legacy.exists()
|
||||
assert not (backup / "legacy-data").exists()
|
||||
@@ -582,7 +582,7 @@ def source_contract_smoke() -> None:
|
||||
assert metrics_process["default-features"] is False
|
||||
assert metrics_process["features"] == ["dummy"]
|
||||
sysinfo_network = manifest["dependencies"]["sysinfo"]
|
||||
assert sysinfo_network["version"] == "=0.38.4"
|
||||
assert sysinfo_network["version"] == "0.39"
|
||||
assert sysinfo_network["default-features"] is False
|
||||
assert set(sysinfo_network["features"]) == {"network", "system"}
|
||||
target_key = 'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "macos", target_os = "windows"))'
|
||||
@@ -617,7 +617,7 @@ def source_contract_smoke() -> None:
|
||||
assert marker in admin, marker
|
||||
anonymous_block = admin.split("fn anonymous_allowed", 1)[1].split("const CERTIFICATE_API_KEY_HEADER", 1)[0]
|
||||
assert '"/api/status"' not in anonymous_block
|
||||
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.31.1"' in openapi_0241
|
||||
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.31.2"' in openapi_0241
|
||||
assert 'id="panel-logging"' not in html
|
||||
for marker in ['id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"', 'id="error-log-form"']:
|
||||
assert marker in html, marker
|
||||
@@ -1108,7 +1108,7 @@ def source_contract_smoke() -> None:
|
||||
assert '.managed-update' in main and "flush_hot_to_cold_for_update" in main
|
||||
openapi = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8")
|
||||
assert '"/api/config/history/restore"' in openapi
|
||||
for marker in ['"version": "0.31.1"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
|
||||
for marker in ['"version": "0.31.2"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
|
||||
assert marker in openapi, marker
|
||||
assert '"software_update"' in openapi and '"update_available"' in openapi
|
||||
for marker in ['"/api/certificates/deploy"', '"CertificateDeploy"', '"CertificateDeployResult"', '"/api/certificates/reload"']:
|
||||
@@ -1177,7 +1177,7 @@ def container_contract_smoke() -> None:
|
||||
assert "LogsDirectory=rustproxy" in generated_unit and "/var/log/rustproxy" in generated_unit
|
||||
assert "Environment=MALLOC_ARENA_MAX=2" in generated_unit
|
||||
for marker in [
|
||||
"FROM rust:1.88-trixie AS build",
|
||||
"FROM rust:1.99-trixie AS build",
|
||||
"cargo generate-lockfile",
|
||||
"cargo build --locked --release",
|
||||
"FROM debian:trixie-slim AS runtime",
|
||||
@@ -1313,7 +1313,7 @@ def main() -> None:
|
||||
source_contract_smoke()
|
||||
container_contract_smoke()
|
||||
grafana_contract_smoke()
|
||||
print("PASS: 0.31.1 SQLite/schema-9/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, external Debian-packaging separation, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.")
|
||||
print("PASS: 0.31.2 SQLite/schema-9/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, external Debian-packaging separation, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -43,7 +43,7 @@ const code = js.slice(js.indexOf(' const runtime = latestStatus?.runtime || {
|
||||
const elements = new Map();
|
||||
const dollar = id => { if (!elements.has(id)) elements.set(id, { textContent: '' }); return elements.get(id); };
|
||||
const status = {
|
||||
version: '0.31.1', uptime_seconds: 4201,
|
||||
version: '0.31.2', uptime_seconds: 4201,
|
||||
runtime: {
|
||||
platform: 'Linux', operating_system: 'Debian GNU/Linux 13', kernel_version: '6.12.0', architecture: 'x86_64',
|
||||
statistics_source: 'systemstat+sysinfo', data_directory: '/var/lib/rustproxy', database_path: '/var/lib/rustproxy/config.sqlite3',
|
||||
@@ -63,7 +63,7 @@ const context = {
|
||||
vm.runInNewContext(code, context);
|
||||
assert.equal(dollar('runtime-operating-system').textContent, 'Debian GNU/Linux 13');
|
||||
assert.equal(dollar('runtime-kernel-version').textContent, '6.12.0');
|
||||
assert.equal(dollar('runtime-version').textContent, '0.31.1');
|
||||
assert.equal(dollar('runtime-version').textContent, '0.31.2');
|
||||
assert.equal(dollar('runtime-logical-cpus').textContent, '8');
|
||||
assert.equal(dollar('runtime-cpu-usage').textContent, '17.2%');
|
||||
assert.equal(dollar('runtime-python').textContent, 'Available · 3.13.5');
|
||||
@@ -124,4 +124,4 @@ for (const key of ['python_version','pip_version','certbot_version']) {
|
||||
assert.ok(runtimeSchema.properties.certificate_runtime.properties[key]);
|
||||
assert.equal(runtimeSchema.properties.certificate_runtime.properties[key].nullable, true);
|
||||
}
|
||||
console.log('PASS: 0.31.1 legend palette, About cleanup, system tab/navigation, read-only API schema and simulated status rendering.');
|
||||
console.log('PASS: 0.31.2 legend palette, About cleanup, system tab/navigation, read-only API schema and simulated status rendering.');
|
||||
@@ -54,7 +54,7 @@ def run_load(
|
||||
port = parsed.port or (443 if parsed.scheme == "https" else 80)
|
||||
tls = ssl._create_unverified_context() if insecure else ssl.create_default_context()
|
||||
connection_type = http.client.HTTPSConnection if parsed.scheme == "https" else http.client.HTTPConnection
|
||||
headers = {"user-agent": "RustProxy-Memory-Stress/0.31.1", "accept": "*/*"}
|
||||
headers = {"user-agent": "RustProxy-Memory-Stress/0.31.2", "accept": "*/*"}
|
||||
if host_header:
|
||||
headers["host"] = host_header
|
||||
|
||||
|
||||
Reference in new issue
Block a user