docs: public CopyObjectResponse copyObject(CopyObjectRequest request) {
          checkNotNull(request, "request should not be null.");
          assertStringNotNullOrEmpty(request.getSourceKey(), "object key should not be null or empty");
  
          InternalRequest internalRequest = this.createRequest(request, HttpMethodName.PUT);
  
          String copySourceHeader = "/" + request.getSourceBucketName() + "/" + request.getSourceKey();
          copySourceHeader = HttpUtils.normalizePath(copySourceHeader);
          internalRequest.addHeader(Headers.BCE_COPY_SOURCE, copySourceHeader);
          if (request.getETag() != null) {
              internalRequest.addHeader(Headers.BCE_COPY_SOURCE_IF_MATCH,
                      "\"" + request.getETag() + "\"");
          }
          if (request.getNoneMatchETagConstraint() != null) {
              internalRequest.addHeader(
                      Headers.BCE_COPY_SOURCE_IF_NONE_MATCH, "\"" + request.getNoneMatchETagConstraint() + "\"");
          }
          if (request.getUnmodifiedSinceConstraint() != null) {
              internalRequest.addHeader(
                      Headers.BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE, request.getUnmodifiedSinceConstraint());
          }
          if (request.getModifiedSinceConstraint() != null) {
              internalRequest.addHeader(
                      Headers.BCE_COPY_SOURCE_IF_MODIFIED_SINCE, request.getModifiedSinceConstraint());
          }
          if (request.getStorageClass() != null) {
              internalRequest.addHeader(Headers.BCE_STORAGE_CLASS, request.getStorageClass());
          }
          ObjectMetadata newObjectMetadata = request.getNewObjectMetadata();
          if (newObjectMetadata != null) {
              internalRequest.addHeader(Headers.BCE_COPY_METADATA_DIRECTIVE, "replace");
              populateRequestMetadata(internalRequest, newObjectMetadata);
          } else {
              internalRequest.addHeader(Headers.BCE_COPY_METADATA_DIRECTIVE, "copy");
          }
  
          this.setZeroContentLength(internalRequest);
  
          CopyObjectResponseWithExceptionInfo intermidiateRes = this.invokeHttpClient(internalRequest,
                  CopyObjectResponseWithExceptionInfo.class);
          // handle exception
          if (intermidiateRes.getETag() == null 
                  && intermidiateRes.getLastModified() == null) {
              if (intermidiateRes.getMessage() != null) {
                  BceServiceException bse = new BceServiceException(intermidiateRes.getMessage());
                  bse.setErrorCode(intermidiateRes.getCode());
                  bse.setRequestId(intermidiateRes.getRequestId());
                  if (bse.getErrorCode() == "InternalError") {
                      bse.setErrorType(ErrorType.Service);
                  } else {
                      bse.setErrorType(ErrorType.Client);
                  }
                  bse.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
                  throw bse;
              }
          }
          return (CopyObjectResponse) intermidiateRes;
      }