From 904c3cb20ba2922157425a33befc44947c78be6c Mon Sep 17 00:00:00 2001 From: Philipp Hartenfeller <34486873+phartenfeller@users.noreply.github.com> Date: Sat, 13 Aug 2022 22:53:56 +0200 Subject: [PATCH] New flag to make trailing slash redirect optional (#131) * added redirect-trailing-slash flag * add default option to test config * fixed existing tests * added tests * added flag to docs * refactor: grouping static-files handle parameters into a new type * implemented change requests Co-authored-by: Jose Quintana --- docker/public/assets/index.html | 18 ++++++++++++++++++ docs/content/configuration/command-line-arguments.md | 3 +++ docs/content/configuration/config-file.md | 3 +++ docs/content/configuration/environment-variables.md | 3 +++ src/handler.rs | 16 +++++++++++----- src/server.rs | 8 ++++++++ src/settings/cli.rs | 9 +++++++++ src/settings/file.rs | 2 ++ src/settings/mod.rs | 5 +++++ src/static_files.rs | 37 ++++++++++++++++++++++--------------- tests/dir_listing.rs | 21 +++++++++++---------- tests/static_files.rs | 437 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------- tests/toml/config.toml | 3 +++ 13 files changed, 414 insertions(+), 151 deletions(-) create mode 100644 docker/public/assets/index.html diff --git a/docker/public/assets/index.html b/docker/public/assets/index.html new file mode 100644 index 0000000..a546342 --- /dev/null +++ b/docker/public/assets/index.html @@ -0,0 +1,18 @@ + + + + + + Assets Page + + + + + +

Assets Page

+

A blazing fast and asynchronous web server for static files-serving. ⚡

+

View on GitHub

+ + + + diff --git a/docs/content/configuration/command-line-arguments.md b/docs/content/configuration/command-line-arguments.md index 3fd7634..28c5c28 100644 --- a/docs/content/configuration/command-line-arguments.md +++ b/docs/content/configuration/command-line-arguments.md @@ -88,6 +88,9 @@ OPTIONS: 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] -p, --port Host port [env: SERVER_PORT=] [default: 80] + --redirect-trailing-slash + Check for trailing slash in the requested directory uri and redirect permanent (308) to the same path with a + trailing slash suffix if it is missing [env: REDIRECT_TRAILING_SLASH=] [default: true] -d, --root Root directory path of static files [env: SERVER_ROOT=] [default: ./public] diff --git a/docs/content/configuration/config-file.md b/docs/content/configuration/config-file.md index ff83cbb..4c7ef8d 100644 --- a/docs/content/configuration/config-file.md +++ b/docs/content/configuration/config-file.md @@ -63,6 +63,9 @@ grace-period = 0 #### Log request Remote Address if available log-remote-address = false +#### Redirect to trailing slash in the requested directory uri +redirect-trailing-slash = true + ### Windows Only diff --git a/docs/content/configuration/environment-variables.md b/docs/content/configuration/environment-variables.md index a64e89d..1540342 100644 --- a/docs/content/configuration/environment-variables.md +++ b/docs/content/configuration/environment-variables.md @@ -75,6 +75,9 @@ Enable cache control headers for incoming requests based on a set of file types. ### SERVER_BASIC_AUTH It provides [The "Basic" HTTP Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc7617) using credentials as `user-id:password` pairs, encoded using `Base64`. Password must be encoded using the [BCrypt](https://en.wikipedia.org/wiki/Bcrypt) password-hashing function. Default empty (disabled). +### REDIRECT_TRAILING_SLASH +Check for trailing slash in the requested directory uri and redirect permanent (308) to the same path with a trailing slash suffix if it is missing. Default `true` (enabled). + ## Windows The following options and commands are Windows platform-specific. diff --git a/src/handler.rs b/src/handler.rs index a68e1e7..fc0cb90 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -4,7 +4,10 @@ use std::{future::Future, net::SocketAddr, path::PathBuf, sync::Arc}; use crate::{ basic_auth, compression, control_headers, cors, custom_headers, error_page, fallback_page, - redirects, rewrites, security_headers, settings::Advanced, static_files, Error, Result, + redirects, rewrites, security_headers, + settings::Advanced, + static_files::{self, HandleOpts}, + Error, Result, }; /// It defines options for a request handler. @@ -22,6 +25,7 @@ pub struct RequestHandlerOpts { pub page_fallback: Vec, pub basic_auth: String, pub log_remote_address: bool, + pub redirect_trailing_slash: bool, // Advanced options pub advanced_opts: Option, @@ -43,12 +47,13 @@ impl RequestHandler { let headers = req.headers(); let uri = req.uri(); - let root_dir = &self.opts.root_dir; + let base_path = &self.opts.root_dir; let mut uri_path = uri.path(); let uri_query = uri.query(); let dir_listing = self.opts.dir_listing; let dir_listing_order = self.opts.dir_listing_order; let log_remote_addr = self.opts.log_remote_address; + let redirect_trailing_slash = self.opts.redirect_trailing_slash; let mut cors_headers: Option = None; @@ -164,15 +169,16 @@ impl RequestHandler { } // Static files - match static_files::handle( + match static_files::handle(&HandleOpts { method, headers, - root_dir, + base_path, uri_path, uri_query, dir_listing, dir_listing_order, - ) + redirect_trailing_slash, + }) .await { Ok(mut resp) => { diff --git a/src/server.rs b/src/server.rs index 9cde513..40a6b76 100644 --- a/src/server.rs +++ b/src/server.rs @@ -167,6 +167,13 @@ impl Server { let log_remote_address = general.log_remote_address; tracing::info!("log remote address: enabled={}", log_remote_address); + // Log redirect trailing slash option + let redirect_trailing_slash = general.redirect_trailing_slash; + tracing::info!( + "redirect trailing slash: enabled={}", + redirect_trailing_slash + ); + // Grace period option let grace_period = general.grace_period; tracing::info!("grace period before graceful shutdown: {}s", grace_period); @@ -186,6 +193,7 @@ impl Server { page_fallback, basic_auth, log_remote_address, + redirect_trailing_slash, advanced_opts, }), }); diff --git a/src/settings/cli.rs b/src/settings/cli.rs index abb83fc..ec3ded3 100644 --- a/src/settings/cli.rs +++ b/src/settings/cli.rs @@ -179,6 +179,15 @@ pub struct General { /// Log incoming requests information along with its remote address if available using the `info` log level. pub log_remote_address: bool, + #[structopt( + long, + parse(try_from_str), + default_value = "true", + env = "REDIRECT_TRAILING_SLASH" + )] + /// Check for trailing slash in the requested directory uri and redirect permanent (308) to the same path with a trailing slash suffix if it is missing. + pub redirect_trailing_slash: bool, + // // Windows specific arguments and commands // diff --git a/src/settings/file.rs b/src/settings/file.rs index fc74816..0197379 100644 --- a/src/settings/file.rs +++ b/src/settings/file.rs @@ -128,6 +128,8 @@ pub struct General { pub log_remote_address: Option, + pub redirect_trailing_slash: Option, + #[cfg(windows)] pub windows_service: Option, } diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 77cbfca..9904a37 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -83,6 +83,7 @@ impl Settings { let mut grace_period = opts.grace_period; let mut page_fallback = opts.page_fallback; let mut log_remote_address = opts.log_remote_address; + let mut redirect_trailing_slash = opts.redirect_trailing_slash; // Windows-only options #[cfg(windows)] @@ -174,6 +175,9 @@ impl Settings { if let Some(v) = general.log_remote_address { log_remote_address = v } + if let Some(v) = general.redirect_trailing_slash { + redirect_trailing_slash = v + } // Windows-only options #[cfg(windows)] @@ -300,6 +304,7 @@ impl Settings { grace_period, page_fallback, log_remote_address, + redirect_trailing_slash, // Windows-only options and commands #[cfg(windows)] diff --git a/src/static_files.rs b/src/static_files.rs index 7781a5c..f612b7b 100644 --- a/src/static_files.rs +++ b/src/static_files.rs @@ -39,30 +39,37 @@ impl AsRef for ArcPath { } } +/// Defines all options needed by the static-files handler. +pub struct HandleOpts<'a> { + pub method: &'a Method, + pub headers: &'a HeaderMap, + pub base_path: &'a PathBuf, + pub uri_path: &'a str, + pub uri_query: Option<&'a str>, + pub dir_listing: bool, + pub dir_listing_order: u8, + pub redirect_trailing_slash: bool, +} + /// Entry point to handle incoming requests which map to specific files /// on file system and return a file response. -pub async fn handle( - method: &Method, - headers: &HeaderMap, - base_path: impl Into, - uri_path: &str, - uri_query: Option<&str>, - dir_listing: bool, - dir_listing_order: u8, -) -> Result, StatusCode> { +pub async fn handle<'a>(opts: &HandleOpts<'a>) -> Result, StatusCode> { + let method = opts.method; + let uri_path = opts.uri_path; + // Check for disallowed HTTP methods and reject request accordently if !(method == Method::GET || method == Method::HEAD || method == Method::OPTIONS) { return Err(StatusCode::METHOD_NOT_ALLOWED); } - let base = Arc::new(base_path.into()); + let base = Arc::new(opts.base_path.into()); let (filepath, meta, auto_index) = path_from_tail(base, uri_path).await?; // NOTE: `auto_index` appends an `index.html` to an `uri_path` of kind directory only. // Check for a trailing slash on the current directory path // and redirect if that path doesn't end with the slash char - if auto_index && !uri_path.ends_with('/') { + if opts.redirect_trailing_slash && auto_index && !uri_path.ends_with('/') { let uri = [uri_path, "/"].concat(); let loc = match HeaderValue::from_str(uri.as_str()) { Ok(val) => val, @@ -97,18 +104,18 @@ pub async fn handle( // 1. Check if "directory listing" feature is enabled // if current path is a valid directory and // if it does not contain an `index.html` file (if a proper auto index is generated) - if dir_listing && auto_index && !filepath.as_ref().exists() { + if opts.dir_listing && auto_index && !filepath.as_ref().exists() { return directory_listing( method, uri_path, - uri_query, + opts.uri_query, filepath.as_ref(), - dir_listing_order, + opts.dir_listing_order, ) .await; } - file_reply(headers, (filepath, &meta, auto_index)).await + file_reply(opts.headers, (filepath, &meta, auto_index)).await } /// Convert an incoming uri into a valid and sanitized path then returns a tuple diff --git a/tests/dir_listing.rs b/tests/dir_listing.rs index 23cdd53..f0430d3 100644 --- a/tests/dir_listing.rs +++ b/tests/dir_listing.rs @@ -9,7 +9,7 @@ mod tests { use http::{Method, StatusCode}; use std::path::PathBuf; - use static_web_server::static_files; + use static_web_server::static_files::{self, HandleOpts}; fn root_dir() -> PathBuf { PathBuf::from("docker/public/") @@ -28,15 +28,16 @@ mod tests { Method::TRACE, ]; for method in methods { - match static_files::handle( - &method, - &HeaderMap::new(), - root_dir(), - "/assets", - None, - true, - 6, - ) + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "/assets", + uri_query: None, + dir_listing: true, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await { Ok(res) => { diff --git a/tests/static_files.rs b/tests/static_files.rs index 44e1ad9..40a9505 100644 --- a/tests/static_files.rs +++ b/tests/static_files.rs @@ -11,7 +11,10 @@ mod tests { use std::fs; use std::path::PathBuf; - use static_web_server::{compression, static_files}; + use static_web_server::{ + compression, + static_files::{self, HandleOpts}, + }; fn root_dir() -> PathBuf { PathBuf::from("docker/public/") @@ -19,15 +22,16 @@ mod tests { #[tokio::test] async fn handle_file() { - let mut res = static_files::handle( - &Method::GET, - &HeaderMap::new(), - root_dir(), - "index.html", - None, - false, - 6, - ) + let mut res = static_files::handle(&HandleOpts { + method: &Method::GET, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await .expect("unexpected error response on `handle` function"); @@ -57,15 +61,16 @@ mod tests { #[tokio::test] async fn handle_file_head() { - let mut res = static_files::handle( - &Method::HEAD, - &HeaderMap::new(), - root_dir(), - "index.html", - None, - false, - 6, - ) + let mut res = static_files::handle(&HandleOpts { + method: &Method::HEAD, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await .expect("unexpected error response on `handle` function"); @@ -96,15 +101,16 @@ mod tests { #[tokio::test] async fn handle_file_not_found() { for method in [Method::HEAD, Method::GET] { - match static_files::handle( - &method, - &HeaderMap::new(), - root_dir(), - "xyz.html", - None, - false, - 6, - ) + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "xyz.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await { Ok(_) => { @@ -119,15 +125,16 @@ mod tests { #[tokio::test] async fn handle_trailing_slash_redirection() { - let mut res = static_files::handle( - &Method::GET, - &HeaderMap::new(), - root_dir(), - "assets", - None, - false, - 0, - ) + let mut res = static_files::handle(&HandleOpts { + method: &Method::GET, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "assets", + uri_query: None, + dir_listing: false, + dir_listing_order: 0, + redirect_trailing_slash: true, + }) .await .expect("unexpected error response on `handle` function"); @@ -142,6 +149,53 @@ mod tests { } #[tokio::test] + async fn handle_trailing_slash_redirection_subdir() { + match static_files::handle(&HandleOpts { + method: &Method::GET, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "assets", + uri_query: None, + dir_listing: false, + dir_listing_order: 0, + redirect_trailing_slash: true, + }) + .await + { + Ok(res) => { + assert_eq!(res.status(), 308); + assert_eq!(res.headers()["location"], "assets/"); + } + Err(status) => { + panic!("expected a status 308 but not a status {}", status) + } + } + } + + #[tokio::test] + async fn handle_disabled_trailing_slash_redirection_subdir() { + match static_files::handle(&HandleOpts { + method: &Method::GET, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "assets", + uri_query: None, + dir_listing: false, + dir_listing_order: 0, + redirect_trailing_slash: false, + }) + .await + { + Ok(res) => { + assert_eq!(res.status(), 200); + } + Err(status) => { + panic!("expected a status 200 but not a status {}", status) + } + } + } + + #[tokio::test] async fn handle_append_index_on_dir() { let buf = fs::read(root_dir().join("index.html")) .expect("unexpected error during index.html reading"); @@ -149,15 +203,16 @@ mod tests { for method in [Method::HEAD, Method::GET] { for uri in ["", "/"] { - match static_files::handle( - &method, - &HeaderMap::new(), - root_dir(), - uri, - None, - false, - 6, - ) + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: uri, + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await { Ok(mut res) => { @@ -192,15 +247,16 @@ mod tests { let buf = Bytes::from(buf); for method in [Method::HEAD, Method::GET] { - match static_files::handle( - &method, - &HeaderMap::new(), - root_dir(), - "/index%2ehtml", - None, - false, - 6, - ) + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "/index%2ehtml", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await { Ok(res) => { @@ -217,15 +273,16 @@ mod tests { #[tokio::test] async fn handle_bad_encoded_path() { for method in [Method::HEAD, Method::GET] { - match static_files::handle( - &method, - &HeaderMap::new(), - root_dir(), - "/%2E%2e.html", - None, - false, - 6, - ) + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "/%2E%2e.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await { Ok(_) => { @@ -245,15 +302,16 @@ mod tests { let buf = Bytes::from(buf); for method in [Method::HEAD, Method::GET] { - let res1 = match static_files::handle( - &method, - &HeaderMap::new(), - root_dir(), - "index.html", - None, - false, - 6, - ) + let res1 = match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await { Ok(res) => { @@ -273,8 +331,17 @@ mod tests { res1.headers()["last-modified"].to_owned(), ); - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 304); @@ -296,8 +363,17 @@ mod tests { "Mon, 18 Nov 1974 00:00:00 GMT".parse().unwrap(), ); - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 200); @@ -317,15 +393,16 @@ mod tests { #[tokio::test] async fn handle_precondition() { for method in [Method::HEAD, Method::GET] { - let res1 = match static_files::handle( - &method, - &HeaderMap::new(), - root_dir(), - "index.html", - None, - false, - 6, - ) + let res1 = match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await { Ok(res) => { @@ -344,8 +421,17 @@ mod tests { res1.headers()["last-modified"].to_owned(), ); - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(res) => { assert_eq!(res.status(), 200); @@ -362,8 +448,17 @@ mod tests { "Mon, 18 Nov 1974 00:00:00 GMT".parse().unwrap(), ); - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 412); @@ -394,15 +489,16 @@ mod tests { Method::TRACE, ]; for method in methods { - match static_files::handle( - &method, - &HeaderMap::new(), - root_dir(), - "index.html", - None, - false, - 6, - ) + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) .await { Ok(mut res) => match method { @@ -451,8 +547,17 @@ mod tests { let mut headers = HeaderMap::new(); headers.insert(http::header::ACCEPT_ENCODING, enc.parse().unwrap()); - match static_files::handle(method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(res) => { let res = compression::auto(method, &headers, res) @@ -503,8 +608,17 @@ mod tests { let buf = Bytes::from(buf); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 206); @@ -535,8 +649,17 @@ mod tests { let buf = Bytes::from(buf); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 416); @@ -568,8 +691,17 @@ mod tests { let buf = Bytes::from(buf); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(res) => { assert_eq!(res.status(), 200); @@ -593,8 +725,17 @@ mod tests { let buf = Bytes::from(buf); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 206); @@ -628,8 +769,17 @@ mod tests { let buf = Bytes::from(buf); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 206); @@ -660,8 +810,17 @@ mod tests { let buf = Bytes::from(buf); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 416); @@ -695,8 +854,17 @@ mod tests { ); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 416); @@ -728,8 +896,17 @@ mod tests { headers.insert("range", "bytes=".parse().unwrap()); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 200); @@ -756,8 +933,17 @@ mod tests { headers.insert("range", format!("bytes=100-{}", buf.len()).parse().unwrap()); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 206); @@ -795,8 +981,17 @@ mod tests { ); for method in [Method::HEAD, Method::GET] { - match static_files::handle(&method, &headers, root_dir(), "index.html", None, false, 6) - .await + match static_files::handle(&HandleOpts { + method: &method, + headers: &headers, + base_path: &root_dir(), + uri_path: "index.html", + uri_query: None, + dir_listing: false, + dir_listing_order: 6, + redirect_trailing_slash: true, + }) + .await { Ok(mut res) => { assert_eq!(res.status(), 206); diff --git a/tests/toml/config.toml b/tests/toml/config.toml index 34b4ded..e785ac6 100644 --- a/tests/toml/config.toml +++ b/tests/toml/config.toml @@ -49,6 +49,9 @@ page-fallback = "" log-remote-address = false +#### Redirect to trailing slash in the requested directory uri +redirect-trailing-slash = true + ### Windows Only #### Run the web server as a Windows Service -- libgit2 1.7.2