use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tako_rs_core::router::Router;
use tako_rs_core::types::BoxError;
use super::request::H3BodyTracker;
use super::request::handle_request;
pub(crate) async fn handle_connection(
conn: quinn::Connection,
router: Arc<Router>,
remote_addr: SocketAddr,
shutdown: tokio_util::sync::CancellationToken,
goaway_grace: Duration,
) -> Result<(), BoxError> {
let mut h3_conn = h3::server::Connection::new(h3_quinn::Connection::new(conn)).await?;
let mut request_tasks = tokio::task::JoinSet::new();
let body_tracker = Arc::new(H3BodyTracker::default());
loop {
tokio::select! {
accepted = h3_conn.accept() => {
match accepted {
Ok(Some(resolver)) => {
let router = router.clone();
let body_tracker = body_tracker.clone();
request_tasks.spawn(async move {
match resolver.resolve_request().await {
Ok((req, stream)) => {
if let Err(e) = handle_request(req, stream, router, remote_addr, body_tracker).await {
tracing::error!("HTTP/3 request error: {e}");
}
}
Err(e) => {
tracing::error!("HTTP/3 request resolve error: {e}");
}
}
});
}
Ok(None) => break,
Err(e) => {
tracing::error!("HTTP/3 accept error: {e}");
break;
}
}
}
() = shutdown.cancelled() => {
if let Err(e) = h3_conn.shutdown(0).await {
tracing::debug!("HTTP/3 GOAWAY error: {e}");
}
break;
}
}
}
let drain_deadline = tokio::time::Instant::now() + goaway_grace;
let drain = tokio::time::timeout_at(drain_deadline, async {
while request_tasks.join_next().await.is_some() {}
});
if drain.await.is_err() {
tracing::debug!(
"HTTP/3 connection grace ({:?}) elapsed; aborting {} request task(s)",
goaway_grace,
request_tasks.len()
);
request_tasks.abort_all();
}
loop {
let notified = body_tracker.drained.notified();
tokio::pin!(notified);
notified.as_mut().enable();
if body_tracker
.active
.load(std::sync::atomic::Ordering::SeqCst)
== 0
{
break;
}
let now = tokio::time::Instant::now();
if now >= drain_deadline {
break;
}
if tokio::time::timeout_at(drain_deadline, notified)
.await
.is_err()
{
break;
}
}
Ok(())
}