diff --git a/tower-http/src/services/fs/serve_dir/mod.rs b/tower-http/src/services/fs/serve_dir/mod.rs index 61b956d1..122907e7 100644 --- a/tower-http/src/services/fs/serve_dir/mod.rs +++ b/tower-http/src/services/fs/serve_dir/mod.rs @@ -76,6 +76,7 @@ impl ServeDir { precompressed_variants: None, variant: ServeVariant::Directory { append_index_html_on_directories: true, + html_as_default_extension: true, }, fallback: None, call_fallback_on_method_not_allowed: false, @@ -105,9 +106,7 @@ impl ServeDir { /// Defaults to `true`. pub fn append_index_html_on_directories(mut self, append: bool) -> Self { match &mut self.variant { - ServeVariant::Directory { - append_index_html_on_directories, - } => { + ServeVariant::Directory { append_index_html_on_directories, .. } => { *append_index_html_on_directories = append; self } @@ -115,6 +114,19 @@ impl ServeDir { } } + /// If the requested path doesn't specify a file extension, append `.html`. + /// + /// Defaults to `true`. + pub fn html_as_default_extension(mut self, append: bool) -> Self { + match &mut self.variant { + ServeVariant::Directory { html_as_default_extension, .. } => { + *html_as_default_extension = append; + self + } + ServeVariant::SingleFile { mime: _ } => self, + } + } + /// Set a specific read buffer chunk size. /// /// The default capacity is 64kb. @@ -443,6 +455,7 @@ opaque_future! { enum ServeVariant { Directory { append_index_html_on_directories: bool, + html_as_default_extension: bool, }, SingleFile { mime: HeaderValue, @@ -454,6 +467,7 @@ impl ServeVariant { match self { ServeVariant::Directory { append_index_html_on_directories: _, + html_as_default_extension: _, } => { let path = requested_path.trim_start_matches('/'); diff --git a/tower-http/src/services/fs/serve_dir/open_file.rs b/tower-http/src/services/fs/serve_dir/open_file.rs index f182d422..d92493c1 100644 --- a/tower-http/src/services/fs/serve_dir/open_file.rs +++ b/tower-http/src/services/fs/serve_dir/open_file.rs @@ -59,6 +59,7 @@ pub(super) async fn open_file( let mime = match variant { ServeVariant::Directory { append_index_html_on_directories, + html_as_default_extension, } => { // Might already at this point know a redirect or not found result should be // returned which corresponds to a Some(output). Otherwise the path might be @@ -67,6 +68,7 @@ pub(super) async fn open_file( &mut path_to_file, req.uri(), append_index_html_on_directories, + html_as_default_extension, ) .await { @@ -254,18 +256,23 @@ async fn maybe_redirect_or_append_path( path_to_file: &mut PathBuf, uri: &Uri, append_index_html_on_directories: bool, + html_as_default_extension: bool, ) -> Option { - if !is_dir(path_to_file).await { + if is_dir(path_to_file).await == Some(false) { return None; } - if !append_index_html_on_directories { + if !append_index_html_on_directories && !html_as_default_extension { return Some(OpenFileOutput::FileNotFound); } - if uri.path().ends_with('/') { + let path = uri.path(); + if append_index_html_on_directories && path.ends_with('/') { path_to_file.push("index.html"); None + } else if html_as_default_extension && path_to_file.extension().is_none() { + path_to_file.set_extension("html"); + None } else { let location = HeaderValue::from_str(&append_slash_on_path(uri.clone()).to_string()).unwrap(); @@ -283,10 +290,11 @@ fn try_parse_range( }) } -async fn is_dir(path_to_file: &Path) -> bool { +async fn is_dir(path_to_file: &Path) -> Option { tokio::fs::metadata(path_to_file) .await - .map_or(false, |meta_data| meta_data.is_dir()) + .ok() + .map(|meta_data| meta_data.is_dir()) } fn append_slash_on_path(uri: Uri) -> Uri {