v0.34.0_1

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-17 20:34:13 +02:00
1 parent 9b3d6731cc
commit 00f29f92c8
68 files changed
+2599 -513

No files matched your search

+216
View File
@@ -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
}
}
}
]
}
+84
View File
@@ -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<CompiledRoute>` 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<bool>` 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**.
+1
View File
@@ -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
+173
View File
@@ -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.
+34 -1
View File
@@ -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