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 1b3cb9122b1 HDDS-15258. Include x-amz-tagging-count header in HEAD 
object tagging responses (#10265).
1b3cb9122b1 is described below

commit 1b3cb9122b19077d9e1e159dcb3820eb39df1ca8
Author: Gargi Jaiswal <[email protected]>
AuthorDate: Fri May 15 08:48:01 2026 +0530

    HDDS-15258. Include x-amz-tagging-count header in HEAD object tagging 
responses (#10265).
---
 .../ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java   | 24 ++++++++++++++++++++++
 .../ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java   | 19 +++++++++++++++++
 .../hadoop/ozone/s3/endpoint/ObjectEndpoint.java   |  1 +
 .../hadoop/ozone/s3/endpoint/TestObjectHead.java   | 21 +++++++++++++++++++
 4 files changed, 65 insertions(+)

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 8238ade3921..8e79e057c03 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
@@ -35,6 +35,7 @@
 import com.amazonaws.AmazonServiceException.ErrorType;
 import com.amazonaws.HttpMethod;
 import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.Headers;
 import com.amazonaws.services.s3.model.AbortMultipartUploadRequest;
 import com.amazonaws.services.s3.model.AccessControlList;
 import com.amazonaws.services.s3.model.Bucket;
@@ -70,6 +71,7 @@
 import com.amazonaws.services.s3.model.S3ObjectInputStream;
 import com.amazonaws.services.s3.model.S3ObjectSummary;
 import com.amazonaws.services.s3.model.SetObjectAclRequest;
+import com.amazonaws.services.s3.model.SetObjectTaggingRequest;
 import com.amazonaws.services.s3.model.Tag;
 import com.amazonaws.services.s3.model.UploadPartRequest;
 import com.amazonaws.services.s3.model.UploadPartResult;
@@ -1101,6 +1103,28 @@ public void testGetObject() throws Exception {
     }
   }
 
+  @Test
+  public void testHeadObjectReturnsTaggingCount() {
+    final String bucketName = getBucketName();
+    final String keyName = getKeyName();
+    final String content = "head-object-tag-count";
+    s3Client.createBucket(bucketName);
+
+    s3Client.putObject(bucketName, keyName,
+        new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), 
new ObjectMetadata());
+
+    List<Tag> tags = Arrays.asList(new Tag("tag1", "v1"), new Tag("tag2", 
"v2"));
+    s3Client.setObjectTagging(
+        new SetObjectTaggingRequest(bucketName, keyName, new 
ObjectTagging(tags)));
+
+    ObjectMetadata head = s3Client.getObjectMetadata(bucketName, keyName);
+    // AWS SDK v1: getTaggingCount() exists on S3Object (GET), not on 
ObjectMetadata (HEAD).
+    // x-amz-tagging-count is exposed via raw metadata.
+    Object tagCountHeader = head.getRawMetadataValue(Headers.S3_TAGGING_COUNT);
+    assertNotNull(tagCountHeader);
+    assertEquals(tags.size(), Integer.parseInt(tagCountHeader.toString()));
+  }
+
   @Test
   public void testGetObjectWithoutETag() throws Exception {
     // Object uploaded using other protocols (e.g. ofs / ozone cli) will not
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 28d8cbf1f61..ea608437361 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
@@ -1249,6 +1249,25 @@ public void testLowLevelMultipartUpload(@TempDir Path 
tempDir) throws Exception
     assertEquals(userMetadata, headObjectResponse.metadata());
   }
 
+  @Test
+  public void testHeadObjectReturnsTaggingCount() {
+    final String bucketName = getBucketName();
+    final String keyName = getKeyName();
+    s3Client.createBucket(b -> b.bucket(bucketName));
+    s3Client.putObject(b -> b.bucket(bucketName).key(keyName), 
RequestBody.fromString("obj"));
+
+    List<Tag> tags = Arrays.asList(
+        Tag.builder().key("tag1").value("v1").build(),
+        Tag.builder().key("tag2").value("v2").build());
+
+    s3Client.putObjectTagging(b -> b.bucket(bucketName).key(keyName)
+        .tagging(Tagging.builder().tagSet(tags).build()));
+
+    HeadObjectResponse head = s3Client.headObject(b -> 
b.bucket(bucketName).key(keyName));
+    assertNotNull(head.tagCount());
+    assertEquals(tags.size(), head.tagCount().intValue());
+  }
+
   @Test
   public void testResumableDownloadWithEtagMismatch() throws Exception {
     // Arrange
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
index 2021790b332..38cd6965a22 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
@@ -580,6 +580,7 @@ public Response head(
     addEntityTagHeader(response, key);
 
     addLastModifiedDate(response, key);
+    addTagCountIfAny(response, key);
     addCustomMetadataHeaders(response, key);
     getMetrics().updateHeadKeySuccessStats(startNanos);
     auditReadSuccess(s3GAction);
diff --git 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectHead.java
 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectHead.java
index 10259005d07..f6795ba8f56 100644
--- 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectHead.java
+++ 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectHead.java
@@ -27,9 +27,12 @@
 import static org.apache.hadoop.ozone.s3.util.S3Consts.IF_MATCH_HEADER;
 import static org.apache.hadoop.ozone.s3.util.S3Consts.IF_NONE_MATCH_HEADER;
 import static 
org.apache.hadoop.ozone.s3.util.S3Consts.IF_UNMODIFIED_SINCE_HEADER;
+import static org.apache.hadoop.ozone.s3.util.S3Consts.TAG_COUNT_HEADER;
+import static org.apache.hadoop.ozone.s3.util.S3Consts.TAG_HEADER;
 import static org.apache.hadoop.ozone.s3.util.S3Consts.X_AMZ_CONTENT_SHA256;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -91,6 +94,9 @@ public void testHeadObject() throws Exception {
 
     DateTimeFormatter.RFC_1123_DATE_TIME
         .parse(response.getHeaderString("Last-Modified"));
+
+    assertNull(response.getHeaderString(TAG_COUNT_HEADER),
+        "HeadObject must omit x-amz-tagging-count (AWS TagCount) when object 
has no tags");
   }
 
   @Test
@@ -210,6 +216,21 @@ public void 
testHeadWhenKeyIsAFileAndKeyPathEndsWithASlash()
     assertStatus(HttpStatus.SC_NOT_FOUND, () -> keyEndpoint.head(bucketName, 
keyPath + "/"));
   }
 
+  @Test
+  public void testHeadObjectIncludesTagCount()
+      throws Exception {
+    String keyName = "head-with-tags";
+    
when(headers.getHeaderString(TAG_HEADER)).thenReturn("tag1=value1&tag2=value2");
+    assertSucceeds(() -> put(keyEndpoint, bucketName, keyName, "c"));
+
+    Response response = keyEndpoint.head(bucketName, keyName);
+    assertEquals(HttpStatus.SC_OK, response.getStatus());
+    // S3 HeadObject TagCount in the AWS API is driven by x-amz-tagging-count
+    assertNotNull(response.getHeaderString(TAG_COUNT_HEADER),
+        "HeadObject must include x-amz-tagging-count when object has tags (AWS 
TagCount)");
+    assertEquals("2", response.getHeaderString(TAG_COUNT_HEADER));
+  }
+
   private byte[] createKey(String keyPath) throws IOException {
     byte[] bytes = 
RandomStringUtils.secure().nextAlphanumeric(32).getBytes(UTF_8);
     try (OutputStream out = bucket.createKey(keyPath, bytes.length)) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to