index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2021-03-20 22:31:03.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2021-03-20 22:31:03.0 +00:00:00
commit
5d8b26632986bc656ae90e46460023abaf9adc8f [patch]
tree
38117c3b14bb11c6309b37ca02c31bcd381cad22
parent
5d4421da61e4ff547dc7409174c62918884ac977
download
5d8b26632986bc656ae90e46460023abaf9adc8f.tar.gz

chore: static error pages content



Diff

 src/bin/server.rs |   5 +-
 src/config.rs     |   9 ++-
 src/lib.rs        |   2 +-
 src/rejection.rs  |  20 ++--
 src/server.rs     | 284 ++++++++++++++++++++++++++++---------------------------
 5 files changed, 171 insertions(+), 149 deletions(-)

diff --git a/src/bin/server.rs b/src/bin/server.rs
index 729c051..7dcc0db 100644
--- a/src/bin/server.rs
+++ b/src/bin/server.rs
@@ -6,11 +6,10 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

extern crate static_web_server;

use self::static_web_server::{Config, Result, Server};
use structopt::StructOpt;
use self::static_web_server::{Result, Server};

fn main() -> Result {
    Server::new(Config::from_args()).run()?;
    Server::new().run()?;

    Ok(())
}
diff --git a/src/config.rs b/src/config.rs
index 2ccf0c5..80ef800 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,5 +1,8 @@
use once_cell::sync::OnceCell;
use structopt::StructOpt;

pub static CONFIG: OnceCell<Config> = OnceCell::new();

/// A blazing fast static files-serving web server powered by Rust
#[derive(Debug, StructOpt)]
pub struct Config {
@@ -73,3 +76,9 @@ pub struct Config {
    /// Specify the file path to read the private key.
    pub http2_tls_key: String,
}

impl Config {
    pub fn global() -> &'static Config {
        CONFIG.get().expect("Config is not initialized")
    }
}
diff --git a/src/lib.rs b/src/lib.rs
index da5ef03..df11e98 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,6 +16,6 @@ pub mod signals;
#[macro_use]
pub mod error;

pub use config::Config;
pub use config::{Config, CONFIG};
pub use error::*;
pub use server::Server;
diff --git a/src/rejection.rs b/src/rejection.rs
index 3292191..75a825a 100644
--- a/src/rejection.rs
+++ b/src/rejection.rs
@@ -1,17 +1,20 @@
use anyhow::Result;
use once_cell::sync::OnceCell;
use std::convert::Infallible;
use warp::http::StatusCode;
use warp::{Rejection, Reply};

pub static PAGE_404: OnceCell<String> = OnceCell::new();
pub static PAGE_50X: OnceCell<String> = OnceCell::new();

