index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2021-04-30 10:37:56.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2021-04-30 10:37:56.0 +00:00:00
commit
6ed3fe5250129feba03211188d6901a87a5a0c1e [patch]
tree
0aa65c06d27f2aae8d04aca3a39d45fbe0c758be
parent
74b9eaf151668980a34a98420e6d7d8ead9dc20f
download
6ed3fe5250129feba03211188d6901a87a5a0c1e.tar.gz

refactor: signals handling



Diff

 Cargo.lock     | 47 ++++++++++++++++++++++++++++++++++++-----------
 Cargo.toml     |  8 +++++++-
 src/lib.rs     |  1 +
 src/server.rs  | 58 +++++++++++++++++++++++++++++++++++-----------------------
 src/signals.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 130 insertions(+), 35 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 1d8ce00..79aaa67 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -62,6 +62,12 @@ checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"

[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
@@ -332,7 +338,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
 "cfg-if",
 "cfg-if 1.0.0",
]

[[package]]
@@ -389,6 +395,19 @@ dependencies = [
]

[[package]]
name = "nix"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
dependencies = [
 "bitflags",
 "cc",
 "cfg-if 0.1.10",
 "libc",
 "void",
]

[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -573,7 +592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f"
dependencies = [
 "block-buffer",
 "cfg-if",
 "cfg-if 1.0.0",
 "cpuid-bool",
 "digest",
 "opaque-debug",
@@ -589,12 +608,13 @@ dependencies = [
]

[[package]]
name = "signal-hook-registry"
version = "1.3.0"
name = "signal"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
checksum = "2f6ce83b159ab6984d2419f495134972b48754d13ff2e3f8c998339942b56ed9"
dependencies = [
 "libc",
 "nix",
]

[[package]]
@@ -624,9 +644,11 @@ dependencies = [
 "hyper",
 "jemallocator",
 "mime_guess",
 "nix",
 "num_cpus",
 "once_cell",
 "percent-encoding",
 "signal",
 "structopt",
 "tokio",
 "tokio-util",
@@ -710,11 +732,8 @@ dependencies = [
 "memchr",
 "mio",
 "num_cpus",
 "once_cell",
 "pin-project-lite",
 "signal-hook-registry",
 "tokio-macros",
 "winapi",
]

[[package]]
@@ -754,7 +773,7 @@ version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
dependencies = [
 "cfg-if",
 "cfg-if 1.0.0",
 "pin-project-lite",
 "tracing-attributes",
 "tracing-core",
@@ -858,9 +877,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"

[[package]]
name = "unicode-xid"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"

[[package]]
name = "version_check"
@@ -869,6 +888,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"

[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 3e393ce..25a0772 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,7 +26,7 @@ path = "src/bin/server.rs"

[dependencies]
hyper = { version = "0.14", features = ["stream", "http1", "tcp", "server"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal", "fs", "io-util"], default-features = false }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util"], default-features = false }
futures = { version = "0.3", default-features = false }
headers = "0.3"
tokio-util = { version = "0.6", features = ["io"] }
@@ -40,6 +40,12 @@ structopt = { version = "0.3", default-features = false }
num_cpus = { version = "1.13" }
once_cell = "1.7"

[target.'cfg(not(windows))'.dependencies.nix]
version = "0.14"

[target.'cfg(not(windows))'.dependencies.signal]
version = "0.7"

[target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator]
version = "0.3"

diff --git a/src/lib.rs b/src/lib.rs
index b2bc1a3..21caaec 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,6 +10,7 @@ pub mod fs;
pub mod helpers;
pub mod logger;
pub mod server;
pub mod signals;

#[macro_use]
pub mod error;
diff --git a/src/server.rs b/src/server.rs
index c18aaba..82946a9 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -3,7 +3,6 @@ use hyper::service::{make_service_fn, service_fn};
use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use structopt::StructOpt;
use tokio::signal;

use crate::{
    config::{Config, CONFIG},
@@ -76,30 +75,26 @@ impl Server {

        // TODO: HTTP/2 + TLS

        // Spawn a new Tokio asynchronous server task determined by the given options

        let span = tracing::info_span!("Server::run", ?addr, threads = ?self.threads);
        tracing::info!(parent: &span, "listening on http://{}", addr);
        // Spawn a new Tokio asynchronous server task with its given options
        tokio::task::spawn(async move {
            let span = tracing::info_span!("Server::run", ?addr, threads = ?self.threads);
            tracing::info!(parent: &span, "listening on http://{}", addr);

            let root_dir = ArcPath(Arc::new(root_dir));
            let create_service = make_service_fn(move |_| {
                let root_dir = root_dir.clone();
                async move {
                    Ok::<_, error::Error>(service_fn(move |req| {
                        let root_dir = root_dir.clone();
                        async move { handle(root_dir.as_ref(), req).await }
                    }))
                }
            });

        let root_dir = ArcPath(Arc::new(root_dir));
        let create_service = make_service_fn(move |_| {
            let root_dir = root_dir.clone();
            async move {
                Ok::<_, error::Error>(service_fn(move |req| {
                    let root_dir = root_dir.clone();
                    async move { handle(root_dir.as_ref(), req).await }
                }))
            }
            HyperServer::bind(&addr).serve(create_service).await
        });
        HyperServer::bind(&addr)
            .serve(create_service)
            .with_graceful_shutdown(async {
                signal::ctrl_c()
                    .await
                    .expect("failed to install CTRL+C signal handler");
                tracing::warn!(parent: &span, "CTRL+C signal caught and execution exited");
            })
            .await?;

        handle_signals();

        Ok(())
    }
@@ -110,3 +105,20 @@ impl Default for Server {
        Self::new()
    }
}

#[cfg(not(windows))]
/// Handle incoming signals for Unix-like OS's only
fn handle_signals() {
    use crate::signals;

    signals::wait(|sig: signals::Signal| {
        let code = signals::as_int(sig);
        tracing::warn!("Signal {} caught. Server execution exited.", code);
        std::process::exit(code)
    });
}

#[cfg(windows)]
fn handle_signals() {
    // TODO: Windows signals...
}
diff --git a/src/signals.rs b/src/signals.rs
new file mode 100644
index 0000000..6947b11
--- /dev/null
+++ b/src/signals.rs
@@ -0,0 +1,51 @@
use nix::errno::Errno;
use nix::libc::c_int;
use nix::sys::signal::{SIGCHLD, SIGINT, SIGTERM};
use nix::sys::wait::WaitStatus::{Exited, Signaled, StillAlive};
use nix::sys::wait::{waitpid, WaitPidFlag};
use nix::Error;

pub use signal::Signal;

/// It waits for an incoming Termination Signal like Ctrl+C (SIGINT), SIGTERM, etc
pub fn wait<F>(func: F)
where
    F: Fn(signal::Signal),
{
    let sig_trap = signal::trap::Trap::trap(&[SIGTERM, SIGINT, SIGCHLD]);
    for sig in sig_trap {
        match sig {
            SIGCHLD => {
                // Current std::process::Command ip does not have a way to find
                // process id, so we just wait until we have no children
                loop {
                    match waitpid(None, Some(WaitPidFlag::WNOHANG)) {
                        Ok(Exited(pid, status)) => {
                            println!("{} exited with status {}", pid, status);
                            continue;
                        }
                        Ok(Signaled(pid, sig, _)) => {
                            println!("{} killed by {}", pid, sig as c_int);
                            continue;
                        }
                        Ok(StillAlive) => break,
                        Ok(status) => {
                            println!("Temporary status {:?}", status);
                            continue;
                        }
                        Err(Error::Sys(Errno::ECHILD)) => return,
                        Err(e) => {
                            panic!("Error {:?}", e);
                        }
                    }
                }
            }
            sig => func(sig),
        }
    }
}

/// It casts a given `signal::Signal` to `i32`.
pub fn as_int(sig: signal::Signal) -> i32 {
    sig as c_int
}