Repository: static-web-server/static-web-server (language: rust)

Issue:
404 Page not change after server start
**Describe the bug**
We have started the server, but the 404 error if you use a page that is not there, is only using the page version that is used on start. but not update it while you have changed the complete source.

**To Reproduce**
1. start server
2. open not existing url
3. change 404.html
4. open another not existing url

**Expected behavior**
use the updated 404.html 

**Logs/Screenshots**
If applicable, add logs or screenshots to help explain your problem.

**Environment and Specs**
 - static-web-server: latest
 - OS: Ubuntu 20.04 with docker
 - Docker: latest
 - Client: latest chrome, latest firefox

**Additional context**
Add any other context about the problem here if needed.



Discussion / hints:
Yeah, but it is kind of on purpose. SWS loads the pages at start up time only.
However we could change that behavior to either load those files right after errors occur or fallback to default errors. In both cases at runtime.
Would it be alright to include a static 404 page for different MIME request types? And by default serve those?
> Would it be alright to include a static 404 page for different MIME request types? And by default serve those?

I didn't get it. Could you show an use case to help me to understand it?

Fix patch:
diff --git a/docs/content/configuration/command-line-arguments.md b/docs/content/configuration/command-line-arguments.md
index f854ad4b..eb9c3412 100644
--- a/docs/content/configuration/command-line-arguments.md
+++ b/docs/content/configuration/command-line-arguments.md
@@ -28,9 +28,9 @@ Options:
   -d, --root <ROOT>
           Root directory path of static files [env: SERVER_ROOT=] [default: ./public]
       --page50x <PAGE50X>
-          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 [env: SERVER_ERROR_PAGE_50X=] [default: ./public/50x.html]
+          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. If a relative path is used then it will be resolved under the root directory [env: SERVER_ERROR_PAGE_50X=] [default: ./50x.html]
       --page404 <PAGE404>
-          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 [env: SERVER_ERROR_PAGE_404=] [default: ./public/404.html]
+          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. If a relative path is used then it will be resolved under the root directory [env: SERVER_ERROR_PAGE_404=] [default: ./404.html]
       --page-fallback <PAGE_FALLBACK>
           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 [env: SERVER_FALLBACK_PAGE=] [default: ]
   -g, --log-level <LOG_LEVEL>
@@ -76,7 +76,7 @@ Options:
   -q, --grace-period <GRACE_PERIOD>
           Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds [env: SERVER_GRACE_PERIOD=] [default: 0]
   -w, --config-file <CONFIG_FILE>
-          Server TOML configuration file path [env: SERVER_CONFIG_FILE=]
+          Server TOML configuration file path [env: SERVER_CONFIG_FILE=] [default: ./config.toml]
       --log-remote-address[=<LOG_REMOTE_ADDRESS>]
           Log incoming requests information along with its remote address if available using the `info` log level [env: SERVER_LOG_REMOTE_ADDRESS=] [default: false] [possible values: true, false]
       --redirect-trailing-slash[=<REDIRECT_TRAILING_SLASH>]
diff --git a/docs/content/configuration/config-file.md b/docs/content/configuration/config-file.md
index 4b29f152..638c5802 100644
--- a/docs/content/configuration/config-file.md
+++ b/docs/content/configuration/config-file.md
@@ -26,8 +26,9 @@ cache-control-headers = true
 compression = true
 
 #### Error pages
-page404 = "./public/404.html"
-page50x = "./public/50x.html"
+# Note: If a relative path is used then it will be resolved under the root directory.
+page404 = "./404.html"
+page50x = "./50x.html"
 
 #### HTTP/2 + TLS
 http2 = false
