index : static-web-server.git

ascending towards madness

author Jose Quintana <1700322+joseluisq@users.noreply.github.com> 2022-11-19 1:24:06.0 +00:00:00
committer GitHub <noreply@github.com> 2022-11-19 1:24:06.0 +00:00:00
commit
800416d91f0eddb3eecb365cb3ed291ed152258b [patch]
tree
ff3ff2b9c02a958603d664dcf07fff8fd88e3749
parent
a09ff1f3e254c564ae001d2a20243a499737cde0
download
800416d91f0eddb3eecb365cb3ed291ed152258b.tar.gz

feat: ignore hidden files/directories via `--ignore-hidden-files` (#162)

it adds the ability to ignore hidden files/directories (dotfiles),
preventing them to be served and being included in auto HTML index
pages (directory listing) via the new boolean `--ignore-hidden-files` option.

Diff

 docs/content/configuration/command-line-arguments.md |  3 +-
 docs/content/configuration/config-file.md            |  3 +-
 docs/content/configuration/environment-variables.md  |  3 +-
 docs/content/features/directory-listing.md           |  1 +-
 docs/content/features/ignore-files.md                | 19 +++++++-
 docs/mkdocs.yml                                      |  1 +-
 src/directory_listing.rs                             | 10 +++-
 src/exts/mod.rs                                      |  1 +-
 src/exts/path.rs                                     | 20 +++++++-
 src/handler.rs                                       |  3 +-
 src/server.rs                                        |  5 ++-
 src/settings/cli.rs                                  |  9 +++-
 src/settings/file.rs                                 |  2 +-
 src/settings/mod.rs                                  |  5 ++-
 src/static_files.rs                                  |  8 +++-
 tests/compression_static.rs                          |  2 +-
 tests/dir_listing.rs                                 | 48 +++++++++++++++++-
 tests/fixtures/public/.dotfile                       |  1 +-
 tests/static_files.rs                                | 59 +++++++++++++++++++++-
 19 files changed, 201 insertions(+), 2 deletions(-)

diff --git a/docs/content/configuration/command-line-arguments.md b/docs/content/configuration/command-line-arguments.md
index ede2217..8aaeca5 100644
--- a/docs/content/configuration/command-line-arguments.md
+++ b/docs/content/configuration/command-line-arguments.md
@@ -82,6 +82,9 @@ OPTIONS:
        --http2-tls-key <http2-tls-key>
            Specify the file path to read the private key [env: SERVER_HTTP2_TLS_KEY=]

        --ignore-hidden-files <ignore-hidden-files>
            Ignore hidden files/directories (dotfiles), preventing them to be served and being included in auto HTML
            index pages (directory listing) [env: SERVER_IGNORE_HIDDEN_FILES=]  [default: false]
    -g, --log-level <log-level>
            Specify a logging level in lower case. Values: error, warn, info, debug or trace [env: SERVER_LOG_LEVEL=]
            [default: error]
diff --git a/docs/content/configuration/config-file.md b/docs/content/configuration/config-file.md
index cd819ab..c1819e9 100644
--- a/docs/content/configuration/config-file.md
+++ b/docs/content/configuration/config-file.md
@@ -74,6 +74,9 @@ redirect-trailing-slash = true
#### Check for existing pre-compressed files
compression-static = false

#### Ignore hidden files/directories (dotfiles)
ignore-hidden-files = false


### Windows Only

diff --git a/docs/content/configuration/environment-variables.md b/docs/content/configuration/environment-variables.md
index 9fd09ec..0263627 100644
--- a/docs/content/configuration/environment-variables.md
+++ b/docs/content/configuration/environment-variables.md
@@ -87,6 +87,9 @@ It provides [The "Basic" HTTP Authentication Scheme](https://datatracker.ietf.or
### SERVER_REDIRECT_TRAILING_SLASH
Check for a 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).

### SERVER_IGNORE_HIDDEN_FILES
Ignore hidden files/directories (dotfiles), preventing them to be served and being included in auto HTML index pages (directory listing).

## Windows
The following options and commands are Windows platform-specific.

diff --git a/docs/content/features/directory-listing.md b/docs/content/features/directory-listing.md
index ffe116d..fc630ae 100644
--- a/docs/content/features/directory-listing.md
+++ b/docs/content/features/directory-listing.md
@@ -23,7 +23,6 @@ However, when the *"redirect trailing slash"* feature is disabled and a director

Note also that in both cases, SWS will append a trailing slash to the entry if is a directory.


## Sorting

Sorting by `Name`, `Last modified` and `Size` is enabled as clickable columns when the directory listing is activated via the `--directory-listing=true` option.
diff --git a/docs/content/features/ignore-files.md b/docs/content/features/ignore-files.md
new file mode 100644
index 0000000..d86067f
--- /dev/null
+++ b/docs/content/features/ignore-files.md
@@ -0,0 +1,19 @@
# Ignore files

SWS provides some options to ignore files or directories from being served and displayed if the directory listing is enabled. 

## Ignore hidden files (dotfiles)

SWS doesn't ignore dotfiles (hidden files) by default.
However, it's possible to ignore those files as shown below. As a result, SWS will respond with a `404 Not Found` status.

This feature is disabled by default and can be controlled by the boolean `--ignore-hidden-files` option or the equivalent [SERVER_IGNORE_HIDDEN_FILES]./../configuration/environment-variables.md#server_ignore_hidden_files env.

Here is an example of how to ignore hidden files:

```sh
static-web-server \
    -p=8787 -d=tests/fixtures/public -g=trace \
    --directory-listing=true \
    --ignore-hidden-files true
```
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 3b8a3ec..d7ef721 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -144,6 +144,7 @@ nav:
    - 'URL Redirects': 'features/url-redirects.md'
    - 'Windows Service': 'features/windows-service.md'
    - 'Trailing Slash Redirect': 'features/trailing-slash-redirect.md'
    - 'Ignore Files': 'features/ignore-files.md'
  - 'Platforms & Architectures': 'platforms-architectures.md'
  - 'Migration from v1 to v2': 'migration.md'
  - 'Changelog v2 (latest stable)': 'https://github.com/static-web-server/static-web-server/blob/master/CHANGELOG.md'
diff --git a/src/directory_listing.rs b/src/directory_listing.rs
index 052b576..1193cad 100644
--- a/src/directory_listing.rs
+++ b/src/directory_listing.rs
@@ -35,6 +35,7 @@ pub fn auto_index<'a>(
    filepath: &'a Path,
    dir_listing_order: u8,
    dir_listing_format: &'a DirListFmt,
    ignore_hidden_files: bool,
) -> impl Future<Output = Result<Response<Body>, StatusCode>> + Send + 'a {
    // Note: it's safe to call `parent()` here since `filepath`
    // value always refer to a path with file ending and under
