index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2022-01-01 21:54:53.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2022-01-01 21:54:53.0 +00:00:00
commit
5926c9b27c5625398c7588c71588b433cffaff5b [patch]
tree
d65f1318b184a5663d95ab635fcf245c16509640
parent
4791f7cbb1654dc37ae2a47260292a0e588f452f
download
5926c9b27c5625398c7588c71588b433cffaff5b.tar.gz

refactor: trailing slash redirection for directories

it check for a trailing slash on the current directory uri and
redirect permanently (308) if that path doesn't end with the slash char

resolves #73

Diff

 src/static_files.rs   | 44 +++++++++++++++++++++++---------------------
 tests/static_files.rs | 43 ++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 63 insertions(+), 24 deletions(-)

diff --git a/src/static_files.rs b/src/static_files.rs
index dbae39c..2433187 100644
--- a/src/static_files.rs
+++ b/src/static_files.rs
@@ -58,30 +58,32 @@ pub async fn handle(
    let base = Arc::new(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('/') {
        let uri = [uri_path, "/"].concat();
        let loc = match HeaderValue::from_str(uri.as_str()) {
            Ok(val) => val,
            Err(err) => {
                tracing::error!("invalid header value from current uri: {:?}", err);
                return Err(StatusCode::INTERNAL_SERVER_ERROR);
            }
        };

        let mut resp = Response::new(Body::empty());
        resp.headers_mut().insert(hyper::header::LOCATION, loc);
        *resp.status_mut() = StatusCode::PERMANENT_REDIRECT;
        tracing::trace!("uri doesn't end with a slash so redirecting permanently");
        return Ok(resp);
    }

    // Directory listing
    // 1. Check if "directory listing" feature is enabled,
    // 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 it does not contain an `index.html` file (if a proper auto index is generated)
    if dir_listing && auto_index && !filepath.as_ref().exists() {
        // Redirect if current path does not end with a slash char
        if !uri_path.ends_with('/') {
            let uri = [uri_path, "/"].concat();
            let loc = match HeaderValue::from_str(uri.as_str()) {
                Ok(val) => val,
                Err(err) => {
                    tracing::error!("invalid header value from current uri: {:?}", err);
                    return Err(StatusCode::INTERNAL_SERVER_ERROR);
                }
            };

            let mut resp = Response::new(Body::empty());
            resp.headers_mut().insert(hyper::header::LOCATION, loc);
            *resp.status_mut() = StatusCode::PERMANENT_REDIRECT;
            tracing::trace!("uri doesn't end with a slash so redirect permanently");

            return Ok(resp);
        }

        return directory_listing(
            method,
            uri_path,
diff --git a/tests/static_files.rs b/tests/static_files.rs
index 9631cd1..9bc8735 100644
--- a/tests/static_files.rs
+++ b/tests/static_files.rs
@@ -116,6 +116,30 @@ 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,
        )
        .await
        .expect("unexpected error response on `handle` function");

        assert_eq!(res.status(), 308);
        assert_eq!(res.headers()["location"], "assets/");

        let body = hyper::body::to_bytes(res.body_mut())
            .await
            .expect("unexpected bytes error during `body` conversion");

        assert_eq!(body, Bytes::new());
    }

    #[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");
@@ -134,9 +158,22 @@ mod tests {
                )
                .await
                {
                    Ok(res) => {
                        assert_eq!(res.status(), 200);
                        assert_eq!(res.headers()["content-length"], buf.len().to_string());
                    Ok(mut res) => {
                        if uri.is_empty() {
                            // it should redirect permanently
                            assert_eq!(res.status(), 308);
                            assert_eq!(res.headers()["location"], "/");

                            let body = hyper::body::to_bytes(res.body_mut())
                                .await
                                .expect("unexpected bytes error during `body` conversion");

                            assert_eq!(body, Bytes::new());
                        } else {
                            // otherwise it should response with ok
                            assert_eq!(res.status(), 200);
                            assert_eq!(res.headers()["content-length"], buf.len().to_string());
                        }
                    }
                    Err(_) => {
                        panic!("expected a status 200 but not a status error")