docs: public void copyObject(String bucketName, String objectName, String destBucketName,
                           String destObjectName, CopyConditions copyConditions,
                           Map<String,String> metadata)
        throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
        NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
        InvalidArgumentException {
  
      if (bucketName == null) {
        throw new InvalidArgumentException("Source bucket name cannot be empty");
      }
      if (objectName == null) {
        throw new InvalidArgumentException("Source object name cannot be empty");
      }
      if (destBucketName == null) {
        throw new InvalidArgumentException("Destination bucket name cannot be empty");
      }
  
      // Escape source object path.
      String sourceObjectPath = S3Escaper.encodePath(bucketName + "/" + objectName);
  
      // Destination object name is optional, if empty default to source object name.
      if (destObjectName == null) {
        destObjectName = objectName;
      }
  
      Map<String, String> headerMap = new HashMap<>();
  
      // Set the object source
      headerMap.put("x-amz-copy-source", sourceObjectPath);
  
      // If no conditions available, skip addition else add the conditions to the header
      if (copyConditions != null) {
        headerMap.putAll(copyConditions.getConditions());
      }
  
      // Set metadata on the destination of object.
      if (metadata != null) {
        headerMap.putAll(metadata);
      }
  
      HttpResponse response = executePut(destBucketName, destObjectName, headerMap,
          null, "", 0);
  
      // For now ignore the copyObjectResult, just read and parse it.
      CopyObjectResult result = new CopyObjectResult();
      try (ResponseBody body = response.body()) {
        result.parseXml(body.charStream());
      }
    }