rich7420 commented on code in PR #9636:
URL: https://github.com/apache/ozone/pull/9636#discussion_r2696669105


##########
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java:
##########
@@ -603,4 +676,197 @@ protected boolean isAccessDenied(OMException ex) {
     return result == ResultCodes.PERMISSION_DENIED
         || result == ResultCodes.INVALID_TOKEN;
   }
+
+  protected ReplicationConfig getReplicationConfig(OzoneBucket ozoneBucket) 
throws OS3Exception {
+    String storageType = getHeaders().getHeaderString(STORAGE_CLASS_HEADER);
+    String storageConfig = 
getHeaders().getHeaderString(CUSTOM_METADATA_HEADER_PREFIX + 
STORAGE_CONFIG_HEADER);
+
+    ReplicationConfig clientConfiguredReplicationConfig =
+        
OzoneClientUtils.getClientConfiguredReplicationConfig(getOzoneConfiguration());
+
+    return S3Utils.resolveS3ClientSideReplicationConfig(storageType, 
storageConfig,
+        clientConfiguredReplicationConfig, ozoneBucket.getReplicationConfig());
+  }
+
+  /**
+   * Parse the key and bucket name from copy header.
+   */
+  public static Pair<String, String> parseSourceHeader(String copyHeader)
+      throws OS3Exception {
+    String header = copyHeader;
+    if (header.startsWith("/")) {
+      header = copyHeader.substring(1);
+    }
+    int pos = header.indexOf('/');
+    if (pos == -1) {
+      OS3Exception ex = newError(INVALID_ARGUMENT, header);
+      ex.setErrorMessage("Copy Source must mention the source bucket and " +
+          "key: sourcebucket/sourcekey");
+      throw ex;
+    }
+
+    try {
+      String bucket = header.substring(0, pos);
+      String key = urlDecode(header.substring(pos + 1));
+      return Pair.of(bucket, key);
+    } catch (UnsupportedEncodingException e) {
+      OS3Exception ex = newError(INVALID_ARGUMENT, header, e);
+      ex.setErrorMessage("Copy Source header could not be url-decoded");
+      throw ex;
+    }
+  }
+
+  protected static int parsePartNumberMarker(String partNumberMarker) {
+    int partMarker = 0;
+    if (partNumberMarker != null) {
+      partMarker = Integer.parseInt(partNumberMarker);
+    }
+    return partMarker;
+  }
+
+  // Parses date string and return long representation. Returns an
+  // empty if DateStr is null or invalid. Dates in the future are
+  // considered invalid.
+  private static OptionalLong parseAndValidateDate(String ozoneDateStr) {
+    long ozoneDateInMs;
+    if (ozoneDateStr == null) {
+      return OptionalLong.empty();
+    }
+    try {
+      ozoneDateInMs = OzoneUtils.formatDate(ozoneDateStr);
+    } catch (ParseException e) {
+      // if time not parseable, then return empty()
+      return OptionalLong.empty();
+    }
+
+    long currentDate = System.currentTimeMillis();
+    if (ozoneDateInMs <= currentDate) {
+      return OptionalLong.of(ozoneDateInMs);
+    } else {
+      // dates in the future are invalid, so return empty()
+      return OptionalLong.empty();
+    }
+  }
+
+  public static boolean checkCopySourceModificationTime(
+      Long lastModificationTime,
+      String copySourceIfModifiedSinceStr,
+      String copySourceIfUnmodifiedSinceStr) {
+    long copySourceIfModifiedSince = Long.MIN_VALUE;
+    long copySourceIfUnmodifiedSince = Long.MAX_VALUE;
+
+    OptionalLong modifiedDate =
+        parseAndValidateDate(copySourceIfModifiedSinceStr);
+    if (modifiedDate.isPresent()) {
+      copySourceIfModifiedSince = modifiedDate.getAsLong();
+    }
+
+    OptionalLong unmodifiedDate =
+        parseAndValidateDate(copySourceIfUnmodifiedSinceStr);
+    if (unmodifiedDate.isPresent()) {
+      copySourceIfUnmodifiedSince = unmodifiedDate.getAsLong();
+    }
+    return (copySourceIfModifiedSince <= lastModificationTime) &&
+        (lastModificationTime <= copySourceIfUnmodifiedSince);
+  }
+
+  /**
+   * Create a {@link S3ChunkInputStreamInfo} that contains the necessary 
information to handle
+   * the S3 chunk upload.
+   */
+  protected S3ChunkInputStreamInfo getS3ChunkInputStreamInfo(
+      InputStream body, long contentLength, String amzDecodedLength, String 
keyPath) throws OS3Exception {
+    final String amzContentSha256Header = 
validateSignatureHeader(getHeaders(), keyPath, signatureInfo.isSignPayload());
+    final InputStream chunkInputStream;
+    final long effectiveLength;
+    if (hasMultiChunksPayload(amzContentSha256Header)) {
+      validateMultiChunksUpload(getHeaders(), amzDecodedLength, keyPath);

Review Comment:
   According to AWS S3 SigV4 streaming docs, x-amz-decoded-content-length must 
be “the length, in bytes” of the decoded payload (numeric).Currently 
validateMultiChunksUpload() only checks null and later calls 
Long.parseLong(...) without handling NumberFormatException, so a malformed 
header (e.g. abc) can trigger 500. I  think maybe there should be treated as a 
client error (400) per S3 error response behavior.
   
   Maybe by adding try-catch like:
   ```
   try {
       Long.parseLong(amzDecodedContentLength);
     } catch (NumberFormatException e) {
       OS3Exception ex = S3ErrorTable.newError(S3ErrorTable.INVALID_ARGUMENT, 
resource, e);
       ex.setErrorMessage("An error occurred (InvalidArgument) for multi chunks 
upload: " +
           "The " + DECODED_CONTENT_LENGTH_HEADER + " header must be a valid 
number");
       throw ex;
     }
   ```
   
   Ref: https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html ,
   https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to