use std::future::{Future, IntoFuture};
use std::time::Duration;
use crate::error::Error;
pub(crate) async fn serve_with_shutdown<L>(
listener: L,
router: axum::Router,
signal: impl Future<Output = ()> + Send + 'static,
drain_timeout: Option<Duration>,
) -> crate::Result<()>
where
L: axum::serve::Listener,
L::Addr: std::fmt::Debug,
{
let (draining_tx, draining_rx) = tokio::sync::oneshot::channel::<()>();
let serve = axum::serve(listener, router)
.with_graceful_shutdown(async move {
signal.await;
let _ = draining_tx.send(());
})
.into_future();
let result = match drain_timeout {
None => serve.await,
Some(limit) => {
let deadline = async move {
if draining_rx.await.is_err() {
std::future::pending::<()>().await;
}
tokio::time::sleep(limit).await;
};
tokio::select! {
result = serve => result,
() = deadline => {
tracing::warn!(
timeout_ms = limit.as_millis() as u64,
"graceful shutdown timed out; returning with connections still open"
);
Ok(())
}
}
}
};
result.map_err(|e| Error::Transport(format!("Server error: {}", e)))
}