@@ -53,6 +54,7 @@ pub fn auto_index<'a>(
                is_head,
                dir_listing_order,
                dir_listing_format,
                ignore_hidden_files,
            )
            .await
            {
@@ -122,6 +124,7 @@ async fn read_dir_entries(
    is_head: bool,
    mut order_code: u8,
    content_format: &DirListFmt,
    ignore_hidden_files: bool,
) -> Result<Response<Body>> {
    let mut dirs_count: usize = 0;
    let mut files_count: usize = 0;
@@ -134,8 +137,13 @@ async fn read_dir_entries(
            .file_name()
            .into_string()
            .map_err(|err| anyhow::anyhow!(err.into_string().unwrap_or_default()))?;
        let mut name_encoded = utf8_percent_encode(&name, NON_ALPHANUMERIC).to_string();

        // Check and ignore the current hidden file/directory (dotfile) if feature enabled
        if ignore_hidden_files && name.starts_with('.') {
            continue;
        }

        let mut name_encoded = utf8_percent_encode(&name, NON_ALPHANUMERIC).to_string();
        let mut filesize = 0_u64;

        if meta.is_dir() {
diff --git a/src/exts/mod.rs b/src/exts/mod.rs
index 5f2e89e..9831796 100644
--- a/src/exts/mod.rs
+++ b/src/exts/mod.rs
@@ -1,3 +1,4 @@
//! Some extension traits for various use cases.

pub mod http;
pub mod path;
diff --git a/src/exts/path.rs b/src/exts/path.rs
new file mode 100644
index 0000000..30628c9
--- /dev/null
+++ b/src/exts/path.rs
@@ -0,0 +1,20 @@
//! Path-related extension traits.

use std::path::{Component, Path};

/// SWS Path extensions trait.
pub trait PathExt {
    fn is_hidden(&self) -> bool;
}

impl PathExt for Path {
    /// Checks if the current path is hidden (dot file).
    fn is_hidden(&self) -> bool {
        self.components()
            .filter_map(|cmp| match cmp {
                Component::Normal(s) => s.to_str(),
                _ => None,
            })
            .any(|s| s.starts_with('.'))
    }
}
diff --git a/src/handler.rs b/src/handler.rs
index 7af5324..b7410b4 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -31,6 +31,7 @@ pub struct RequestHandlerOpts {
    pub basic_auth: String,
    pub log_remote_address: bool,
    pub redirect_trailing_slash: bool,
    pub ignore_hidden_files: bool,

    // Advanced options
    pub advanced_opts: Option<Advanced>,
@@ -61,6 +62,7 @@ impl RequestHandler {
        let log_remote_addr = self.opts.log_remote_address;
        let redirect_trailing_slash = self.opts.redirect_trailing_slash;
        let compression_static = self.opts.compression_static;
        let ignore_hidden_files = self.opts.ignore_hidden_files;

        let mut cors_headers: Option<http::HeaderMap> = None;

@@ -198,6 +200,7 @@ impl RequestHandler {
                dir_listing_format,
                redirect_trailing_slash,
                compression_static,
                ignore_hidden_files,
            })
            .await
            {
diff --git a/src/server.rs b/src/server.rs
index 2d931be..0ff4d52 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -183,6 +183,10 @@ impl Server {
            redirect_trailing_slash
        );

        // Ignore hidden files option
        let ignore_hidden_files = general.ignore_hidden_files;
        tracing::info!("ignore hidden files: enabled={}", ignore_hidden_files);

        // Grace period option
        let grace_period = general.grace_period;
        tracing::info!("grace period before graceful shutdown: {}s", grace_period);
@@ -205,6 +209,7 @@ impl Server {
                basic_auth,
                log_remote_address,
                redirect_trailing_slash,
                ignore_hidden_files,
                advanced_opts,
            }),
        });
diff --git a/src/settings/cli.rs b/src/settings/cli.rs
index 54802eb..8746958 100644
--- a/src/settings/cli.rs
+++ b/src/settings/cli.rs
@@ -219,6 +219,15 @@ pub struct General {
    /// Check for a trailing slash in the requested directory URI and redirect permanently (308) to the same path with a trailing slash suffix if it is missing.
    pub redirect_trailing_slash: bool,

    #[structopt(
        long,
        parse(try_from_str),
        default_value = "false",
        env = "SERVER_IGNORE_HIDDEN_FILES"
    )]
    /// Ignore hidden files/directories (dotfiles), preventing them to be served and being included in auto HTML index pages (directory listing).
    pub ignore_hidden_files: bool,

    //
    // Windows specific arguments and commands
    //
diff --git a/src/settings/file.rs b/src/settings/file.rs
index e2ecc60..682573a 100644
--- a/src/settings/file.rs
+++ b/src/settings/file.rs
@@ -136,6 +136,8 @@ pub struct General {

    pub redirect_trailing_slash: Option<bool>,

    pub ignore_hidden_files: Option<bool>,

    #[cfg(windows)]
    pub windows_service: Option<bool>,
}
diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index 39ae749..9dbf386 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -87,6 +87,7 @@ impl Settings {
        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;
        let mut ignore_hidden_files = opts.ignore_hidden_files;

        // Windows-only options
        #[cfg(windows)]
@@ -190,6 +191,9 @@ impl Settings {
                    if let Some(v) = general.redirect_trailing_slash {
                        redirect_trailing_slash = v
                    }
                    if let Some(v) = general.ignore_hidden_files {
                        ignore_hidden_files = v
                    }

                    // Windows-only options
                    #[cfg(windows)]
@@ -320,6 +324,7 @@ impl Settings {
                page_fallback,
                log_remote_address,
                redirect_trailing_slash,
                ignore_hidden_files,

                // Windows-only options and commands
                #[cfg(windows)]
diff --git a/src/static_files.rs b/src/static_files.rs
index dcb1db7..ba217f7 100644
--- a/src/static_files.rs
+++ b/src/static_files.rs
@@ -23,6 +23,7 @@ use std::task::{Context, Poll};

use crate::directory_listing::DirListFmt;
use crate::exts::http::{MethodExt, HTTP_SUPPORTED_METHODS};
use crate::exts::path::PathExt;
use crate::{compression_static, directory_listing, Result};

/// Defines all options needed by the static-files handler.
@@ -37,6 +38,7 @@ pub struct HandleOpts<'a> {
    pub dir_listing_format: &'a DirListFmt,
    pub redirect_trailing_slash: bool,
    pub compression_static: bool,
    pub ignore_hidden_files: bool,
}

/// Entry point to handle incoming requests which map to specific files
@@ -58,6 +60,11 @@ pub async fn handle<'a>(opts: &HandleOpts<'a>) -> Result<(Response<Body>, bool),
    let (file_path, meta, is_dir, precompressed_variant) =
        composed_file_metadata(&mut file_path, headers_opt, compression_static_opt).await?;

    // Check for a hidden file/directory (dotfile) and ignore it if feature enabled
    if opts.ignore_hidden_files && file_path.is_hidden() {
        return Err(StatusCode::NOT_FOUND);
    }

    // `is_precompressed` relates to `opts.compression_static` value
    let is_precompressed = precompressed_variant.is_some();

@@ -105,6 +112,7 @@ pub async fn handle<'a>(opts: &HandleOpts<'a>) -> Result<(Response<Body>, bool),
                file_path.as_ref(),
                opts.dir_listing_order,
                opts.dir_listing_format,
                opts.ignore_hidden_files,
            )
            .await?;

diff --git a/tests/compression_static.rs b/tests/compression_static.rs
index c697c6f..c15de60 100644
--- a/tests/compression_static.rs
+++ b/tests/compression_static.rs
@@ -43,6 +43,7 @@ mod tests {
            dir_listing_format: &DirListFmt::Html,
            redirect_trailing_slash: true,
            compression_static: true,
            ignore_hidden_files: false,
        })
        .await
        .expect("unexpected error response on `handle` function");
