This is an automated email from the ASF dual-hosted git repository.
Gargi-jais11 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new b23218a8cff HDDS-16251. Preserve signed empty Content-Type during
preprocessing (#11088).
b23218a8cff is described below
commit b23218a8cff1bd7b84c44df2a132761e0f39b525
Author: Shuo Huang <[email protected]>
AuthorDate: Fri Aug 28 14:49:28 2026 +0800
HDDS-16251. Preserve signed empty Content-Type during preprocessing
(#11088).
---
.../ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java | 18 +++++++++++
.../ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java | 18 +++++++++++
.../hadoop/ozone/s3/EmptyContentTypeFilter.java | 30 ++++++++++++-------
.../ozone/s3/TestEmptyContentTypeFilter.java | 35 ++++++++++++++++++++++
.../s3/signature/TestAWSSignatureProcessor.java | 14 +++++++++
5 files changed, 105 insertions(+), 10 deletions(-)
diff --git
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
index b52ed18b511..615c3bc8083 100644
---
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
+++
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
@@ -572,6 +572,24 @@ public void testPutObject() {
assertEquals("37b51d194a7513e45b56f6524f2d51f2",
putObjectResult.getETag());
}
+ @Test
+ public void testPutObjectWithEmptyContentType() {
+ final String bucketName = getBucketName();
+ final String keyName = getKeyName();
+ final String content = "bar";
+ s3Client.createBucket(bucketName);
+
+ ObjectMetadata metadata = new ObjectMetadata();
+ metadata.setContentType("");
+ InputStream inputStream = new ByteArrayInputStream(
+ content.getBytes(StandardCharsets.UTF_8));
+
+ PutObjectResult putObjectResult = s3Client.putObject(
+ bucketName, keyName, inputStream, metadata);
+ assertEquals("37b51d194a7513e45b56f6524f2d51f2",
+ putObjectResult.getETag());
+ }
+
@Test
public void testPutObjectIfNoneMatch() {
final String bucketName = getBucketName();
diff --git
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
index 00224584f7a..63c0607f0d6 100644
---
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
+++
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
@@ -296,6 +296,24 @@ public void testPutObject() {
assertEquals("\"37b51d194a7513e45b56f6524f2d51f2\"",
getObjectResponse.eTag());
}
+ @Test
+ public void testPutObjectWithEmptyContentType() {
+ final String bucketName = getBucketName();
+ final String keyName = getKeyName();
+ final String content = "bar";
+ s3Client.createBucket(b -> b.bucket(bucketName));
+
+ PutObjectResponse putObjectResponse = s3Client.putObject(b -> b
+ .bucket(bucketName)
+ .key(keyName)
+ .overrideConfiguration(c ->
+ c.putHeader("Content-Type", "")),
+ RequestBody.fromString(content));
+
+ assertEquals("\"37b51d194a7513e45b56f6524f2d51f2\"",
+ putObjectResponse.eTag());
+ }
+
static Stream<Arguments> onlyTagKeyCasesV2() {
Map<String, String> fooBarEmptyBar = new HashMap<>();
fooBarEmptyBar.put("foo", "bar");
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EmptyContentTypeFilter.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EmptyContentTypeFilter.java
index dcd632545d1..a2177cc2f4e 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EmptyContentTypeFilter.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EmptyContentTypeFilter.java
@@ -18,6 +18,7 @@
package org.apache.hadoop.ozone.s3;
import java.io.IOException;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import javax.servlet.Filter;
@@ -56,6 +57,9 @@ public String getHeader(String name) {
if (name.equalsIgnoreCase("Content-Type")) {
return null;
}
+ if (name.equalsIgnoreCase(HeaderPreprocessor.ORIGINAL_CONTENT_TYPE))
{
+ return "";
+ }
return super.getHeader(name);
}
@@ -64,6 +68,9 @@ public Enumeration<String> getHeaders(String name) {
if ("Content-Type".equalsIgnoreCase(name)) {
return null;
}
+ if (HeaderPreprocessor.ORIGINAL_CONTENT_TYPE.equalsIgnoreCase(name))
{
+ return Collections.enumeration(Collections.singletonList(""));
+ }
return super.getHeaders(name);
}
@@ -84,8 +91,8 @@ public void destroy() {
}
/**
- * Enumeration Wrapper which removes Content-Type from the original
- * enumeration.
+ * Enumeration Wrapper which replaces Content-Type with the internal header
+ * used to preserve its original value.
*/
public static class EnumerationWrapper implements Enumeration<String> {
@@ -93,24 +100,27 @@ public static class EnumerationWrapper implements
Enumeration<String> {
private String nextElement;
+ private boolean contentTypeReplaced;
+
public EnumerationWrapper(Enumeration<String> original) {
this.original = original;
step();
}
private void step() {
- if (original.hasMoreElements()) {
+ while (original.hasMoreElements()) {
nextElement = original.nextElement();
- } else {
- nextElement = null;
- }
- if ("Content-Type".equalsIgnoreCase(nextElement)) {
- if (original.hasMoreElements()) {
- nextElement = original.nextElement();
+ if ("Content-Type".equalsIgnoreCase(nextElement)) {
+ if (!contentTypeReplaced) {
+ nextElement = HeaderPreprocessor.ORIGINAL_CONTENT_TYPE;
+ contentTypeReplaced = true;
+ return;
+ }
} else {
- nextElement = null;
+ return;
}
}
+ nextElement = null;
}
@Override
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestEmptyContentTypeFilter.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestEmptyContentTypeFilter.java
index ab9f1dfb0e2..1b5479f850b 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestEmptyContentTypeFilter.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestEmptyContentTypeFilter.java
@@ -19,9 +19,15 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import java.util.Collections;
import java.util.Vector;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.servlet.http.HttpServletRequest;
import org.apache.hadoop.ozone.s3.EmptyContentTypeFilter.EnumerationWrapper;
import org.junit.jupiter.api.Test;
@@ -42,6 +48,9 @@ public void enumerationWithContentType() {
new EnumerationWrapper(values.elements());
assertTrue(enumerationWrapper.hasMoreElements());
+ assertEquals(HeaderPreprocessor.ORIGINAL_CONTENT_TYPE,
+ enumerationWrapper.nextElement());
+ assertTrue(enumerationWrapper.hasMoreElements());
assertEquals("1", enumerationWrapper.nextElement());
assertTrue(enumerationWrapper.hasMoreElements());
assertEquals("2", enumerationWrapper.nextElement());
@@ -56,7 +65,33 @@ public void enumerationWithOneContentType() {
final EnumerationWrapper enumerationWrapper =
new EnumerationWrapper(values.elements());
+ assertTrue(enumerationWrapper.hasMoreElements());
+ assertEquals(HeaderPreprocessor.ORIGINAL_CONTENT_TYPE,
+ enumerationWrapper.nextElement());
assertFalse(enumerationWrapper.hasMoreElements());
}
+ @Test
+ public void preserveEmptyContentType() throws Exception {
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.getContentType()).thenReturn("");
+ when(request.getHeaderNames()).thenReturn(
+
Collections.enumeration(Collections.singletonList(HeaderPreprocessor.CONTENT_TYPE)));
+
+ AtomicReference<HttpServletRequest> wrappedRequest =
+ new AtomicReference<>();
+ new EmptyContentTypeFilter().doFilter(request, null,
+ (filteredRequest, response) -> wrappedRequest.set(
+ (HttpServletRequest) filteredRequest));
+
+ assertNull(wrappedRequest.get().getContentType());
+
assertNull(wrappedRequest.get().getHeader(HeaderPreprocessor.CONTENT_TYPE));
+ assertEquals("",
wrappedRequest.get().getHeader(HeaderPreprocessor.ORIGINAL_CONTENT_TYPE));
+ assertEquals(Collections.singletonList(""),
+
Collections.list(wrappedRequest.get().getHeaders(HeaderPreprocessor.ORIGINAL_CONTENT_TYPE)));
+ assertEquals(Collections.singletonList(
+ HeaderPreprocessor.ORIGINAL_CONTENT_TYPE), Collections.list(
+ wrappedRequest.get().getHeaderNames()));
+ }
+
}
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAWSSignatureProcessor.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAWSSignatureProcessor.java
index 2e2e259d112..7f9681054d2 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAWSSignatureProcessor.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAWSSignatureProcessor.java
@@ -20,6 +20,7 @@
import static
org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.assertErrorResponse;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
+import org.apache.hadoop.ozone.s3.HeaderPreprocessor;
import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
import org.junit.jupiter.api.Test;
@@ -46,6 +48,18 @@ public void
testLowerCaseHeaderMapRemovesKeysCaseInsensitively() {
assertFalse(headers.containsKey("authorization"));
}
+ @Test
+ public void testRestoreEmptyContentType() {
+ MultivaluedMap<String, String> rawHeaders = new MultivaluedHashMap<>();
+ rawHeaders.putSingle(HeaderPreprocessor.ORIGINAL_CONTENT_TYPE, "");
+
+ AWSSignatureProcessor.LowerCaseKeyStringMap headers =
+ AWSSignatureProcessor.LowerCaseKeyStringMap.fromHeaderMap(rawHeaders);
+
+ assertTrue(headers.containsKey(HeaderPreprocessor.CONTENT_TYPE));
+ assertEquals("", headers.get(HeaderPreprocessor.CONTENT_TYPE));
+ }
+
@Test
public void testOutOfRangeExpiresPreSignedUrlReturns403() throws Exception {
// A pre-signed URL whose X-Amz-Expires is out of range must be rejected
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]