index : static-web-server.git

ascending towards madness

author Jose Quintana <1700322+joseluisq@users.noreply.github.com> 2022-02-01 22:03:54.0 +00:00:00
committer GitHub <noreply@github.com> 2022-02-01 22:03:54.0 +00:00:00
commit
09f5a88662830cf9d4e39894df01229f020eace7 [patch]
tree
91db650592dba7049c9de67680aa4c25de6463fb
parent
5f9f9f9cbb4ca342bcade4ae7406d152bae53b0f
parent
322426146c2daeff2d31e760b12494c1d47e2896
download
09f5a88662830cf9d4e39894df01229f020eace7.tar.gz

Merge pull request #80 from joseluisq/feature/Configurable_grace_period_on_SIGTERM

feat: configurable grace period support after a `SIGTERM` signal caught

Diff

 src/config.rs  |  4 ++++
 src/server.rs  | 14 ++++++++++----
 src/signals.rs | 26 ++++++++++++++++++++++----
 3 files changed, 36 insertions(+), 8 deletions(-)

diff --git a/src/config.rs b/src/config.rs
index 7d29740..a6bd12c 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -154,4 +154,8 @@ pub struct Config {
    /// It provides The "Basic" HTTP Authentication scheme using credentials as "user-id:password" pairs. Password must be encoded using the "BCrypt" password-hashing function.
    #[structopt(long, default_value = "", env = "SERVER_BASIC_AUTH")]
    pub basic_auth: String,

    #[structopt(long, short = "q", default_value = "0", env = "SERVER_GRACE_PERIOD")]
    /// Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds.
    pub grace_period: u8,
}
diff --git a/src/server.rs b/src/server.rs
index 8769033..6e826ae 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -121,6 +121,10 @@ impl Server {
        );
        let basic_auth = Arc::from(basic_auth);

        // Grace period option
        let grace_period = opts.grace_period;
        tracing::info!("grace period before graceful shutdown: {}s", grace_period);

        // Create a service router for Hyper
        let router_service = RouterService::new(RequestHandler {
            opts: RequestHandlerOpts {
@@ -170,9 +174,10 @@ impl Server {
                HyperServer::builder(TlsAcceptor::new(tls, incoming)).serve(router_service);

            #[cfg(not(windows))]
            let server = server.with_graceful_shutdown(signals::wait_for_signals(signals));
            let server =
                server.with_graceful_shutdown(signals::wait_for_signals(signals, grace_period));
            #[cfg(windows)]
            let server = server.with_graceful_shutdown(signals::wait_for_ctrl_c());
            let server = server.with_graceful_shutdown(signals::wait_for_ctrl_c(grace_period));

            tracing::info!(
                parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads),
@@ -200,9 +205,10 @@ impl Server {
                .serve(router_service);

            #[cfg(not(windows))]
            let server = server.with_graceful_shutdown(signals::wait_for_signals(signals));
            let server =
                server.with_graceful_shutdown(signals::wait_for_signals(signals, grace_period));
            #[cfg(windows)]
            let server = server.with_graceful_shutdown(signals::wait_for_ctrl_c());
            let server = server.with_graceful_shutdown(signals::wait_for_ctrl_c(grace_period));

            tracing::info!(
                parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads),
diff --git a/src/signals.rs b/src/signals.rs
index 438ebcf..6fc576d 100644
--- a/src/signals.rs
+++ b/src/signals.rs
@@ -4,6 +4,8 @@ use {
    signal_hook_tokio::Signals,
};

use tokio::time::{sleep, Duration};

#[cfg(not(windows))]
/// It creates a common list of signals stream for `SIGTERM`, `SIGINT` and `SIGQUIT` to be observed.
pub fn create_signals() -> Result<Signals> {
@@ -12,27 +14,43 @@ pub fn create_signals() -> Result<Signals> {

#[cfg(not(windows))]
/// It waits for a specific type of incoming signals included `ctrl+c`.
pub async fn wait_for_signals(signals: Signals) {
pub async fn wait_for_signals(signals: Signals, grace_period_secs: u8) {
    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
                // NOTE: for now we don't do something for SIGHUPs
                tracing::debug!("SIGHUP caught, nothing to do about")
            }
            SIGTERM | SIGINT | SIGQUIT => {
                tracing::debug!("SIGTERM, SIGINT or SIGQUIT signal received, delegating graceful shutdown to the server");
                tracing::info!("SIGTERM, SIGINT or SIGQUIT signal caught");
                break;
            }
            _ => unreachable!(),
        }
    }
    // NOTE: once loop above is done then an upstream graceful shutdown should come next.
    delay_graceful_shutdown(grace_period_secs).await;
    tracing::info!("delegating server's graceful shutdown");
}

/// Function intended to delay the server's graceful shutdown providing a grace period in seconds.
async fn delay_graceful_shutdown(grace_period_secs: u8) {
    if grace_period_secs > 0 {
        tracing::info!(
            "grace period of {}s after the SIGTERM started",
            grace_period_secs
        );
        sleep(Duration::from_secs(grace_period_secs.into())).await;
        tracing::info!("grace period has elapsed");
    }
}

#[cfg(windows)]
/// It waits for an incoming `ctrl+c` signal on Windows.
pub async fn wait_for_ctrl_c() {
pub async fn wait_for_ctrl_c(grace_period_secs: u8) {
    tokio::signal::ctrl_c()
        .await
        .expect("failed to install ctrl+c signal handler");
    delay_graceful_shutdown(grace_period_secs).await;
}