@@ -96,6 +97,7 @@ mod tests {
            dir_listing_format: &DirListFmt::Html,
            redirect_trailing_slash: true,
            compression_static: true,
            ignore_hidden_files: false,
        })
        .await
        .expect("unexpected error response on `handle` function");
diff --git a/tests/dir_listing.rs b/tests/dir_listing.rs
index ed3c94e..01ad20e 100644
--- a/tests/dir_listing.rs
+++ b/tests/dir_listing.rs
@@ -47,6 +47,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -76,6 +77,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -115,6 +117,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: false,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -154,6 +157,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: false,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -183,6 +187,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -233,6 +238,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Json,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: true,
            })
            .await
            {
@@ -301,6 +307,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Json,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -327,4 +334,45 @@ mod tests {
            }
        }
    }

    #[tokio::test]
    async fn dir_listing_ignore_hidden_files() {
        for method in METHODS {
            match static_files::handle(&HandleOpts {
                method: &method,
                headers: &HeaderMap::new(),
                base_path: &root_dir("tests/fixtures/public"),
                uri_path: "/",
                uri_query: None,
                dir_listing: true,
                dir_listing_order: 1,
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: true,
            })
            .await
            {
                Ok((mut res, _)) => {
                    assert_eq!(res.status(), 200);
                    assert_eq!(res.headers()["content-type"], "text/html; charset=utf-8");

                    let body = hyper::body::to_bytes(res.body_mut())
                        .await
                        .expect("unexpected bytes error during `body` conversion");
                    let body_str = std::str::from_utf8(&body).unwrap();

                    if method == Method::GET {
                        assert!(!body_str.contains(".dotfile"))
                    } else {
                        assert!(body_str.is_empty());
                    }
                }
                Err(status) => {
                    assert!(method != Method::GET && method != Method::HEAD);
                    assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
                }
            }
        }
    }
}
diff --git a/tests/fixtures/public/.dotfile b/tests/fixtures/public/.dotfile
new file mode 100644
index 0000000..5cb3ae4
--- /dev/null
+++ b/tests/fixtures/public/.dotfile
@@ -0,0 +1 @@
dotfile!
diff --git a/tests/static_files.rs b/tests/static_files.rs
index 81c623f..dffe3a3 100644
--- a/tests/static_files.rs
+++ b/tests/static_files.rs
@@ -34,6 +34,7 @@ mod tests {
            dir_listing_format: &DirListFmt::Html,
            redirect_trailing_slash: true,
            compression_static: false,
            ignore_hidden_files: false,
        })
        .await
        .expect("unexpected error response on `handle` function");
