From cb1bd02e061d7cbd75ac6222ed4eb856d7700f33 Mon Sep 17 00:00:00 2001 From: Tim Small Date: Thu, 13 May 2021 16:28:36 +0100 Subject: [PATCH] Support inheriting TCP listener from parent process via file descriptor zero. Closes #36 --- Cargo.lock | 21 +++++++++++++++++++++ Cargo.toml | 1 + src/config.rs | 13 +++++++++++++ src/server.rs | 43 ++++++++++++++++++++++++++++++++++--------- 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e50970..94ff916 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -485,6 +485,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" [[package]] +name = "listenfd" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "492158e732f2e2de81c592f0a2427e57e12cd3d59877378fe7af624b6bbe0ca1" +dependencies = [ + "libc", + "uuid", + "winapi", +] + +[[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -858,6 +869,7 @@ dependencies = [ "humansize", "hyper", "jemallocator", + "listenfd", "mime_guess", "nix", "num_cpus", @@ -1117,6 +1129,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] +name = "uuid" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] name = "version_check" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index b3dc9f0..a51c49b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ pin-project = "1.0" tokio-rustls = { version = "0.22" } humansize = "1.1" time = "0.1" +listenfd = "0.3.3" [target.'cfg(not(windows))'.dependencies.nix] version = "0.14" diff --git a/src/config.rs b/src/config.rs index f77f09e..b60e250 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,6 +13,19 @@ pub struct Config { #[structopt( long, + short = "f", + env = "SERVER_LISTEN_FD", + conflicts_with_all(&["host", "port"]) + )] + /// Instead of binding to a TCP port, accept incoming connections to an already-bound TCP + /// socket listener on the specified file descriptor number (usually zero). Requires that the + /// parent process (e.g. inetd, launchd, or systemd) binds an address and port on behalf of + /// static-web-server, before arranging for the resulting file descriptor to be inherited by + /// static-web-server. Cannot be used in conjunction with the port and host arguments. + pub fd: Option, + + #[structopt( + long, short = "n", default_value = "1", env = "SERVER_THREADS_MULTIPLIER" diff --git a/src/server.rs b/src/server.rs index 4751a7e..97637a1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,7 +1,8 @@ use hyper::server::conn::AddrIncoming; use hyper::server::Server as HyperServer; use hyper::service::{make_service_fn, service_fn}; -use std::net::{IpAddr, SocketAddr}; +use listenfd::ListenFd; +use std::net::{IpAddr, SocketAddr, TcpListener}; use std::sync::Arc; use structopt::StructOpt; @@ -58,8 +59,26 @@ impl Server { tracing::info!("runtime worker threads: {}", self.threads); - let ip = opts.host.parse::()?; - let addr = SocketAddr::from((ip, opts.port)); + let (tcplistener, addr_string); + match opts.fd { + Some(fd) => { + addr_string = format!("@FD({})", fd); + tcplistener = ListenFd::from_env() + .take_tcp_listener(fd)? + .expect("Failed to convert inherited FD into a a TCP listener"); + tracing::info!( + "Converted inherited file descriptor {} to a TCP listener", + fd + ); + } + None => { + let ip = opts.host.parse::()?; + let addr = SocketAddr::from((ip, opts.port)); + tcplistener = TcpListener::bind(addr)?; + addr_string = format!("{:?}", addr); + tracing::info!("Bound to TCP socket {}", addr_string); + } + } // Check for a valid root directory let root_dir = helpers::get_valid_dirpath(&opts.root)?; @@ -111,7 +130,12 @@ impl Server { } }); - let mut incoming = AddrIncoming::bind(&addr)?; + tcplistener + .set_nonblocking(true) + .expect("Cannot set non-blocking"); + let listener = tokio::net::TcpListener::from_std(tcplistener) + .expect("Failed to create tokio::net::TcpListener"); + let mut incoming = AddrIncoming::from_listener(listener)?; incoming.set_nodelay(true); let tls = TlsConfigBuilder::new() @@ -124,9 +148,9 @@ impl Server { HyperServer::builder(TlsAcceptor::new(tls, incoming)).serve(make_service); tracing::info!( - parent: tracing::info_span!("Server::start_server", ?addr, ?threads), + parent: tracing::info_span!("Server::start_server", ?addr_string, ?threads), "listening on https://{}", - addr + addr_string ); server.await @@ -153,14 +177,15 @@ impl Server { } }); - let server = HyperServer::bind(&addr) + let server = HyperServer::from_tcp(tcplistener) + .unwrap() .tcp_nodelay(true) .serve(make_service); tracing::info!( - parent: tracing::info_span!("Server::start_server", ?addr, ?threads), + parent: tracing::info_span!("Server::start_server", ?addr_string, ?threads), "listening on http://{}", - addr + addr_string ); server.await -- libgit2 1.7.2