From 37541ffba1fbaea2cc80a5cbcc7e14429ced03b7 Mon Sep 17 00:00:00 2001 From: kqjy Date: Fri, 24 Apr 2026 20:18:41 +0800 Subject: [PATCH] Gate 206 response checksum headers on full-range coverage --- Cargo.lock | 12 +-- Cargo.toml | 2 +- crates/myfsio-server/src/handlers/mod.rs | 4 +- crates/myfsio-server/tests/integration.rs | 93 +++++++++++++++++++++++ 4 files changed, 103 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ca53d0..d52b982 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2639,7 +2639,7 @@ dependencies = [ [[package]] name = "myfsio-auth" -version = "0.4.5" +version = "0.5.0" dependencies = [ "aes", "base64", @@ -2664,7 +2664,7 @@ dependencies = [ [[package]] name = "myfsio-common" -version = "0.4.5" +version = "0.5.0" dependencies = [ "chrono", "serde", @@ -2675,7 +2675,7 @@ dependencies = [ [[package]] name = "myfsio-crypto" -version = "0.4.5" +version = "0.5.0" dependencies = [ "aes-gcm", "base64", @@ -2696,7 +2696,7 @@ dependencies = [ [[package]] name = "myfsio-server" -version = "0.4.5" +version = "0.5.0" dependencies = [ "aes-gcm", "async-trait", @@ -2753,7 +2753,7 @@ dependencies = [ [[package]] name = "myfsio-storage" -version = "0.4.5" +version = "0.5.0" dependencies = [ "chrono", "dashmap", @@ -2777,7 +2777,7 @@ dependencies = [ [[package]] name = "myfsio-xml" -version = "0.4.5" +version = "0.5.0" dependencies = [ "chrono", "myfsio-common", diff --git a/Cargo.toml b/Cargo.toml index f0a7591..3aa50b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ ] [workspace.package] -version = "0.4.5" +version = "0.5.0" edition = "2021" [workspace.dependencies] diff --git a/crates/myfsio-server/src/handlers/mod.rs b/crates/myfsio-server/src/handlers/mod.rs index 76d92ed..43dce2c 100644 --- a/crates/myfsio-server/src/handlers/mod.rs +++ b/crates/myfsio-server/src/handlers/mod.rs @@ -2783,7 +2783,9 @@ async fn stream_partial_content( headers.insert("x-amz-server-side-encryption", alg.parse().unwrap()); } apply_stored_response_headers(&mut headers, &meta.internal_metadata); - apply_stored_checksum_headers(&mut headers, &meta.internal_metadata); + if start == 0 && end + 1 == plaintext_size { + apply_stored_checksum_headers(&mut headers, &meta.internal_metadata); + } if let Some(ref requested_version) = query.version_id { if let Ok(value) = requested_version.parse() { headers.insert("x-amz-version-id", value); diff --git a/crates/myfsio-server/tests/integration.rs b/crates/myfsio-server/tests/integration.rs index 616cb04..965dc7e 100644 --- a/crates/myfsio-server/tests/integration.rs +++ b/crates/myfsio-server/tests/integration.rs @@ -1905,6 +1905,99 @@ async fn test_range_request() { assert_eq!(&body[..], b"data."); } +#[tokio::test] +async fn test_range_get_omits_whole_object_checksum() { + use base64::engine::general_purpose::STANDARD as B64; + use sha2::{Digest, Sha256}; + + let (app, _tmp) = test_app(); + + app.clone() + .oneshot(signed_request(Method::PUT, "/csum-bucket", Body::empty())) + .await + .unwrap(); + + let data = b"Hello, World! This is range checksum data."; + let sha = B64.encode(Sha256::digest(data)); + + app.clone() + .oneshot( + Request::builder() + .method(Method::PUT) + .uri("/csum-bucket/obj.bin") + .header("x-access-key", TEST_ACCESS_KEY) + .header("x-secret-key", TEST_SECRET_KEY) + .header("x-amz-checksum-sha256", &sha) + .body(Body::from(&data[..])) + .unwrap(), + ) + .await + .unwrap(); + + let full = app + .clone() + .oneshot( + Request::builder() + .uri("/csum-bucket/obj.bin") + .header("x-access-key", TEST_ACCESS_KEY) + .header("x-secret-key", TEST_SECRET_KEY) + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(full.status(), StatusCode::OK); + assert_eq!( + full.headers().get("x-amz-checksum-sha256").unwrap(), + sha.as_str() + ); + + let partial = app + .clone() + .oneshot( + Request::builder() + .uri("/csum-bucket/obj.bin") + .header("x-access-key", TEST_ACCESS_KEY) + .header("x-secret-key", TEST_SECRET_KEY) + .header("range", "bytes=0-4") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(partial.status(), StatusCode::PARTIAL_CONTENT); + for algo in ["sha256", "sha1", "crc32", "crc32c", "crc64nvme"] { + let header = format!("x-amz-checksum-{}", algo); + assert!( + partial.headers().get(&header).is_none(), + "ranged GET must not include {}", + header + ); + } + + let body_bytes = partial.into_body().collect().await.unwrap().to_bytes(); + assert_eq!(&body_bytes[..], &data[..5]); + + let full_range = format!("bytes=0-{}", data.len() - 1); + let covering = app + .oneshot( + Request::builder() + .uri("/csum-bucket/obj.bin") + .header("x-access-key", TEST_ACCESS_KEY) + .header("x-secret-key", TEST_SECRET_KEY) + .header("range", &full_range) + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(covering.status(), StatusCode::PARTIAL_CONTENT); + assert_eq!( + covering.headers().get("x-amz-checksum-sha256").unwrap(), + sha.as_str() + ); +} + #[tokio::test] async fn test_copy_object() { let (app, _tmp) = test_app();