Fix streaming-body watchdog bug (review #009 C2) + dead-upstream test wiring (C3)
C2: IdleTrackingService decremented in_flight when the handler returned
the Response, but for streaming responses (e.g. git clone) the body is
still in flight, so the watchdog could kill the connection mid-stream.
Fix: wrap the response body in IdleTrackingBody which owns the decrement,
releasing it on body EOF/error/drop (guarded against double-decrement by
an AtomicBool) and calling touch() on each data frame. The handler-level
decrement now only happens on the Err branch. Verified by a reproducer
test (streaming_body_not_killed_by_idle_watchdog) that streams 15 chunks
over 3s with a 500ms idle timeout — red on 4ab8c51, green with this fix.
C3: the two pre-existing idle-timeout tests pointed at 127.0.0.1:18080
where nothing listens, so they validated watchdog behaviour against an
immediate 504 error response (HTTP/1.1 prefix + in_flight==1 hold for
a 504 just as for a 200) and never exercised a real upstream round-trip.
Rewired them to spawn a real TestUpstream and assert HTTP/1.1 200 OK.
Removed the dead no-arg make_proxy_router/start_test_https_server wrappers.
Adds http-body as a direct dep (for the Frame/Body trait) and
tokio-stream as a dev-dep (for the slow-stream test helper).
cargo test: 226 unit + 40 integration green; cargo clippy --all-targets clean.
C1 (deploy to dev1) remains open and out of scope for this repo session.
This commit is contained in:
1 parent
ff8819e950
commit
71ad3c2905
6 files changed
+366
-23
No files matched your search
@@ -1,4 +1,5 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
@@ -34,4 +35,35 @@ impl TestUpstream {
|
||||
pub async fn spawn_ok() -> Self {
|
||||
Self::spawn(|| Router::new().route("/", get(|| async { "ok" }))).await
|
||||
}
|
||||
|
||||
/// Upstream that emits one chunk every `chunk_interval` for `num_chunks`
|
||||
/// chunks, then ends. The body stream outlasts the idle timeout so the
|
||||
/// watchdog's behaviour for in-progress streaming responses is observable.
|
||||
/// Emits `b"<n>"` per chunk so the client can verify it received the whole
|
||||
/// stream.
|
||||
pub async fn spawn_slow_stream(chunk_interval: Duration, num_chunks: usize) -> Self {
|
||||
Self::spawn(move || {
|
||||
let interval = chunk_interval;
|
||||
let n = num_chunks;
|
||||
Router::new().route(
|
||||
"/",
|
||||
get(move || async move {
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<
|
||||
Result<axum::body::Bytes, std::convert::Infallible>,
|
||||
>(16);
|
||||
tokio::spawn(async move {
|
||||
for i in 0..n {
|
||||
tokio::time::sleep(interval).await;
|
||||
let _ = tx
|
||||
.send(Ok(format!("<{i}>").into_bytes().into()))
|
||||
.await;
|
||||
}
|
||||
});
|
||||
let stream = tokio_stream::wrappers::ReceiverStream::new(rx);
|
||||
axum::body::Body::from_stream(stream)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user