From 5d8b26632986bc656ae90e46460023abaf9adc8f Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Sat, 20 Mar 2021 23:31:03 +0100 Subject: [PATCH] chore: static error pages content --- 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 = 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 = OnceCell::new(); +pub static PAGE_50X: OnceCell = 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 { +pub async fn handle_rejection(err: Rejection) -> Result { 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::() @@ -25,7 +28,10 @@ pub async fn handle_rejection( } else if err.find::().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() + } +} -- libgit2 1.7.2