Repository: actix/actix-web
File: src/middleware/normalize.rs (Rust)

Code before:
    trailing_slash_behavior: TrailingSlash,
}

impl<S, B> Service<ServiceRequest> for NormalizePathNormalization<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = S::Future;

    actix_service::forward_ready!(service);

    fn call(&self, mut req: ServiceRequest) -> Self::Future {
        let head = req.head_mut();

        let original_path = head.uri.path();

        // An empty path here means that the URI has no valid path. We skip normalization in this
        // case, because adding a path can make the URI invalid
        if !original_path.is_empty() {
            // Either adds a string to the end (duplicates will be removed anyways) or trims all
            // slashes from the end
            let path = match self.trailing_slash_behavior {
                TrailingSlash::Always => original_path.to_string() + "/",
                TrailingSlash::MergeOnly => original_path.to_string(),
                TrailingSlash::Trim => original_path.trim_end_matches('/').to_string(),
            };

            // normalize multiple /'s to one /
            let path = self.merge_slash.replace_all(&path, "/");

            // Ensure root paths are still resolvable. If resulting path is blank after previous
            // step it means the path was one or more slashes. Reduce to single slash.
            let path = if path.is_empty() { "/" } else { path.as_ref() };

            // Check whether the path has been changed
            //
            // This check was previously implemented as string length comparison
            //
            // That approach fails when a trailing slash is added,
            // and a duplicate slash is removed,
            // since the length of the strings remains the same
            //
            // For example, the path "/v1//s" will be normalized to "/v1/s/"
            // Both of the paths have the same length,
            // so the change can not be deduced from the length comparison
            if path != original_path {
                let mut parts = head.uri.clone().into_parts();
                let query = parts.path_and_query.as_ref().and_then(|pq| pq.query());

Review comment (suggestion):
```suggestion
                TrailingSlash::Always => format!("{}/", original_path),
```

Code after:
    trailing_slash_behavior: TrailingSlash,
}

impl<S, B> Service<ServiceRequest> for NormalizePathNormalization<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = S::Future;

    actix_service::forward_ready!(service);

    fn call(&self, mut req: ServiceRequest) -> Self::Future {
        let head = req.head_mut();

        let original_path = head.uri.path();

        // An empty path here means that the URI has no valid path. We skip normalization in this
        // case, because adding a path can make the URI invalid
        if !original_path.is_empty() {
            // Either adds a string to the end (duplicates will be removed anyways) or trims all
            // slashes from the end
            let path = match self.trailing_slash_behavior {
                TrailingSlash::Always => format!("{}/", original_path),
                TrailingSlash::MergeOnly => original_path.to_string(),
                TrailingSlash::Trim => original_path.trim_end_matches('/').to_string(),
            };

            // normalize multiple /'s to one /
            let path = self.merge_slash.replace_all(&path, "/");

            // Ensure root paths are still resolvable. If resulting path is blank after previous
            // step it means the path was one or more slashes. Reduce to single slash.
            let path = if path.is_empty() { "/" } else { path.as_ref() };

            // Check whether the path has been changed
            //
            // This check was previously implemented as string length comparison
            //
            // That approach fails when a trailing slash is added,
            // and a duplicate slash is removed,
            // since the length of the strings remains the same
            //
            // For example, the path "/v1//s" will be normalized to "/v1/s/"
            // Both of the paths have the same length,
            // so the change can not be deduced from the length comparison
            if path != original_path {
                let mut parts = head.uri.clone().into_parts();
                let query = parts.path_and_query.as_ref().and_then(|pq| pq.query());