diff --git a/docs/content/configuration/environment-variables.md b/docs/content/configuration/environment-variables.md
index a32308a4..bcb0da45 100644
--- a/docs/content/configuration/environment-variables.md
+++ b/docs/content/configuration/environment-variables.md
@@ -31,10 +31,12 @@ Specify a logging level in lower case. Possible values are `error`, `warn`, `inf
 Log incoming requests information along with its Remote Address (IP) if available using the `info` log level. Default `false`.
 
 ### 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. Default `./public/404.html`.
+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.
+If a relative path is used then it will be resolved under the root directory. Default `./404.html`.
 
 ### 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. Default `./public/50x.html`
+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.
+If a relative path is used then it will be resolved under the root directory. Default `./50x.html`
 
 ### 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 (E.g `React Router`). If the path is not specified or simply doesn't exist then this feature will not be active.
@@ -110,10 +112,13 @@ Activate the health endpoint.
 
 ### SERVER_INDEX_FILES
 List of files that will be used as an index for requests ending with the slash character (‘/’). Files are checked in the specified order. Default `index.html`.
+
 ### SERVER_MAINTENANCE_MODE
 Enable the server's maintenance mode functionality.
+
 ### SERVER_MAINTENANCE_MODE_STATUS
 Provide a custom HTTP status code when entering into maintenance mode. Default `503`.
+
 ### SERVER_MAINTENANCE_MODE_FILE
 Provide a custom maintenance mode HTML file. If not provided then a generic message will be displayed.
 
diff --git a/src/error_page.rs b/src/error_page.rs
index e97211b3..f86c8c54 100644
--- a/src/error_page.rs
+++ b/src/error_page.rs
@@ -9,16 +9,17 @@
 use headers::{AcceptRanges, ContentLength, ContentType, HeaderMapExt};
 use hyper::{Body, Method, Response, StatusCode, Uri};
 use mime_guess::mime;
+use std::path::Path;
 
-use crate::{exts::http::MethodExt, Result};
+use crate::{exts::http::MethodExt, helpers, Result};
 
 /// It returns a HTTP error response which also handles available `404` or `50x` HTML content.
 pub fn error_response(
     uri: &Uri,
     method: &Method,
     status_code: &StatusCode,
-    page404: &[u8],
-    page50x: &[u8],
+    page404: &Path,
+    page50x: &Path,
 ) -> Result<Response<Body>> {
     tracing::warn!(
         method = ?method, uri = ?uri, status = status_code.as_u16(),
@@ -26,7 +27,7 @@ pub fn error_response(
     );
 
     // Check for 4xx/50x status codes and handle their corresponding HTML content
-    let mut error_page_content = String::new();
+    let mut page_content = String::new();
     let status_code = match status_code {
         // 4xx
         &StatusCode::BAD_REQUEST
@@ -48,8 +49,18 @@ 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 && !page404.is_empty() {
-                error_page_content = String::from_utf8_lossy(page404).to_string();
+            if status_code == &StatusCode::NOT_FOUND {
+                if page404.is_file() {
+                    page_content = String::from_utf8_lossy(&helpers::read_bytes_default(page404))
+                        .to_string()
+                        .trim()
+                        .to_owned();
+                } else {
+                    tracing::debug!(
+                        "page404 file path not found or not a regular file: {}",
+                        page404.display()
+                    );
+                }
             }
             status_code
         }
@@ -64,8 +75,16 @@ pub fn error_response(
         | &StatusCode::INSUFFICIENT_STORAGE
         | &StatusCode::LOOP_DETECTED => {
             // HTML content check for status codes 50x
-            if !page50x.is_empty() {
-                error_page_content = String::from_utf8_lossy(page50x).to_string();
+            if page50x.is_file() {
+                page_content = String::from_utf8_lossy(&helpers::read_bytes_default(page50x))
+                    .to_string()
+                    .trim()
+                    .to_owned();
+            } else {
+                tracing::debug!(
+                    "page50x file path not found or not a regular file: {}",
+                    page50x.display()
+                );
             }
             status_code
         }
@@ -73,8 +92,8 @@ pub fn error_response(
         _ => status_code,
     };
 
-    if error_page_content.is_empty() {
-        error_page_content = [
+    if page_content.is_empty() {
+        page_content = [
             "<html><head><title>",
             status_code.as_str(),
             " ",
@@ -89,10 +108,10 @@ pub fn error_response(
     }
 
     let mut body = Body::empty();
-    let len = error_page_content.len() as u64;
+    let len = page_content.len() as u64;
 
     if !method.is_head() {
-        body = Body::from(error_page_content)
+        body = Body::from(page_content)
     }
 
     let mut resp = Response::new(body);
diff --git a/src/handler.rs b/src/handler.rs
index 65257111..2767c434 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -59,9 +59,9 @@ pub struct RequestHandlerOpts {
     /// Cache control headers feature.
     pub cache_control_headers: bool,
     /// Page for 404 errors.
-    pub page404: Vec<u8>,
+    pub page404: PathBuf,
     /// Page for 50x errors.
-    pub page50x: Vec<u8>,
+    pub page50x: PathBuf,
     /// Page fallback feature.
     #[cfg(feature = "fallback-page")]
     #[cfg_attr(docsrs, doc(cfg(feature = "fallback-page")))]
diff --git a/src/server.rs b/src/server.rs
index a98e97c4..e13e8d6c 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -159,9 +159,28 @@ impl Server {
         let root_dir = helpers::get_valid_dirpath(&general.root)
             .with_context(|| "root directory was not found or inaccessible")?;
 
-        // Custom error pages content
-        let page404 = helpers::read_bytes_default(&general.page404);
-        let page50x = helpers::read_bytes_default(&general.page50x);
+        // Custom HTML error page files
+        // NOTE: in the case of relative paths, they're joined to the root directory
+        let mut page404 = general.page404;
+        if page404.is_relative() && !page404.starts_with(&root_dir) {
+            page404 = root_dir.join(page404);
+        }
+        if !page404.is_file() {
+            tracing::debug!(
+                "404 file path not found or not a regular file: {}",
+                page404.display()
+            );
+        }
+        let mut page50x = general.page50x;
+        if page50x.is_relative() && !page50x.starts_with(&root_dir) {
+            page50x = root_dir.join(page50x);
+        }
+        if !page50x.is_file() {
+            tracing::debug!(
+                "50x file path not found or not a regular file: {}",
+                page50x.display()
+            );
+        }
 
         // Fallback page option
         #[cfg(feature = "fallback-page")]
diff --git a/src/settings/cli.rs b/src/settings/cli.rs
index 7bc1df99..71eaa2a1 100644
--- a/src/settings/cli.rs
+++ b/src/settings/cli.rs
@@ -101,20 +101,16 @@ pub struct General {
     /// Root directory path of static files.
     pub root: PathBuf,
 
-    #[arg(
-        long,
-        default_value = "./public/50x.html",
-        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.
+    #[arg(long, default_value = "./50x.html", 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.
+    /// If a relative path is used then it will be resolved under the root directory.
     pub page50x: PathBuf,
 
-    #[arg(
-        long,
-        default_value = "./public/404.html",
-        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.
+    #[arg(long, default_value = "./404.html", 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.
+    /// If a relative path is used then it will be resolved under the root directory.
     pub page404: PathBuf,
 
     #[cfg(feature = "fallback-page")]


Test patch: