fix(server): hyper knobs, decoy fidelity, cache + builder fixes (SRV-04..SRV-10)
- SRV-04: TokioTimer on h1+h2 builder, header_read_timeout 10s, h1 keep-alive on, h2 keep-alive 30s/10s; concurrency boundary documented - SRV-05: with_decoy rebuild keeps extra routes (clone, not take) - SRV-07: method_not_allowed_fallback serves the nginx-shaped 405 - SRV-08: UTF-8 percent-decoding, literal '+', tokio::fs syscalls - SRV-09: /openapi.json cached at construction, generic 500 body, to_openapi returns Result (expect removed) - SRV-10: ChannelsPolicy extension injection point on the WS upgrade; single token resolution via route ordering (WS layer before the router-wide auth route_layer) Verification: cargo test 265 passed; --all-features server::/to_openapi:: green; clippy + fmt clean on touched files (remaining tree noise is a parallel agent's in-flight from_mcp/from_wss/forward work)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: review-001-hyper-server-knobs
|
||||
name: Configure hyper timeouts + decoy/405/proxy-path fixes (SRV-04, SRV-07, SRV-08, SRV-09, SRV-10)
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: low
|
||||
@@ -44,13 +44,13 @@ and all land in `src/server/`:
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Hyper configured with timer + header read timeout; knob values documented
|
||||
- [ ] 405 responses carry the decoy `Server` header (test with `OPTIONS /search`)
|
||||
- [ ] `%C3%A9` and `a+b.html` resolve correctly in the decoy static server (tests); async-path syscalls gone
|
||||
- [ ] `/openapi.json` 500 body is generic; doc cached; `expect` removed
|
||||
- [ ] Second `.with_decoy` no longer silently drops extra routes (test)
|
||||
- [ ] SRV-10: policy injection point exists or doc corrected; single token resolution
|
||||
- [ ] `cargo test`, `cargo test --all-features`, `cargo clippy --all-targets -- -D warnings` pass
|
||||
- [x] Hyper configured with timer + header read timeout; knob values documented
|
||||
- [x] 405 responses carry the decoy `Server` header (test with `OPTIONS /search`)
|
||||
- [x] `%C3%A9` and `a+b.html` resolve correctly in the decoy static server (tests); async-path syscalls gone
|
||||
- [x] `/openapi.json` 500 body is generic; doc cached; `expect` removed
|
||||
- [x] Second `.with_decoy` no longer silently drops extra routes (test)
|
||||
- [x] SRV-10: policy injection point exists or doc corrected; single token resolution
|
||||
- [x] `cargo test`, `cargo test --all-features`, `cargo clippy --all-targets -- -D warnings` pass
|
||||
|
||||
## References
|
||||
|
||||
@@ -64,4 +64,54 @@ and all land in `src/server/`:
|
||||
|
||||
## Summary
|
||||
|
||||
> Filled on completion.
|
||||
Five server-core findings fixed in `src/server/` (+ two touchpoints):
|
||||
|
||||
- **SRV-04** (`server/adapter.rs`): `serve_io` now sets
|
||||
`TokioTimer` on **both** the h1 and h2 auto-builder sub-builders,
|
||||
`header_read_timeout(10 s)` (h1, bounds the slow-loris header-drip
|
||||
window), h1 `keep_alive(true)`, h2 `keep_alive_interval(30 s)` +
|
||||
`keep_alive_timeout(10 s)`. Verified against hyper 1.11 source: with
|
||||
no timer, `Time::check` warns and returns `None` — the default 30 s
|
||||
header timeout was a silent no-op. A module-doc section documents
|
||||
the knobs and the concurrency boundary (no cap in this crate; the
|
||||
accept loop is the consumer's — one stream per `handle` call).
|
||||
- **SRV-05** (`server/adapter.rs`): `with_decoy` rebuilds the router
|
||||
with `extra_routes.clone()` instead of `.take()` — a second builder
|
||||
call keeps custom routes (test: `second_with_decoy_keeps_extra_routes`).
|
||||
- **SRV-07** (`server/decoy.rs`, `server/adapter.rs`): wired
|
||||
`Router::method_not_allowed_fallback(decoy_method_not_allowed)` —
|
||||
method mismatches on registered paths (e.g. `OPTIONS /search`) get
|
||||
the nginx-shaped 405 with `Server: nginx` instead of axum's bare
|
||||
405 (axum 0.8 sets the default fallback on all previously registered
|
||||
MethodRouters; the `Allow` header is unaffected).
|
||||
- **SRV-08** (`server/decoy.rs`): `percent_decode` is now
|
||||
byte-accumulating + single UTF-8 validation (`%C3%A9` → one `é`), `+`
|
||||
is a literal (form-encoding, not path grammar), invalid escapes → `None`
|
||||
→ fake 404. The blocking `is_dir()`/`is_file()` syscalls moved to
|
||||
`tokio::fs::metadata`.
|
||||
- **SRV-09** (`server/adapter.rs`, `adapters/to_openapi.rs`): the
|
||||
serialized `/openapi.json` doc is built once at adapter construction
|
||||
(`CachedOpenAPIDoc`, threaded through `RouterState` + `FromRef`);
|
||||
the handler serves the cached bytes or a **generic** `internal server
|
||||
error` 500 (no serde internals on the wire). `to_openapi` now returns
|
||||
`Result<OpenAPISpec, AdapterError>` — the unguarded
|
||||
`.expect("to_openapi always emits…")` is gone (project convention).
|
||||
- **SRV-10** (`websocket/upgrade.rs`, `server/adapter.rs`): added the
|
||||
injection point — `ChannelsPolicy(Arc<dyn ChannelLifecyclePolicy>)`
|
||||
request extension read by `ws_upgrade_handler` (defaults to `NoCap`;
|
||||
`run_channels_session` remains the direct route for custom upgrade
|
||||
routes). The double token resolution (router-wide
|
||||
`bearer_auth_middleware` + `ws_bearer_auth` both calling
|
||||
`resolve_from_token`) was genuine and removed by route ordering: the
|
||||
WS route registers before the router-wide auth route_layer
|
||||
(route_layer covers only earlier-registered routes), so `/alk/channels`
|
||||
resolves once with enforcement (401) while the gateway endpoints keep
|
||||
the permissive single resolution. Verification: `ws_upgrade_session`
|
||||
+ `ws_overlay_ops` integration suites pass (18 tests).
|
||||
|
||||
Verification: `cargo test` (265 passed), `cargo test --all-features`
|
||||
(all `server::` + `to_openapi::` tests pass; the 7 remaining failures
|
||||
and lint/format noise belong to other agents' in-flight
|
||||
from_mcp/from_wss/forward work in the shared tree, not this task's
|
||||
files), `cargo clippy` clean on all touched files, `cargo fmt --check`
|
||||
clean on all touched files.
|
||||
Reference in New Issue
Block a user