From 4a91844cc671b55b40151ce2f639037a8cb90902 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Fri, 29 Oct 2021 12:31:22 +0200 Subject: [PATCH] refactor: proper SIGTERM, SIGINT and SIGQUIT handling --- Cargo.lock | 27 +++++++++++++++++++++++++-- Cargo.toml | 4 +++- src/server.rs | 17 ++++++++++++----- src/signals.rs | 34 +++++++++++++++++++++++++++------- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b929e4e..ac8840e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -822,6 +822,17 @@ dependencies = [ ] [[package]] +name = "signal-hook" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" +dependencies = [ + "cc", + "libc", + "signal-hook-registry", +] + +[[package]] name = "signal-hook-registry" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -831,6 +842,18 @@ dependencies = [ ] [[package]] +name = "signal-hook-tokio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6c5d32165ff8b94e68e7b3bdecb1b082e958c22434b363482cfb89dcd6f3ff8" +dependencies = [ + "futures-core", + "libc", + "signal-hook", + "tokio", +] + +[[package]] name = "slab" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -877,6 +900,8 @@ dependencies = [ "num_cpus", "percent-encoding", "pin-project", + "signal-hook", + "signal-hook-tokio", "structopt", "time", "tokio", @@ -961,9 +986,7 @@ dependencies = [ "memchr", "mio", "num_cpus", - "once_cell", "pin-project-lite", - "signal-hook-registry", "tokio-macros", "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index e238a43..d49f90d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,11 +44,13 @@ percent-encoding = "2.1" pin-project = "1.0" structopt = { version = "0.3", default-features = false } time = "0.1" -tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"], default-features = false } +tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util"], default-features = false } tokio-rustls = { version = "0.22" } tokio-util = { version = "0.6", features = ["io"] } tracing = "0.1" tracing-subscriber = "0.2" +signal-hook = { version = "0.3.4", features = ["extended-siginfo"] } +signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"], default-features = false } [target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator] version = "0.3" diff --git a/src/server.rs b/src/server.rs index 2d50dee..7b268ec 100644 --- a/src/server.rs +++ b/src/server.rs @@ -156,9 +156,12 @@ impl Server { "error during TLS server initialization, probably cert or key file missing", ); + let signals = signals::create_signals()?; + let handle = signals.handle(); + let server = HyperServer::builder(TlsAcceptor::new(tls, incoming)) .serve(router_service) - .with_graceful_shutdown(signals::wait_for_ctrl_c()); + .with_graceful_shutdown(signals::wait_for_signals(signals)); tracing::info!( parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads), @@ -166,17 +169,20 @@ impl Server { addr_str ); - tracing::info!("press Ctrl + C to shut down the server"); + tracing::info!("press ctrl+c to shut down the server"); server.await?; + handle.close(); } else { // HTTP/1 + let signals = signals::create_signals()?; + let handle = signals.handle(); let server = HyperServer::from_tcp(tcp_listener) .unwrap() .tcp_nodelay(true) .serve(router_service) - .with_graceful_shutdown(signals::wait_for_ctrl_c()); + .with_graceful_shutdown(signals::wait_for_signals(signals)); tracing::info!( parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads), @@ -184,12 +190,13 @@ impl Server { addr_str ); - tracing::info!("press Ctrl + C to shut down the server"); + tracing::info!("press ctrl+c to shut down the server"); server.await?; + handle.close(); } - tracing::warn!("Ctrl+C signal caught, shutting down the server execution"); + tracing::warn!("termination signal caught, shutting down the server execution"); Ok(()) } diff --git a/src/signals.rs b/src/signals.rs index 2130315..0fe03d0 100644 --- a/src/signals.rs +++ b/src/signals.rs @@ -1,8 +1,28 @@ -/// It waits for a `Ctrl-C` incoming signal. -pub async fn wait_for_ctrl_c() { - tracing::debug!("server waiting for incoming Ctrl+C signals"); - tokio::signal::ctrl_c() - .await - .expect("failed to install the Ctrl+C signal handler"); - tracing::debug!("server caught an incoming Ctrl+C signal, starting graceful shutdown"); +use futures_util::stream::StreamExt; +use signal_hook::consts::signal::*; +use signal_hook_tokio::Signals; + +use crate::Result; + +/// It creates a common list of signals stream for `SIGTERM`, `SIGINT` and `SIGQUIT` to be observed. +pub fn create_signals() -> Result { + Ok(Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?) +} + +/// It waits for a specific type of incoming signals. +pub async fn wait_for_signals(signals: Signals) { + let mut signals = signals.fuse(); + while let Some(signal) = signals.next().await { + match signal { + SIGHUP => { + // Note: for now we don't do something for SIGHUPs + tracing::debug!("SIGHUP caught, nothing to do about") + } + SIGTERM | SIGINT | SIGQUIT => { + tracing::debug!("an incoming SIGTERM received, SIGINT or SIGQUIT signal, delegating graceful shutdown to server"); + break; + } + _ => unreachable!(), + } + } } -- libgit2 1.7.2