@@ -75,6 +76,7 @@ mod tests {
            dir_listing_format: &DirListFmt::Html,
            redirect_trailing_slash: true,
            compression_static: false,
            ignore_hidden_files: false,
        })
        .await
        .expect("unexpected error response on `handle` function");
@@ -117,6 +119,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -143,6 +146,7 @@ mod tests {
            dir_listing_format: &DirListFmt::Html,
            redirect_trailing_slash: true,
            compression_static: false,
            ignore_hidden_files: false,
        })
        .await
        .expect("unexpected error response on `handle` function");
@@ -170,6 +174,7 @@ mod tests {
            dir_listing_format: &DirListFmt::Html,
            redirect_trailing_slash: true,
            compression_static: false,
            ignore_hidden_files: false,
        })
        .await
        {
@@ -196,6 +201,7 @@ mod tests {
            dir_listing_format: &DirListFmt::Html,
            redirect_trailing_slash: false,
            compression_static: false,
            ignore_hidden_files: false,
        })
        .await
        {
@@ -227,6 +233,7 @@ mod tests {
                    dir_listing_format: &DirListFmt::Html,
                    redirect_trailing_slash: true,
                    compression_static: false,
                    ignore_hidden_files: false,
                })
                .await
                {
@@ -273,6 +280,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -301,6 +309,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -332,6 +341,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -363,6 +373,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -397,6 +408,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -429,6 +441,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -459,6 +472,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -488,6 +502,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -531,6 +546,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -591,6 +607,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -654,6 +671,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -697,6 +715,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -740,6 +759,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -784,6 +804,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -820,6 +841,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -866,6 +888,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -909,6 +932,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -955,6 +979,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -999,6 +1024,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -1038,6 +1064,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -1088,6 +1115,7 @@ mod tests {
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: false,
                ignore_hidden_files: false,
            })
            .await
            {
@@ -1112,4 +1140,35 @@ mod tests {
            }
        }
    }

    #[tokio::test]
    async fn handle_ignore_hidden_files() {
        let root_dir = PathBuf::from("tests/fixtures/public/");
        let headers = HeaderMap::new();

        for method in [Method::HEAD, Method::GET] {
            match static_files::handle(&HandleOpts {
                method: &method,
                headers: &headers,
                base_path: &root_dir,
                uri_path: ".dotfile",
                uri_query: None,
                dir_listing: false,
                dir_listing_order: 6,
                dir_listing_format: &DirListFmt::Html,
                redirect_trailing_slash: true,
                compression_static: true,
                ignore_hidden_files: true,
            })
            .await
            {
                Ok(_) => {
                    panic!("expected a status error 404 but not status 200")
                }
                Err(status) => {
                    assert_eq!(status, StatusCode::NOT_FOUND);
                }
            }
        }
    }
}