use {
::axum::{extract::*, http::StatusCode, response::*},
axum_server::*,
std::{io, time::*},
tokio::{signal::*, sync::oneshot::*, task::*, *},
tokio_util::sync::*,
};
#[derive(Clone, Debug)]
pub struct Shutdown<AddressT>
where
AddressT: Address,
{
pub handle: Handle<AddressT>,
pub grace_period: Option<Duration>,
}
impl<AddressT> Shutdown<AddressT>
where
AddressT: Address,
{
pub fn new(grace_period: Option<Duration>) -> Self {
Self { handle: Default::default(), grace_period }
}
pub fn shutdown(&self) {
self.handle.graceful_shutdown(self.grace_period);
}
pub fn shutdown_now(&self) {
self.handle.shutdown();
}
pub fn cancellation_token(&self) -> (CancellationToken, JoinHandle<()>)
where
AddressT: 'static + Send,
{
let token = CancellationToken::default();
let shutdown = self.clone();
(
token.clone(),
spawn(async move {
tracing::info!("waiting on cancellation token");
token.cancelled().await;
tracing::info!("cancellation token activated");
shutdown.shutdown();
}),
)
}
pub fn on_channel(&self) -> (Sender<()>, JoinHandle<()>)
where
AddressT: 'static + Send,
{
let (sender, receiver) = channel();
let shutdown = self.clone();
(
sender,
spawn(async move {
tracing::info!("listening on shutdown channel");
match receiver.await {
Ok(_) => {
tracing::info!("received shutdown message");
}
Err(error) => {
tracing::error!("shutdown channel error: {}", error);
}
}
shutdown.shutdown();
}),
)
}
pub fn on_signals(&self) -> io::Result<JoinHandle<()>>
where
AddressT: 'static + Send,
{
#[cfg(all(not(unix), not(windows)))]
{
tracing::warn!("signals not supported on this platform");
return Ok(());
}
let shutdown = self.clone();
#[cfg(unix)]
let mut interrupt = unix::signal(unix::SignalKind::interrupt())?; #[cfg(unix)]
let mut terminate = unix::signal(unix::SignalKind::terminate())?;
Ok(spawn(async move {
tracing::info!("listening for shutdown signals");
#[cfg(unix)]
select! {
_ = interrupt.recv() => {},
_ = terminate.recv() => {},
}
#[cfg(windows)]
select! {
_ = windows::ctrl_c() => {},
_ = windows::ctrl_break() => {},
_ = windows::ctrl_close() => {},
_ = windows::ctrl_logoff() => {},
_ = windows::ctrl_shutdown() => {},
}
tracing::info!("received shutdown signal");
shutdown.shutdown();
}))
}
}
pub async fn shutdown_handler<AddressT>(State(shutdown): State<Shutdown<AddressT>>) -> Response
where
AddressT: Address,
{
tracing::info!("shutting down");
shutdown.shutdown();
StatusCode::NO_CONTENT.into_response()
}