/// It receives a `Rejection` and tries to return the corresponding HTML error reply.
pub async fn handle_rejection(
    page_404: String,
    page_50x: String,
    err: Rejection,
) -> Result<impl Reply, Infallible> {
pub async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
    let mut content = String::new();
    let code = if err.is_not_found() {
        content = page_404;
        content = PAGE_404
            .get()
            .expect("page 404 is not initialized")
            .to_string();
        StatusCode::NOT_FOUND
    } else if err
        .find::<warp::filters::body::BodyDeserializeError>()
@@ -25,7 +28,10 @@ pub async fn handle_rejection(
    } else if err.find::<warp::reject::UnsupportedMediaType>().is_some() {
        StatusCode::UNSUPPORTED_MEDIA_TYPE
    } else {
        content = page_50x;
        content = PAGE_50X
            .get()
            .expect("page 50x is not initialized")
            .to_string();
        StatusCode::INTERNAL_SERVER_ERROR
    };

diff --git a/src/server.rs b/src/server.rs
index 39d8dfc..d8afdd3 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -1,24 +1,27 @@
use std::net::{IpAddr, SocketAddr};
use structopt::StructOpt;
use warp::Filter;

use crate::{cache, config, cors, filters, helpers, logger, rejection, signals, Result};
use crate::config::{Config, CONFIG};
use crate::{cache, cors, filters, helpers, logger, rejection, signals, Result};

/// Define a multi-thread HTTP/HTTPS web server.
pub struct Server {
    opts: config::Config,
    threads: usize,
}

impl Server {
    /// Create new multi-thread server instance.
    pub fn new(opts: config::Config) -> Self {
        let n = if opts.threads_multiplier == 0 {
            1
        } else {
            opts.threads_multiplier
    pub fn new() -> Self {
        // Initialize global config
        CONFIG.set(Config::from_args()).unwrap();
        let opts = Config::global();

        let threads = match opts.threads_multiplier {
            0 | 1 => 1,
            _ => num_cpus::get() * opts.threads_multiplier,
        };
        let threads = num_cpus::get() * n;
        Self { opts, threads }
        Self { threads }
    }

    /// Build and run the `Server` forever on the current thread.
@@ -27,7 +30,6 @@ impl Server {
            .enable_all()
            .thread_name("static-web-server")
            .worker_threads(self.threads)
            .max_blocking_threads(self.threads)
            .build()?
            .block_on(async {
                let r = self.start_server().await;
@@ -41,7 +43,7 @@ impl Server {

    /// Run the inner `Warp` server forever on the current thread.
    async fn start_server(self) -> Result {
        let opts = self.opts;
        let opts = Config::global();

        logger::init(&opts.log_level)?;

@@ -52,13 +54,15 @@ impl Server {
        let addr = SocketAddr::from((ip, opts.port));

        // Check for a valid root directory
        let root_dir = helpers::get_valid_dirpath(opts.root)?;
        let root_dir = helpers::get_valid_dirpath(&opts.root)?;

        // Custom error pages content
        let page404 = helpers::read_file_content(opts.page404.as_ref());
        let page50x = helpers::read_file_content(opts.page50x.as_ref());
        let page404_a = page404.clone();
        let page50x_a = page50x.clone();
        rejection::PAGE_404
            .set(helpers::read_file_content(opts.page404.as_ref()))
            .expect("page 404 is not initialized");
        rejection::PAGE_50X
            .set(helpers::read_file_content(opts.page50x.as_ref()))
            .expect("page 50x is not initialized");

        // CORS support
        let (cors_filter, cors_allowed_origins) =
@@ -68,11 +72,7 @@ impl Server {
        let base_dir_filter = warp::fs::dir(root_dir.clone())
            .map(cache::control_headers)
            .with(warp::trace::request())
            .recover(move |rej| {
                let page404_a = page404_a.clone();
                let page50x_a = page50x_a.clone();
                async move { rejection::handle_rejection(page404_a, page50x_a, rej).await }
            });
            .recover(rejection::handle_rejection);

        // Public HEAD endpoint
        let public_head = warp::head().and(base_dir_filter.clone());
@@ -82,8 +82,8 @@ impl Server {

        // HTTP/2 + TLS
        let http2 = opts.http2;
        let http2_tls_cert_path = opts.http2_tls_cert;
        let http2_tls_key_path = opts.http2_tls_key;
        let http2_tls_cert_path = &opts.http2_tls_cert;
        let http2_tls_key_path = &opts.http2_tls_key;

        // Public GET/HEAD endpoints with compression (gzip, brotli or none)
        match opts.compression.as_ref() {
@@ -92,50 +92,49 @@ impl Server {
                    .map(cache::control_headers)
                    .with(warp::trace::request())
                    .with(warp::compression::brotli(true))
                    .recover(move |rej| {
                        let page404 = page404.clone();
                        let page50x = page50x.clone();
                        async move { rejection::handle_rejection(page404, page50x, rej).await }
                    });

                if let Some(cors_filter) = cors_filter {
                    tracing::info!(
                        cors_enabled = ?true,
                        allowed_origins = ?cors_allowed_origins
                    );
                    let server = warp::serve(
                        public_head.with(cors_filter.clone()).or(warp::get()
                            .and(filters::has_accept_encoding("br"))
                            .and(with_dir)
                            .with(cors_filter.clone())
                            .or(public_get_default.with(cors_filter))),
                    );
                    if http2 {
                        server
                            .tls()
                            .cert_path(http2_tls_cert_path)
                            .key_path(http2_tls_key_path)
                            .run(addr)
                            .await;
                    } else {
                        server.run(addr).await
                    .recover(rejection::handle_rejection);

                match cors_filter {
                    Some(cors_filter) => {
                        tracing::info!(
                            cors_enabled = ?true,
                            allowed_origins = ?cors_allowed_origins
                        );
                        let server = warp::serve(
                            public_head.with(cors_filter.clone()).or(warp::get()
                                .and(filters::has_accept_encoding("br"))
                                .and(with_dir)
                                .with(cors_filter.clone())
                                .or(public_get_default.with(cors_filter))),
                        );
                        if http2 {
                            server
                                .tls()
                                .cert_path(http2_tls_cert_path)
                                .key_path(http2_tls_key_path)
                                .run(addr)
                                .await;
                        } else {
                            server.run(addr).await
                        }
                    }
                } else {
                    let server = warp::serve(
                        public_head.or(warp::get()
                            .and(filters::has_accept_encoding("br"))
                            .and(with_dir)
                            .or(public_get_default)),
                    );
                    if http2 {
                        server
                            .tls()
                            .cert_path(http2_tls_cert_path)
                            .key_path(http2_tls_key_path)
                            .run(addr)
                            .await;
                    } else {
                        server.run(addr).await
                    None => {
                        let server = warp::serve(
                            public_head.or(warp::get()
                                .and(filters::has_accept_encoding("br"))
                                .and(with_dir)
                                .or(public_get_default)),
                        );
                        if http2 {
                            server
                                .tls()
                                .cert_path(http2_tls_cert_path)
                                .key_path(http2_tls_key_path)
                                .run(addr)
                                .await;
                        } else {
                            server.run(addr).await
                        }
                    }
                }
            }),
@@ -144,84 +143,87 @@ impl Server {
                    .map(cache::control_headers)
                    .with(warp::trace::request())
                    .with(warp::compression::gzip(true))
                    .recover(move |rej| {
                        let page404 = page404.clone();
                        let page50x = page50x.clone();
                        async move { rejection::handle_rejection(page404, page50x, rej).await }
                    });

                if let Some(cors_filter) = cors_filter {
                    tracing::info!(
                        cors_enabled = ?true,
                        allowed_origins = ?cors_allowed_origins
                    );
                    let server = warp::serve(
                        public_head.with(cors_filter.clone()).or(warp::get()
                            .and(filters::has_accept_encoding("gzip"))
                            .and(with_dir)
                            .with(cors_filter.clone())
                            .or(public_get_default.with(cors_filter))),
                    );
                    if http2 {
                        server
                            .tls()
                            .cert_path(http2_tls_cert_path)
                            .key_path(http2_tls_key_path)
                            .run(addr)
                            .await;
                    } else {
                        server.run(addr).await
                    .recover(rejection::handle_rejection);

                match cors_filter {
                    Some(cors_filter) => {
                        tracing::info!(
                            cors_enabled = ?true,
                            allowed_origins = ?cors_allowed_origins
                        );
                        let server = warp::serve(
                            public_head.with(cors_filter.clone()).or(warp::get()
                                .and(filters::has_accept_encoding("gzip"))
                                .and(with_dir)
                                .with(cors_filter.clone())
                                .or(public_get_default.with(cors_filter))),
                        );
                        if http2 {
                            server
                                .tls()
                                .cert_path(http2_tls_cert_path)
                                .key_path(http2_tls_key_path)
                                .run(addr)
                                .await;
                        } else {
                            server.run(addr).await
                        }
                    }
                } else {
                    let server = warp::serve(
                        public_head.or(warp::get()
                            .and(filters::has_accept_encoding("gzip"))
                            .and(with_dir)
                            .or(public_get_default)),
                    );
                    if http2 {
                        server
                            .tls()
                            .cert_path(http2_tls_cert_path)
                            .key_path(http2_tls_key_path)
                            .run(addr)
                            .await;
                    } else {
                        server.run(addr).await
                    None => {
                        let server = warp::serve(
                            public_head.or(warp::get()
                                .and(filters::has_accept_encoding("gzip"))
                                .and(with_dir)
                                .or(public_get_default)),
                        );
                        if http2 {
                            server
                                .tls()
                                .cert_path(http2_tls_cert_path)
                                .key_path(http2_tls_key_path)
                                .run(addr)
                                .await;
                        } else {
                            server.run(addr).await
                        }
                    }
                }
            }),
            _ => tokio::task::spawn(async move {
                if let Some(cors_filter) = cors_filter {
                    tracing::info!(
                        cors_enabled = ?true,
                        allowed_origins = ?cors_allowed_origins
                    );
                    let public_get_default = warp::get()
                        .and(base_dir_filter.clone())
                        .with(cors_filter.clone());
                    let server = warp::serve(public_head.or(public_get_default.with(cors_filter)));
                    if http2 {
                        server
                            .tls()
                            .cert_path(http2_tls_cert_path)
                            .key_path(http2_tls_key_path)
                            .run(addr)
                            .await;
                    } else {
                        server.run(addr).await
                match cors_filter {
                    Some(cors_filter) => {
                        tracing::info!(
                            cors_enabled = ?true,
                            allowed_origins = ?cors_allowed_origins
                        );
                        let public_get_default = warp::get()
                            .and(base_dir_filter.clone())
                            .with(cors_filter.clone());
                        let server =
                            warp::serve(public_head.or(public_get_default.with(cors_filter)));
                        if http2 {
                            server
                                .tls()
                                .cert_path(http2_tls_cert_path)
                                .key_path(http2_tls_key_path)
                                .run(addr)
                                .await;
                        } else {
                            server.run(addr).await
                        }
                    }
                } else {
                    let server = warp::serve(public_head.or(public_get_default));
                    if http2 {
                        server
                            .tls()
                            .cert_path(http2_tls_cert_path)
                            .key_path(http2_tls_key_path)
                            .run(addr)
                            .await;
                    } else {
                        server.run(addr).await
                    None => {
                        let server = warp::serve(public_head.or(public_get_default));
                        if http2 {
                            server
                                .tls()
                                .cert_path(http2_tls_cert_path)
                                .key_path(http2_tls_key_path)
                                .run(addr)
                                .await;
                        } else {
                            server.run(addr).await
                        }
                    }
                }
            }),
@@ -236,3 +238,9 @@ impl Server {
        Ok(())
    }
}

impl Default for Server {
    fn default() -> Self {
        Self::new()
    }
}