Files
RustProxy/docs/V0.22.2_AUDIT.md
T
2026-10-03 00:27:09 +01:00

4.9 KiB

RustProxy 0.22.2 specification audit

Scope

This audit covers the 0.22.2 delta over 0.22.1: replay-safe backend failover retry, explicit HTTP/2 upstream request handling, bounded per-backend request concurrency, richer upstream diagnostics, an expanded backend operations view, and public X.509 expiry metadata. The retained 0.22.1 RFC 8441/h2c-listener/tunnel work and 0.22.0 cache work remain historical evidence for retained functionality.

Requirement matrix

Requirement Source implementation Regression / verification
Retry only replay-safe methods proxy/mod.rs::retry_safe_method permits GET/HEAD/OPTIONS only proxy source regression; compiler gate open here
Never replay body-bearing writes retry requires an exact empty request body; POST/PUT/PATCH fail the method gate source regression + release checks
Retry another backend Pool::select_retry_wait(..., excluded) excludes attempted IDs failover_selection_never_reuses_an_attempted_backend
Bounded retry policy RetryPolicy.max_retries validates 0..3; bounded 5xx list balance policy regressions
Explicit H2 upstream explicit protocol=http2 uses HTTP/2 request versioning proxy regression + real Hyper h2c upstream test source
Per-backend request concurrency PoolMember.max_concurrent_requests, atomic full-exchange lease acquisition backend_concurrency_limit_queues_then_rejects_and_recovers
Bounded capacity queue / 503 queue_timeout_ms validates 0..60000; async selection waits in bounded intervals and returns SelectionFailure::Capacity capacity timeout regression
Reject vs queue-timeout observability capacity_rejections_total always counts final capacity rejection; queue_timeouts_total only counts an elapsed non-zero queue zero_capacity_queue_timeout_rejects_without_counting_a_queue_timeout
No false TCP connection claim limit is documented and named as concurrent proxy requests/exchanges, not max_connections TODO/roadmap explicitly keep socket-count limit open
Failure classification DNS, TLS, connect, response-header timeout, read/send timeout and generic I/O flow through Lease::error_detail source contract checks + detailed failure regression
Last failure backend view exposes class, bounded detail and timestamp detailed_backend_failures_are_exposed_without_losing_aggregate_counters
Health ejection/recovery timestamps health transitions store last ejected/recovered timestamps source contract checks
Backend operational view dashboard combines status, active/limit, requests/errors, latency, traffic, last failure and last recovery JS/language/source checks
X.509 validity metadata public leaf PEM parser extracts SAN, issuer, notBefore/notAfter and days remaining without reading keys X.509 time + generated-certificate regression sources
Expiry warning inventory reports ok, warning (<=30 days), expired, or unknown source/OpenAPI/UI checks
Renewal status in certificate view inventory flattens current renewal status, last check and last failure message OpenAPI/UI/source checks
Full downstream H2 -> RustProxy -> upstream H2 interoperability implementation path present OPEN: live/compiler-enabled E2E required

Capacity semantics

max_concurrent_requests is a full proxy-exchange permit. The permit remains held for the response body/tunnel lifetime through the existing backend lease, preventing a streaming response from releasing capacity after headers only. A value of 0 preserves the historical unlimited behavior. queue_timeout_ms=0 rejects immediately when no eligible backend has capacity; a positive value performs a bounded wait and then returns HTTP 503.

Pool readiness remains a health/availability signal rather than a transient-capacity signal. Backup members can receive traffic when eligible primary members are saturated. Sticky affinity is honored only when the matched backend has capacity; otherwise normal eligible selection can move the request to another backend.

A physical TCP connection/socket cap is deliberately not claimed in 0.22.2 because Hyper connection pooling requires a distinct transport-level mechanism.

Certificate safety

Certificate inventory parsing opens only the configured public certificate PEM. Private keys are not opened by the metadata path. The parser is deliberately narrow: it extracts the leaf issuer, RFC 5280 validity values and DNS SAN coverage while rustls remains responsible for certificate/key validation in serving paths.

Release gate

This packaging workspace has no cargo, rustc or rustfmt, and outbound DNS is unavailable, so Rust parsing/type checking, compilation, linking and execution cannot be claimed here. Rust-independent JavaScript/JSON/language/source/package/Go gates are run locally. A Rust-enabled release host must still run ./dev.sh check, ./dev.sh test, release build and live H1/H2/H3/H2-to-H2 acceptance before deployment.