index : static-web-server.git

ascending towards madness

author Tim Small <tim@seoss.co.uk> 2021-05-13 15:28:36.0 +00:00:00
committer Tim Small <tim@seoss.co.uk> 2021-05-30 14:44:52.0 +00:00:00
commit
cb1bd02e061d7cbd75ac6222ed4eb856d7700f33 [patch]
tree
ba8ca2613f52fd93f9b15675e81e83741b9e645c
parent
c3389cc173e969a54b46b9f16f1e49cd2a822343
download
cb1bd02e061d7cbd75ac6222ed4eb856d7700f33.tar.gz

Support inheriting TCP listener from parent process via file descriptor zero.

Closes #36

Diff

 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<usize>,

    #[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::<IpAddr>()?;
        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::<IpAddr>()?;
                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