From cba4a8337da7b149f185d564c4f218fb46e94e99 Mon Sep 17 00:00:00 2001 From: Paul Hennig Date: Tue, 15 Mar 2022 21:01:24 +0100 Subject: [PATCH] Add fallback page option (#91) feat: fallback page option via `--page-fallback` argument --- docs/content/features/error-pages.md | 15 ++++++++++++++- src/config.rs | 8 ++++++-- src/fallback_page.rs | 19 +++++++++++++++++++ src/handler.rs | 25 ++++++++++++++++++------- src/lib.rs | 1 + src/server.rs | 4 ++++ 6 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 src/fallback_page.rs diff --git a/docs/content/features/error-pages.md b/docs/content/features/error-pages.md index 5c270da..b772cfb 100644 --- a/docs/content/features/error-pages.md +++ b/docs/content/features/error-pages.md @@ -5,7 +5,7 @@ This feature is enabled by default and can be controlled either by the string `--page404` ([SERVER_ERROR_PAGE_404](./../configuration/environment-variables.md#server_error_page_404)) or the `--page50x` ([SERVER_ERROR_PAGE_50X](./../configuration/environment-variables.md#server_error_page_50x)) arguments. !!! info "Tip" - Either `--page404` and `--page50x` have defaults (optional values) so they can be specified or omitted as required. +Either `--page404` and `--page50x` have defaults (optional values) so they can be specified or omitted as required. Below an example of how to customize those HTML pages. @@ -16,3 +16,16 @@ static-web-server \ --page404 ./my-page-404.html \ --page50x ./my-page-50x.html ``` + +## Fallback Page for use with Client Routers + +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 like `React Router` or similar. If the path is not specified or simply doesn't exist then this feature will not be active. + +It can be set with the `SERVER_FALLBACK_PAGE` environment variable or with the cli argument `--page-fallback`. + +```sh +static-web-server \ + --port 8787 \ + --root ./my-public-dir \ + --page-fallback ./my-public-dir/index.html +``` diff --git a/src/config.rs b/src/config.rs index 5126d9b..e04d838 100644 --- a/src/config.rs +++ b/src/config.rs @@ -47,7 +47,7 @@ pub struct Config { default_value = "./public/50x.html", env = "SERVER_ERROR_PAGE_50X" )] - /// HTML file path for 50x errors. If path is not specified or simply don't exists then server will use a generic HTML error message. + /// 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, #[structopt( @@ -55,9 +55,13 @@ pub struct Config { default_value = "./public/404.html", env = "SERVER_ERROR_PAGE_404" )] - /// HTML file path for 404 errors. If path is not specified or simply don't exists then server will use a generic HTML error message. + /// 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, + #[structopt(long, default_value = "", 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, + #[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 pub log_level: String, diff --git a/src/fallback_page.rs b/src/fallback_page.rs new file mode 100644 index 0000000..79528b5 --- /dev/null +++ b/src/fallback_page.rs @@ -0,0 +1,19 @@ +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 { + let body = Body::from(page_fallback.to_owned()); + let len = page_fallback.len() as u64; + + let mut resp = Response::new(body); + *resp.status_mut() = StatusCode::OK; + + resp.headers_mut().typed_insert(ContentLength(len)); + resp.headers_mut() + .typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8)); + resp.headers_mut().typed_insert(AcceptRanges::bytes()); + + resp +} diff --git a/src/handler.rs b/src/handler.rs index bbae8f5..9870485 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -2,7 +2,8 @@ use hyper::{header::WWW_AUTHENTICATE, Body, Method, Request, Response, StatusCod use std::{future::Future, path::PathBuf, sync::Arc}; use crate::{ - basic_auth, compression, control_headers, cors, error_page, security_headers, static_files, + basic_auth, compression, control_headers, cors, error_page, fallback_page, security_headers, + static_files, }; use crate::{Error, Result}; @@ -17,6 +18,7 @@ pub struct RequestHandlerOpts { pub cache_control_headers: bool, pub page404: String, pub page50x: String, + pub page_fallback: String, pub basic_auth: String, } @@ -154,12 +156,21 @@ impl RequestHandler { Ok(resp) } - Err(status) => error_page::error_response( - method, - &status, - &self.opts.page404, - &self.opts.page50x, - ), + Err(status) => { + if !self.opts.page_fallback.is_empty() + && (status == StatusCode::NOT_FOUND) + && (method == Method::GET) + { + Ok(fallback_page::fallback_response(&self.opts.page_fallback)) + } else { + error_page::error_response( + method, + &status, + &self.opts.page404, + &self.opts.page50x, + ) + } + } } } } diff --git a/src/lib.rs b/src/lib.rs index 3969b8d..2cb08e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod config; pub mod control_headers; pub mod cors; pub mod error_page; +pub mod fallback_page; pub mod handler; pub mod helpers; pub mod logger; diff --git a/src/server.rs b/src/server.rs index 3d937c5..a46fb66 100644 --- a/src/server.rs +++ b/src/server.rs @@ -95,6 +95,9 @@ impl Server { let page404 = helpers::read_file_content(&opts.page404); let page50x = helpers::read_file_content(&opts.page50x); + // Fallback page content + let page_fallback = helpers::read_file_content(&opts.page_fallback); + // Number of worker threads option let threads = self.threads; tracing::info!("runtime worker threads: {}", self.threads); @@ -148,6 +151,7 @@ impl Server { cache_control_headers, page404, page50x, + page_fallback, basic_auth, }), }); -- libgit2 1.7.2