You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by ad...@apache.org on 2022/08/19 11:38:11 UTC

[ozone] branch master updated: HDDS-7113. Support overriding response header values (#3680)

This is an automated email from the ASF dual-hosted git repository.

adoroszlai 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 3efb039091 HDDS-7113. Support overriding response header values (#3680)
3efb039091 is described below

commit 3efb03909111f5a018660713fa0a591a94b4289f
Author: XiChen <32...@users.noreply.github.com>
AuthorDate: Fri Aug 19 19:38:05 2022 +0800

    HDDS-7113. Support overriding response header values (#3680)
---
 .../hadoop/ozone/s3/endpoint/ObjectEndpoint.java   |  55 +++++++---
 .../hadoop/ozone/s3/endpoint/TestObjectGet.java    | 115 +++++++++++++++++++--
 .../ozone/s3/metrics/TestS3GatewayMetrics.java     |  10 ++
 3 files changed, 161 insertions(+), 19 deletions(-)

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 90aae3b388..940df2f858 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
@@ -31,9 +31,11 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
 import javax.ws.rs.core.Response.Status;
@@ -46,7 +48,6 @@ import java.text.ParseException;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -84,6 +85,7 @@ import org.apache.hadoop.ozone.s3.util.S3Utils;
 import org.apache.hadoop.ozone.web.utils.OzoneUtils;
 import org.apache.hadoop.util.Time;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.annotations.VisibleForTesting;
 import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
 import static javax.ws.rs.core.HttpHeaders.LAST_MODIFIED;
@@ -128,19 +130,25 @@ public class ObjectEndpoint extends EndpointBase {
       LoggerFactory.getLogger(ObjectEndpoint.class);
 
   @Context
-  private HttpHeaders headers;
+  private ContainerRequestContext context;
 
+  @Context
+  private HttpHeaders headers;
 
-  private List<String> customizableGetHeaders = new ArrayList<>();
+  /*FOR the feature Overriding Response Header
+  https://docs.aws.amazon.com/de_de/AmazonS3/latest/API/API_GetObject.html */
+  private Map<String, String> overrideQueryParameter;
   private int bufferSize;
 
   public ObjectEndpoint() {
-    customizableGetHeaders.add("Content-Type");
-    customizableGetHeaders.add("Content-Language");
-    customizableGetHeaders.add("Expires");
-    customizableGetHeaders.add("Cache-Control");
-    customizableGetHeaders.add("Content-Disposition");
-    customizableGetHeaders.add("Content-Encoding");
+    overrideQueryParameter = ImmutableMap.<String, String>builder()
+        .put("Content-Type", "response-content-type")
+        .put("Content-Language", "response-content-language")
+        .put("Expires", "response-expires")
+        .put("Cache-Control", "response-cache-control")
+        .put("Content-Disposition", "response-content-disposition")
+        .put("Content-Encoding", "response-content-encoding")
+        .build();
   }
 
   @Inject
@@ -346,10 +354,28 @@ public class ObjectEndpoint extends EndpointBase {
       }
       responseBuilder.header(ACCEPT_RANGE_HEADER,
           RANGE_HEADER_SUPPORTED_UNIT);
-      for (String responseHeader : customizableGetHeaders) {
-        String headerValue = headers.getHeaderString(responseHeader);
+
+      // if multiple query parameters having same name,
+      // Only the first parameters will be recognized
+      // eg:
+      // http://localhost:9878/bucket/key?response-expires=1&response-expires=2
+      // only response-expires=1 is valid
+      MultivaluedMap<String, String> queryParams = context
+          .getUriInfo().getQueryParameters();
+
+      for (Map.Entry<String, String> entry :
+          overrideQueryParameter.entrySet()) {
+        String headerValue = headers.getHeaderString(entry.getKey());
+
+        /* "Overriding Response Header" by query parameter, See:
+        https://docs.aws.amazon.com/de_de/AmazonS3/latest/API/API_GetObject.html
+        */
+        String queryValue = queryParams.getFirst(entry.getValue());
+        if (queryValue != null) {
+          headerValue = queryValue;
+        }
         if (headerValue != null) {
-          responseBuilder.header(responseHeader, headerValue);
+          responseBuilder.header(entry.getKey(), headerValue);
         }
       }
       addLastModifiedDate(responseBuilder, keyDetails);
@@ -849,6 +875,11 @@ public class ObjectEndpoint extends EndpointBase {
     this.headers = headers;
   }
 
+  @VisibleForTesting
+  public void setContext(ContainerRequestContext context) {
+    this.context = context;
+  }
+
   void copy(OzoneVolume volume, InputStream src, long srcKeyLen,
       String destKey, String destBucket,
       ReplicationConfig replication) throws IOException {
diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectGet.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectGet.java
index a5e8dd039f..d9de8b0f9e 100644
--- a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectGet.java
+++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectGet.java
@@ -20,8 +20,11 @@
 
 package org.apache.hadoop.ozone.s3.endpoint;
 
+import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedHashMap;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.time.format.DateTimeFormatter;
@@ -36,10 +39,12 @@ import org.apache.hadoop.ozone.s3.exception.OS3Exception;
 
 import org.apache.commons.io.IOUtils;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.mockito.Mockito.doReturn;
 
 /**
  * Test get object.
@@ -47,11 +52,30 @@ import static java.nio.charset.StandardCharsets.UTF_8;
 public class TestObjectGet {
 
   public static final String CONTENT = "0123456789";
+  public static final String CONTENT_TYPE1 = "video/mp4";
+  public static final String CONTENT_TYPE2 = "text/html; charset=UTF-8";
+  public static final String CONTENT_LANGUAGE1 = "en-CA";
+  public static final String CONTENT_LANGUAGE2 = "de-DE, en-CA";
+  public static final String EXPIRES1 = "Wed, 21 Oct 2015 07:29:00 GMT";
+  public static final String EXPIRES2 = "Wed, 21 Oct 2015 07:28:00 GMT";
+  public static final String CACHE_CONTROL1 = "no-cache";
+  public static final String CACHE_CONTROL2 = "max-age=604800";
+  public static final String CONTENT_DISPOSITION1 = "inline";
+  public static final String CONTENT_DISPOSITION2 = "attachment; "
+      + "filename=\"filename.jpg\"";
+  public static final String CONTENT_ENCODING1 = "gzip";
+  public static final String CONTENT_ENCODING2 = "compress";
 
-  @Test
-  public void get() throws IOException, OS3Exception {
+  private HttpHeaders headers;
+  private ObjectEndpoint rest;
+  private OzoneClient client;
+  private ByteArrayInputStream body;
+  private ContainerRequestContext context;
+
+  @Before
+  public void init() throws IOException {
     //GIVEN
-    OzoneClient client = new OzoneClientStub();
+    client = new OzoneClientStub();
     client.getObjectStore().createS3Bucket("b1");
     OzoneBucket bucket = client.getObjectStore().getS3Bucket("b1");
     OzoneOutputStream keyStream =
@@ -59,14 +83,22 @@ public class TestObjectGet {
     keyStream.write(CONTENT.getBytes(UTF_8));
     keyStream.close();
 
-    ObjectEndpoint rest = new ObjectEndpoint();
+    rest = new ObjectEndpoint();
     rest.setClient(client);
     rest.setOzoneConfiguration(new OzoneConfiguration());
-    HttpHeaders headers = Mockito.mock(HttpHeaders.class);
+    headers = Mockito.mock(HttpHeaders.class);
     rest.setHeaders(headers);
-    ByteArrayInputStream body =
-        new ByteArrayInputStream(CONTENT.getBytes(UTF_8));
+    body = new ByteArrayInputStream(CONTENT.getBytes(UTF_8));
 
+    context = Mockito.mock(ContainerRequestContext.class);
+    Mockito.when(context.getUriInfo()).thenReturn(Mockito.mock(UriInfo.class));
+    Mockito.when(context.getUriInfo().getQueryParameters())
+        .thenReturn(new MultivaluedHashMap<>());
+    rest.setContext(context);
+  }
+
+  @Test
+  public void get() throws IOException, OS3Exception {
     //WHEN
     Response response = rest.get("b1", "key1", null, 0, null, body);
 
@@ -85,4 +117,73 @@ public class TestObjectGet {
         .parse(response.getHeaderString("Last-Modified"));
 
   }
+
+  @Test
+  public void inheritRequestHeader() throws IOException, OS3Exception {
+    setDefaultHeader();
+
+    Response response = rest.get("b1", "key1", null, 0, null, body);
+
+    Assert.assertEquals(CONTENT_TYPE1,
+        response.getHeaderString("Content-Type"));
+    Assert.assertEquals(CONTENT_LANGUAGE1,
+        response.getHeaderString("Content-Language"));
+    Assert.assertEquals(EXPIRES1,
+        response.getHeaderString("Expires"));
+    Assert.assertEquals(CACHE_CONTROL1,
+        response.getHeaderString("Cache-Control"));
+    Assert.assertEquals(CONTENT_DISPOSITION1,
+        response.getHeaderString("Content-Disposition"));
+    Assert.assertEquals(CONTENT_ENCODING1,
+        response.getHeaderString("Content-Encoding"));
+  }
+
+  @Test
+  public void overrideResponseHeader() throws IOException, OS3Exception {
+    setDefaultHeader();
+
+    MultivaluedHashMap<String, String> queryParameter =
+        new MultivaluedHashMap<>();
+    // overrider request header
+    queryParameter.putSingle("response-content-type", CONTENT_TYPE2);
+    queryParameter.putSingle("response-content-language", CONTENT_LANGUAGE2);
+    queryParameter.putSingle("response-expires", EXPIRES2);
+    queryParameter.putSingle("response-cache-control", CACHE_CONTROL2);
+    queryParameter.putSingle("response-content-disposition",
+        CONTENT_DISPOSITION2);
+    queryParameter.putSingle("response-content-encoding", CONTENT_ENCODING2);
+
+    Mockito.when(context.getUriInfo().getQueryParameters())
+        .thenReturn(queryParameter);
+    body = new ByteArrayInputStream(CONTENT.getBytes(UTF_8));
+    Response response = rest.get("b1", "key1", null, 0, null, body);
+
+    Assert.assertEquals(CONTENT_TYPE2,
+        response.getHeaderString("Content-Type"));
+    Assert.assertEquals(CONTENT_LANGUAGE2,
+        response.getHeaderString("Content-Language"));
+    Assert.assertEquals(EXPIRES2,
+        response.getHeaderString("Expires"));
+    Assert.assertEquals(CACHE_CONTROL2,
+        response.getHeaderString("Cache-Control"));
+    Assert.assertEquals(CONTENT_DISPOSITION2,
+        response.getHeaderString("Content-Disposition"));
+    Assert.assertEquals(CONTENT_ENCODING2,
+        response.getHeaderString("Content-Encoding"));
+  }
+
+  private void setDefaultHeader() {
+    doReturn(CONTENT_TYPE1)
+        .when(headers).getHeaderString("Content-Type");
+    doReturn(CONTENT_LANGUAGE1)
+        .when(headers).getHeaderString("Content-Language");
+    doReturn(EXPIRES1)
+        .when(headers).getHeaderString("Expires");
+    doReturn(CACHE_CONTROL1)
+        .when(headers).getHeaderString("Cache-Control");
+    doReturn(CONTENT_DISPOSITION1)
+        .when(headers).getHeaderString("Content-Disposition");
+    doReturn(CONTENT_ENCODING1)
+        .when(headers).getHeaderString("Content-Encoding");
+  }
 }
diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/metrics/TestS3GatewayMetrics.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/metrics/TestS3GatewayMetrics.java
index 9ce6a95001..daae572df9 100644
--- a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/metrics/TestS3GatewayMetrics.java
+++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/metrics/TestS3GatewayMetrics.java
@@ -39,8 +39,11 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedHashMap;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -70,6 +73,7 @@ public class TestS3GatewayMetrics {
   private static final String ACL_MARKER = "acl";
   private static final String CONTENT = "0123456789";
   private S3GatewayMetrics metrics;
+  private ContainerRequestContext context;
 
 
   @Before
@@ -93,6 +97,12 @@ public class TestS3GatewayMetrics {
         "STANDARD");
     keyEndpoint.setHeaders(headers);
     metrics = bucketEndpoint.getMetrics();
+
+    context = Mockito.mock(ContainerRequestContext.class);
+    Mockito.when(context.getUriInfo()).thenReturn(Mockito.mock(UriInfo.class));
+    Mockito.when(context.getUriInfo().getQueryParameters())
+        .thenReturn(new MultivaluedHashMap<>());
+    keyEndpoint.setContext(context);
   }
 
   /**


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org