Address 4 critical, 8 warning, and 5 suggestion findings from the security and bug review by creating atomic, dependency-ordered tasks: Critical fixes (C1-C4): rate limiter IP source (ADR-025), InFlightCounter increment + drain interval, connector timeout ceiling (ADR-026), JSON format without log file. Validation tightening (W1, W2): upstream host validation, ACME contact email validation. Robustness (W3, W4, W5, W12): upstream URI error handling (502 not silent drop), admin socket resource limits (ADR-027), TlsMode wildcard mismatch, http_port u32→u16. Code quality (W6, W10, W11, S1, S3, W8/W9): config type consolidation, TokenBucket field visibility, reload_mutex #[cfg(test)], dead code removal, root cert count logging, misleading test names. Test coverage (S10): rate limiter ConnectInfo tests (depends on C1 fix). Review: post-security-fix-review checkpoint covering all critical fixes and sensitive config consolidation path.
2.5 KiB
id, name, status, depends_on, scope, risk, impact, level, review_findings
| id | name | status | depends_on | scope | risk | impact | level | review_findings | |
|---|---|---|---|---|---|---|---|---|---|
| fix/inflight-counter-increment | Fix InFlightCounter to increment before spawning task (C2 + drain interval) | pending | narrow | medium | component | implementation |
|
Description
InFlightCounter::increment() is never called anywhere in the codebase. The
InFlightGuard only decrements on drop. Since count stays at 0, the first
guard drop does fetch_sub(1) on an AtomicUsize with value 0, which wraps to
usize::MAX. is_zero() checks count == 0, which never becomes true again.
The drain logic in drain_in_flight always times out.
The spec (operations.md shutdown sequence) states: "each request must increment the counter when it begins and decrement when it completes (via guard drop). The increment must happen before the request task is spawned."
Additionally, the spec states the drain polls every 100ms, but the current implementation uses 50ms. Align with the spec.
Changes Required
src/server.rs:
- Fold the increment into
InFlightGuard::new()so it's impossible to forget:impl InFlightGuard { fn new(counter: Arc<InFlightCounter>) -> Self { counter.increment(); Self(counter) } } - Update
serve_https_listenerto useInFlightGuard::new(in_flight.clone())instead ofInFlightGuard(in_flight.clone()). - Make
InFlightGuard's tuple struct private (if it isn't already) so callers must usenew().
src/server.rs — drain_in_flight:
- Change polling interval from 50ms to 100ms per the spec (operations.md):
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
Acceptance Criteria
InFlightGuard::new()callscounter.increment()before returningInFlightGuardis constructed vianew()only, not the tuple structserve_https_listenerusesInFlightGuard::new(in_flight.clone())drain_in_flightpolls every 100ms (not 50ms)cargo testpassescargo clippypasses with no warnings
References
- docs/architecture/operations.md — shutdown sequence, in-flight counter
- docs/reviews/003-security-and-bug-review.md — C2 finding
- src/server.rs — InFlightCounter, InFlightGuard, drain_in_flight
- src/main.rs — drain_in_flight caller
Notes
The previous
fix/graceful-shutdowntask addressed the abort-vs-join logic but did not fix the increment bug. This task completes that work.
Summary
To be filled on completion