From 959c3258453ae44f333d3c147bb480b9a9404479 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Tue, 3 May 2022 23:19:51 +0200 Subject: [PATCH] refactor: `PathBuf` data type for cli and file options options data types updated: - root - page50x - page404 - page-fallback - http2-tls-cert - http2-tls-key --- docs/content/configuration/config-file.md | 16 ++++++++-------- src/control_headers.rs | 2 +- src/error_page.rs | 12 +++++++----- src/fallback_page.rs | 7 ++++--- src/handler.rs | 12 ++++++------ src/helpers.rs | 15 ++++++--------- src/server.rs | 23 ++++++++++++++++------- src/settings/cli.rs | 28 +++++++++------------------- src/settings/file.rs | 14 +++++++------- src/settings/mod.rs | 50 +++++++++++++++++++++++++------------------------- 10 files changed, 89 insertions(+), 90 deletions(-) diff --git a/docs/content/configuration/config-file.md b/docs/content/configuration/config-file.md index 9d0a63c..ba48223 100644 --- a/docs/content/configuration/config-file.md +++ b/docs/content/configuration/config-file.md @@ -13,11 +13,11 @@ Below just an example showing all features with its default values. #### Address & Root dir host = "::" -port = 8087 -root = "docker/public" +port = 80 +root = "./public" #### Logging -log-level = "trace" +log-level = "error" #### Cache Control headers cache-control-headers = true @@ -26,13 +26,13 @@ cache-control-headers = true compression = true #### Error pages -page404 = "docker/public/404.html" -page50x = "docker/public/50x.html" +page404 = "./public/404.html" +page50x = "./public/50x.html" #### HTTP/2 + TLS http2 = false -http2-tls-cert = "" -http2-tls-key = "" +# http2-tls-cert = "some.cert" +# http2-tls-key = "some.key" #### Security headers security-headers = true @@ -58,7 +58,7 @@ threads-multiplier = 1 grace-period = 0 #### Page fallback for 404s -page-fallback = "" +# page-fallback = "some_page.html" [advanced] diff --git a/src/control_headers.rs b/src/control_headers.rs index 2611e31..1fcfc4b 100644 --- a/src/control_headers.rs +++ b/src/control_headers.rs @@ -7,7 +7,7 @@ use hyper::{Body, Response}; // Cache-Control `max-age` variants const MAX_AGE_ONE_HOUR: u64 = 60 * 60; -const MAX_AGE_ONE_DAY: u64 = 60 * 60 * 24_u64; +const MAX_AGE_ONE_DAY: u64 = 60 * 60 * 24; const MAX_AGE_ONE_YEAR: u64 = 60 * 60 * 24 * 365; // `Cache-Control` list of extensions diff --git a/src/error_page.rs b/src/error_page.rs index 9370ccf..949f066 100644 --- a/src/error_page.rs +++ b/src/error_page.rs @@ -8,8 +8,8 @@ use crate::Result; pub fn error_response( method: &Method, status_code: &StatusCode, - page404: &str, - page50x: &str, + page404: &[u8], + page50x: &[u8], ) -> Result> { tracing::warn!(method = ?method, status = status_code.as_u16(), error = ?status_code.to_owned()); @@ -36,8 +36,8 @@ pub fn error_response( | &StatusCode::RANGE_NOT_SATISFIABLE | &StatusCode::EXPECTATION_FAILED => { // Extra check for 404 status code and its HTML content - if status_code == &StatusCode::NOT_FOUND { - error_page_content = page404.to_owned(); + if status_code == &StatusCode::NOT_FOUND && !page404.is_empty() { + error_page_content = String::from_utf8_lossy(page404).to_string(); } status_code } @@ -52,7 +52,9 @@ pub fn error_response( | &StatusCode::INSUFFICIENT_STORAGE | &StatusCode::LOOP_DETECTED => { // HTML content check for status codes 50x - error_page_content = page50x.to_owned(); + if !page50x.is_empty() { + error_page_content = String::from_utf8_lossy(page50x).to_string(); + } status_code } // other status codes diff --git a/src/fallback_page.rs b/src/fallback_page.rs index 51d8576..67c5ab6 100644 --- a/src/fallback_page.rs +++ b/src/fallback_page.rs @@ -2,9 +2,10 @@ use headers::{AcceptRanges, ContentLength, ContentType, HeaderMapExt}; use hyper::{Body, Response, StatusCode}; use mime_guess::mime; -/// Checks if a fallback response can be generated, i.e. if it is a GET request that would result in a 404 error and a fallback page is configured. -/// If a response can be generated, it is returned, else `None` is returned. -pub fn fallback_response(page_fallback: &str) -> Response { +/// Checks if a fallback response can be generated, i.e. if it is a `GET` request +/// that would result in a `404` error and a fallback page is configured. +/// If a response can be generated then is returned otherwise `None`. +pub fn fallback_response(page_fallback: &[u8]) -> Response { let body = Body::from(page_fallback.to_owned()); let len = page_fallback.len() as u64; diff --git a/src/handler.rs b/src/handler.rs index 61eab62..dc02c39 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -16,9 +16,9 @@ pub struct RequestHandlerOpts { pub cors: Option, pub security_headers: bool, pub cache_control_headers: bool, - pub page404: String, - pub page50x: String, - pub page_fallback: String, + pub page404: Vec, + pub page50x: Vec, + pub page_fallback: Vec, pub basic_auth: String, // Advanced options @@ -166,14 +166,14 @@ impl RequestHandler { } Err(status) => { // Check for a fallback response - if !self.opts.page_fallback.is_empty() + if method == Method::GET && status == StatusCode::NOT_FOUND - && method == Method::GET + && !self.opts.page_fallback.is_empty() { return Ok(fallback_page::fallback_response(&self.opts.page_fallback)); } - // Response error + // Otherwise return a response error error_page::error_response( method, &status, diff --git a/src/helpers.rs b/src/helpers.rs index 66fc20e..c30f2e2 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -30,17 +30,14 @@ where } } -/// Read the entire contents of a file into a string if valid or returns empty otherwise. -pub fn read_file_content(p: &str) -> String { - if !p.is_empty() && Path::new(p).exists() { - return fs::read_to_string(p).unwrap_or_default(); - } - String::new() -} - /// Read the entire contents of a file into a bytes vector. pub fn read_bytes(path: &Path) -> Result> { - fs::read(path).with_context(|| format!("failed to read `{}`", path.display())) + fs::read(path).with_context(|| format!("failed to read file `{}`", path.display())) +} + +/// Read the entire contents of a file into a bytes vector or default to empty. +pub fn read_bytes_default(path: &Path) -> Vec { + fs::read(path).unwrap_or_default() } /// Read an UTF-8 file from a specific path. diff --git a/src/server.rs b/src/server.rs index a39f298..38e5154 100644 --- a/src/server.rs +++ b/src/server.rs @@ -99,11 +99,11 @@ impl Server { .with_context(|| "root directory was not found or inaccessible")?; // Custom error pages content - let page404 = helpers::read_file_content(&general.page404); - let page50x = helpers::read_file_content(&general.page50x); + let page404 = helpers::read_bytes_default(&general.page404); + let page50x = helpers::read_bytes_default(&general.page50x); // Fallback page content - let page_fallback = helpers::read_file_content(&general.page_fallback); + let page_fallback = helpers::read_bytes_default(&general.page_fallback.unwrap_or_default()); // Number of worker threads option let threads = self.threads; @@ -171,7 +171,7 @@ impl Server { tcp_listener .set_nonblocking(true) - .expect("cannot set non-blocking"); + .with_context(|| "failed to set TCP non-blocking mode")?; let listener = tokio::net::TcpListener::from_std(tcp_listener) .with_context(|| "failed to create tokio::net::TcpListener")?; let mut incoming = AddrIncoming::from_listener(listener).with_context(|| { @@ -179,12 +179,21 @@ impl Server { })?; incoming.set_nodelay(true); + let http2_tls_cert = match general.http2_tls_cert { + Some(v) => v, + _ => bail!("failed to initialize TLS because cert file missing"), + }; + let http2_tls_key = match general.http2_tls_key { + Some(v) => v, + _ => bail!("failed to initialize TLS because key file missing"), + }; + let tls = TlsConfigBuilder::new() - .cert_path(&general.http2_tls_cert) - .key_path(&general.http2_tls_key) + .cert_path(&http2_tls_cert) + .key_path(&http2_tls_key) .build() .with_context(|| { - "failed to initialize TLS, probably wrong cert/key or file missing" + "failed to initialize TLS probably because invalid cert or key file" })?; #[cfg(unix)] diff --git a/src/settings/cli.rs b/src/settings/cli.rs index 4889cef..75f5395 100644 --- a/src/settings/cli.rs +++ b/src/settings/cli.rs @@ -44,7 +44,7 @@ pub struct General { #[structopt(long, short = "d", default_value = "./public", env = "SERVER_ROOT")] /// Root directory path of static files. - pub root: String, + pub root: PathBuf, #[structopt( long, @@ -52,7 +52,7 @@ pub struct General { env = "SERVER_ERROR_PAGE_50X" )] /// HTML file path for 50x errors. If the path is not specified or simply doesn't exist then the server will use a generic HTML error message. - pub page50x: String, + pub page50x: PathBuf, #[structopt( long, @@ -60,11 +60,11 @@ pub struct General { env = "SERVER_ERROR_PAGE_404" )] /// HTML file path for 404 errors. If the path is not specified or simply doesn't exist then the server will use a generic HTML error message. - pub page404: String, + pub page404: PathBuf, - #[structopt(long, default_value = "", env = "SERVER_FALLBACK_PAGE")] + #[structopt(long, env = "SERVER_FALLBACK_PAGE")] /// HTML file path that is used for GET requests when the requested path doesn't exist. The fallback page is served with a 200 status code, useful when using client routers. If the path is not specified or simply doesn't exist then this feature will not be active. - pub page_fallback: String, + pub page_fallback: Option, #[structopt(long, short = "g", default_value = "error", env = "SERVER_LOG_LEVEL")] /// Specify a logging level in lower case. Values: error, warn, info, debug or trace @@ -98,23 +98,13 @@ pub struct General { /// Enable HTTP/2 with TLS support. pub http2: bool, - #[structopt( - long, - required_if("http2", "true"), - default_value = "", - env = "SERVER_HTTP2_TLS_CERT" - )] + #[structopt(long, required_if("http2", "true"), env = "SERVER_HTTP2_TLS_CERT")] /// Specify the file path to read the certificate. - pub http2_tls_cert: String, + pub http2_tls_cert: Option, - #[structopt( - long, - required_if("http2", "true"), - default_value = "", - env = "SERVER_HTTP2_TLS_KEY" - )] + #[structopt(long, required_if("http2", "true"), env = "SERVER_HTTP2_TLS_KEY")] /// Specify the file path to read the private key. - pub http2_tls_key: String, + pub http2_tls_key: Option, #[structopt( long, diff --git a/src/settings/file.rs b/src/settings/file.rs index bff1623..d0b565c 100644 --- a/src/settings/file.rs +++ b/src/settings/file.rs @@ -2,8 +2,8 @@ use headers::HeaderMap; use serde::Deserialize; -use std::collections::BTreeSet; use std::path::Path; +use std::{collections::BTreeSet, path::PathBuf}; use crate::{helpers, Context, Result}; @@ -53,7 +53,7 @@ pub struct General { // Address & Root dir pub host: Option, pub port: Option, - pub root: Option, + pub root: Option, // Logging pub log_level: Option, @@ -65,13 +65,13 @@ pub struct General { pub compression: Option, // Error pages - pub page404: Option, - pub page50x: Option, + pub page404: Option, + pub page50x: Option, // HTTP/2 + TLS pub http2: Option, - pub http2_tls_cert: Option, - pub http2_tls_key: Option, + pub http2_tls_cert: Option, + pub http2_tls_key: Option, // Security headers pub security_headers: Option, @@ -95,7 +95,7 @@ pub struct General { pub grace_period: Option, - pub page_fallback: Option, + pub page_fallback: Option, } /// Full server configuration diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 65ebf9f..e168204 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -36,28 +36,28 @@ impl Settings { let opts = General::from_args(); // Define the general CLI/file options - let mut host = opts.host.to_owned(); + let mut host = opts.host; let mut port = opts.port; - let mut root = opts.root.to_owned(); - let mut log_level = opts.log_level.to_owned(); + let mut root = opts.root; + let mut log_level = opts.log_level; let mut config_file = opts.config_file.clone(); let mut cache_control_headers = opts.cache_control_headers; let mut compression = opts.compression; - let mut page404 = opts.page404.to_owned(); - let mut page50x = opts.page50x.to_owned(); + let mut page404 = opts.page404; + let mut page50x = opts.page50x; let mut http2 = opts.http2; - let mut http2_tls_cert = opts.http2_tls_cert.to_owned(); - let mut http2_tls_key = opts.http2_tls_key.to_owned(); + let mut http2_tls_cert = opts.http2_tls_cert; + let mut http2_tls_key = opts.http2_tls_key; let mut security_headers = opts.security_headers; - let mut cors_allow_origins = opts.cors_allow_origins.to_owned(); - let mut cors_allow_headers = opts.cors_allow_headers.to_owned(); + let mut cors_allow_origins = opts.cors_allow_origins; + let mut cors_allow_headers = opts.cors_allow_headers; let mut directory_listing = opts.directory_listing; let mut directory_listing_order = opts.directory_listing_order; - let mut basic_auth = opts.basic_auth.to_owned(); + let mut basic_auth = opts.basic_auth; let mut fd = opts.fd; let mut threads_multiplier = opts.threads_multiplier; let mut grace_period = opts.grace_period; - let mut page_fallback = opts.page_fallback.to_owned(); + let mut page_fallback = opts.page_fallback; // Define the advanced file options let mut settings_advanced: Option = None; @@ -79,14 +79,14 @@ impl Settings { // Assign the corresponding file option values if let Some(general) = settings.general { - if let Some(ref v) = general.host { - host = v.to_owned() + if let Some(v) = general.host { + host = v } if let Some(v) = general.port { port = v } - if let Some(ref v) = general.root { - root = v.to_owned() + if let Some(v) = general.root { + root = v } if let Some(ref v) = general.log_level { log_level = v.name().to_lowercase(); @@ -97,20 +97,20 @@ impl Settings { if let Some(v) = general.compression { compression = v } - if let Some(ref v) = general.page404 { - page404 = v.to_owned() + if let Some(v) = general.page404 { + page404 = v } - if let Some(ref v) = general.page50x { - page50x = v.to_owned() + if let Some(v) = general.page50x { + page50x = v } if let Some(v) = general.http2 { http2 = v } - if let Some(ref v) = general.http2_tls_cert { - http2_tls_cert = v.to_owned() + if let Some(v) = general.http2_tls_cert { + http2_tls_cert = Some(v) } - if let Some(ref v) = general.http2_tls_key { - http2_tls_key = v.to_owned() + if let Some(v) = general.http2_tls_key { + http2_tls_key = Some(v) } if let Some(v) = general.security_headers { security_headers = v @@ -139,8 +139,8 @@ impl Settings { if let Some(v) = general.grace_period { grace_period = v } - if let Some(ref v) = general.page_fallback { - page_fallback = v.to_owned() + if let Some(v) = general.page_fallback { + page_fallback = Some(v) } } -- libgit2 1.7.2