- 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)
6.1 KiB
6.1 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||
|---|---|---|---|---|---|---|---|---|---|---|---|
| review-001-hyper-server-knobs | Configure hyper timeouts + decoy/405/proxy-path fixes (SRV-04, SRV-07, SRV-08, SRV-09, SRV-10) | completed | moderate | low | component | implementation |
|
Description
Review 001 server-core minors, grouped because they are small, disjoint,
and all land in src/server/:
- SRV-04:
HyperBuilder(src/server/adapter.rs:217-224) sets no timer — hyper 1.11 silently ignoresheader_read_timeoutwithout one (verified against hyper source), so the slow-loris surface is open and every keep-alive knob is off. Set a timer (tokio) +header_read_timeout- h1/h2 keep-alive knobs. No concurrency cap exists in this crate (the accept loop is the consumer's) — document that boundary in the module doc.
- SRV-05:
with_decoyconsumesextra_routesvia.take()(adapter.rs:104); a second builder call silently rebuilds without the custom routes. Clone (or error on double-build). - SRV-07: the decoy is only the router
fallback— method-mismatch requests return axum's bare 405 with noServer: nginxheader, which single-handedly distinguishes alkhttp from nginx in decoy deployments (empirically verified). Wrap 405s with the decoy response. - SRV-08:
percent_decodemaps+→space (wrong in a URI path) and per-byte→char (mojibake for non-ASCII filenames) —decoy.rs:106-127. Fix with proper UTF-8 percent-decoding; also move the blockingis_dir()/is_file()syscalls off the async path. - SRV-09:
/openapi.jsonerror path echoes raw serde internals to unauthenticated callers and re-serializes the whole projection per request (adapter.rs:250-254); companion unguarded.expect("to_openapi always emits…")into_openapi.rs:67. Cache the serialized doc, return a generic 500 body, remove theexpect. - SRV-10: WS upgrade hardcodes
NoCapwhile the doc claims a stricter policy is passable — add the injection point or fix the doc; drop the double token resolution (router middleware +ws_bearer_auth).
Acceptance Criteria
- Hyper configured with timer + header read timeout; knob values documented
- 405 responses carry the decoy
Serverheader (test withOPTIONS /search) %C3%A9anda+b.htmlresolve correctly in the decoy static server (tests); async-path syscalls gone/openapi.json500 body is generic; doc cached;expectremoved- Second
.with_decoyno 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 warningspass
References
- docs/reviews/001-initial-implementation-review.md (Part A, SRV-04..SRV-10)
- docs/architecture/decisions/010-alpn-router-and-endpoint.md
Notes
Agent fills during implementation. Independent of the other review-001 tasks; can proceed in parallel.
Summary
Five server-core findings fixed in src/server/ (+ two touchpoints):
- SRV-04 (
server/adapter.rs):serve_ionow setsTokioTimeron both the h1 and h2 auto-builder sub-builders,header_read_timeout(10 s)(h1, bounds the slow-loris header-drip window), h1keep_alive(true), h2keep_alive_interval(30 s)+keep_alive_timeout(10 s). Verified against hyper 1.11 source: with no timer,Time::checkwarns and returnsNone— 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 perhandlecall). - SRV-05 (
server/adapter.rs):with_decoyrebuilds the router withextra_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): wiredRouter::method_not_allowed_fallback(decoy_method_not_allowed)— method mismatches on registered paths (e.g.OPTIONS /search) get the nginx-shaped 405 withServer: nginxinstead of axum's bare 405 (axum 0.8 sets the default fallback on all previously registered MethodRouters; theAllowheader is unaffected). - SRV-08 (
server/decoy.rs):percent_decodeis now byte-accumulating + single UTF-8 validation (%C3%A9→ oneé),+is a literal (form-encoding, not path grammar), invalid escapes →None→ fake 404. The blockingis_dir()/is_file()syscalls moved totokio::fs::metadata. - SRV-09 (
server/adapter.rs,adapters/to_openapi.rs): the serialized/openapi.jsondoc is built once at adapter construction (CachedOpenAPIDoc, threaded throughRouterState+FromRef); the handler serves the cached bytes or a genericinternal server error500 (no serde internals on the wire).to_openapinow returnsResult<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 byws_upgrade_handler(defaults toNoCap;run_channels_sessionremains the direct route for custom upgrade routes). The double token resolution (router-widebearer_auth_middleware+ws_bearer_authboth callingresolve_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/channelsresolves once with enforcement (401) while the gateway endpoints keep the permissive single resolution. Verification:ws_upgrade_sessionws_overlay_opsintegration 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.