From 00f29f92c89fafd5b54a59ba92b0f477bfdb1e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sat, 17 Oct 2026 20:34:13 +0200 Subject: [PATCH 01/16] v0.34.0_1 --- AGENTS.md | 2 +- CHANGELOG.md | 50 +++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 64 +++- ROADMAP.md | 30 ++ SHA256SUMS | 247 +++++++------- docs/NGINX_GAP_0.34.0.json | 216 +++++++++++++ docs/PROXY_HOTPATH_0.34.0.md | 84 +++++ docs/README.md | 1 + docs/TEST_REPORT_0.34.0.md | 173 ++++++++++ docs/WEB_SERVER.md | 35 +- langs/en_US.json | 3 + langs/pl_pl.json | 3 + scripts/performance/README.md | 33 ++ scripts/performance/simulate_cache_034.py | 112 +++++++ scripts/performance/simulate_nginx_gap_034.py | 98 ++++++ scripts/tests/cache_scope_034_static_test.py | 39 +++ .../tests/proxy_hotpath_034_static_test.py | 47 +++ .../tests/sendfile_034_panel_browser_test.py | 82 +++++ scripts/tests/sendfile_034_static_test.py | 44 +++ src/admin/openapi.json | 6 + src/admin/web/index.html | 5 + src/admin/web/web-server.js | 4 +- src/balance/health.rs | 21 +- src/balance/model.rs | 3 + src/balance/pool_core.rs | 124 +++++--- src/balance/tests.rs | 48 ++- src/cache/cold/demotion.rs | 8 + src/cache/cold/startup.rs | 8 +- src/cache/cold/worker.rs | 1 + src/cache/lifecycle.rs | 23 ++ src/cache/model/cache.rs | 7 + src/cache/model/maintenance.rs | 2 + src/cache/tests/cold_tier_tests.rs | 35 ++ src/cache/tests/scope_tests.rs | 16 + src/cache/tickets.rs | 37 ++- src/config/backend.rs | 4 +- src/config/internal_routing.rs | 16 +- src/config/model.rs | 4 +- src/config/routing.rs | 12 +- src/config/snapshot.rs | 52 +-- src/config/tests.rs | 13 + src/logging/format.rs | 6 +- src/proxy/body.rs | 91 ++++-- src/proxy/body/tests.rs | 30 +- src/proxy/cache_helpers.rs | 4 +- src/proxy/error_fallback.rs | 7 +- src/proxy/forward/route_cache.rs | 21 +- src/proxy/forward/upstream.rs | 47 ++- src/proxy/forward/web_source.rs | 43 ++- src/proxy/forward_entry.rs | 17 +- src/proxy/types.rs | 11 +- src/proxy/upstream_helpers.rs | 20 +- src/runtime/constructors.rs | 4 +- src/runtime/tests.rs | 49 ++- src/runtime/types.rs | 116 +++++-- src/runtime/updates.rs | 12 + src/server.rs | 7 + src/server/http1_connection.rs | 207 ++++++++++++ src/server/sendfile.rs | 301 ++++++++++++++++++ src/server/tls_runtime.rs | 219 ++----------- src/web_server/config/compile.rs | 1 + src/web_server/config/model.rs | 13 + src/web_server/config/validation.rs | 1 + src/web_server/mod.rs | 2 +- src/web_server/response/file.rs | 16 + src/web_server/tests.rs | 51 +++ 68 files changed, 2599 insertions(+), 513 deletions(-) create mode 100644 docs/NGINX_GAP_0.34.0.json create mode 100644 docs/PROXY_HOTPATH_0.34.0.md create mode 100644 docs/TEST_REPORT_0.34.0.md create mode 100644 scripts/performance/simulate_cache_034.py create mode 100644 scripts/performance/simulate_nginx_gap_034.py create mode 100644 scripts/tests/cache_scope_034_static_test.py create mode 100644 scripts/tests/proxy_hotpath_034_static_test.py create mode 100644 scripts/tests/sendfile_034_panel_browser_test.py create mode 100644 scripts/tests/sendfile_034_static_test.py create mode 100644 src/server/http1_connection.rs create mode 100644 src/server/sendfile.rs diff --git a/AGENTS.md b/AGENTS.md index 5bc647b..cb64bc3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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.33.8. +compatibility. Current source version: 0.34.0. ## Delivery diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f229d8..c86aba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,56 @@ > Operator-facing release history. Historical engineering notes are kept in > [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md). +## 0.34.0 + +- Optimize reverse-proxy hot path with shared immutable compiled route handles, + reusing the observer's host/location selection in forwarding without changing + ACL, rate, canonical authority or protocol enforcement. +- Avoid wildcard allocations for exact-host hits; preserve host precedence, + listener bindings, regex and internal named-location fallback behavior. +- Add allocation-free, scheduler-lock-free single-backend LB selection for + non-cookie pools. Keep health/drain/capacity admission, retry and lease + lifetime; preserve multi-backend algorithms and cookie-affinity semantics. +- Make healthy-backend selection lock-free using a synchronized atomic status + tag; share compiled health/passive policies between leases rather than + deep-cloning their strings per request. Preserve recovery state machine. +- Prepare error-fallback and retry request headers only when the operation is + configured/eligible. Keep error-response interception and safe-method replay. +- Replace exchange source, cache and transform telemetry mutexes with atomic + tags without changing log/event field names or tri-state flags. Do not clone + live-stream log entries when there are no subscribers and move terminal + exchange fields into the final log record; history and sinks stay. +- Add routing/health/LB/telemetry regression cases and source-only guardrails. + No new configuration fields, migrations, LB switches, or changed keepalive + defaults. Actual Rust compilation and benchmark are required before rollout. + +- Separate global cache representation compatibility from per-vhost scopes. + Keep HOT and COLD entries across vhost keepalive/recycle/LB diagnostics saves; + invalidate only modified content-bearing scopes and cancel their in-flight fills. +- Persist a content fingerprint alongside COLD entries so interrupted scoped + purges cannot resurrect old responses after restart; retain COLD format v3. +- Remove per-frame `Exchange.backend` mutex acquisition from response body + metering by retaining the final backend lease with the body; request metering, + lease lifetime, counters and error handling retain their semantics. +- Guard cache-ticket creation against in-flight requests using an obsolete + configuration snapshot after a successful save, without evicting unchanged + cache entries. +- Add regression tests for operational saves, scoped content changes and COLD + restart compatibility. No LB UI changes or new database migration. +- Add `sendfile: off | auto` to Web Service configuration and Delivery editor. + For eligible Linux cleartext HTTP/1.1 GET >=64 KiB, transfer already-authorized + static file descriptors using native `sendfile(2)` behind a bounded connection + bridge. Keep normal streaming for TLS/H2/H3, range, transforms, compressed + bodies, active object cache and other operating systems. Disabled by default. +- Keep the kernel transport switch outside global cache identity; toggling it + must not invalidate HOT or COLD. Include native transport/fallback regression + test cases and panel save/reload tests (Rust tests still need execution). +- Include an explicitly hypothetical Amdahl model of nginx parity using the + existing 0.33.8 benchmark, with no claim of measured 0.34.0 RPS improvement. +- Actual Rust compilation, protocol/regression execution and comparative RPS + benchmarking are required before deployment; do not infer throughput gains + from the source-level changes alone. + ## 0.33.8 - Skip cache-only policy maps, request snapshots and ticket work when caching diff --git a/Cargo.lock b/Cargo.lock index 97ce6b5..39e8104 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1881,7 +1881,7 @@ dependencies = [ [[package]] name = "rust-proxy" -version = "0.33.8" +version = "0.34.0" dependencies = [ "anyhow", "arc-swap", diff --git a/Cargo.toml b/Cargo.toml index c59084a..0a55f37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-proxy" -version = "0.33.8" +version = "0.34.0" edition = "2021" rust-version = "1.99" description = "RustProxy - independent reverse proxy with local administration" diff --git a/README.md b/README.md index 8d14ffb..d167a52 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,66 @@ -# RustProxy 0.33.8 +# RustProxy 0.34.0 + +## 0.34.0: reverse-proxy hot-path optimization revision (source candidate) + +This revision retains the **0.34.0** version and all prior scoped-cache, +`sendfile`, security, logging and vhost/administration controls. In the +uncached reverse-proxy hot path, request observation now passes the immutable +selected route/frontend directly to forwarding; compiled locations are stored +as shared `Arc` handles instead of cloning their headers, regexes and policies. +Exact-host routing avoids wildcard temporary strings unless needed. + +Single-member non-cookie load balancers bypass candidate vector allocation +and the shared weighted round-robin scheduler mutex, but still enforce +health, drain, pool eligibility, capacity and leases. Multi-member policies, +sticky cookies and weighted RR retain their existing selection rules. Healthy +backend observations use an atomic fast path; resolved per-member health +settings are shared across leases. Failure/quarantine/recovery transitions +retain their existing locked state machine. + +Requests without configured error fallbacks no longer clone request headers +for error replay. Safe/empty request retry headers are saved only if a +multi-backend retry or upstream H2 close replay is possible. Exchange source, +cache-enabled and transform-enabled log flags move from mutexes to atomic tags +with **unchanged emitted log fields**. Live log records are cloned for +subscribers only while subscribers exist; history and external sinks remain. + +These changes have **not** been compiled or timed in this environment, and +there is no claimed 0.34.0 RPS improvement. Build and run the regression and +nginx benchmark matrix before deployment. See +[`docs/PROXY_HOTPATH_0.34.0.md`](docs/PROXY_HOTPATH_0.34.0.md) for invariants, +checks and exact measurement commands. + +## 0.34.0: scoped cache, proxy accounting and opt-in Linux sendfile + +An operational vhost save (including every upstream keepalive/recycle knob, +LB health/weight, backend concurrency, diagnostic controls and timeouts) +no longer purges the HOT or COLD cache. Origin/content policy changes purge +only the affected vhost(s); global cache/transform/headers/forwarding and +Web Service changes still trigger the conservative global invalidation. +Persistent COLD entries carry a vhost content fingerprint, preventing stale +reloading after an interrupted scoped purge. The global cache identity changed +for this release, so existing 0.33.8 COLD entries may require one-time refilling. + +Reverse-proxy response metering now snapshots the selected backend lease after +routing and avoids locking backend selection for each response DATA frame. +Request-body metering, timeouts, cancellation, accounting and HTTP transport +behaviors stay unchanged. This is a source-level optimization candidate: +**build and live RPS comparison are required before claiming a performance gain**. +There are no new LB controls. In **Web Server -> Edit service -> Delivery**, +`sendfile: off | auto` is an opt-in Linux `sendfile(2)` transport bridge for +full static GET responses of at least 64 KiB over plain HTTP/1.1. The already +validated file descriptor is sent directly to the TCP socket; cache-enabled, +compressed, ranged, transformed, TLS/H2/H3 and non-Linux requests retain the +existing streaming path. The default is `off`; toggling this transport option +does not purge HOT/COLD cache. **Native compilation and live protocol tests +remain pending; do not deploy this candidate without them.** + +An offline cache-retention simulation for a 20-vhost workload shows 92.123% +fewer invalidated objects and 92.113% fewer cache misses than the old global +purge policy **under its stated synthetic assumptions**. These are not RPS +figures; real 0.34.0 performance remains unmeasured. + +See `docs/TEST_REPORT_0.34.0.md` for verification scope and remaining gates. ## 0.33.8: request-path performance and compatibility verification diff --git a/ROADMAP.md b/ROADMAP.md index 6dd2bac..32d91fa 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,5 +1,35 @@ # RustProxy roadmap +## 0.34.0 - Scoped cache correctness and proxy body hot path + +- [x] Share compiled route handles and reuse middleware routing in forwarding; + avoid exact-host wildcard allocation and full-route cloning. +- [x] Fast-path single-backend LB; share immutable member health settings + between leases; reduce health locks and fallback/retry header snapshots. +- [x] Convert three exchange metadata locks to atomic tags; avoid unused live + log clones and add Rust regression fixtures for modified semantics. +- [ ] Compile this hot-path revision, run Rust tests and verify live LB health, + sticky/session behavior and HTTP/2 graceful shutdown retries. +- [ ] Profile CPU flamegraphs and measure real gains vs 0.33.8/nginx; tune + idle pool/recycle only as a separately labelled experiment. + +- [x] Disconnect keepalive/LB/diagnostic-only vhost saves from cache revisioning. +- [x] Invalidate only a vhost scope when its origin/representation policy changes. +- [x] Persist per-scope COLD fingerprints; old metadata cannot revive after an + interrupted content update or restart. +- [x] Reduce response-body per-frame backend mutex operations without losing metrics. +- [x] Add Rust regression cases and preserve unchanged LB controls/API. +- [ ] Compile, run the full Rust suites and repeat live HTTP/TLS/H2/H3/WS/LB + regressions with a compatible Rust toolchain. +- [ ] Run paired 0.33.8 vs 0.34.0 vs nginx benchmarks; record real RPS/p95/p99, + CPU, RSS, cache retention after saves, and rollback on failed acceptance. +- [x] Implement opt-in Linux HTTP/1.1 `sendfile(2)` and a per-Web-Service + Delivery toggle, with TLS/H2/H3/range/cache/compression fallback and bounded + per-connection slots. Implementation is source-only until native tests run. +- [ ] Compile and validate native `sendfile`/fallback functionality and safety + on the target Linux OS; compare live WWW RPS against nginx. +- [ ] Complete long-running soak, external dependency integration and staged rollout. + ## 0.33.8 - Performance with retained functionality - [x] Reduce cache-disabled request-policy work, header copies, timer allocation diff --git a/SHA256SUMS b/SHA256SUMS index 45796b2..5affa5e 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -1,13 +1,13 @@ 61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore 4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example 9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore -293fdbc2fc7318a9f544543c2ad518061ba1377edab1c73e93bd3c881d916195 AGENTS.md -38124997eb546aa2fb54c1b7801a127994a7911c469ae93b04b96e9816dec842 CHANGELOG.md -fcbd81ae693bf052a67a8815e227221a236e0486669e2b4668eece1f4e73bee7 Cargo.lock -8e72d66bf61579ceb5237d9195a4c09baf936abc9c9b541f17454c7946acdb58 Cargo.toml +46bab1d2c2d6e0a80d57a6b232b1ba4c220884b95cd45ddccbafce2aa160cddb AGENTS.md +37ed26e41820a69fe6417e7600c9ec5a2eeab69fd11e5a389be912a2a3cd7642 CHANGELOG.md +ca201e3cd3f9842eae0006d0b40a0a72bcd3d4da6e4e5ecaf200d1b9a1390db2 Cargo.lock +fec5f1bae9a59ef1fabd27c5e45d31448dd8f811736eebfa8826b3806a7e3349 Cargo.toml 25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile -bcf49d2ed52433772e249d9d991fdaf7cc82636da4de30f098193c08c4b0d7b8 README.md -61870c209a50f1c683d221b3a11b18507b084c3b602111ef62f8bd90f285f51a ROADMAP.md +4dba0f83d900cbfb131eed06768084f18fbe018952cecfd4857533b36bccb747 README.md +1119f4de537b90a6263d93eb1ce2b6b0f233fd7a398f32d758c892ba16ea7dd6 ROADMAP.md da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md @@ -48,13 +48,15 @@ d8829cc68d982c933d04207cf7bc78cc918044c72e024e7ecad5220953bb3a79 docs/MEMORY_ST e36247774051aa9c83c1a2fda91c1f7857991dcaa4fa90320ca5b492e31dbcf8 docs/MIGRATION.md 5f090a190000c6badab2a74e5d4968e70c34de43b59c1452f5b05503b5c5c18a docs/MOCK_AUTH.md 1f85fac0e06e432ef6f744f0b5f9c1b7a27a1e9e1ad30dc1a218eab96b1caffe docs/MOCK_SERVER.md +6cd54c6e2c3e5c23ae0104e09e88ab6d93bde26bd92b23c1a078fe7c4ee04b55 docs/NGINX_GAP_0.34.0.json be53a58afbeddbd7deb4b5f6447f2dc74db0327839bd83407c20915df98dcfa0 docs/NOT_FOUND_HOSTS.md 89a1af6c9fe10c572b211f9632ce256166e67990b5dacacf2ae07265d99a5172 docs/NPM_MIGRATION.md 02126c23264b3afdf1e639c0334d621520e7538ff92d60eb6398c5fb10a23f2e docs/PERFORMANCE_0.33.8.md e68d5a5cf0991de9308f78afb0264e02979f7463a5f395eb7a6e9ec2bd3a3d54 docs/PHP_FPM.md bc068a176056f5ea4d3b637071e644ed58a2140494c233ef22aa66e9700e3299 docs/PHP_FPM_ISOLATION.md ad6e3f003b820a4f45e30e39577d120a69d167c70910bda4891971759458c3dd docs/PROMETHEUS.md -bb15713876184682cf1907d8c5edd18e657735b6de6cc5f8ca976dbdec4dd936 docs/README.md +deea89aed410c0619bdbdb12437edc7aff9dca2fec7fdc059e918c649fc36b51 docs/PROXY_HOTPATH_0.34.0.md +887d1fdcc3b546b024afcd18b1716b8e3a74a3800012bf4bda7a122d2eabe233 docs/README.md 6e46a137b9c88dac8cb9e3fdca877430092392de9913d746278dbff8652460c2 docs/REDIRECTS.md 0dfd0d431c5aedc67abac1ce5de040ffa790ec9578fd7d6f91a987c333d73358 docs/REFERENCES.md d85c5567fc6a7714b96ad7cfe4ee9ff2085f84c4f31534a6484340d03a73e0f7 docs/RENEWAL.md @@ -66,12 +68,13 @@ ec7e32fd19e6ba6ed1732f1f3dd92ac0860609e8c0bbd3c1998840cdfb24bf54 docs/SECURITY. 4dcf3243a13d9ae1827b5cefdbb9e27bd6310321c50d4d5934527d44437df6ef docs/SETTINGS.md 8fc112cb45a2f35ef5b0dd4371aa6f43aca4df85db62c6ae02e56d74a3b702ae docs/STREAMS.md 95148bfa1d2f8ff52b591c9acefc6c8992dadc4ebda43a195101d9b31d67a576 docs/TEST_REPORT.md +4c82db7fab8c8fe8cd1508fbb85751b7f72071edd913ee7def4a9ffde8efa13e docs/TEST_REPORT_0.34.0.md cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md 751c9b13b47d0959a8575d6e82bdecba988b950a374ba0b084fbba08b4b65ebe docs/TRANSFORM.md fe3ec6ca16ff933239c12dacf39dd7b45717c0551a8b9f6ce601bf78136c9e37 docs/TRANSPORT.md 711c69c9e05761ba5cb96dadc3c368eb2961e2d2287ca039eb49c7fbca0dd63c docs/VHOSTS.md 09b60fb7b40db4d0139fa2a9e69a2aa9f28d4e61b8643521cfc7e936d7df8774 docs/VHOST_ACCESS.md -4257729a4c1b1f3949538410af78a12067bd3417c70525a15bd01411155d25fd docs/WEB_SERVER.md +ca5d1d1d7e8aa372dcd3e2bd56bb7a9b8977953482ece560cfef58a19314242e docs/WEB_SERVER.md c336f998027330c58d5afbbbb3a3a682fd44ae3997643882fe342633822f8bb3 docs/screenshots/README.md 5502e8ce44276213803b66c58e3a4d804a1354f5ed5d83f47ee805e8c44ab088 examples/acl-headers.json 2431dd88a2d9ad2cf3cd4b8fcdfa602563ab1013c612d8917fddbe7febfb2dc3 examples/cache-dashboard.json @@ -97,8 +100,8 @@ cb01fdef286f38b442dd9252c23c4f8e44eb72e9ecdd99adb926e8e9f6318bb1 integrations/r 96563a83e87cfb33e238c2a970ce36b1a1b51e91318d09b123c3755002ba2646 integrations/routeros-oci-build/build-routeros-oci.sh 3f267cafa16006e5456ade78ee2d5fae02cc7ce747839523068ffd8d7c98798a integrations/routeros-oci-build/docker-entrypoint.sh 13189fa546c3488dddd8efc80c47f6da5241e5fc28b79cbfe795414ea05c7a4f langs/README.md -1a69ed38b730c3b379b8b14b5707d8ae923320a0c66d084c7ee478e6a01eb53c langs/en_US.json -6513435292eb947935f50eeadf9ef2ee0995f4b57e989699dfc9cda686f21685 langs/pl_pl.json +462339bc35535fa69ebd95aaf43e9405913d940406bfa14b27d15afb62accef5 langs/en_US.json +22efd93e26f71bd78c1e6cf83a9f0aaab3a8a5a4adb98822cbf73ba6e22cb991 langs/pl_pl.json a99ad040be10fa028351e290b9363236b3513a1cadbdc9fb9e0260d517b9b0ae make_zip.py e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a migrations/0002_admin.sql 20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 migrations/0003_certificate_renewals.sql @@ -125,7 +128,7 @@ a641bd35400c56f3b68b0a92e97b602d06090f03972ca8f01fa744e90dd97403 scripts/deploy 2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migration/migrate_npm.py a459d95bbb0076f33ade8b273b813308167f9d8d45242b309a2d18320ca0093c scripts/migration/migrate_npm_api.py 31ccad3ec3c880119f3b7002f0c6652f8b43634edad8ab3763cb0e4497d0bb1f scripts/migration/migrate_npm_core.py -afea7479fd10b7d723f147f6605283174ef81ff5e482f65819db4f01ffb02fe5 scripts/performance/README.md +e4fd7fa94d661d4610d7d86fcdeaae9d6deb220e95b8d3bbd67ec15fbd017ccb scripts/performance/README.md 0380406a059ee9ec27473f8a46435f638f73a6dc729382ae1561c08dbf7ccb53 scripts/performance/benchmark.py 9ac220163256e08513597c397c016a48345b114d7876b439259cc310b36c83fc scripts/performance/checks_cache.py f09125d081d0eed9a44fdca75de14e6bbf09dcb00fe1f027ba2270f89e3ff192 scripts/performance/checks_policy.py @@ -138,6 +141,8 @@ c1d8c184811ae8fb1960d1ada08d8a4a95405c3b83dd18e1a27d711b596140be scripts/perfor 830a360609e2b8ebbb1ef6f3d89e3e6151e8fefb36c3673d20df68ff8932eabb scripts/performance/profile_syscalls.py ceb26650fc8b63d0107eb9100ccd572e5c70f7a0ce5cc533a94083e20485095d scripts/performance/regression.py d6480962a23f55d19a54aba27648ca1b28b50f03afe2decbfa272efd142df756 scripts/performance/resources.py +2dcd4c7e39dd58fd77c3c713d9bdee5025cace7191ca5f12bd6e7fe28f333358 scripts/performance/simulate_cache_034.py +4a5af2d3870d9517b4ad9c008789490cfb07affe98ffd629e51f7674ff54a642 scripts/performance/simulate_nginx_gap_034.py f85dd0c29d5a96ad59a71e035ac4303c7b6a70a8d61280a15cdb966b32a218c7 scripts/performance/soak.py fe6ff5bfd76ac7a2752a8a2e1e45c63e549c4a61c753008671e478df3ebff304 scripts/performance/telemetry.py 7c1ba46c623e1ade47b872fcdf4526a48ca85c56515dffeac626188b0357d23c scripts/performance/transport_clients.py @@ -150,6 +155,7 @@ e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/runtim 174c8059625e3315a3d9fefe74c17f78ed850ff25ee15bfdabc148107dd344eb scripts/start_stream_backends.sh 046b4282550f5cb4e53906665c91c7442be70fc68fd0743466fd832582f7c979 scripts/test_stream_tcp.py f0b48ec0cc4411755cc0b4128eebce710375afa99344cb76198cf31074f8eb63 scripts/test_stream_udp.py +955c150f8d0490069cb14b659d4fb4e0b529a28b87c27bd6697935c23e7ef192 scripts/tests/cache_scope_034_static_test.py efb7a1fde2c74f30cd4a1abb88ae07c1cac7378bb33685dd09d52bb30849daac scripts/tests/dashboard_chart_headers_0335_browser_test.py eb2c9e5c968938b6fbe4675b547ba1f537519878b9095e1fea133f4d83f96fc8 scripts/tests/dashboard_chart_typography_0337_browser_test.py 6a908fd434d2a8a80adfd06a66d4b83c4174e448d453a8c10b1812475d036677 scripts/tests/dashboard_chart_width_0333_browser_test.py @@ -168,6 +174,9 @@ fa42ec0475e4398d647b3c094309b4332cd2b5649db223cae0b8e821d6b44a28 scripts/tests/ 72185f59f31bf04a4e88d53fe2fca52509f3663cb5f40b1d3bc319a5f945ef23 scripts/tests/panel_html.mjs b0136544af717b75df2cb107900ea65c3f945fdd0040369dbb93919008fb954c scripts/tests/panel_html.py 8d1903e2b17b3d59cd0a5bec3e298e468b3d88ed2dda8b724239cc45d8353c59 scripts/tests/progressbar_0332_browser_test.py +f94612eaefb4098e1a4efc0d970ce52a656db239aaa0a012df47567527bf3708 scripts/tests/proxy_hotpath_034_static_test.py +5be5cdc60324f4f0f6638640000b888a5babb0a9bbf29511c824a0ba38878c2a scripts/tests/sendfile_034_panel_browser_test.py +978789bfe89d43034dfebddba49b9b17daedb1be031732734f95375e56c78306 scripts/tests/sendfile_034_static_test.py 1bb99b148d0297235b707f16865ca077329246fc19ecbb20a143bdeb56a23f37 scripts/tests/sidebar_nav_0333_browser_test.py 12f2b18b0ed2171c49eb8cde2c2b88fc627054c2f574c04bd9847821b00cd4c1 scripts/tests/system_ram_meter_0332_browser_test.py 046b4282550f5cb4e53906665c91c7442be70fc68fd0743466fd832582f7c979 scripts/tests/test_stream_tcp.py @@ -179,41 +188,41 @@ f0a2e3daa0a49aac54723cb5a281360e49a97c076281372c5cca2a85e096c02c scripts/tests/ f55efa99245bbdbc98fe3202e52952d224ac3200612561580d52f6e251810049 scripts/tests/web_server_0330_ui_test.py 0dd1cef06a1c3e1546e2fb9b085bf369dfbed02107b9ab01b4fb4f3b894099c0 scripts/tests/web_server_0334_browser_test.py 22e880e98c7b9d700c7a4c02b9b1d543e5dbd1f4ecdca42813a1083165b32a19 scripts/version.py -55485a513e8c4da2a58daa7abcb15b32ed408a5fc062cf23599fb83d16b90cfe src/acl.rs -63efab1e95d41134fc30cc57320c32810bbae9f81a640bdb24b1be2059e63d8a src/acl/dynamic.rs 4803e44de39b872a5b3b55f9ca9e81c066ba52cb8f6adb879fd57c5f94b93e89 src/acl/dynamic/tests.rs +63efab1e95d41134fc30cc57320c32810bbae9f81a640bdb24b1be2059e63d8a src/acl/dynamic.rs a22e5ea6381a1eacafe650be0f5cd6e653773e31f6491a562fe0459a309fb7cc src/acl/tests.rs -6f5d3137bf24451da05c1db9f0f9e2a451305db17b6bc2590815ad54a6511829 src/acme.rs +55485a513e8c4da2a58daa7abcb15b32ed408a5fc062cf23599fb83d16b90cfe src/acl.rs 587097620b2f25ce7c6f59f907b47660184b095e3040c7615e74509a82e7cdd8 src/acme/jobs.rs 3f158f46e80b403b658b4e9b1e3710e38c70e47b2d476b8223d9c394ffeeba95 src/acme/model.rs -c5334de6c732d76b2bea7bfc17b3ac305a250bbaad31a5420d6a7d7abb200449 src/acme/providers.rs d1dcb7bdfb7cfdc9c3b88cfb6908201c58532bc888744a13f45853079f214659 src/acme/providers/tests.rs +c5334de6c732d76b2bea7bfc17b3ac305a250bbaad31a5420d6a7d7abb200449 src/acme/providers.rs 29ff0647417b0f1d50a4cc075ab51afecff9ddf47db3eb7948d51db5a38f33f3 src/acme/runtime_bootstrap.rs 87f64198770ae026c03a242fa542db2590ca734b02e756f1e5c28d66236f753a src/acme/runtime_env.rs f8075a5ef9ae5651d72a33ac0f0fd5b44ad0ae30f6e2d2318412177e0ed837f5 src/acme/runtime_versions.rs e38331a1b186b49fe9d04051de52b603e1fa028def37e4e2f52e386b4d7b29ac src/acme/tests.rs +6f5d3137bf24451da05c1db9f0f9e2a451305db17b6bc2590815ad54a6511829 src/acme.rs b6f38f3fd75d6685bbc882ed1077a38bf495cfcf01c397f69ed9b98be68b446f src/admin/access.rs b4a2a068acfa1143b0032b9cd072c19cf3fdda2580f0b932d11d6368cd2ab3ad src/admin/certificate_api.rs 51f46a2cb3167c4e3a784dfbb59b8fe0e9d5d3d51365b26699498e31d0afb6a9 src/admin/certificates.rs b5f6d49f1add583cfc8cd4aa36ffe4db56dac4de3b151fe8239fe42f507019cc src/admin/dashboard_presets.rs -5e150d4ef0413a2f92db5b6631669933b84d4b4796633d01476c4b39188cd05c src/admin/documentation.rs 958546c43c05bf4bed8121b24db3b3096f9243f3c56a71e1762592b755684013 src/admin/documentation/documents.rs 67b901485d5858d5b5bf78a6260035db32241531d99481754c3533fdf404facb src/admin/documentation/tests.rs +5e150d4ef0413a2f92db5b6631669933b84d4b4796633d01476c4b39188cd05c src/admin/documentation.rs 1ce779b84aa8f47bdb3c34f83593c1f2f06d2235f0319847096cfcd035434168 src/admin/file_log_search.rs ab18acf6fb4ac86fb22635b9219a12dac76d24952c8f15538486267a9b4b7855 src/admin/file_logs.rs c6b552cd5acd9e743c28b1a5564420c214c619fe891ab0561b7d4499aae8d6c0 src/admin/file_logs_io.rs c720a7cad6a15c57b1f2ae7b3e5fbf30a97c64afb9137915f37b23f4e576bdc5 src/admin/file_logs_query.rs 74636c6560618ee95757335452baf8db03394e1ad18acd54252f54b15746b8d5 src/admin/logging_api.rs e552c4b565e3198cc2224d1efa3bef21db02a9872cb80cd96f446181035f19fb src/admin/mod.rs -28dd33ef19d7eab08cfa243b253785012d6c731bc4c56b4f69f28edd3d9e07a7 src/admin/openapi.json +93f63f7bbde1ed84fc6cfbbb7cf6b96b7d1a1411fd6dbf6d90a3a9070ed9f5f8 src/admin/openapi.json d9120f7dc61d27621c5ac27ea49815dc7945c44d5b99d208da503818c198f649 src/admin/operations.rs 30895e05c5d5d0a26be29f667e0f6d72f16a96a5c8a98e09cd11d8ef2d7b4763 src/admin/panel_assets.rs 75bbedd54fdabb2b489642a642ee7c6533f69cdacb85e251763a56ba11174d5c src/admin/panel_content.rs 92b3e05f04711d8001b56816405859631d9e917868e15be965c7e76ef4ddef99 src/admin/preferences.rs 4a5cb4c3a3aa8b9a519020854506ce4be5e688a6c4298260322ae641dded7fe9 src/admin/preferences_columns.rs 9a3f13e2477769ae680c03d63336880c3b7b7d5aba4a91e44390a976608b9b1d src/admin/preferences_tests.rs -4a7a2017ba57c9e7a8b29e73764d439f6d03653403909ef198ded8c48d152784 src/admin/public.rs 7a00e4ab943f74d2b1bdcbfab7f9cd3f584e9f59536b4aca6c4c31f5f5508d12 src/admin/public/tests.rs +4a7a2017ba57c9e7a8b29e73764d439f6d03653403909ef198ded8c48d152784 src/admin/public.rs 9926827131254677a848c178c80850b3ca1993c05b4bf913687dc0c7992ad048 src/admin/refresh_preferences.rs daef5e663c1bc99a22b3b8fa4481ae5e4916b91db132c104bc050f51b1fdf77f src/admin/router.rs a40adfbaa908da3ea726d7aa3d45394a70a6a55b6fc0063270ec4d2a2f2026a3 src/admin/session.rs @@ -267,7 +276,7 @@ e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c src/admin/web/ 0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg 7cbdef9192a9dee1f8eac42b85f52b3190f7f6efae50004aa7ad116a910e4036 src/admin/web/file-logs.js b14e7b3578322f309627f6fe965cc16abcf85da3437c9070be6caecae1908743 src/admin/web/host404.js -6684e7de2540499f9129b78c790f9a820b0e6c5de33e3b60c318d9a1afc931a5 src/admin/web/index.html +7ffdda1b657c613f2bd3b7a9c7721d67d3d7a159351aaeccbde2a213597b77f7 src/admin/web/index.html 03ad9572bb14c34059636d612cf6b876d6361ce601817cff674c57b715dc65dd src/admin/web/integrations.js 7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json cbd41678eadd994dc5252628ee8ef9551fa9f7d8694ca5abd206240d9a96ad30 src/admin/web/login.html @@ -289,127 +298,126 @@ b3239e67901f1796d7a974ade0796f2e3590ff8064628a5629b78c7f6ce2f32e src/admin/web/ 2170cfddf5c611672de34b0525d36ade8fcbe459cc21269281d6b615d8601b93 src/admin/web/vhosts/rates.js bb93e2017f40e3a45ff7f4ca92e4188bb464f432704b6d2c39da9d9621b7984c src/admin/web/vhosts/render.js 4dfdeb7c73c2775eacaca65fd8ef1dc80b275421b15522d319d338f5b96288cc src/admin/web/web-server-profiles.js -0049e5c2a35e3ec25f3c1590879684a7b15165584e96eb1b022fb60d3c548f2c src/admin/web/web-server.js -53237ae8b08d261ce21e0aa6fd225bf4f3bb17a37a80529e2a4039d45b13f7c5 src/auth/files.rs +fd476445a88eb40270e706814eee50cbfbba4069a26dd843c095367e55a147a1 src/admin/web/web-server.js b2f948bcc3dbd24952c7b1b02d41d06757f49f0c38a78caf51f8f8a687d8480d src/auth/files/tests.rs +53237ae8b08d261ce21e0aa6fd225bf4f3bb17a37a80529e2a4039d45b13f7c5 src/auth/files.rs 775178ee5781e64574bdd7c1fb7d64e7e283977fbd9c98c99896532a7f1df612 src/auth/mod.rs c96a87e82ff0c1c9ce3d0cd5135903bb53af78d1c4fb6ebfe5fea503bf5644ea src/auth/store.rs 745226cabb007819370fc6aa680995a03a8a13fa057dacfab0fcc21d0c43bbe4 src/auth/tests.rs -09c3a622a7d2169e49e3369ea5f0f816eab2f717eaf1675c625d518c8f34d367 src/backup.rs 76aa2afe9e7302be39b3512f30d0448d9134300f8ae930d64ada40cf3ad4a8a3 src/backup/archive.rs 732cd4f9ed38ae69f8bbae61b1db8baaa6f86b07ca21c41f24a206a30f0a96fc src/backup/ownership.rs 076350506215faec3cfe7d9de4e60baf29672cc22a41eabe601dcc510c257799 src/backup/snapshot.rs 5c19490d834d9971166b6b784a75b081bb95fd50c8c634ef3c87924dfffd4a19 src/backup/tests.rs f5afae218f3a6f91bc2f1f5a90094630ce5b7ca574de61807b5a317b13937ec2 src/backup/validation.rs -426a946b2c9e244f09f6cbc5f9664b8ad0737529a9bb5a3d549d27196a1f8043 src/balance.rs -d3257fa5222dd4f70a82400f5060d3cbd33c13862a9320931361fe0e1c5bd452 src/balance/health.rs +09c3a622a7d2169e49e3369ea5f0f816eab2f717eaf1675c625d518c8f34d367 src/backup.rs +ca1e03b56a872dca37ed9d05ead41c1538a808e80b09fb9494823c443cd51c61 src/balance/health.rs 59d90e709bfc2be6cd48576f1c1d222891523853a46f2561f0562d74d01f60a5 src/balance/lease_runtime.rs -aed9ed896a122d7adcbedf0897cfd5acf62be3c5b63a71a4a425c8fe8474c0eb src/balance/model.rs -41c3c020a60c372dbf965bd277c330dd9cf2ab079f6608b472adf72a656f4cb3 src/balance/pool_core.rs +554b730c39a935b7da46bfb76b474b99cee07f8c482cb0490f2cf81ba7a51cf2 src/balance/model.rs +9a10649c02eeb2c05e0ca520ef6ab701f79d46591190b4432977c2d5dbc6d7f5 src/balance/pool_core.rs 477132dd381e23f1be197c32ff58a0925f41d3e6aa63fe77c10973cefb2b76f6 src/balance/pool_selection.rs 8abb6048303ee8c022546a528d5693167766e7450efd8e20f37ec6fd0fa9ef5e src/balance/pool_view.rs -f1a92d188628ab3fb587375e80956e1552301647f2732cafb55e1471c7f743e7 src/balance/tests.rs -5049d2bd01a9c866f3bad2288d8d594df96bba6045dfe15cd22b4173562b7388 src/basic_auth.rs +842318f21a919055522f8bd9dae67bf4ec1c8c32dc52314cc048ba1d54a0b379 src/balance/tests.rs +426a946b2c9e244f09f6cbc5f9664b8ad0737529a9bb5a3d549d27196a1f8043 src/balance.rs 9a8858b7ff461a4d961ba701ca3ed6eaec15c3f7f05eef8e0e30edc63617605a src/basic_auth/tests.rs -7def03ace07dc38a4806277d75b6ce673d33ddaaf1c4855fa0a3372be1cf9489 src/cache/cold.rs +5049d2bd01a9c866f3bad2288d8d594df96bba6045dfe15cd22b4173562b7388 src/basic_auth.rs a9eeb93519aae2de7a1282296ced91d2eb8307cc966f8fbf6085af0fd21b0ad2 src/cache/cold/cleanup.rs -e022003be144598100389cf38d76f509b5dbcc035d5bae556bb9b94cc8bd30ba src/cache/cold/demotion.rs -9fc18368e2ce278ef0ad2b73ff016aa24936c2186be371ca4010a1f6f63bb0ed src/cache/cold/startup.rs -ea319b6e200b0c6ca93da5e2af7a3f86e3667cef918716f54f82b8012111b9bd src/cache/cold/worker.rs +8354ae90c1a0857773a0c6d79427925e0115d94ccfafb809ccc1ebf4ef10f9b9 src/cache/cold/demotion.rs +2eaba3261da2a547e9af91330319a3abd3a8ad8b5b44a337a84040720209017d src/cache/cold/startup.rs +e7c7fd71727d542c2df3e03a4a1cba9d839c516963f66bbfde1c29ec81053500 src/cache/cold/worker.rs +7def03ace07dc38a4806277d75b6ce673d33ddaaf1c4855fa0a3372be1cf9489 src/cache/cold.rs d51fa4f320be56c572f767617428c3f3838543cb0eeb7d1f8ae791e1a1ce9a71 src/cache/collapse.rs 284f1cc56113fbcc3b1e617103d75a7b70fd7d43e536d6017c15912154921424 src/cache/dashboard.rs 780c2654c67dbe176d8310678e6deec8022bcebfd85b95df7262069ed41f57b1 src/cache/fill.rs -675d18afb53f25c56cba49bbb431fa39ecd9d7bdd192c7b39b0698225de7a2a2 src/cache/invalidation.rs c1357a13658e1ed5158084d3a41ba9ea906512774a5863ce9c56cb508d3fa6c8 src/cache/invalidation/ban.rs 0cc31d8e70fb3774c4ce3713b94ed79c184a8d8f0d38ce66a97724410733e17e src/cache/invalidation/invalidate.rs -b74945c7460b76ed7afcb02a45ede09d76f08e0703c47920b9b9d794d4b74980 src/cache/lifecycle.rs -bd831807ca4720b3a41a6ea1d9a6c0ed2d935225e2dacbd9313be064f2e4a20e src/cache/lookup.rs +675d18afb53f25c56cba49bbb431fa39ecd9d7bdd192c7b39b0698225de7a2a2 src/cache/invalidation.rs +373031e18d0c23d006a31822ef977015c0263637e3da70c1442407f7bfed1033 src/cache/lifecycle.rs 88f6210c03d7a8e7a35be1f123c9c15675af6e6a599c50db927d99866851b634 src/cache/lookup/hot.rs 1779abcee77600c13050a2882f896a5b35625b1ae53737c2554179492ce0e40d src/cache/lookup/insert.rs 107a589974b9689009a9bbf088818dfaec71cd1aab8676bbc6d0118359a54a31 src/cache/lookup/tiered.rs +bd831807ca4720b3a41a6ea1d9a6c0ed2d935225e2dacbd9313be064f2e4a20e src/cache/lookup.rs 58c03fde09e5eab40968679793b364f4f1cf5fd832bd79ebcdc92bb23871ad82 src/cache/mod.rs -0c8b0d550e94d4474dcdf991aa0340228dc5f2f904f39350e6e666a16f16fe87 src/cache/model.rs -ba94226addfd6cdab67d666dc15b8412c4d117111e44639e503e89a95175d0a2 src/cache/model/cache.rs +a7b6dfeb63510e4710eb11a4939b360b8863a4b3f71de263d8f3e50dfd006c57 src/cache/model/cache.rs 0ec4f6d0adea1ed80a3ca17c68c9c85502491da2904d9d690b4169ab9b025b21 src/cache/model/cold_metadata.rs 29bfce96b2ffd102d9c848c7b717a45fedb9aa44511f62f7a1f28a53dafd063a src/cache/model/entry.rs 88d74fc1b7b9ca15a54a8f4893ad3993105952148bcfee099e9bc9727b622aa3 src/cache/model/http_invalidation.rs -09a4417f82984eca8c040ea28eb82b3f129c347d951d4b4fab704bb194d9b985 src/cache/model/maintenance.rs +d890524999dc50aeb1dbda90264b46a1d53f08ac923d831b89ba82e4a564d923 src/cache/model/maintenance.rs 5441fa3a384add28b6fdd15300cf2648f8237dbfc211cad9117754203be76911 src/cache/model/settings.rs 55dcc0ad84d6b73689ba43e759d5685c406419c7733e23e78d5b072891ab17d7 src/cache/model/views.rs -e26dce02df9c043329ca164fed215d271781bce8fb03240f528331bf3df6b9d7 src/cache/policy.rs +0c8b0d550e94d4474dcdf991aa0340228dc5f2f904f39350e6e666a16f16fe87 src/cache/model.rs 989df11e246123f760ef31689370f3500e0913fcc6b70d7ac0c2c95e2f412fb0 src/cache/policy/request.rs d426d341a7678f8ec70406d86052c97d0f49804bbcd8402951103d61a735b9da src/cache/policy/response.rs +e26dce02df9c043329ca164fed215d271781bce8fb03240f528331bf3df6b9d7 src/cache/policy.rs 7e2bcfbb2b57732cdc704d9df7a9298fb80e735e37984431bc936630e41e7f8a src/cache/range.rs -7ad96b3baae36ec49c90d03b380e749944fb3d54abb045c90db3a35f6b89b1c1 src/cache/tests.rs -a1c750a48b4562d88d904b45dee5c783d4bf0ae1a62e67719df6bb2a7374465b src/cache/tests/cold_tier_tests.rs +291d0dcc3a07926eadbc9419b8fc1740b4023ef01c0521269ae2d83f4a5acb6d src/cache/tests/cold_tier_tests.rs 3842cdcf7c3414b9c30f5fbed927b93e835eec3ef96d4c6ee44250038b834276 src/cache/tests/dashboard_tests.rs d7a02eb7bad37e6a5524600aabb5e60ccf50e26c7a487e2806891c9d8acd48f3 src/cache/tests/http_invalidation_tests.rs -1db70ea5f3cc1f1543504a68498ba23cafce5f9bb48c4616a4bf70d9a536790e src/cache/tests/scope_tests.rs +1659ffd8d227d2ec8b98e6a5e06b6143e6ac4ac06ab423c184980abcb47085da src/cache/tests/scope_tests.rs ddbcf27a667d779065d7a654d8094910e92958d2b8862a2ec2a85ca41b7f21e5 src/cache/tests/tests.rs af28534625859ff66011e10c54cff8f82c2cfa1c173c4d99b89c27c1437f0659 src/cache/tests/v022_cache_tests.rs -a8a0ce446eb53ffdb8f8d97efa3e1344410dc3798b25f62b8817e8e160d28940 src/cache/tickets.rs -e0146bf5d2e2e33aedcccd9205e5bd457559c9d77dc7ce266f8c165d1b867f0f src/compression.rs +7ad96b3baae36ec49c90d03b380e749944fb3d54abb045c90db3a35f6b89b1c1 src/cache/tests.rs +22f8f16cf7383c579b19810819030072fc827681478e17068dddc85da8dc53ad src/cache/tickets.rs 4bdbde197d135fee8be20e93427eba8ca7b60b801717ed7ca734319bedb6ef05 src/compression/tests.rs -7bc8285cb2012506fd1a38a71dffa561f885f4ab65a9b2a7c8344319e3cd1cf6 src/config.rs -34be288c858a037c7174f6c4dee23178b01b565aa9d65f0951f351e1685847df src/config/backend.rs +e0146bf5d2e2e33aedcccd9205e5bd457559c9d77dc7ce266f8c165d1b867f0f src/compression.rs +dbd57ef3307b937614cdfdb6c1df82acb80cbbef39f09d19fe2edfc24334aa58 src/config/backend.rs beb831a208c4e5f7ebe8327686b1e0edc66608c7032f895cb4fc3366b189aae0 src/config/compile.rs -490d05d84b2fe349ddde54eae44880993fab70811652484dd9ba38b5069d603b src/config/internal_routing.rs -a19fc675720c7c2dbf4bba706e1b1c30c83c6df1da3f29e3cbb3b82442e7246a src/config/model.rs -f66c2388f31b19142c12469df23d43451e0f7c48b158d4996920282c06d32076 src/config/routing.rs -b25e212a493c6d9f6cd1d1136935182504163c7b8d7e776364389451ca2247aa src/config/snapshot.rs -f5431467dec55540f1f881541bf13d9402753680c7f83e3deb9d0e1fa747313e src/config/tests.rs -23541660151d9005555a07db6d491455d61b185f0cc70c3ec3a0bee2aef08dc3 src/db.rs +dfa89a9e214d0b33b4efce8fa678f1fec17d17656e203acf84dfd09ce33868e0 src/config/internal_routing.rs +70741af46ad6f712b0c0e029c066d04c14a689eae0c831af264b09f6e5c41bae src/config/model.rs +fc993c356386a9ff17379d48a70ef388df5b2b10ca9ee04f06342f68f9dbbaae src/config/routing.rs +a7ccffdeeb258adae41c616022ed0181cf590d708d305d48c63317e4a3f68699 src/config/snapshot.rs +41a4fa4f9a919a8c848ee01d01423d82a136790c9d74f27319c1e5a6d9f18097 src/config/tests.rs +7bc8285cb2012506fd1a38a71dffa561f885f4ab65a9b2a7c8344319e3cd1cf6 src/config.rs 30f0b7269224a64e3f1ec9e6fbe9e27ddb1d308dc84cc9882e50545cf612695d src/db/certificate_api.rs 20ba52d3755c359d670968efa53b0604ff7cbed63a416f9f765b50a1f45074ac src/db/dashboard_presets.rs 2ff744223934685439374572b83fd639bb3eabdbac168bea4a6501a6e9ce0ee0 src/db/history.rs c05a02618d476e4670b4f9930555f199cac776cdf09edb8fcc1a675695b8f99f src/db/tests.rs +23541660151d9005555a07db6d491455d61b185f0cc70c3ec3a0bee2aef08dc3 src/db.rs 19ab6c3ae37009c544291cb0b65c1bb9c721e168f95c1ed3fa88155aca7463ad src/demo.rs e69031875ffec32c9f548c43bed5f1a5c4c626e9568c9173e3b093f9a8b73036 src/diagnostics.rs -b45d6040810c93ae2acb04ef6b0a59c189751edf9008cd81a4521c9e04e7f07d src/docker.rs 93a8a3f6ba12761ddcbd19f22f32fb2c0e6948b660fb774ddd7ebdba9b6562a5 src/docker/client.rs 4c2c988191a97f81568a8df64af772463d010844f1abf327871901cdb32d4336 src/docker/model.rs 5f4610c59b2d4ff6a205a2d9ae11bc84be689b2a68302459f546b3f43e53593c src/docker/reconcile.rs a12c47df4b90b6fc36c978a42477e6ddffbbbf69aed7252101c2fd829709bd6e src/docker/tests.rs -2a02ea2e010cde1b1fe99b2e6baa80ea344ea6b332dbb3c4be795b71541f64a7 src/forwarding.rs +b45d6040810c93ae2acb04ef6b0a59c189751edf9008cd81a4521c9e04e7f07d src/docker.rs 057e652f216a1478638cbb53b1c64ac88c7f413a65825caacbf893c5af004a63 src/forwarding/tests.rs -af7a4e91c32ffbe4701c8c06e48ba3f8bbbe55e8b9e877aecb30ccdf858e9e8f src/geoip.rs +2a02ea2e010cde1b1fe99b2e6baa80ea344ea6b332dbb3c4be795b71541f64a7 src/forwarding.rs 80850f7acd1ed4c0048b065e9e81740869a8efbb0d9b5d2e83d8ec7fbc494703 src/geoip/mmdb.rs 06aa056d789742cc11c8826c338d985e6a6fb22da6c3530937eec38e35478034 src/geoip/mmdb_tests.rs d3dc58628683472ef4d205fa5c42f0b7ef83cf90846146d96941cd3b36a40766 src/geoip/tests.rs 70091bdf6f38f03b6df8c8c4407c300677e57f293130122503f936500963d37c src/geoip/update.rs c1b49bef28d87712e4c46138e70b52c4bcfb1a00196579031fac043d0b4aaa7b src/geoip/update_http.rs 9a912dad0b8d1e10410fd95d0da3684656092ef85cf8b23eda4ffe7fb92921d8 src/geoip/update_tests.rs -f6ebafafa82391f8cb3b5a09331f3762b6ca5a9fb64bafb5772cde3ab814dfcf src/headers.rs +af7a4e91c32ffbe4701c8c06e48ba3f8bbbe55e8b9e877aecb30ccdf858e9e8f src/geoip.rs e4f07bfb7eb75b6897ef812870caff158679a3046d64582428abe8c6df490dbe src/headers/tests.rs -9e21edc67a14ef8a2ce3dda0ed18784d0fd0be305973e5d216058b7ac802c6cb src/http3.rs +f6ebafafa82391f8cb3b5a09331f3762b6ca5a9fb64bafb5772cde3ab814dfcf src/headers.rs eb57117938a447d9a6c2ec1806ebd9fecc640a68c88950a8b35fa9bad14d0dc6 src/http3/body.rs ed25da264a2d25b40c4fcddc97c626a3a43d4f3a9be6c6d31af58b9e4925c198 src/http3/diagnostics.rs 37f54961d8f52b79a75fb2be31e81f4fdb1e66b06350345c4483d6197b8a0bbc src/http3/identity.rs f2a8c2a98f4ccfb708f9d75a48ec7b351232be9b8a4364b7e63ba14d28eead7c src/http3/settings.rs a31db7eac12b578b1eb5a74fae98bbe36312da95b7d7173135dbc41fc454da21 src/http3/tests.rs +9e21edc67a14ef8a2ce3dda0ed18784d0fd0be305973e5d216058b7ac802c6cb src/http3.rs 68fb5ab6a889995a66ad12cb6acf88c5d04f0aca572e2ae15f1a9cd171a5f24b src/lib.rs -2551189a3e5701371026c9abd2c5d44d37495f28fb67e220180421bfd315e3dd src/logging.rs 18c8d74c21a47f234be93551d7264b1f8f8c00d1bbfbe555d91618c58cea0740 src/logging/error_runtime.rs -87948a511b680e1c274eb46d13b938b45792e63bd6cff1621afe3477708ad2a8 src/logging/format.rs +33b4b7f69560c2acc0de0eac62604161d38dbd7044c5789a1352f0cd3882ea70 src/logging/format.rs f5815d778cc1785723faed4abb15f9880f22b6524b5289fe1e7158905aac0986 src/logging/model.rs 42fef80af50735cd566c6801dad86ba81be98cf4a8ec7d45c2ea26f53f556a22 src/logging/runtime.rs 584782215ea78a88526cdb5515031d8d5b87910875e26c0024b93a06680f6791 src/logging/tests.rs -3836ed14b6710e183f048ee4d7807a35366c27a27338057fe4a2811129b63302 src/main.rs +2551189a3e5701371026c9abd2c5d44d37495f28fb67e220180421bfd315e3dd src/logging.rs fc8e99eac83c083d65101f6402aacbcc8392d66508c31566a50de3ebcb12e7da src/main/admin_runtime.rs 42a9197475823f67c1ca762951ede5d7a2f10124bd6ed4d1a3365f4297be894c src/main/bootstrap.rs 5a98d0e249338e73aa57ae2e352dc16db2f79c2d042c1544ce1c2022da76a31c src/main/proxy_runtime.rs -4dd7c1084a6214c0f2513ebf86fe8ef144470695557772bfe25979edd64491ab src/main/shutdown.rs 0a6578a0f4023b9f8d73043bf942dc5e096ef9afb8c0d2f131c14f65d08ac01d src/main/shutdown/tests.rs +4dd7c1084a6214c0f2513ebf86fe8ef144470695557772bfe25979edd64491ab src/main/shutdown.rs e5c2e9189777725f4afa1c3c052e4f75047ec3f2f486630d990f5d3c194bcc96 src/main/tasks.rs -49d7e104585c8791ac9a203dddd95802784b68c0ba65d6f90f3d57154b4c7871 src/metrics.rs +3836ed14b6710e183f048ee4d7807a35366c27a27338057fe4a2811129b63302 src/main.rs 201ab702722d6d21585fe8bb49939150d61bfac7ed8dc198b98c2cc6098ffb5f src/metrics/global.rs -49b11df65b4fb0b5b727f9f867436584680b45e3f4fbd53f74bc0cab8b9a88ec src/metrics/vhost.rs a54b5b34ea65f29723b89b81bf7968a238e8247b9c9760f3430d3504cb1017c9 src/metrics/vhost/tests.rs -a4702c95c7e5077c2ccff86e2e3216e1da6ff9ee4383ae199a0aff2f78db325f src/not_found.rs +49b11df65b4fb0b5b727f9f867436584680b45e3f4fbd53f74bc0cab8b9a88ec src/metrics/vhost.rs +49d7e104585c8791ac9a203dddd95802784b68c0ba65d6f90f3d57154b4c7871 src/metrics.rs c915d30bbd7032d867e7916b4cd45f3bb7e7604d3f4558f4383c59394b4300c6 src/not_found/tests.rs -d66936cc15fd5d56566d40fa8cab93f80d94b05f10ac44478a30f63572a95e32 src/pages.rs +a4702c95c7e5077c2ccff86e2e3216e1da6ff9ee4383ae199a0aff2f78db325f src/not_found.rs da40e8ece87245efc49a0c4e750526b1d19fd97565856bd5caf641fcc1dfe18d src/pages/tests.rs -0dba7b6a33866cc7bbe05509b01961e545892645d1dad4eb88ac8a4bec65e3bf src/prometheus.rs +d66936cc15fd5d56566d40fa8cab93f80d94b05f10ac44478a30f63572a95e32 src/pages.rs cc6fb14351b009a16d07ed0f412c2bcc68cd7e551421aea03d8d97ceec9bee40 src/prometheus/core.rs c46882ed26742d93806a976257674326e105b8ca1fc23f5ca81e317b268057aa src/prometheus/docker_misc.rs d3c6b08088df0003144544936a55e2a3661aac85292821b594f00adfd19a2951 src/prometheus/model.rs @@ -417,141 +425,144 @@ d3c6b08088df0003144544936a55e2a3661aac85292821b594f00adfd19a2951 src/prometheus 028e1dd9f2ec2afaf699a6b21c2d4b0d96e76f11f5e4ed009c233dadf0f3be51 src/prometheus/traffic.rs e03f0d6b3f35dae6b98c394837aa0d17e4d73a8306767b4c607868bc00c30286 src/prometheus/vhosts_backends.rs fb2d8d45cd2e947f783eb981b37fa418c39fcb327a3af17a648fadea7f37f888 src/prometheus/web_php.rs -fb02fb31ab62ac47c998d418fb8fe2f1d810a451b68805f31d6ecc244ae20820 src/proxy/body.rs -b8fb54359a7c60e5a8bd8ca45a5a64641633c595f0deb84d6d906c31fbbc1312 src/proxy/body/tests.rs -c3dcd88f4b00a9dbcbb65f6fa70ee2ab98c2fba20e9a2dde3b8852306111836f src/proxy/cache_helpers.rs +0dba7b6a33866cc7bbe05509b01961e545892645d1dad4eb88ac8a4bec65e3bf src/prometheus.rs +f0f3e21350e4fb15fa2ae282da6b290857dcd6716e62b183e9ecb728b7c24c63 src/proxy/body/tests.rs +4c7199c04e6d314e89825f80feaab35a2efb3b893511f70da0cda46ab1d0bc9e src/proxy/body.rs +b88d1d7a6837a2a470e5863f1f3247878daee62791fc3d8e72baeb66469bdfbf src/proxy/cache_helpers.rs aa49c9928bb91e3d5bae0d340d88396a0417b9edec6192d41e1403533319a897 src/proxy/cache_http_invalidation.rs -676c1b325fd8e55bcdd86451c7d1d8b8d347ca00e8da6b3e5b148a58c376a9b4 src/proxy/cache_plan.rs 3404b03096862de1050088f62cdd811ea1ab61d191f902501173107f81f23b9b src/proxy/cache_plan/tests.rs -771cc1a38927f96e593b144dbab66cd64a5dc0ed42d54b1615c359845b2b7223 src/proxy/error_fallback.rs +676c1b325fd8e55bcdd86451c7d1d8b8d347ca00e8da6b3e5b148a58c376a9b4 src/proxy/cache_plan.rs +a8848cbdf85368548b003af2b43823c453866c472af4c7d2ff0ec7da14c44c8d src/proxy/error_fallback.rs bf2e6bbd64fb45fceed54af0bff1c1e00da22b79c64dd8f5c23d2af0dbab0b7d src/proxy/forward/cache_lookup.rs cfa941b60b5d167ec9d8e2566ea3342b588d1a9078a6ca711ad6062ddf00fec9 src/proxy/forward/response.rs -b4be19f2dccc97270f3596d7e56dc60237b7d4d254cd9362a355812026d90295 src/proxy/forward/route_cache.rs +8168a42c3992d002e87fdd5d52a37f53dcdf69cbc859221350d888e94c305ab8 src/proxy/forward/route_cache.rs 96c97928986ca89c2d6d415d5b107b9cc7228410bae93a2c08c24974324f4d8d src/proxy/forward/success_finish.rs 0a921474ef2b93eabdaf3888651cf29130eace5ccb1e0f5d6bc25e6aa69280be src/proxy/forward/success_prepare.rs -a064cf54ee62a374c1753f7a64940990a591888efc4516a07942c52c4e0842e2 src/proxy/forward/upstream.rs -1f36225516caefc756a49bbdf37b227a7ea97b72ab78df021c9234e5c3dd41fc src/proxy/forward/web_source.rs -a9c011061585c067f04f7885215d53e6c40474bdced15d2be5a5ba65190910a0 src/proxy/forward_entry.rs +bb24da9c9ab986a9c4d4144d4d1be0f661561d4afc755f298b58bc679b182850 src/proxy/forward/upstream.rs +11dccc239ac8a8e9ea06e884c2f2658f86b7807d14a59b8e052bc7dd75e86d63 src/proxy/forward/web_source.rs +facde401ba5bcbafdcea5524f236cfae37eec11144ceeea879e83314c84bc97e src/proxy/forward_entry.rs 9b25f5e3ea4443a63ee6f45d12e1957dd316fe8e85325fc00ea416faaab1025a src/proxy/h2_graceful.rs -c438066616527a3108f2ad53e3698ae9b2a80961714394cdd0a450c92f99d992 src/proxy/headers.rs 5cb7522ceb9a9f083a15596ac4898559295e38d1138c80099577211de8a868ec src/proxy/headers/tests.rs +c438066616527a3108f2ad53e3698ae9b2a80961714394cdd0a450c92f99d992 src/proxy/headers.rs 3f25429418941eb72c3847125340db74684ad316eb278ecf658953026fd41f4f src/proxy/mod.rs 55133ceace2e5262d9c54fa41bea099a27b817d1e09c1cd0e2493fd9cee4da38 src/proxy/redirect_helpers.rs -c61738672f210e97333c8c89f9b35f8db454cbc1c36955288dfa4cfff2bbbc04 src/proxy/request_id.rs 6ff45990f6beb914e89924bec2c41022f3240a14357cf66ec4f8ada414a49767 src/proxy/request_id/tests.rs +c61738672f210e97333c8c89f9b35f8db454cbc1c36955288dfa4cfff2bbbc04 src/proxy/request_id.rs 8ad3619359f3befa14ea71ba43c638fea8f4d8a859cd07e4ed505731b8e32e72 src/proxy/tests/authority_tests.rs 1aa9bbbfb2659facdaeaf185e11221b1c8cd5fe6e23c7c1dacd3aa55e3db4fcb src/proxy/tests/cache_fast_path_tests.rs 0f2eacadf1447db8aa1c1a78005aa262e0f747096bfe7e0a0e41a2cd31a0d17f src/proxy/tests/h3_navigation_tests.rs d134b0eb617f82f9971b3f3461379ea37c57bd0079bf54748d0c434b2ef1b475 src/proxy/tests/http_invalidation_tests.rs -591b47fc40cbcb36d7c71998933dc873340dfcf2657e16c561ab64361c4dbab6 src/proxy/types.rs -8b053027bbdbbebb624164d9c1b15b2b8f5a13b8a23a9e1477ef4e840ce0334f src/proxy/upstream_helpers.rs -2d9142e3ae95895b5271b4009ab48236a3a2074ce51179c021193b35777560e4 src/proxy/web_cache_revalidation.rs +d5717fac0e7d2cb2939626eb95f4520241b677fe1f3e045df12c100315302a5a src/proxy/types.rs +8d7848645c25932589d0dfc54149bb71a51edd7188aed60dc4d6ca8179b361ab src/proxy/upstream_helpers.rs d7b4b809b12ee89293df45e9a64b1c9981fb3f89deaab3762f4a3ad3992dd7b1 src/proxy/web_cache_revalidation/tests.rs -69f775f6746b4c6d729dc10961dbef54168846f4ce9cf7fe453ab5a2b879c9b8 src/proxy/websocket.rs +2d9142e3ae95895b5271b4009ab48236a3a2074ce51179c021193b35777560e4 src/proxy/web_cache_revalidation.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 -545247eb00b0116efbd4cc913cc251195a55feded1530e0d8d8945abbae78039 src/rate_limit.rs +69f775f6746b4c6d729dc10961dbef54168846f4ce9cf7fe453ab5a2b879c9b8 src/proxy/websocket.rs e7e3a898e2c769b1ef11e58a13c1053104ada93dd694f8a03ea4b647c44c7d69 src/rate_limit/tests.rs -d40ad63939097d2b91339127d5835a74b618d83b7ba31db0c512f05a82107d6e src/redirect.rs +545247eb00b0116efbd4cc913cc251195a55feded1530e0d8d8945abbae78039 src/rate_limit.rs 811c83ae9fd0f5a13d35b5443cd37843f82b9d600bee4bb136ba9b87732a5347 src/redirect/tests.rs -d0939e1b0829c25356a0b67ecb3a178c74936dd07baa15fee24eb32544db6cad src/runtime.rs +d40ad63939097d2b91339127d5835a74b618d83b7ba31db0c512f05a82107d6e src/redirect.rs 8d0d7b01871c8c588c40a54330a1053c85024655fbe38516ca6c94c3c19a8956 src/runtime/certificates.rs -8d2443df8dab1ce6f11878e991b4928046b7362c417c5703f6d1f3a977646cbe src/runtime/constructors.rs +2ccafa8a5a8462417f65d05a0c6c42f294afa4ad1067ea0b93623181546d1f96 src/runtime/constructors.rs 92b4b284f1b69dca58e4e3f76199f0aecc397eed26d10123b427af1bb8a4a57e src/runtime/docker.rs -6b2b57888863a396461fff202bfdaa6d8e869b2628cc171c0823f31f388e5178 src/runtime/tests.rs -4036b48925f5b25de70e21dfbb278488c20b39248dcda4cad4d19d5d7aed2b8d src/runtime/types.rs -e1d172acd2b230b8f4dd671cae8862a76f28416cbf0e43f86214a796edd90fab src/runtime/updates.rs -208a378b14ca80e1412ffa19ef1b140594182971e2e797ee0bb95bcdb8404da7 src/secret_store.rs +ee2050b9f8efb45a209c91d76043a1ba29c4289978896aaf7e7d344d48fdc369 src/runtime/tests.rs +779137f320133ec96f73c8b3d357ad4f0b825ee2367e9e8300f4f143b38eecb7 src/runtime/types.rs +5947afaf7d070ea2047eb7969a8e0d84f981e91ace283f6506cf7685fb8afab9 src/runtime/updates.rs +d0939e1b0829c25356a0b67ecb3a178c74936dd07baa15fee24eb32544db6cad src/runtime.rs 6dca8da072a3bd610704a391bb6e25c9042971f4ebc60edb47665ce1d74cbc58 src/secret_store/tests.rs -6fa1d8c352bce7a16ff0208bc4cdbe80404715df0aff4ed26910a5df57cbbcd4 src/security.rs +208a378b14ca80e1412ffa19ef1b140594182971e2e797ee0bb95bcdb8404da7 src/secret_store.rs 0e5fc2aff856bf6fd959cba12b5ddd1e3b3cbeb0fbaadde906c811cdfafdaae7 src/security/tests.rs -53a4bed4d1233aa434712b946857ea2050acaaf5de011c6dadca5b5c22155a83 src/server.rs +6fa1d8c352bce7a16ff0208bc4cdbe80404715df0aff4ed26910a5df57cbbcd4 src/security.rs f2f6196632b2fdb2444189624f961edaf5bdfff57f700b0207b57e2491c1daa5 src/server/http.rs +050c48ea1127aa87dba92c074ab84e8346e65dbe253621ea53f8ca2f68d791ab src/server/http1_connection.rs 8c6ca5eaffb9acab57199d0349722703b903cfff3752baad2cf2596bd51188de src/server/listeners.rs -71899a9b1897fa22ab9e8eb6a670054a6d382bad78714006b9b218d07ba99902 src/server/serve.rs +4424a99b0b688d1119ee25626778b4804aedfaa0799a4a9bc0ee99bc5c7ba96b src/server/sendfile.rs e9e478cf45b88994eaffd115f51764b6b07852ee3c941cafdabb20584d3dabc0 src/server/serve/tests.rs -048f2c7a8ef698a172ba7ed844558f4118b367b2cf340748c5c28da7723d8e85 src/server/tls_runtime.rs -73ec453ac2850f6963f5bc33401cb8c92aa26d5123f15d68e6633860d91f265e src/settings.rs +71899a9b1897fa22ab9e8eb6a670054a6d382bad78714006b9b218d07ba99902 src/server/serve.rs +960a82e02f09d75141fe3c1eecca915b66f17fdc8167196cd2fe21d5e6859376 src/server/tls_runtime.rs +98c52aa3b50d2e211ab752ccaf353c9a8c1500a87f9c1684b5dd0ba9d10e4619 src/server.rs 240ecdd70e8662ff3b84c21830dc0cbbbacadd755d56146e54214bc2887f068c src/settings/public_origin.rs 1f1121c3562c8f8f3fdee2113be2afffb8849be2d303e4c4409f87478b6e3307 src/settings/tests.rs -9dbf814870b8636770f579c3d36d16f5b6afe8bc66adfad356c26d7ae7dbce45 src/stream.rs +73ec453ac2850f6963f5bc33401cb8c92aa26d5123f15d68e6633860d91f265e src/settings.rs 254840126be2310426f6332f5efbe98147d9d8de1999731f40a3d13325c1dc6c src/stream/health.rs bdc56be7dae85a97a4d5b8d46ef31ec4177292b1cadd507fe423b6d4ee1403bd src/stream/model.rs de138a4ac68bcddba3a4f17ceba008bab643359d76a68726a755069f19334bed src/stream/pool.rs 6ec42b8647c95a0c2025462e0be29c71481ce1c3b1f4d4193926471f1cf06be0 src/stream/runtime.rs 9346cbd71e006e41a7188f28eb86f9b00bac822e4cb31c76c8d6d2ec5d4c4711 src/stream/tests.rs -3070d78a7b88c2e47ed59960fa35e7d220a4cd7d78e48f6c6d2bcc5ff77f9620 src/system_stats/linux.rs +9dbf814870b8636770f579c3d36d16f5b6afe8bc66adfad356c26d7ae7dbce45 src/stream.rs d9123c623e2a97d09e41edaa29993b4b2c8cba39a3abdf2b48a205aa9a9c2939 src/system_stats/linux/tests.rs +3070d78a7b88c2e47ed59960fa35e7d220a4cd7d78e48f6c6d2bcc5ff77f9620 src/system_stats/linux.rs 751d59582fb92c84e0c0cc7c0855c9d0a545805c9ba3be61826e50fb96c297ed src/system_stats/mod.rs 050aa831b1012c4aaade63863f433f01bfacf5d3f95787e57975572116a30ab2 src/system_stats/tests.rs -7467d83066bb6986d8c9103597f7a7617ac677579ed46d573c573ff6ca57bbd9 src/tinyauth.rs 0e1d87554db55e724f30c41ec4a5c80bdb8fc9c8cba8700ac509864d4abf8871 src/tinyauth/tests.rs -1084cccd9de28acd6cb0f0813de7c0953910f51726f73f4acdb2e3ada4593976 src/tls.rs +7467d83066bb6986d8c9103597f7a7617ac677579ed46d573c573ff6ca57bbd9 src/tinyauth.rs 4972a436f8878dec5f9eb8ff17eb82670b0bfd9e94e821c516575479f5fc1919 src/tls/certificates.rs 436575144aa352498a4696f12c2258bef3a898f3f50e6fbc6d67c3b93afa099f src/tls/external_paths.rs 3e6947dde167f82a5efc407ccf9bd4c9f5b26cc8633d1418a10284bc1a1503d4 src/tls/server_config.rs -31c0f51a3a0bab433f438c3ba1b1cdaf3c63fae6086e7d99b3464a537f75a5e1 src/tls/tests.rs 2af2164ecdf2b7fd9eef7c6e358dec053dfac9aedaa501dbb75df742d18638b2 src/tls/tests/acme.rs 93b359e9defd62506729bc90e56c96783e2f14dd477dd67f337ca83149da7168 src/tls/tests/external_paths.rs +31c0f51a3a0bab433f438c3ba1b1cdaf3c63fae6086e7d99b3464a537f75a5e1 src/tls/tests.rs 10c4a187e3aeb0c72a8c8968ea0cc8bfae4e3cb2f3d834ebd3da1056c4171ec5 src/tls/types.rs -c2f802d95da4332fb8480e83f0c7502334457b3408141c1c4516c9ba320a0463 src/traffic.rs +1084cccd9de28acd6cb0f0813de7c0953910f51726f73f4acdb2e3ada4593976 src/tls.rs 36301bcbd20b557dd3d58e77a9175f3581947f577c0bb0b972899cec6786b03f src/traffic/tests.rs +c2f802d95da4332fb8480e83f0c7502334457b3408141c1c4516c9ba320a0463 src/traffic.rs d88c92f5a2a7773bc3e715d24039db35da4df53881f1a0859eec819b96f06d3b src/transform/apply.rs 87f8af5261d79072f8215f1fb8a90715a58e7626544fd8176ce840e3111bdb92 src/transform/compile.rs b2c5236589093454ec60984b785ad6a4af6149d47f6db37d8e08c17fec7a2c1b src/transform/css.rs -5478670cef87777b14a2dab308f5a9182df56d7ea2f5625bdc94aa7e3d661503 src/transform/html.rs aa99b0d3029ef92c78371cb90e11f2026cce890d1be8e10ebd7fa9bc5cb13287 src/transform/html/tests.rs -a1d2fec07617c8a8dc425bfaeaf77c844152a0a174568a88a5baf4c317cc6c5c src/transform/js.rs +5478670cef87777b14a2dab308f5a9182df56d7ea2f5625bdc94aa7e3d661503 src/transform/html.rs cf5195ab6bacccae0082da7b3f1bd0382b73fdeb3607f4ae569ca6daab9d10d0 src/transform/js/tests.rs -226ae561dc7b0243c04e55e28cdfd2b76acd48cf614d1919b81c196660b03cfb src/transform/json.rs +a1d2fec07617c8a8dc425bfaeaf77c844152a0a174568a88a5baf4c317cc6c5c src/transform/js.rs 216e24e29ea87be4a124f95f898e155f127fd959d7d5d9b30f8cd0b2b5018f30 src/transform/json/tests.rs +226ae561dc7b0243c04e55e28cdfd2b76acd48cf614d1919b81c196660b03cfb src/transform/json.rs 6a8c28be93fb590942493c075b29ace1527981542bd183f232aa82b62e951eca src/transform/mod.rs fde2fa3aa94b30d8536afec1c11348cfbd922d0b5ff89e91d34266e8e3863e26 src/transform/model.rs -a4e1d2aa2913587c250f251356431097b76995aee6e8bf9e35c4978a95491de1 src/transform/svg.rs 0f53c64f442390ee20a59087a10f8f9ffb58053263e9aa848ddabfae1a6f3f51 src/transform/svg/tests.rs +a4e1d2aa2913587c250f251356431097b76995aee6e8bf9e35c4978a95491de1 src/transform/svg.rs d1f3f53fedae0d8ef69eeee457f7d30ed5a670a1ed4ced60683400ee46680260 src/transform/tests.rs -6146c59f36cf419623747d497b3de685da6c84964df43317bebdcb21deb9ee09 src/update_check.rs c42d000d1d576721af3ffe930589388693001b8113554a7658f6dcbc665b5d1b src/update_check/tests.rs -92efcdaafb9f5a8514daf63d7fcde29be977d7bdcd90162eda0ce00d6967ffbd src/upstream.rs +6146c59f36cf419623747d497b3de685da6c84964df43317bebdcb21deb9ee09 src/update_check.rs ca695e246bee5389b3757fe8588e48bff5d0ad473bc8588ad4ba7b52c99d7dfa src/upstream/client.rs ad29f3fc15c9b627b5d752af5adc9f99ee1364fec2df63f1e4509cb223988efb src/upstream/client_tests.rs 75fef0a488d3f3dabfa03c2eabc5995442dfb3c9a1fde8eb6653a124dda25d43 src/upstream/protocol_tests.rs 8a112154b9631f62a54584302a3ff6a64e4f177ce79aba678b4cb23e466c6ea3 src/upstream/tests.rs -a6b4905c245a22eafb1aa535d303d0fa24e4353db94e1d67fd42b825bf8c5adb src/user_agent.rs +92efcdaafb9f5a8514daf63d7fcde29be977d7bdcd90162eda0ce00d6967ffbd src/upstream.rs 6d7cb73de7d8c6f3780736480f5f81ef08813f4626fd5ed494aca0552cf214a1 src/user_agent/tests.rs -3aa1b07cd26d4a5b9e5e94d1d6a712ef1397d827e7f1e8c01f7195b98d2c5f67 src/vhost.rs +a6b4905c245a22eafb1aa535d303d0fa24e4353db94e1d67fd42b825bf8c5adb src/user_agent.rs 6678192de56bc10c5cf82bdff8da14eece7685f409592450c07478333aa3b99c src/vhost/error_fallback.rs 63e9fc7f7be78dc3e16a89f015a56a2da6ba3aff0197b70ff48321dfb17529bb src/vhost/model.rs 35a0aca3eee8d47d1bab27c83d4532cd572fc3e17b5f0d4a8d4515a8f85b3d96 src/vhost/redirect.rs 9b16b5307e140db97363f56c9fac8319e56df846bb1c919d3bc06091ab6ce69c src/vhost/tests.rs b7d0f7f5d0bbd74add3fda9b1308350c4f70f8b6debf979fb977a4f2f659c32f src/vhost/validation.rs +3aa1b07cd26d4a5b9e5e94d1d6a712ef1397d827e7f1e8c01f7195b98d2c5f67 src/vhost.rs +9744e8c29ae72a4d9fbc9a76166a97f8292c4c312295ea40d1cb699e03d80a9d src/web_server/config/compile.rs +bcaa1ae15134d85668b05a88d8abea01e5d188f535ad83cd4459732fd56f2f44 src/web_server/config/model.rs +a863ad1337fbc353c0f42caea4ac8b3644792e2aa08c37f992b7882b4b8dc6d5 src/web_server/config/validation.rs 8d1eff4b24f01d137414ef5419f75dd281879228adb70409645b11b3c78c20e9 src/web_server/config.rs -aa38e89e84f928bd1952d3e2cb67e48ef2e92c5f444c095b6288034adc5e3d3d src/web_server/config/compile.rs -7853f6ec127a91163a4418ca45b93a92f424d5c618f27b29f9d526598a26f7ef src/web_server/config/model.rs -d9178aa934ee1a7236cb04d864fa842faf64156e9103786e3103c92bc4e18fde src/web_server/config/validation.rs -86c744216d7132d8294a6e90ba9be5fd871646006313d1c62506d40c81db9b12 src/web_server/fastcgi.rs 200cba8cb3482e49db73d2c60cb60786175177c7d4be2a566cd281fec7ba7fca src/web_server/fastcgi/protocol.rs cef44ccdf882d84892a64bdbfdc1ba01380fce5e5f86d2829b0f9613f738b964 src/web_server/fastcgi/response.rs +86c744216d7132d8294a6e90ba9be5fd871646006313d1c62506d40c81db9b12 src/web_server/fastcgi.rs e15361622683268c5606a39a9e5db3938e630999767898391226e94ad2e421e8 src/web_server/filesystem.rs -d4fec3682532b0f2d11a0aefcd024f69fb623a39922ddb6c528a2d2de265347d src/web_server/htaccess.rs ca3cd376b313af1ce0ee65081b3327eb213f000d5a215aa4ee6b764de9702126 src/web_server/htaccess/model.rs 76de1d11df75f4a64556b2821fd0a1774d26c9f0ca0e5f2b5fcfad0722d43d47 src/web_server/htaccess/parse.rs 66260d159986d41eab0dfde2cb9f0713fc1cc209c2fb47e9023bd24fd37919e8 src/web_server/htaccess/rewrite.rs 8ca74d9cc4470045301f70eb4a9fbabd0dbfe74434429148d2230df9f996c06c src/web_server/htaccess/tests.rs +d4fec3682532b0f2d11a0aefcd024f69fb623a39922ddb6c528a2d2de265347d src/web_server/htaccess.rs 3341264f6e8041ac5a5d3c4b1601a69787386116019b5febc1a5e22bdb1186f9 src/web_server/metrics.rs -7b945065e636ca74147048aa53cc5b34028cced72a780fbaf0aa2499b00718fa src/web_server/mod.rs +0dbef9b71869fc52cf01e9cb400ffd48fb433ae625383edee6c6c2d735608a76 src/web_server/mod.rs 67ab8bef567452f123c4b9b46a942b39c93f1f765c128edca7ee808ff1c5e304 src/web_server/request.rs -d98c80663847d6d83073df074ebff65952755415574708abcc153bd8d3adfaa9 src/web_server/response.rs -c89e412584cf524cd1cd332ce555ff922a9e5549cd5d20b2e9a29393456be889 src/web_server/response/file.rs -ba793a140699956a9aef6bc79b0c731c481af8ab7ac3d816a28695743febcb30 src/web_server/response/file_body.rs +833c9fea744b9649046e1a64d6fc6972fbf6cea640e1943b0db52b7ccfa9faad src/web_server/response/file.rs c2e54fd803a10f74490f183e8c030fd0e3e72f5de4e79c55bc393ab57d90ab9a src/web_server/response/file_body/tests.rs +ba793a140699956a9aef6bc79b0c731c481af8ab7ac3d816a28695743febcb30 src/web_server/response/file_body.rs 20509f9302b79d3a7be76dda6edaf5711826d3ddf47d19ebe07cb289427ca328 src/web_server/response/headers.rs +d98c80663847d6d83073df074ebff65952755415574708abcc153bd8d3adfaa9 src/web_server/response.rs 305c0109b4f869a99381a142853f9e2205f5c7c4f904b2dfa4643274bfc542af src/web_server/routing.rs 400e1c8aa2fe857d96036a64499fbc3de44c1e3a53811c5b25101fb843359792 src/web_server/security.rs 54cf72a82e023fe46513b9cf3130adfef1cd96c40f790c533016b4825d542e06 src/web_server/serve.rs -bda82f531f61251772d3bb4bc67d98ca613aebd51b489b7cac1fd5086c653b1b src/web_server/tests.rs +23904fc0ca6d5e816b332303b148a2ec5ea3b226b5ff8f68db05c42954472a3e src/web_server/tests.rs f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json 1725c10331745dc04a07675784d385260da7b14567b8dba3cbae5e891eb5dadf tests/http2.rs 347a4ff9e028a1e999ad129590c355b5734cf928dd81e5099aea0c8aca8d5908 tests/http3.rs diff --git a/docs/NGINX_GAP_0.34.0.json b/docs/NGINX_GAP_0.34.0.json new file mode 100644 index 0000000..2ba50ad --- /dev/null +++ b/docs/NGINX_GAP_0.34.0.json @@ -0,0 +1,216 @@ +{ + "kind": "HYPOTHETICAL_AMDAHL_SENSITIVITY_NOT_A_BENCHMARK", + "comparison": "Existing 0.33.8 measurements (5 runs, cache off) vs measured nginx", + "not_measured": "0.34.0 server performance and sendfile syscall/runtime correctness", + "conditions": [ + "Identical CPU and network assumptions to supplied 0.33.8 baseline", + "WWW zero-copy is limited to Linux, cleartext HTTP/1.1, GET >=64KiB, no cache, range, transform, compression or TLS", + "WWW low/middle/high: file-copy contributes 20%/40%/60% of total request time; modeled sendfile eliminates 50% of that component", + "Reverse proxy low/middle/high: removing response-frame mutex saves a hypothetical 1%/3%/5% of total service time", + "These fractions are chosen sensitivity parameters; not measured or inferred from profiling", + "No change is assumed for WWW 1040B or out-of-scope protocol paths", + "No network saturation, TLS record processing, queueing, origin latency, filesystem cache misses, CPU contention or concurrency effects modeled", + "No additional speedup due to cache-retention changes is assumed" + ], + "formula": "RPS_sim = RPS_0.33.8 / (1 - fraction_of_total_service_time_eliminated)", + "entries": [ + { + "kind": "reverse_proxy", + "bytes": 1040, + "baseline_version": "0.33.8", + "baseline_rust_rps": 29334.12, + "baseline_nginx_rps": 170610.16, + "baseline_of_nginx_pct": 17.19, + "factor_to_match_nginx": 5.82, + "sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": false, + "scenarios": { + "low": { + "modeled_total_time_saved_pct": 1.0, + "simulated_rps": 29630, + "simulated_rps_gain_pct": 1.01, + "simulated_of_nginx_pct": 17.37, + "simulated_remaining_gap_to_nginx_pct_points": 82.63 + }, + "middle": { + "modeled_total_time_saved_pct": 3.0, + "simulated_rps": 30241, + "simulated_rps_gain_pct": 3.09, + "simulated_of_nginx_pct": 17.73, + "simulated_remaining_gap_to_nginx_pct_points": 82.27 + }, + "high": { + "modeled_total_time_saved_pct": 5.0, + "simulated_rps": 30878, + "simulated_rps_gain_pct": 5.26, + "simulated_of_nginx_pct": 18.1, + "simulated_remaining_gap_to_nginx_pct_points": 81.9 + } + } + }, + { + "kind": "reverse_proxy", + "bytes": 65536, + "baseline_version": "0.33.8", + "baseline_rust_rps": 20735.07, + "baseline_nginx_rps": 35393.29, + "baseline_of_nginx_pct": 58.58, + "factor_to_match_nginx": 1.71, + "sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": false, + "scenarios": { + "low": { + "modeled_total_time_saved_pct": 1.0, + "simulated_rps": 20945, + "simulated_rps_gain_pct": 1.01, + "simulated_of_nginx_pct": 59.18, + "simulated_remaining_gap_to_nginx_pct_points": 40.82 + }, + "middle": { + "modeled_total_time_saved_pct": 3.0, + "simulated_rps": 21376, + "simulated_rps_gain_pct": 3.09, + "simulated_of_nginx_pct": 60.4, + "simulated_remaining_gap_to_nginx_pct_points": 39.6 + }, + "high": { + "modeled_total_time_saved_pct": 5.0, + "simulated_rps": 21826, + "simulated_rps_gain_pct": 5.26, + "simulated_of_nginx_pct": 61.67, + "simulated_remaining_gap_to_nginx_pct_points": 38.33 + } + } + }, + { + "kind": "reverse_proxy", + "bytes": 1048576, + "baseline_version": "0.33.8", + "baseline_rust_rps": 4461.19, + "baseline_nginx_rps": 2730.23, + "baseline_of_nginx_pct": 163.4, + "factor_to_match_nginx": 0.61, + "sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": false, + "scenarios": { + "low": { + "modeled_total_time_saved_pct": 1.0, + "simulated_rps": 4506, + "simulated_rps_gain_pct": 1.01, + "simulated_of_nginx_pct": 165.05, + "simulated_remaining_gap_to_nginx_pct_points": -65.05 + }, + "middle": { + "modeled_total_time_saved_pct": 3.0, + "simulated_rps": 4599, + "simulated_rps_gain_pct": 3.09, + "simulated_of_nginx_pct": 168.45, + "simulated_remaining_gap_to_nginx_pct_points": -68.45 + }, + "high": { + "modeled_total_time_saved_pct": 5.0, + "simulated_rps": 4696, + "simulated_rps_gain_pct": 5.26, + "simulated_of_nginx_pct": 172.0, + "simulated_remaining_gap_to_nginx_pct_points": -72.0 + } + } + }, + { + "kind": "www", + "bytes": 1040, + "baseline_version": "0.33.8", + "baseline_rust_rps": 24906.2, + "baseline_nginx_rps": 351605.13, + "baseline_of_nginx_pct": 7.08, + "factor_to_match_nginx": 14.12, + "sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": false, + "scenarios": { + "low": { + "modeled_total_time_saved_pct": 0.0, + "simulated_rps": 24906, + "simulated_rps_gain_pct": 0.0, + "simulated_of_nginx_pct": 7.08, + "simulated_remaining_gap_to_nginx_pct_points": 92.92 + }, + "middle": { + "modeled_total_time_saved_pct": 0.0, + "simulated_rps": 24906, + "simulated_rps_gain_pct": 0.0, + "simulated_of_nginx_pct": 7.08, + "simulated_remaining_gap_to_nginx_pct_points": 92.92 + }, + "high": { + "modeled_total_time_saved_pct": 0.0, + "simulated_rps": 24906, + "simulated_rps_gain_pct": 0.0, + "simulated_of_nginx_pct": 7.08, + "simulated_remaining_gap_to_nginx_pct_points": 92.92 + } + } + }, + { + "kind": "www", + "bytes": 65536, + "baseline_version": "0.33.8", + "baseline_rust_rps": 20856.67, + "baseline_nginx_rps": 203709.91, + "baseline_of_nginx_pct": 10.24, + "factor_to_match_nginx": 9.77, + "sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": true, + "scenarios": { + "low": { + "modeled_total_time_saved_pct": 10.0, + "simulated_rps": 23174, + "simulated_rps_gain_pct": 11.11, + "simulated_of_nginx_pct": 11.38, + "simulated_remaining_gap_to_nginx_pct_points": 88.62 + }, + "middle": { + "modeled_total_time_saved_pct": 20.0, + "simulated_rps": 26071, + "simulated_rps_gain_pct": 25.0, + "simulated_of_nginx_pct": 12.8, + "simulated_remaining_gap_to_nginx_pct_points": 87.2 + }, + "high": { + "modeled_total_time_saved_pct": 30.0, + "simulated_rps": 29795, + "simulated_rps_gain_pct": 42.86, + "simulated_of_nginx_pct": 14.63, + "simulated_remaining_gap_to_nginx_pct_points": 85.37 + } + } + }, + { + "kind": "www", + "bytes": 1048576, + "baseline_version": "0.33.8", + "baseline_rust_rps": 2833.71, + "baseline_nginx_rps": 12611.49, + "baseline_of_nginx_pct": 22.47, + "factor_to_match_nginx": 4.45, + "sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": true, + "scenarios": { + "low": { + "modeled_total_time_saved_pct": 10.0, + "simulated_rps": 3149, + "simulated_rps_gain_pct": 11.11, + "simulated_of_nginx_pct": 24.97, + "simulated_remaining_gap_to_nginx_pct_points": 75.03 + }, + "middle": { + "modeled_total_time_saved_pct": 20.0, + "simulated_rps": 3542, + "simulated_rps_gain_pct": 25.0, + "simulated_of_nginx_pct": 28.09, + "simulated_remaining_gap_to_nginx_pct_points": 71.91 + }, + "high": { + "modeled_total_time_saved_pct": 30.0, + "simulated_rps": 4048, + "simulated_rps_gain_pct": 42.86, + "simulated_of_nginx_pct": 32.1, + "simulated_remaining_gap_to_nginx_pct_points": 67.9 + } + } + } + ] +} diff --git a/docs/PROXY_HOTPATH_0.34.0.md b/docs/PROXY_HOTPATH_0.34.0.md new file mode 100644 index 0000000..165206c --- /dev/null +++ b/docs/PROXY_HOTPATH_0.34.0.md @@ -0,0 +1,84 @@ +# RustProxy 0.34.0 — reverse-proxy hot-path candidate + +## What was changed + +1. **Routes:** `Snapshot.routes` and `fallbacks` now hold + `Arc` handles. The same compiled location is shared across + hostname aliases. `observe` resolves vhost/location once from the accepted + listener/TLS-state and stores a request-local handle. `forward_request` + consumes the same immutable snapshot/route; internal error fallbacks still + resolve the explicitly configured named location independently. +2. **Host matching:** wildcard candidate strings are generated lazily only + after an exact host failed, without changing longest-specific-suffix order. +3. **LB:** single-backend non-cookie pools use a direct admitted lease without + temporary candidate vectors or scheduler mutex. Multi-backend weighted + round-robin retains its ordering and mutex; least-connection/traffic, + adaptive, IP hash and cookie-affinity retain their behavior. IP hash avoids + repeated canonicalization/temporary `format!` strings for each candidate. +4. **Health:** a `healthy_cached` atomic allows checking healthy backends + without the health mutex; all state transitions remain inside the existing + state machine, including passive quarantine and half-open recovery. + Resolved active/passive per-member policies are held in shared immutable + `Arc` handles so a new request lease does not deep-clone health paths, + headers or optional host strings. +5. **Fallback/retry:** request body+HeaderMap replay templates are made only + for configured error fallbacks. Retry headers are cloned only for eligible + safe replay requests where another member or H2 graceful close makes retry + possible; writes, streaming bodies and WebSockets are not replayed. +6. **Telemetry:** three per-request mutex-backed scalar tags are now atomic; + their original event strings and `Option` log fields are decoded at + exchange completion. Request ID, method, host and path are moved into the + terminal log record instead of deep-cloned in Drop; the traffic sample + still receives its own required copies. Logs avoid a broadcast clone if + there are no subscribers, preserving history, sink and counters. + +## Preserved boundaries + +All vhost, ACL, GeoIP, Basic Auth, Tinyauth, WebSocket, retry, cache, +transform, compression, log, metrics, `sendfile`, PHP-FPM, HTTPS/H2/H3 and +admin panel configuration paths are intended to remain compatible. Defaults +and SQLite schema are unchanged; no new performance toggle was added. The +optimization reuses route objects within one immutable snapshot but does not +allow stale snapshot cache fills (existing revision ticket barrier remains). +Sticky cookies deliberately retain the general selection path, and upstream +request-count recycling is unchanged (default 25,000 per generation). + +## Locally executed checks vs required checks + +- **Executed locally:** scoped-cache/sendfile/hot-path *source-only* guards, + Python syntax, SQLite migration and upgrade preservation tests, selected JS + panel scripts and simulated editor tests. These do **not** prove Rust builds. +- **Prepared, NOT executed:** Rust unit tests for shared routing, single-member + LB capacity/health, passive ejection/recovery, atomic log fields, retained + sendfile, cache and protocol behavior. +- **Unmeasured:** 0.34.0 requests/sec, latency, CPU cost, memory and nginx gap. + Do not infer a throughput gain from fewer source-code locks/allocations. + +## Build and acceptance + +Run on the intended Linux builder with exactly the same compiler, dependencies, +release flags and workload as the 0.33.8 reference: + +```bash +./dev.sh doctor +./dev.sh check +CARGO_INCREMENTAL=0 ./dev.sh test +CARGO_INCREMENTAL=0 cargo build --release --locked +python3 scripts/tests/proxy_hotpath_034_static_test.py +python3 scripts/tests/cache_scope_034_static_test.py +python3 scripts/tests/sendfile_034_static_test.py +python3 scripts/tests/dashboard_presets_sql_test.py +python3 scripts/tests/upgrade_performance_test.py +``` + +Next run `scripts/performance/regression.py`, `lb_regression.py`, +`transport_regression.py` and `concurrency_regression.py` against both binary +versions. After functional acceptance, run the paired +`scripts/performance/benchmark.py` suite against the unmodified baseline, +this candidate and nginx; use the instructions in +`scripts/performance/README.md` including separate CPU affinities. + +Measure RPS/p95/p99, CPU microseconds per request, RSS, FDs, pool rotations, +health transitions, cache HIT ratio and logger drops. For each payload report +`(0.34.0 RPS / 0.33.8 RPS - 1)*100` and `0.34.0 RPS/nginx RPS*100` from real +medians. A source-only revision is **not production-accepted**. diff --git a/docs/README.md b/docs/README.md index 8a15786..42bfdea 100644 --- a/docs/README.md +++ b/docs/README.md @@ -42,6 +42,7 @@ This is the canonical index for the current project documentation. The authentic - [MOCK_SERVER](MOCK_SERVER.md) - local Python mock origin used by development/integration checks. - [MOCK_AUTH](MOCK_AUTH.md) - local fake Tinyauth peer for development only. - [MEMORY_STRESS](MEMORY_STRESS.md) - sustained-memory audit, offline load simulations, real RustProxy staging stress and RSS measurement procedure. +- [TEST_REPORT_0.34.0](TEST_REPORT_0.34.0.md) - 0.34.0 source-stage test evidence, simulated cache retention and required acceptance gates. ## Technical documentation diff --git a/docs/TEST_REPORT_0.34.0.md b/docs/TEST_REPORT_0.34.0.md new file mode 100644 index 0000000..00b1b04 --- /dev/null +++ b/docs/TEST_REPORT_0.34.0.md @@ -0,0 +1,173 @@ +# RustProxy 0.34.0 verification report + +## Acceptance status + +**Source candidate, not verified production release.** Rust (`cargo`, `rustc`) is +not installed in this execution environment, and the runtime has no access to +its dependency registry. Consequently the modified Rust source was **not +compiled**, the new Rust tests were **not executed**, and no 0.34.0 RustProxy +binary or live throughput figures are supplied. Source inspection and simulated +UI/cache tests do not close the runtime acceptance gate. + +## Later 0.34.0 reverse-proxy optimization revision + +The refreshed source archive additionally reuses immutable route handles, +limits lazy host wildcard allocation, fast-paths one-backend non-cookie LB, +atomically caches healthy state and exchange scalar log fields, shares +compiled per-member health policies across leases, and avoids +unneeded fallback/retry header clones and empty-subscriber broadcast clones. +The existing cache/sendfile work and all panel controls remain. Added Rust +regression cases and the source-only hot-path guard are **not a substitute** +for compilation and live HTTP, health, capacity, sticky, and H2 retry tests. +No RPS improvement, corrected nginx gap, or full functional parity has been +measured for this new revision. Details: `docs/PROXY_HOTPATH_0.34.0.md`. + +## Changes and correctness boundaries + +- Operational per-vhost saves (upstream keepalive/recycling, connection limits, + load-balancing/health policy, timeouts, request gate and diagnostics) retain + HOT and COLD entries. Backend origin, routing, content, cache and transform + changes retain conservative invalidation. Vhost-specific changes purge only + that vhost; global cache, transform, forwarding, headers and Web Service or + PHP-FPM profile changes may conservatively purge all tiers. +- Scope-specific cold-cache records persist an origin/content compatibility + fingerprint. COLD startup rejects stale scope records and deletes their cache + artifacts. Legacy COLD metadata defaults its fingerprint to zero; production + vhosts have a nonzero SHA-derived fingerprint. A one-time cold refill can + occur on the 0.33.8 -> 0.34.0 upgrade because the global identity changed. +- A separate administrative configuration revision acts as a cache-ticket + publication barrier: a request bound to a pre-save routing snapshot cannot + create a new cache ticket after the save. The data revision stays stable for + operational-only saves, so previously cached objects remain available. +- Reverse-proxy response-body byte metering captures the backend lease once at + wrapper creation instead of acquiring a backend-selection mutex per DATA + frame. Request-body accounting, error paths and exchange ownership remain. +- An **opt-in `sendfile: auto`** setting is wired through Web Service serde, + compiled config, the Delivery UI and documented API. The Linux HTTP/1.1 + TCP transport adapter calls `libc::sendfile` for eligible static files; + cache-enabled, ranged, compressed, transformed and TLS/H2/H3 paths retain + existing streaming. It is **disabled by default** and must pass native + compilation, correctness, transport and load tests before use. No persisted + SQLite schema migration or LB UI change is required. + +## Local verification + +| Check | Outcome | Coverage / caveat | +|---|---|---| +| `./dev.sh doctor` | PASS | Project source layout and tool inventory | +| SQL migration/preset test | PASS | All ten migrations re-applied, preset access and limits | +| Upgrade test | PASS | Stop/lock guards, SQLite/.env/language preservation, symlink rejection | +| Python compileall | PASS | Python syntax only | +| Vhost LB/Cache Chromium test | PASS | Simulated browser quick-view, 320/375/768/1440px | +| Other Chromium scripts | 15 PASS | Simulated API/CSS/JS, not a live Rust server | +| Sidebar navigation Chromium test | INCOMPLETE | Execution time budget; not counted as a pass | +| Three Node dashboard scripts | PASS | Simulated UI logic | +| `./dev.sh check` and `./dev.sh test` | BLOCKED | `cargo`/`rustc` unavailable | +| Rust unit/integration tests (including new scoped COLD regression) | NOT RUN | Requires Rust toolchain | +| Live HTTP/protocol/LB regressions | NOT RUN | Requires built 0.34.0 binary | +| 0.33.8 / 0.34.0 / nginx paired RPS benchmarks | NOT RUN | No verified 0.34.0 binary | +| Zero-copy sendfile WWW setting | SOURCE IMPLEMENTED / NOT RUNTIME VERIFIED | Linux plain H1 static GET only, `off` by default | +| Sendfile editor Chromium save/reload | PASS | 375/1440px, simulated admin API, off -> auto -> off | +| Sendfile Rust adapter + fallback test code | ADDED / NOT RUN | TCP marker replacement, persistent socket, HEAD, range, conditional; compiler unavailable | +| Amdahl sendfile/nginx sensitivity study | PASS MODEL | No runtime evidence for simulated improvements | + +The 0.33.8 report in the supplied evidence archive recorded **706 passing +Rust tests**, **152 HTTP assertions**, **111 transport assertions** and +**17 LB assertions**. Those are *prior-version* results and are **not evidence +that 0.34.0 passes**. This report never transfers previous pass counts. + +## Synthetic cache-retention scenario (not an RPS measurement) + +Run: + +```bash +python3 scripts/performance/simulate_cache_034.py +``` + +Assume 20 vhosts, 100 HOT + 100 COLD objects each, all public/cacheable with +infinite TTL and adequate cache capacity; 1,000 configuration saves (800 +operational-only, 200 content changes to one vhost), followed by 500 uniformly +random GETs per save. A miss immediately reloads an object into its original +tier. No networking, filesystem, CPU timing, origin delay or concurrency is +modeled. Results with seed 3400: + +| Synthetic metric | Old global purge | 0.34.0 scoped retention | +|---|---:|---:| +| Object invalidations | 473,540 | 37,300 | +| GET misses | 470,006 | 37,069 | +| GET hits | 29,994 | 462,931 | +| HIT ratio | 5.999% | 92.586% | + +The modeled change yields **92.123% fewer invalidations**, **92.113% fewer +misses**, and **+86.587 percentage points** of synthetic cache HIT ratio. +**Measured 0.34.0 requests/s gain: unknown.** These values must not be +presented as a real speedup or a projection against nginx. + +## Previously measured comparison (0.33.8, not this candidate) + +From the attached 0.33.8 evidence, five paired benchmark runs with cache OFF, +median RPS: + +| Mode / payload | 0.33.8 RPS | nginx RPS | 0.33.8 / nginx | +|---|---:|---:|---:| +| Reverse proxy 1,040 B | 29,334.12 | 170,610.16 | 17.19% | +| Reverse proxy 64 KiB | 20,735.07 | 35,393.29 | 58.58% | +| Reverse proxy 1 MiB | 4,461.19 | 2,730.23 | 163.40% | +| WWW 1,040 B | 24,906.20 | 351,605.13 | 7.08% | +| WWW 64 KiB | 20,856.67 | 203,709.91 | 10.24% | +| WWW 1 MiB | 2,833.71 | 12,611.49 | 22.47% | + +The 0.33.8 1 MiB proxy path was **2.77% slower than rebuilt 0.33.7**. +Removing a per-response-frame mutex is a performance candidate but is **not** +a measured correction of that regression. No throughput gains are attributed +here to the new source changes. + +## Hypothetical 0.34.0/nginx gap sensitivity (NOT MEASURED) + +Run `python3 scripts/performance/simulate_nginx_gap_034.py`. Using the **real +0.33.8 measured medians** above, assume zero-copy removes 50% of the +read/copy part of per-request cost; that part hypothetically accounts for +20/40/60% of *total* cost for eligible static files. Model **1/3/5%** total +service-time savings from removed proxy-frame mutexes, independently. +Amdahl: `RPS_sim = RPS_0.33.8 / (1 - hypothetical_fraction_saved)`. The +result is a sensitivity calculation, **not a prediction or 0.34.0 benchmark**. + +| Workload | Measured 0.33.8/nginx | Modeled low | Modeled middle | Modeled high | +|---|---:|---:|---:|---:| +| Proxy 1040 B | 17.19% | 17.37% | 17.73% | 18.10% | +| Proxy 64 KiB | 58.58% | 59.18% | 60.40% | 61.67% | +| Proxy 1 MiB | 163.40% | 165.05% | 168.45% | 172.00% | +| WWW 1040 B | 7.08% | 7.08% | 7.08% | 7.08% | +| WWW 64 KiB | 10.24% | 11.38% | 12.80% | 14.63% | +| WWW 1 MiB | 22.47% | 24.97% | 28.09% | 32.10% | + +These ratios apply only to eligible Linux plain HTTP/1.1 static workloads +with cache OFF. TLS, HTTP/2, HTTP/3, ranged, compressed and object-cached +responses are **not** accelerated by this bridge. Bandwidth, contention, +queueing, realistic filesystem and actual CPU profiles are not modeled. +Even the favorable modeled 1 MiB case remains ~3.1x slower than nginx WWW; +small WWW remains ~14.1x slower. Proxy 1 MiB already exceeds nginx in the +**prior baseline**, not as a demonstrated new 0.34.0 improvement. + +## Required rollout gates + +1. On a trusted Linux build worker with the pinned Rust toolchain, run + `./dev.sh check`, `CARGO_INCREMENTAL=0 ./dev.sh test`, and a locked release + build. Resolve any compiler/test issues before a deployment. +2. Follow `scripts/performance/README.md` to run `regression.py`, + `transport_regression.py`, `lb_regression.py`, and + `concurrency_regression.py` against verified 0.33.8 and 0.34.0 binaries. +3. Exercise administration saves during simultaneous requests: save keepalive + values and confirm unchanged HOT/COLD entries and hashes; change one backend + origin and confirm only its scope is purged, both before/after restart. Check + that old snapshots cannot recreate invalidated objects. +4. Run the five-repeat, 15-second paired benchmark against 0.33.8, 0.34.0 and + nginx on the *same host and affinity*, including large proxy responses, CPU, + RSS, resource limits and response correctness. +5. Compile the source-level Linux `sendfile` bridge and validate on a running + RustProxy process: plain HTTP/1 sendfile path, headers/body exactness, + keepalive/pipelining, slow clients, cancellation, HEAD, range, cache, + compression, TLS/H2/H3 fallback, symlink security and observability. Do + **not** activate Auto on production before that gate passes. +6. Complete a mixed-traffic soak and staged rollback rehearsal. Until then, + do not describe 0.34.0 as production-accepted. diff --git a/docs/WEB_SERVER.md b/docs/WEB_SERVER.md index 064ab05..cebb2f9 100644 --- a/docs/WEB_SERVER.md +++ b/docs/WEB_SERVER.md @@ -99,12 +99,45 @@ The Web Service editor is split into stable configuration tabs so later features - **.htaccess** - bounded migration-oriented Apache compatibility, recursive scan and strict-mode behavior. - **MIME** - per-extension MIME overrides and fallback MIME type. - **Cache & headers** - default `Cache-Control`, ordered cache rules and fixed response headers. -- **Delivery** - precompressed `.br`/`.gz` sidecars, forced-download extensions and Range/streaming information. +- **Delivery** - opt-in Linux `sendfile`, precompressed `.br`/`.gz` sidecars, forced-download extensions and Range/streaming information. - **PHP-FPM** - attach a shared PHP-FPM profile through the selector, open the profile-management modal to expand/edit/create profiles, or choose local configuration for all FastCGI tuning options. - **Security** - symlink policy, request/response hardening summary and filesystem-confinement notes. After the Web Service is saved, attach it under **Vhosts** by selecting `Web Server` and the service ID for the main source or a custom location. +### Optional native Linux sendfile (0.34.0 source candidate) + +Set **Web Server -> Edit service -> Delivery -> Linux sendfile** to **Auto** +(or `web_services[].sendfile: "auto"`). The default is `"off"` for full +backward compatibility. This option changes only transport; switching it +never invalidates the HOT or COLD object cache. No database migration is needed. + +**Platforms other than Linux (FreeBSD, OpenBSD, NetBSD, DragonFly BSD, macOS and Windows):** `sendfile: "auto"` is accepted as configuration but does **not** activate kernel zero-copy in RustProxy 0.34.0. The Linux-specific bridge is compiled only on Linux; on other targets ordinary Tokio `ReaderStream`/Hyper streaming serves the exact file content, including HEAD, ranges, validators, PHP and existing authorization/cache policies. `"off"` and `"auto"` behave the same on those targets. This describes the `sendfile` feature only and is **not** a claim that the entire application currently passes a cross-platform compile/test suite. FreeBSD/macOS native `sendfile` and Windows `TransmitFile` require separate implementations and runtime tests; Linux `libc::sendfile` cannot be reused directly. + +The kernel `sendfile(2)` bridge is selected **only** if all conditions hold: + +- Linux, plain TCP **HTTP/1.1** (no TLS, HTTP/2, HTTP/3 or QUIC); +- static **GET** of an already-constrained file, full **200 OK** response; +- file at least **64 KiB**, exact `Content-Length`, no `Range`/`If-Range`; +- no negotiated compression/precompressed variant, response transform or + object cache participation, and no rewritten content/encoding headers. + +The file descriptor is duplicated from the already-authorized confined open +file (not re-resolved by path), then attached to a bounded HTTP/1.1 TCP +connection bridge. The bridge sends the physical file bytes using Linux +`sendfile(2)` and never sends the internal marker bytes to the client. If +conditions are not met, RustProxy uses the existing stream/counters unchanged. +Only new cleartext connections pick up a newly enabled adapter; this option +does not change the listener protocol configuration. + +**Release acceptance:** This source candidate has not been compiled or +tested against a running 0.34.0 RustProxy binary in the current environment. +The included Rust unit/integration test and real-network test cases must pass +on a Linux build worker before enabling in production. Sendfile cannot by +itself close the small-response gap to nginx. See +`docs/TEST_REPORT_0.34.0.md` and +`scripts/performance/simulate_nginx_gap_034.py`. + ### Intercept application HTTP errors (0.29.7) Open **Vhosts → Edit vhost → Pages** (not the Web Service PHP tab), enable diff --git a/langs/en_US.json b/langs/en_US.json index 68c8093..0ed8d10 100644 --- a/langs/en_US.json +++ b/langs/en_US.json @@ -1903,6 +1903,9 @@ "webServiceRewritesHelp": "One regex rule per line. Use pattern => /target for an internal rewrite or pattern => 301:/target for a redirect.", "webServiceRewritesInvalid": "Rewrite rules must use pattern => /target or pattern => 301:/target (301/302/307/308).", "webServiceCompression": "Dynamic compression", + "webServiceSendfile": "Linux sendfile (zero-copy)", + "webServiceSendfileAuto": "Auto (eligible plain HTTP/1.1)", + "webServiceSendfileHelp": "Opt-in for static GET files >=64 KiB on Linux plain HTTP/1.1 with cache and transforms bypassed and no negotiated compression. TLS, HTTP/2, HTTP/3, ranges and sidecars keep streaming. Does not clear cache.", "webServiceCompressionHelp": "Inherits the vhost/global policy by default. Individual codecs can be filtered here; globally disabled codecs remain unavailable and minimum size stays global.", "webServicePrecompressed": "Serve precompressed sidecars", "webServicePrecompressedBr": "Brotli (.br)", diff --git a/langs/pl_pl.json b/langs/pl_pl.json index af4a849..d0cd46a 100644 --- a/langs/pl_pl.json +++ b/langs/pl_pl.json @@ -1903,6 +1903,9 @@ "webServiceRewritesHelp": "Jedna reguła regex na linię. Użyj wzorzec => /cel dla rewrite wewnętrznego albo wzorzec => 301:/cel dla przekierowania.", "webServiceRewritesInvalid": "Reguły rewrite muszą mieć format wzorzec => /cel albo wzorzec => 301:/cel (301/302/307/308).", "webServiceCompression": "Kompresja dynamiczna", + "webServiceSendfile": "Linux sendfile (zero-copy)", + "webServiceSendfileAuto": "Auto (HTTP/1.1 bez TLS)", + "webServiceSendfileHelp": "Dla statycznych plików GET od 64 KiB w Linuksie przez HTTP/1.1 bez TLS, przy wyłączonym cache, transformacjach i bez kompresji. TLS, HTTP/2, HTTP/3, Range i pliki skompresowane korzystają ze standardowego strumieniowania. Zmiana opcji nie czyści cache.", "webServiceCompressionHelp": "Domyślnie dziedziczy politykę vhosta/globalną. Tutaj można ograniczyć poszczególne kodeki; kodeki wyłączone globalnie pozostają niedostępne, a minimalny rozmiar pozostaje globalny.", "webServicePrecompressed": "Serwuj gotowe pliki skompresowane", "webServicePrecompressedBr": "Brotli (.br)", diff --git a/scripts/performance/README.md b/scripts/performance/README.md index 17b62a1..55e98bd 100644 --- a/scripts/performance/README.md +++ b/scripts/performance/README.md @@ -6,6 +6,26 @@ controllable test **upstream**, used to verify streaming, non-replayable writes, headers, cache policy and WebSocket frames. Performance measurements use nginx as the shared upstream instead of Python. +## Reverse-proxy optimization revision 0.34.0 + +After compiling the refreshed candidate, measure the same unmodified 0.33.8 +binary and nginx reference as before, without turning off logging, request +IDs, ACL, cache-policy enforcement or LB health. Profile request-size 1,040 B +first. Before RPS runs execute full proxy, LB, transport and concurrency +regressions (including single-member capacity, recovery, sticky cookies, +replays on safe methods, rejected non-replayable writes and H2 connection +shutdown). Treat `--max-idle` tuning and recycles as separately labelled +experiments rather than mixing them into the primary comparison. There are no +new measured RPS figures in the source candidate. + +## Offline cache-retention model for 0.34.0 + +`python3 scripts/performance/simulate_cache_034.py` compares cache invalidation +policies under a deterministic synthetic sequence of vhost saves and uniform +cacheable GETs. The model contains **no RustProxy server**, no timing and no +network traffic; its percentages are **not measured RPS gains**. See +`docs/TEST_REPORT_0.34.0.md` for assumptions and the live acceptance gap. + ## Requirements - Linux with readable `/proc`, Python 3.10+, executable RustProxy binaries. @@ -274,3 +294,16 @@ are not presented as proven server-side cancellations. Memory trend is descriptive: switching caching and allocator retention affect RSS. This is a 30-minute resource gate, not a 24-hour leak proof. A short `--exploratory --duration ...` sanity run stays explicitly outside that gate. + +## 0.34.0 hypothesis-only sendfile/nginx gap simulator + +```bash +python3 scripts/performance/simulate_nginx_gap_034.py --output /tmp/nginx-gap.json +``` + +This is **not** a benchmark of RustProxy 0.34.0. It reproduces measured +0.33.8/nginx medians with cache OFF and applies explicitly hypothetical +Amdahl service-time reductions (WWW and reverse proxy separately). A real +RustProxy binary is required to validate the code path, detect regressions +and measure latency/RPS against nginx under identical conditions. + diff --git a/scripts/performance/simulate_cache_034.py b/scripts/performance/simulate_cache_034.py new file mode 100644 index 0000000..0c11fac --- /dev/null +++ b/scripts/performance/simulate_cache_034.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +"""Deterministic analytical/synthetic cache-retention scenario, not a RustProxy load test. + +Model: 20 vhosts with 100 HOT + 100 COLD uniformly requested objects each. +A new configuration is saved 1,000 times. Eighty percent of saves edit only +keepalive/LB operational controls, and 20% edit content policy on one vhost. +Every save is followed by 500 uniform GETs; every object is public/cacheable and +has infinite TTL. A miss immediately inserts the object in its original tier. +Capacity is assumed adequate and there is no origin latency or concurrent IO. +The baseline flushes both tiers globally on every save, as in 0.33.8. +The candidate changes only the affected vhost after content edits. +""" +import argparse +import json +import random + + +def simulate(vhosts=20, objects_per_tier=100, saves=1000, requests_per_save=500, seed=3400): + if min(vhosts, objects_per_tier, saves, requests_per_save) < 1: + raise ValueError("all counts must be positive") + rng = random.Random(seed) + objects = vhosts * objects_per_tier * 2 + old = set(range(objects)) + new = set(range(objects)) + old_hits = new_hits = old_misses = new_misses = 0 + old_evicted = new_evicted = 0 + edits_operational = edits_content = 0 + for step in range(saves): + changed_vhost = rng.randrange(vhosts) + content_edit = step % 5 == 0 + if content_edit: + edits_content += 1 + else: + edits_operational += 1 + old_evicted += len(old) + old.clear() + if content_edit: + low = changed_vhost * objects_per_tier * 2 + high = low + objects_per_tier * 2 + removed = set(range(low, high)) & new + new_evicted += len(removed) + new.difference_update(removed) + for _ in range(requests_per_save): + object_id = rng.randrange(objects) + if object_id in old: + old_hits += 1 + else: + old_misses += 1 + old.add(object_id) + if object_id in new: + new_hits += 1 + else: + new_misses += 1 + new.add(object_id) + total = saves * requests_per_save + assert old_hits + old_misses == total + assert new_hits + new_misses == total + assert new_evicted <= old_evicted + assert new_misses <= old_misses + return { + "scenario": { + "vhosts": vhosts, + "hot_objects_per_vhost": objects_per_tier, + "cold_objects_per_vhost": objects_per_tier, + "saves": saves, + "operational_saves": edits_operational, + "single_vhost_content_saves": edits_content, + "gets_per_save": requests_per_save, + "total_gets": total, + "seed": seed, + "assumptions": "All objects public/cacheable, infinite TTL, instantly refilled, uniform GETs; no bandwidth/CPU/origin timings", + }, + "legacy_global_purge": { + "invalidated_objects": old_evicted, + "misses": old_misses, + "hits": old_hits, + "hit_ratio_percent": round(100 * old_hits / total, 3), + }, + "scoped_cache": { + "invalidated_objects": new_evicted, + "misses": new_misses, + "hits": new_hits, + "hit_ratio_percent": round(100 * new_hits / total, 3), + }, + "improvement": { + "fewer_invalidations_percent": round(100 * (1 - new_evicted / old_evicted), 3) if old_evicted else 0, + "fewer_cache_misses_percent": round(100 * (1 - new_misses / old_misses), 3) if old_misses else 0, + "hit_ratio_gain_percentage_points": round(100 * (new_hits - old_hits) / total, 3), + "observed_rps_gain_percent": None, + }, + } + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--vhosts", type=int, default=20) + parser.add_argument("--objects-per-tier", type=int, default=100) + parser.add_argument("--saves", type=int, default=1000) + parser.add_argument("--gets-per-save", type=int, default=500) + parser.add_argument("--seed", type=int, default=3400) + parser.add_argument("--output", help="optional JSON results file") + args = parser.parse_args() + result = simulate(args.vhosts, args.objects_per_tier, args.saves, args.gets_per_save, args.seed) + encoded = json.dumps(result, indent=2, sort_keys=True) + if args.output: + from pathlib import Path + Path(args.output).write_text(encoded + "\n") + print(encoded) + + +if __name__ == "__main__": + main() diff --git a/scripts/performance/simulate_nginx_gap_034.py b/scripts/performance/simulate_nginx_gap_034.py new file mode 100644 index 0000000..6e7ab4b --- /dev/null +++ b/scripts/performance/simulate_nginx_gap_034.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Amdahl sensitivity study only: 0.33.8 measured baseline, 0.34.0 not measured. + +No synthetic RPS is described as benchmarked RustProxy throughput. Amdahl uses +RPS_new = RPS_old / (1 - eligible_cost_fraction * fraction_eliminated). +""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path + +# Verified medians reproduced from the supplied 0.33.8 evidence. Cache OFF. +BASELINE = [ + ("reverse_proxy", 1040, 29334.12, 170610.16, False), + ("reverse_proxy", 65536, 20735.07, 35393.29, False), + ("reverse_proxy", 1048576, 4461.19, 2730.23, False), + ("www", 1040, 24906.20, 351605.13, False), + ("www", 65536, 20856.67, 203709.91, True), + ("www", 1048576, 2833.71, 12611.49, True), +] + +# WWW scenarios: share of per-request service time due to file copy/read; +# assume sendfile removes HALF of that part, not the full service time. +WWW_SHARES = {"low": 0.20, "middle": 0.40, "high": 0.60} +COPY_COST_REMOVED = 0.50 +# Proxy independent optimization: hypothetical reduction in *total* request +# service time, modeling removal of redundant response-frame mutex, nothing else. +PROXY_TIME_SAVED = {"low": 0.01, "middle": 0.03, "high": 0.05} + + +def run() -> dict: + items = [] + for kind, payload_bytes, rust_rps, nginx_rps, eligible in BASELINE: + baseline_ratio = rust_rps / nginx_rps + row = { + "kind": kind, + "bytes": payload_bytes, + "baseline_version": "0.33.8", + "baseline_rust_rps": rust_rps, + "baseline_nginx_rps": nginx_rps, + "baseline_of_nginx_pct": round(100 * baseline_ratio, 2), + "factor_to_match_nginx": round(nginx_rps / rust_rps, 2), + "sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": eligible, + "scenarios": {}, + } + for level in ("low", "middle", "high"): + if kind == "www" and eligible: + saved_fraction = WWW_SHARES[level] * COPY_COST_REMOVED + elif kind == "reverse_proxy": + saved_fraction = PROXY_TIME_SAVED[level] + else: + saved_fraction = 0.0 + simulated_rps = rust_rps / (1.0 - saved_fraction) + row["scenarios"][level] = { + "modeled_total_time_saved_pct": round(100 * saved_fraction, 2), + "simulated_rps": round(simulated_rps), + "simulated_rps_gain_pct": round(100 * (simulated_rps / rust_rps - 1), 2), + "simulated_of_nginx_pct": round(100 * simulated_rps / nginx_rps, 2), + "simulated_remaining_gap_to_nginx_pct_points": round( + 100 * (1 - simulated_rps / nginx_rps), 2 + ), + } + items.append(row) + return { + "kind": "HYPOTHETICAL_AMDAHL_SENSITIVITY_NOT_A_BENCHMARK", + "comparison": "Existing 0.33.8 measurements (5 runs, cache off) vs measured nginx", + "not_measured": "0.34.0 server performance and sendfile syscall/runtime correctness", + "conditions": [ + "Identical CPU and network assumptions to supplied 0.33.8 baseline", + "WWW zero-copy is limited to Linux, cleartext HTTP/1.1, GET >=64KiB, no cache, range, transform, compression or TLS", + "WWW low/middle/high: file-copy contributes 20%/40%/60% of total request time; modeled sendfile eliminates 50% of that component", + "Reverse proxy low/middle/high: removing response-frame mutex saves a hypothetical 1%/3%/5% of total service time", + "These fractions are chosen sensitivity parameters; not measured or inferred from profiling", + "No change is assumed for WWW 1040B or out-of-scope protocol paths", + "No network saturation, TLS record processing, queueing, origin latency, filesystem cache misses, CPU contention or concurrency effects modeled", + "No additional speedup due to cache-retention changes is assumed", + ], + "formula": "RPS_sim = RPS_0.33.8 / (1 - fraction_of_total_service_time_eliminated)", + "entries": items, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", type=Path, help="write JSON to this file") + args = parser.parse_args() + payload = json.dumps(run(), indent=2, ensure_ascii=False) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(payload) + print(f"wrote {args.output}") + else: + print(payload) + + +if __name__ == "__main__": + main() diff --git a/scripts/tests/cache_scope_034_static_test.py b/scripts/tests/cache_scope_034_static_test.py new file mode 100644 index 0000000..35fc424 --- /dev/null +++ b/scripts/tests/cache_scope_034_static_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +"""0.34.0 source-wiring checks only; do not mistake these for Rust tests.""" +from pathlib import Path +import tomllib + +root = Path(__file__).resolve().parents[2] +read = lambda path: (root / path).read_text() +assert tomllib.loads(read("Cargo.toml"))["package"]["version"] == "0.34.0" +assert 'version = "0.34.0"' in read("Cargo.lock") +app = read("src/runtime/updates.rs") +assert "CacheInvalidation::Scopes(scopes)" in app +assert "this.cache.purge_scope(&scope)" in app +assert "this.cache.set_control_revision(envelope.revision)" in app +assert app.index("this.cache.set_control_revision(envelope.revision)") < app.index("match invalidation") +source = read("src/runtime/types.rs") +assert "view.load_balancing = Default::default();" in source +assert "member.max_concurrent_requests = 0;" in source +assert "fn vhost_cache_revision" in source +assert "&web_services" in source +assert "service.sendfile = crate::web_server::SendfileMode::Off;" in source +assert "&config.php_fpm_profiles" in source +for filename in ("src/proxy/forward/route_cache.rs", "src/proxy/forward/web_source.rs"): + assert "ticket_scoped_for_config(" in read(filename) + assert "snapshot.envelope.revision" in read(filename) +assert "expected_control_revision" in read("src/cache/tickets.rs") +assert "file.scope_revision != expected_scope_revision" in read("src/cache/cold/startup.rs") +assert "scope_revision: draft.scope_revision" in read("src/cache/cold/worker.rs") +body = read("src/proxy/body.rs") +assert "response_backend: Option>" in body +assert "lease.bytes(count, false);" in body +assert "exchange.backend_bytes(count, true)" in body +assert "cold_scope_fingerprint_preserves_unmodified_vhost" in read("src/cache/tests/cold_tier_tests.rs") +assert "stale_snapshot_cannot_issue_cache_ticket" in read("src/cache/tests/scope_tests.rs") +for file in (root / 'src').rglob('*.rs'): + if '/tests/' in file.as_posix() or file.name == 'tests.rs': + continue + if len(file.read_text().splitlines()) > 500: + raise AssertionError(f"production Rust source exceeds 500 lines: {file}") +print('PASS source wiring for scoped invalidation, cold fingerprints, stale-snapshot barrier, response accounting and <=500-line modules (STATIC ONLY)') diff --git a/scripts/tests/proxy_hotpath_034_static_test.py b/scripts/tests/proxy_hotpath_034_static_test.py new file mode 100644 index 0000000..afdd690 --- /dev/null +++ b/scripts/tests/proxy_hotpath_034_static_test.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Source-only guardrails for the uncompiled 0.34.0 hot-path candidate.""" +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +def src(path): + return (ROOT / path).read_text() + +snapshot = src("src/config/snapshot.rs") +assert "fn host_patterns(host: &str) -> impl Iterator" in snapshot +assert "pub fn select_on_arc(" in snapshot +assert "self.select_on_arc(host, path, local, tls).map(Arc::as_ref)" in snapshot +assert "Vec>" in src("src/config/model.rs") +assert "route: Option>" in src("src/proxy/types.rs") +assert "request.extensions_mut().insert(SelectedRouteContext" in src("src/proxy/upstream_helpers.rs") +assert "selection.route.as_deref()" in src("src/proxy/forward_entry.rs") +assert "let fallback_request = selected" in src("src/proxy/forward_entry.rs") +assert "filter(|route| route.error_fallback.is_some())" in src("src/proxy/forward_entry.rs") +assert "let Some(next_request) = replay_headers" in src("src/proxy/forward/upstream.rs") or "replay_headers.as_ref().and_then" in src("src/proxy/forward/upstream.rs") +assert "let replay_headers = ((pool_retry_possible || h2_replay_possible)" in src("src/proxy/forward/upstream.rs") +pool = src("src/balance/pool_core.rs") +assert "if self.members.len() == 1 && !self.cookie_affinity()" in pool +assert "let mut scheduling = lock(&self.scheduling);" in pool +assert "fn candidates_excluding" in pool +assert "health: member.health_settings.clone()" in pool +assert "health: selected.health_settings.clone()" in pool +assert "health_settings: Arc" in src("src/balance/health.rs") +assert "healthy_cached: AtomicBool" in src("src/balance/model.rs") +assert "self.healthy_cached.store(h.healthy, Ordering::Release)" in src("src/balance/health.rs") +exchange = src("src/proxy/body.rs") +for field in ("source", "cache_effective_enabled", "transform_effective_enabled"): + assert f"pub {field}: AtomicU8" in exchange + assert f"pub {field}: Mutex" not in exchange +assert "exchange.set_source(" in src("src/proxy/forward/route_cache.rs") +assert "exchange.set_cache_enabled(" in src("src/proxy/forward/web_source.rs") +assert "self.live.receiver_count() != 0" in src("src/logging/format.rs") +assert "fn route_handles_reuse_snapshot_ownership_without_cloning_compiled_routes" in src("src/config/tests.rs") +assert "fn single_backend_fast_path_observes_capacity_and_health_without_affinity_cookie" in src("src/balance/tests.rs") +assert "fn atomic_exchange_tags_keep_original_observability_fields" in src("src/proxy/body/tests.rs") +for path in (ROOT / "src").rglob("*.rs"): + if "/tests/" in path.as_posix() or path.name == "tests.rs" or path.is_relative_to(ROOT / "tests"): + continue + assert len(path.read_text().splitlines()) <= 500, path +assert 'version = "0.34.0"' in src("Cargo.toml") +assert "max_idle_per_host: 4" in src("src/balance/model.rs") +assert "pool_recycle_requests: 25_000" in src("src/balance/model.rs") +print("PASS 0.34.0 proxy hot-path source wiring, observability, preserved defaults and source-size limits (STATIC ONLY)") diff --git a/scripts/tests/sendfile_034_panel_browser_test.py b/scripts/tests/sendfile_034_panel_browser_test.py new file mode 100644 index 0000000..4c0a782 --- /dev/null +++ b/scripts/tests/sendfile_034_panel_browser_test.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Offline Chromium test: sendfile toggle serializes in real admin bundle. + +It uses simulated config and a mocked saveSection; never claims native +sendfile execution, Rust compilation or live server acceptance. +""" +from pathlib import Path +import json +import re +from playwright.sync_api import sync_playwright +from panel_html import render_admin_index + +ROOT = Path(__file__).resolve().parents[2] +ROUTER = (ROOT / "src/admin/router.rs").read_text() +start = re.search(r'fn admin_app_javascript\([^)]*\)[^{]*\{', ROUTER) +assert start +array = ROUTER[start.end():] +array = array[:re.search(r'\]\s*\.join\("\\n"\)', array).start()] +chunks = [] +for line in array.splitlines(): + line = line.strip() + match = re.search(r'include_str!\("([^"]+)"\)', line) + if match: + chunks.append((ROOT / "src/admin" / match.group(1)).read_text()) + elif line == 'admin_vhosts_javascript(),': + chunks.append('(() => {\n' + '\n'.join( + (ROOT / 'src/admin/web/vhosts' / (f + '.js')).read_text() + for f in ('core', 'protocol', 'health', 'rates', 'editor', 'render')) + '\n})();') + elif line.startswith('"') and line.endswith('",'): + chunks.append(json.loads(line[:-1])) + +with sync_playwright() as p: + browser = p.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox']) + for width in (375, 1440): + page = browser.new_page(viewport={'width': width, 'height': 950}) + page.set_default_timeout(4500) + errors = [] + page.on('pageerror', lambda err: errors.append(str(err))) + page.set_content(render_admin_index(ROOT)) + page.add_style_tag(content=(ROOT / 'src/admin/web/style.css').read_text()) + page.add_script_tag(content='\n'.join(chunks)) + page.evaluate('''() => { + envelope = {revision: 10, config: {web_services: [{ + id: 'site-A', name: 'Static Site', document_root: '/srv/static', + index: ['index.html'], php: {enabled: false} + }], vhosts: [{id: 'vhost-A', keepalive: {max_connections: 8}}], php_fpm_profiles: []}}; + currentPanel = 'web-server'; + document.documentElement.classList.remove('app-loading', 'app-startup-failed'); + document.getElementById('login').hidden = true; + document.getElementById('workspace').hidden = false; + document.getElementById('panel-web-server').hidden = false; + window.__saved = []; + saveSection = async (section, value) => { + window.__saved.push({section, value: structuredClone(value)}); + if (section === 'web-services') envelope.config.web_services = structuredClone(value); + return true; + }; + window.rustProxyWebServer.render(); + }''') + page.evaluate("document.querySelector('#web-service-list .entity-actions button').click()") + assert page.locator('#web-service-sendfile').input_value() == 'off' + page.evaluate("document.getElementById('web-service-sendfile').value = 'auto'") + page.evaluate('''() => { const form = document.getElementById('web-service-form'); if (!form.checkValidity()) throw new Error('invalid fields: ' + [...form.elements].filter(e=>!e.checkValidity()).map(e=>e.id).join(', ')); form.requestSubmit(); }''') + page.wait_for_function('window.__saved.length === 1') + result = page.evaluate('''() => ({ + saved: window.__saved[0], + cacheStillUnrelated: envelope.config.vhosts[0].keepalive.max_connections === 8, + closed: !document.getElementById('web-service-dialog').open + })''') + assert result['saved']['section'] == 'web-services', result + assert result['saved']['value'][0]['sendfile'] == 'auto', result + assert result['saved']['value'][0]['document_root'] == '/srv/static', result + assert result['cacheStillUnrelated'] and result['closed'], result + page.evaluate("document.querySelector('#web-service-list .entity-actions button').click()") + assert page.locator('#web-service-sendfile').input_value() == 'auto' + page.evaluate("document.getElementById('web-service-sendfile').value = 'off'") + page.evaluate('''() => { const form = document.getElementById('web-service-form'); if (!form.checkValidity()) throw new Error('invalid fields: ' + [...form.elements].filter(e=>!e.checkValidity()).map(e=>e.id).join(', ')); form.requestSubmit(); }''') + page.wait_for_function('window.__saved.length === 2') + assert page.evaluate('envelope.config.web_services[0].sendfile') == 'off' + assert not errors, errors + print(f'PASS simulated panel sendfile off -> auto -> off config save at {width}px') + browser.close() diff --git a/scripts/tests/sendfile_034_static_test.py b/scripts/tests/sendfile_034_static_test.py new file mode 100644 index 0000000..b56ed15 --- /dev/null +++ b/scripts/tests/sendfile_034_static_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""RustProxy 0.34.0 sendfile source-wiring assertions ONLY, not native tests.""" +from pathlib import Path +import json +import re + +root = Path(__file__).resolve().parents[2] +get = lambda path: (root / path).read_text() + +model = get('src/web_server/config/model.rs') +assert 'pub enum SendfileMode' in model and 'sendfile: SendfileMode::Off' in model +assert 'pub sendfile: SendfileMode' in model +assert 'sendfile: self.sendfile' in get('src/web_server/config/compile.rs') +assert 'pub sendfile: SendfileMode' in get('src/web_server/config/validation.rs') +assert 'service.sendfile = crate::web_server::SendfileMode::Off;' in get('src/runtime/types.rs') +source = get('src/proxy/forward/web_source.rs') +for guard in ('!enabled && identity_request', 'request_method == Method::GET', + 'response.status() == StatusCode::OK', 'content-encoding', + 'transfer-encoding', 'content-range', 'candidate.length', + 'http::Version::HTTP_11'): + assert guard in source, guard +assert 'content_encoding.is_none()' in get('src/web_server/response/file.rs') +assert 'single_range.is_none()' in get('src/web_server/response/file.rs') +assert 'opened.file.try_clone()' in get('src/web_server/response/file.rs') +bridge = get('src/server/sendfile.rs') +for item in ('libc::sendfile(', 'MAX_PENDING: usize = 8', 'MIN_SIZE: u64 = 64 * 1024', + 'registry.slots.is_empty()', 'native_sendfile_transfers_exact_file', + 'sendfile placeholder did not match', 'UnexpectedEof'): + assert item in bridge, item +assert 'pub(crate) mod sendfile' in get('src/server.rs') +assert 'SendfileIo::new(stream)' in get('src/server/tls_runtime.rs') +assert 'sendfile_slots' in get('src/server/http1_connection.rs') +js = get('src/admin/web/web-server.js') +html = get('src/admin/web/index.html') +assert '"web-service-sendfile"' in js and 'sendfile: "off"' in js +assert 'sendfile: $("web-service-sendfile").value' in js +assert html.count('id="web-service-sendfile"') == 1 +assert 'value="auto"' in html +for locale in ('en_US', 'pl_pl'): + keys = json.loads(get(f'langs/{locale}.json'))['translations'] + assert all(k in keys for k in ('webServiceSendfile', 'webServiceSendfileHelp', 'webServiceSendfileAuto')) +api = json.loads(get('src/admin/openapi.json')) +assert 'sendfile' in json.dumps(api) +print('PASS 0.34.0 sendfile source guards, default OFF, Linux syscall, UI, translations, API and scoped cache (STATIC ONLY)') diff --git a/src/admin/openapi.json b/src/admin/openapi.json index 6f5ce50..e580ce8 100644 --- a/src/admin/openapi.json +++ b/src/admin/openapi.json @@ -5415,6 +5415,12 @@ "type": "boolean", "default": false }, + "sendfile": { + "type": "string", + "enum": ["off", "auto"], + "default": "off", + "description": "Linux TCP HTTP/1.1 kernel sendfile for eligible unmodified static GET responses. Transport-only; off by default." + }, "mime_types": { "type": "object", "maxProperties": 64, diff --git a/src/admin/web/index.html b/src/admin/web/index.html index 1fbe8a8..72de14e 100644 --- a/src/admin/web/index.html +++ b/src/admin/web/index.html @@ -3567,6 +3567,11 @@ responses are never stored.