diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index d23f03445..327c81cbf 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -75,14 +75,26 @@ where fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let head = req.head_mut(); + let original_path = head.uri.path(); + // always add trailing slash, might be an extra one - let path = head.uri.path().to_string() + "/"; - let original_len = path.len(); + let path = original_path.to_string() + "/"; // normalize multiple /'s to one / let path = self.merge_slash.replace_all(&path, "/"); - if original_len != path.len() { + // 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 pq = parts.path_and_query.as_ref().unwrap(); @@ -178,11 +190,11 @@ mod tests { } #[actix_rt::test] - async fn should_normalize_nothing_notrail() { + async fn should_normalize_notrail() { const URI: &str = "/v1/something"; let srv = |req: ServiceRequest| { - assert_eq!(URI, req.path()); + assert_eq!(URI.to_string() + "/", req.path()); ok(req.into_response(HttpResponse::Ok().finish())) };