diff --git a/src/main.rs b/src/main.rs index 986451b6..84757c1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use axum::{ }; use forward::info::Layer; use http::header::ToStrError; -use log::info; +use log::{info, debug, error}; use thiserror::Error; #[cfg(debug_assertions)] use tower_http::services::{ServeDir, ServeFile}; @@ -37,6 +37,7 @@ mod forward; mod media; mod metrics; mod path; +mod signal; #[tokio::main] async fn main() { @@ -91,10 +92,11 @@ async fn main() { .route("/metrics", get(metrics)) .with_state(app_state); app = static_server(app); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + tokio::select!{ + Err(e) = axum::Server::bind(&addr).serve(app.into_make_service()) => error!("Application error: {e}"), + msg = signal::wait_for_stop_signal() => debug!("Received signal: {}", msg), + } + info!("Server shutdown"); } async fn metrics() -> String { diff --git a/src/signal.rs b/src/signal.rs new file mode 100644 index 00000000..e4d0836a --- /dev/null +++ b/src/signal.rs @@ -0,0 +1,43 @@ +/// References: https://stackoverflow.com/questions/77585473/rust-tokio-how-to-handle-more-signals-than-just-sigint-i-e-sigquit + +/// Waits for a signal that requests a graceful shutdown, like SIGTERM or SIGINT. +#[cfg(unix)] +async fn wait_for_signal_impl() -> String { + use tokio::signal::unix::{signal, SignalKind}; + + // Infos here: + // https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html + let mut signal_terminate = signal(SignalKind::terminate()).unwrap(); + let mut signal_interrupt = signal(SignalKind::interrupt()).unwrap(); + + tokio::select! { + _ = signal_terminate.recv() => "SIGTERM".to_string(), + _ = signal_interrupt.recv() => "SIGINT".to_string(), + } +} + +/// Waits for a signal that requests a graceful shutdown, Ctrl-C (SIGINT). +#[cfg(windows)] +async fn wait_for_signal_impl() -> String { + use tokio::signal::windows; + + // Infos here: + // https://learn.microsoft.com/en-us/windows/console/handlerroutine + let mut signal_c = windows::ctrl_c().unwrap(); + let mut signal_break = windows::ctrl_break().unwrap(); + let mut signal_close = windows::ctrl_close().unwrap(); + let mut signal_shutdown = windows::ctrl_shutdown().unwrap(); + + tokio::select! { + _ = signal_c.recv() => "CTRL_C".to_string(), + _ = signal_break.recv() => "CTRL_BREAK".to_string(), + _ = signal_close.recv() => "CTRL_CLOSE".to_string(), + _ = signal_shutdown.recv() => "CTRL_SHUTDOWN".to_string(), + } +} + +/// Registers signal handlers and waits for a signal that +/// indicates a shutdown request. +pub(crate) async fn wait_for_stop_signal() -> String { + wait_for_signal_impl().await +}