From 06097a3c33f8502162d786f32d978c9be910a2f9 Mon Sep 17 00:00:00 2001 From: cxymds Date: Thu, 7 May 2026 10:52:21 +0800 Subject: [PATCH] fix(admin): normalize empty admin POST content length (#2843) --- rustfs/src/server/layer.rs | 68 ++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/rustfs/src/server/layer.rs b/rustfs/src/server/layer.rs index fb55147f4..c24e52518 100644 --- a/rustfs/src/server/layer.rs +++ b/rustfs/src/server/layer.rs @@ -259,19 +259,31 @@ where } fn should_force_zero_content_length_for_admin_empty_body(req: &HttpRequest) -> bool { - req.method() == Method::PUT - && is_empty_body_admin_put_path(req.uri().path()) - && !req.headers().contains_key(http::header::CONTENT_LENGTH) + is_empty_body_admin_path(req.method(), req.uri().path()) && !req.headers().contains_key(http::header::CONTENT_LENGTH) } -fn is_empty_body_admin_put_path(path: &str) -> bool { - matches!( - path, - "/minio/admin/v3/set-user-status" - | "/minio/admin/v3/set-group-status" - | "/rustfs/admin/v3/set-user-status" - | "/rustfs/admin/v3/set-group-status" - ) +fn is_empty_body_admin_path(method: &Method, path: &str) -> bool { + match *method { + Method::PUT => matches!( + path, + "/minio/admin/v3/set-user-status" + | "/minio/admin/v3/set-group-status" + | "/rustfs/admin/v3/set-user-status" + | "/rustfs/admin/v3/set-group-status" + ), + Method::POST => matches!( + path, + "/minio/admin/v3/rebalance/start" + | "/minio/admin/v3/rebalance/stop" + | "/minio/admin/v3/pools/decommission" + | "/minio/admin/v3/pools/cancel" + | "/rustfs/admin/v3/rebalance/start" + | "/rustfs/admin/v3/rebalance/stop" + | "/rustfs/admin/v3/pools/decommission" + | "/rustfs/admin/v3/pools/cancel" + ), + _ => false, + } } #[derive(Clone)] @@ -951,6 +963,29 @@ mod tests { assert!(should_force_zero_content_length_for_admin_empty_body(&request)); } + #[test] + fn admin_empty_body_post_without_content_length_is_normalized() { + let paths = [ + "/minio/admin/v3/rebalance/start", + "/minio/admin/v3/rebalance/stop", + "/minio/admin/v3/pools/decommission?pool=http%3A%2F%2Fminio-%7B1...4%7D%3A9000%2Fdata%7B1...2%7D", + "/minio/admin/v3/pools/cancel?pool=http%3A%2F%2Fminio-%7B1...4%7D%3A9000%2Fdata%7B1...2%7D", + "/rustfs/admin/v3/rebalance/start", + "/rustfs/admin/v3/rebalance/stop", + "/rustfs/admin/v3/pools/decommission?pool=http%3A%2F%2Fminio-%7B1...4%7D%3A9000%2Fdata%7B1...2%7D", + "/rustfs/admin/v3/pools/cancel?pool=http%3A%2F%2Fminio-%7B1...4%7D%3A9000%2Fdata%7B1...2%7D", + ]; + + for path in paths { + let request = Request::builder().method(Method::POST).uri(path).body(()).expect("request"); + + assert!( + should_force_zero_content_length_for_admin_empty_body(&request), + "{path} should force Content-Length: 0" + ); + } + } + #[test] fn admin_request_with_explicit_content_length_is_left_unchanged() { let request = Request::builder() @@ -974,6 +1009,17 @@ mod tests { assert!(!should_force_zero_content_length_for_admin_empty_body(&request)); } + #[test] + fn non_empty_body_admin_post_path_is_not_normalized() { + let request = Request::builder() + .method(Method::POST) + .uri("/minio/admin/v3/update-service-account") + .body(()) + .expect("request"); + + assert!(!should_force_zero_content_length_for_admin_empty_body(&request)); + } + #[test] fn test_strip_quotes_from_first_etag_removes_quotes() { let input = String::from("\"abc\"");