kutil_http/axum/
shutdown.rs1use {
2 ::axum::{extract::*, http::StatusCode, response::*},
3 axum_server::*,
4 std::{io, time::*},
5 tokio::{signal::*, sync::oneshot::*, task::*, *},
6 tokio_util::sync::*,
7};
8
9#[derive(Clone, Debug)]
17pub struct Shutdown {
18 pub handle: Handle,
22
23 pub grace_period: Option<Duration>,
25}
26
27impl Shutdown {
28 pub fn new(grace_period: Option<Duration>) -> Self {
30 Self { handle: Handle::new(), grace_period }
31 }
32
33 pub fn shutdown(&self) {
35 self.handle.graceful_shutdown(self.grace_period);
36 }
37
38 pub fn shutdown_now(&self) {
40 self.handle.shutdown();
41 }
42
43 pub fn cancellation_token(&self) -> (CancellationToken, JoinHandle<()>) {
52 let token = CancellationToken::new();
53
54 let shutdown = self.clone();
55
56 (
57 token.clone(),
58 spawn(async move {
59 tracing::info!("waiting on cancellation token");
60
61 token.cancelled().await;
62 tracing::info!("cancellation token activated");
63 shutdown.shutdown();
64 }),
65 )
66 }
67
68 pub fn on_channel(&self) -> (Sender<()>, JoinHandle<()>) {
77 let (sender, receiver) = channel();
78 let shutdown = self.clone();
79
80 (
81 sender,
82 spawn(async move {
83 tracing::info!("listening on shutdown channel");
84
85 match receiver.await {
86 Ok(_) => {
87 tracing::info!("received shutdown message");
88 }
89
90 Err(error) => {
91 tracing::error!("shutdown channel error: {}", error);
92 }
93 }
94
95 shutdown.shutdown();
96 }),
97 )
98 }
99
100 pub fn on_signals(&self) -> io::Result<JoinHandle<()>> {
104 #[cfg(all(not(unix), not(windows)))]
105 {
106 tracing::warn!("signals not supported on this platform");
107 return Ok(());
108 }
109
110 let shutdown = self.clone();
111
112 #[cfg(unix)]
113 let mut interrupt = unix::signal(unix::SignalKind::interrupt())?; #[cfg(unix)]
115 let mut terminate = unix::signal(unix::SignalKind::terminate())?;
116
117 Ok(spawn(async move {
118 tracing::info!("listening for shutdown signals");
119
120 #[cfg(unix)]
121 select! {
122 _ = interrupt.recv() => {},
123 _ = terminate.recv() => {},
124 }
125
126 #[cfg(windows)]
127 select! {
128 _ = windows::ctrl_c() => {},
129 _ = windows::ctrl_break() => {},
130 _ = windows::ctrl_close() => {},
131 _ = windows::ctrl_logoff() => {},
132 _ = windows::ctrl_shutdown() => {},
133 }
134
135 tracing::info!("received shutdown signal");
136 shutdown.shutdown();
137 }))
138 }
139}
140
141pub async fn shutdown_handler(State(shutdown): State<Shutdown>) -> Response {
146 tracing::info!("shutting down");
147 shutdown.shutdown();
148 StatusCode::NO_CONTENT.into_response()
149}