From 3208f930c58aa089bfa4b047efebd5969b4771f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=AD=A3=E8=B6=85?= Date: Tue, 5 May 2026 12:29:25 +0800 Subject: [PATCH] fix(s3): advertise byte ranges on head object (#2802) --- crates/e2e_test/src/head_object_range_test.rs | 52 +++++++++++++++++++ crates/e2e_test/src/lib.rs | 3 ++ rustfs/src/app/object_usecase.rs | 5 +- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 crates/e2e_test/src/head_object_range_test.rs diff --git a/crates/e2e_test/src/head_object_range_test.rs b/crates/e2e_test/src/head_object_range_test.rs new file mode 100644 index 000000000..5cc4b7a12 --- /dev/null +++ b/crates/e2e_test/src/head_object_range_test.rs @@ -0,0 +1,52 @@ +use crate::common::{RustFSTestEnvironment, init_logging}; +use aws_sdk_s3::primitives::ByteStream; +use serial_test::serial; +use tracing::info; + +const RANGE_HEAD_BUCKET: &str = "range-head-test-bucket"; +const RANGE_HEAD_KEY: &str = "range-head-object.bin"; +const ACCEPT_RANGES_BYTES: &str = "bytes"; + +#[tokio::test] +#[serial] +async fn head_object_advertises_accept_ranges() -> Result<(), Box> { + init_logging(); + info!("Starting HeadObject Accept-Ranges regression test"); + + let mut env = RustFSTestEnvironment::new().await?; + env.start_rustfs_server(Vec::new()).await?; + + let client = env.create_s3_client(); + env.create_test_bucket(RANGE_HEAD_BUCKET).await?; + + client + .put_object() + .bucket(RANGE_HEAD_BUCKET) + .key(RANGE_HEAD_KEY) + .body(ByteStream::from_static(b"0123456789abcdef")) + .send() + .await?; + + let head = client + .head_object() + .bucket(RANGE_HEAD_BUCKET) + .key(RANGE_HEAD_KEY) + .send() + .await?; + assert_eq!( + head.accept_ranges(), + Some(ACCEPT_RANGES_BYTES), + "HeadObject should advertise byte range support" + ); + + client + .delete_object() + .bucket(RANGE_HEAD_BUCKET) + .key(RANGE_HEAD_KEY) + .send() + .await?; + env.delete_test_bucket(RANGE_HEAD_BUCKET).await?; + env.stop_server(); + + Ok(()) +} diff --git a/crates/e2e_test/src/lib.rs b/crates/e2e_test/src/lib.rs index a06f0eb9f..e4bf65045 100644 --- a/crates/e2e_test/src/lib.rs +++ b/crates/e2e_test/src/lib.rs @@ -104,6 +104,9 @@ mod checksum_upload_test; #[cfg(test)] mod group_delete_test; +#[cfg(test)] +mod head_object_range_test; + // S3 dummy-compat bucket API tests #[cfg(test)] mod bucket_logging_test; diff --git a/rustfs/src/app/object_usecase.rs b/rustfs/src/app/object_usecase.rs index a8b3e0819..a1691cdeb 100644 --- a/rustfs/src/app/object_usecase.rs +++ b/rustfs/src/app/object_usecase.rs @@ -131,6 +131,8 @@ use tokio_util::io::{ReaderStream, StreamReader}; use tracing::{debug, error, info, instrument, warn}; use uuid::Uuid; +const ACCEPT_RANGES_BYTES: &str = "bytes"; + struct DeadlockRequestGuard { deadlock_detector: Arc, request_id: String, @@ -2117,7 +2119,7 @@ impl DefaultObjectUsecase { last_modified, content_type, content_encoding: info.content_encoding.clone(), - accept_ranges: Some("bytes".to_string()), + accept_ranges: Some(ACCEPT_RANGES_BYTES.to_string()), content_range, e_tag: info.etag.map(|etag| to_s3s_etag(&etag)), metadata: filter_object_metadata(&info.user_defined), @@ -3625,6 +3627,7 @@ impl DefaultObjectUsecase { cache_control, content_disposition, content_language, + accept_ranges: Some(ACCEPT_RANGES_BYTES.to_string()), website_redirect_location, expires, last_modified,