feat: default graceful shutdown support for http1/http2 servers
resolves #61
Diff
Cargo.lock | 44 +++++++------------------------
Cargo.toml | 3 +--
src/server.rs | 84 ++++++++++++++++++++++++++++++-----------------------------
src/signals.rs | 25 ++++--------------
4 files changed, 62 insertions(+), 94 deletions(-)
@@ -203,16 +203,6 @@ dependencies = [
]
[[package]]
name = "ctrlc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf"
dependencies = [
"nix",
"winapi",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -547,15 +537,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -604,19 +585,6 @@ dependencies = [
]
[[package]]
name = "nix"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -854,6 +822,15 @@ dependencies = [
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -889,7 +866,6 @@ dependencies = [
"async-compression",
"bcrypt",
"bytes",
"ctrlc",
"futures-util",
"headers",
"http",
@@ -985,7 +961,9 @@ dependencies = [
"memchr",
"mio",
"num_cpus",
"once_cell",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"winapi",
]
@@ -32,7 +32,6 @@ anyhow = "1.0"
async-compression = { version = "0.3", features = ["brotli", "deflate", "gzip", "tokio"] }
bcrypt = "0.10"
bytes = "1.0"
ctrlc = { version = "3.1", features = ["termination"] }
futures-util = { version = "0.3", default-features = false, features = ["sink"] }
headers = { git = "https://github.com/joseluisq/hyper-headers.git", branch = "headers_encoding" }
http = "0.2"
@@ -45,7 +44,7 @@ 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"], default-features = false }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"], default-features = false }
tokio-rustls = { version = "0.22" }
tokio-util = { version = "0.6", features = ["io"] }
tracing = "0.1"
@@ -132,7 +132,7 @@ impl Server {
},
});
if opts.http2 {
@@ -140,54 +140,58 @@ impl Server {
let cert_path = opts.http2_tls_cert.clone();
let key_path = opts.http2_tls_key.clone();
tokio::task::spawn(async move {
tcp_listener
.set_nonblocking(true)
.expect("cannot set non-blocking");
let listener = tokio::net::TcpListener::from_std(tcp_listener)
.expect("failed to create tokio::net::TcpListener");
let mut incoming = AddrIncoming::from_listener(listener)?;
incoming.set_nodelay(true);
let tls = TlsConfigBuilder::new()
.cert_path(cert_path)
.key_path(key_path)
.build()
.expect(
"error during TLS server initialization, probably cert or key file missing",
);
let server =
HyperServer::builder(TlsAcceptor::new(tls, incoming)).serve(router_service);
tracing::info!(
parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads),
"listening on https://{}",
addr_str
tcp_listener
.set_nonblocking(true)
.expect("cannot set non-blocking");
let listener = tokio::net::TcpListener::from_std(tcp_listener)
.expect("failed to create tokio::net::TcpListener");
let mut incoming = AddrIncoming::from_listener(listener)?;
incoming.set_nodelay(true);
let tls = TlsConfigBuilder::new()
.cert_path(cert_path)
.key_path(key_path)
.build()
.expect(
"error during TLS server initialization, probably cert or key file missing",
);
server.await
});
let server = HyperServer::builder(TlsAcceptor::new(tls, incoming))
.serve(router_service)
.with_graceful_shutdown(signals::wait_for_ctrl_c());
tracing::info!(
parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads),
"listening on https://{}",
addr_str
);
tracing::info!("press Ctrl + C to shut down the server");
server.await?;
} else {
tokio::task::spawn(async move {
let server = HyperServer::from_tcp(tcp_listener)
.unwrap()
.tcp_nodelay(true)
.serve(router_service);
let server = HyperServer::from_tcp(tcp_listener)
.unwrap()
.tcp_nodelay(true)
.serve(router_service)
.with_graceful_shutdown(signals::wait_for_ctrl_c());
tracing::info!(
parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads),
"listening on http://{}",
addr_str
);
tracing::info!(
parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads),
"listening on http://{}",
addr_str
);
server.await
});
tracing::info!("press Ctrl + C to shut down the server");
server.await?;
}
signals::wait_for_ctrl_c()
tracing::warn!("Ctrl+C signal caught, shutting down the server execution");
Ok(())
}
}
@@ -1,21 +1,8 @@
use ctrlc;
use std::sync::mpsc::channel;
use crate::{Context, Result};
pub fn wait_for_ctrl_c() -> Result {
let (tx, rx) = channel();
ctrlc::set_handler(move || tx.send(()).expect("could not send signal on channel"))
.with_context(|| "error setting Ctrl-C handler".to_owned())?;
tracing::info!("press Ctrl+C to shutdown server");
rx.recv()
.with_context(|| "could not receive signal from channel".to_owned())?;
tracing::warn!("Ctrl+C signal caught, shutting down server execution");
Ok(())
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");
}