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 580b7243c9..c504387a05 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 @@ -35,7 +35,6 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; @@ -48,22 +47,27 @@ import org.apache.hadoop.ozone.s3.SignedChunksInputStream; import org.apache.hadoop.ozone.s3.exception.OS3Exception; import org.apache.hadoop.ozone.s3.exception.S3ErrorTable; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.io.IOUtils; - import org.apache.hadoop.ozone.s3.io.S3WrapperInputStream; +import org.apache.hadoop.ozone.s3.util.RFC1123Util; import org.apache.hadoop.ozone.s3.util.RangeHeader; import org.apache.hadoop.ozone.s3.util.S3StorageType; 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.annotations.VisibleForTesting; +import static javax.ws.rs.core.HttpHeaders.LAST_MODIFIED; +import org.apache.commons.io.IOUtils; +import static org.apache.hadoop.ozone.s3.util.S3Consts.ACCEPT_RANGE_HEADER; +import static org.apache.hadoop.ozone.s3.util.S3Consts.CONTENT_RANGE_HEADER; +import static org.apache.hadoop.ozone.s3.util.S3Consts.COPY_SOURCE_HEADER; +import static org.apache.hadoop.ozone.s3.util.S3Consts.RANGE_HEADER; +import static org.apache.hadoop.ozone.s3.util.S3Consts.RANGE_HEADER_SUPPORTED_UNIT; +import static org.apache.hadoop.ozone.s3.util.S3Consts.STORAGE_CLASS_HEADER; import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.apache.hadoop.ozone.s3.util.S3Consts.*; - /** * Key level rest endpoints. */ @@ -241,7 +245,7 @@ public Response get( responseBuilder.header(responseHeader, headerValue); } } - + addLastModifiedDate(responseBuilder, keyDetails); return responseBuilder.build(); } catch (IOException ex) { if (ex.getMessage().contains("NOT_FOUND")) { @@ -254,6 +258,18 @@ public Response get( } } + private void addLastModifiedDate( + ResponseBuilder responseBuilder, OzoneKeyDetails key) { + + ZonedDateTime lastModificationTime = + Instant.ofEpochMilli(key.getModificationTime()) + .atZone(ZoneId.of("GMT")); + + responseBuilder + .header(LAST_MODIFIED, + RFC1123Util.FORMAT.format(lastModificationTime)); + } + /** * Rest endpoint to check existence of an object in a bucket. *
@@ -279,16 +295,12 @@ public Response head( } } - ZonedDateTime lastModificationTime = - Instant.ofEpochMilli(key.getModificationTime()) - .atZone(ZoneId.of("GMT")); - - return Response.ok().status(HttpStatus.SC_OK) - .header("Last-Modified", - DateTimeFormatter.RFC_1123_DATE_TIME.format(lastModificationTime)) + ResponseBuilder response = Response.ok().status(HttpStatus.SC_OK) .header("ETag", "" + key.getModificationTime()) .header("Content-Length", key.getDataSize()) - .header("Content-Type", "binary/octet-stream") + .header("Content-Type", "binary/octet-stream"); + addLastModifiedDate(response, key); + return response .build(); } diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/RFC1123Util.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/RFC1123Util.java new file mode 100644 index 0000000000..15a09b4bce --- /dev/null +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/RFC1123Util.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ +package org.apache.hadoop.ozone.s3.util; + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.SignStyle; +import java.util.HashMap; +import java.util.Map; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; + +/** + * Stricter RFC1123 data format. + *
+ * This format always use two digits for the days to make it compatible with
+ * golang clients.
+ */
+public final class RFC1123Util {
+
+ private RFC1123Util() {
+ }
+
+ /**
+ * An RFC-1123 compatible file format which always use two digits for the
+ * days.
+ */
+ public static final DateTimeFormatter FORMAT;
+
+ static {
+ Map