From 2c86fe30ec9c7e0f3266a68bbc98fb05cdda8ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 10 Dec 2025 08:21:51 +0100 Subject: [PATCH] Content encoding (#1089) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörg Thalheim Co-authored-by: loverustfs --- crates/e2e_test/src/content_encoding_test.rs | 85 ++++++++++++++++++++ crates/e2e_test/src/lib.rs | 4 + rustfs/src/storage/ecfs.rs | 2 + 3 files changed, 91 insertions(+) create mode 100644 crates/e2e_test/src/content_encoding_test.rs diff --git a/crates/e2e_test/src/content_encoding_test.rs b/crates/e2e_test/src/content_encoding_test.rs new file mode 100644 index 00000000..ef5ecdb2 --- /dev/null +++ b/crates/e2e_test/src/content_encoding_test.rs @@ -0,0 +1,85 @@ +// Copyright 2024 RustFS Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! End-to-end test for Content-Encoding header handling +//! +//! Tests that the Content-Encoding header is correctly stored during PUT +//! and returned in GET/HEAD responses. This is important for clients that +//! upload pre-compressed content and rely on the header for decompression. + +#[cfg(test)] +mod tests { + use crate::common::{RustFSTestEnvironment, init_logging}; + use aws_sdk_s3::primitives::ByteStream; + use serial_test::serial; + use tracing::info; + + /// Verify Content-Encoding header roundtrips through PUT, GET, and HEAD operations + #[tokio::test] + #[serial] + async fn test_content_encoding_roundtrip() { + init_logging(); + info!("Starting Content-Encoding roundtrip test"); + + let mut env = RustFSTestEnvironment::new().await.expect("Failed to create test environment"); + env.start_rustfs_server(vec![]).await.expect("Failed to start RustFS"); + + let client = env.create_s3_client(); + let bucket = "content-encoding-test"; + let key = "logs/app.log.zst"; + let content = b"2024-01-15 10:23:45 INFO Application started\n2024-01-15 10:23:46 DEBUG Loading config\n"; + + client + .create_bucket() + .bucket(bucket) + .send() + .await + .expect("Failed to create bucket"); + + info!("Uploading object with Content-Encoding: zstd"); + client + .put_object() + .bucket(bucket) + .key(key) + .content_type("text/plain") + .content_encoding("zstd") + .body(ByteStream::from_static(content)) + .send() + .await + .expect("PUT failed"); + + info!("Verifying GET response includes Content-Encoding"); + let get_resp = client.get_object().bucket(bucket).key(key).send().await.expect("GET failed"); + + assert_eq!(get_resp.content_encoding(), Some("zstd"), "GET should return Content-Encoding: zstd"); + assert_eq!(get_resp.content_type(), Some("text/plain"), "GET should return correct Content-Type"); + + let body = get_resp.body.collect().await.unwrap().into_bytes(); + assert_eq!(body.as_ref(), content, "Body content mismatch"); + + info!("Verifying HEAD response includes Content-Encoding"); + let head_resp = client + .head_object() + .bucket(bucket) + .key(key) + .send() + .await + .expect("HEAD failed"); + + assert_eq!(head_resp.content_encoding(), Some("zstd"), "HEAD should return Content-Encoding: zstd"); + assert_eq!(head_resp.content_type(), Some("text/plain"), "HEAD should return correct Content-Type"); + + env.stop_server(); + } +} diff --git a/crates/e2e_test/src/lib.rs b/crates/e2e_test/src/lib.rs index b29e37a3..ac6f3805 100644 --- a/crates/e2e_test/src/lib.rs +++ b/crates/e2e_test/src/lib.rs @@ -25,3 +25,7 @@ mod kms; // Special characters in path test modules #[cfg(test)] mod special_chars_test; + +// Content-Encoding header preservation test +#[cfg(test)] +mod content_encoding_test; diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index de863b78..97e1a668 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -2272,6 +2272,7 @@ impl S3 for FS { content_length: Some(response_content_length), last_modified, content_type, + content_encoding: info.content_encoding.clone(), accept_ranges: Some("bytes".to_string()), content_range, e_tag: info.etag.map(|etag| to_s3s_etag(&etag)), @@ -2487,6 +2488,7 @@ impl S3 for FS { let output = HeadObjectOutput { content_length: Some(content_length), content_type, + content_encoding: info.content_encoding.clone(), last_modified, e_tag: info.etag.map(|etag| to_s3s_etag(&etag)), metadata: filter_object_metadata(&metadata_map),