use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time::Duration;
const STREAM_LIMIT: usize = 200;
const ATTEMPTED_STREAMS: usize = 250;
#[tokio::test]
async fn default_config_limits_concurrent_streams_per_connection() {
let in_flight = Arc::new(AtomicUsize::new(0));
let peak = Arc::new(AtomicUsize::new(0));
let (release_tx, release_rx) = tokio::sync::watch::channel(false);
let app = {
let in_flight = in_flight.clone();
let peak = peak.clone();
axum::Router::new()
.route("/warmup", axum::routing::get(|| async { "ok" }))
.route(
"/hold",
axum::routing::get(move || {
let in_flight = in_flight.clone();
let peak = peak.clone();
let mut release_rx = release_rx.clone();
async move {
let now = in_flight.fetch_add(1, Ordering::SeqCst) + 1;
peak.fetch_max(now, Ordering::SeqCst);
let _ = release_rx.wait_for(|released| *released).await;
in_flight.fetch_sub(1, Ordering::SeqCst);
"ok"
}
}),
)
};
let handle = sui_http::Builder::new()
.serve(("localhost", 0), app)
.unwrap();
let addr = *handle.local_addr();
let tcp = tokio::net::TcpStream::connect(addr).await.unwrap();
let (mut send_request, connection) = h2::client::handshake(tcp).await.unwrap();
tokio::spawn(connection);
let warmup = http::Request::builder()
.uri(format!("http://{addr}/warmup"))
.body(())
.unwrap();
send_request = send_request.ready().await.unwrap();
let (response, _) = send_request.send_request(warmup, true).unwrap();
assert!(response.await.unwrap().status().is_success());
let issuer = tokio::spawn(async move {
let mut responses = Vec::with_capacity(ATTEMPTED_STREAMS);
for _ in 0..ATTEMPTED_STREAMS {
send_request = send_request.ready().await.unwrap();
let request = http::Request::builder()
.uri(format!("http://{addr}/hold"))
.body(())
.unwrap();
let (response, _) = send_request.send_request(request, true).unwrap();
responses.push(tokio::spawn(async move {
let response = response.await.unwrap();
assert!(response.status().is_success());
let mut body = response.into_body();
let mut flow_control = body.flow_control().clone();
while let Some(chunk) = body.data().await {
let chunk = chunk.unwrap();
let _ = flow_control.release_capacity(chunk.len());
}
}));
}
responses
});
tokio::time::timeout(Duration::from_secs(10), async {
while in_flight.load(Ordering::SeqCst) < STREAM_LIMIT {
tokio::time::sleep(Duration::from_millis(10)).await;
}
})
.await
.expect("server never reached the advertised stream limit");
tokio::time::sleep(Duration::from_millis(250)).await;
assert_eq!(in_flight.load(Ordering::SeqCst), STREAM_LIMIT);
assert_eq!(peak.load(Ordering::SeqCst), STREAM_LIMIT);
release_tx.send(true).unwrap();
let responses = tokio::time::timeout(Duration::from_secs(10), issuer)
.await
.expect("issuer did not finish after release")
.unwrap();
assert_eq!(responses.len(), ATTEMPTED_STREAMS);
for response in responses {
tokio::time::timeout(Duration::from_secs(10), response)
.await
.expect("response did not complete after release")
.unwrap();
}
assert_eq!(peak.load(Ordering::SeqCst), STREAM_LIMIT);
}