docs: @Override
      public CopyPartResult copyPart(CopyPartRequest copyPartRequest) {
          copyPartRequest = beforeClientExecution(copyPartRequest);
          rejectNull(copyPartRequest.getSourceBucketName(),
                  "The source bucket name must be specified when copying a part");
          rejectNull(copyPartRequest.getSourceKey(),
                  "The source object key must be specified when copying a part");
          rejectNull(copyPartRequest.getDestinationBucketName(),
                  "The destination bucket name must be specified when copying a part");
          rejectNull(copyPartRequest.getUploadId(),
                  "The upload id must be specified when copying a part");
          rejectNull(copyPartRequest.getDestinationKey(),
                  "The destination object key must be specified when copying a part");
          rejectNull(copyPartRequest.getPartNumber(),
                  "The part number must be specified when copying a part");
  
          String destinationKey = copyPartRequest.getDestinationKey();
          String destinationBucketName = copyPartRequest.getDestinationBucketName();
  
          Request<CopyPartRequest> request = createRequest(destinationBucketName, destinationKey, copyPartRequest,
                  HttpMethodName.PUT);
          request.addHandlerContext(HandlerContextKey.OPERATION_NAME, "UploadPartCopy");
  
          populateRequestWithCopyPartParameters(request, copyPartRequest);
  
          request.addParameter("uploadId", copyPartRequest.getUploadId());
          request.addParameter("partNumber", Integer.toString(copyPartRequest.getPartNumber()));
  
          populateRequesterPaysHeader(request, copyPartRequest.isRequesterPays());
  
          /*
           * We can't send a non-zero length Content-Length header if the user
           * specified it, otherwise it messes up the HTTP connection when the
           * remote server thinks there's more data to pull.
           */
          setZeroContentLength(request);
          CopyObjectResultHandler copyObjectResultHandler = null;
          try {
              @SuppressWarnings("unchecked")
              ResponseHeaderHandlerChain<CopyObjectResultHandler> handler = new ResponseHeaderHandlerChain<CopyObjectResultHandler>(
                      // xml payload unmarshaller
                      new Unmarshallers.CopyObjectUnmarshaller(),
                      // header handlers
                      new ServerSideEncryptionHeaderHandler<CopyObjectResultHandler>(),
                      new S3VersionHeaderHandler<CopyObjectResultHandler>());
              copyObjectResultHandler = invoke(request, handler, destinationBucketName, destinationKey);
          } catch ( AmazonS3Exception ase ) {
              /*
               * If the request failed because one of the specified constraints
               * was not met (ex: matching ETag, modified since date, etc.), then
               * return null, so that users don't have to wrap their code in
               * try/catch blocks and check for this status code if they want to
               * use constraints.
               */
              if ( ase.getStatusCode() == Constants.FAILED_PRECONDITION_STATUS_CODE ) {
                  return null;
              }
  
              throw ase;
          }
  
          /*
           * CopyPart has two failure modes: 1 - An HTTP error code is returned
           * and the error is processed like any other error response. 2 - An HTTP
           * 200 OK code is returned, but the response content contains an XML
           * error response.
           *
           * This makes it very difficult for the client runtime to cleanly detect
           * this case and handle it like any other error response. We could
           * extend the runtime to have a more flexible/customizable definition of
           * success/error (per request), but it's probably overkill for this one
           * special case.
           */
          if ( copyObjectResultHandler.getErrorCode() != null ) {
              String errorCode = copyObjectResultHandler.getErrorCode();
              String errorMessage = copyObjectResultHandler.getErrorMessage();
              String requestId = copyObjectResultHandler.getErrorRequestId();
              String hostId = copyObjectResultHandler.getErrorHostId();
  
              AmazonS3Exception ase = new AmazonS3Exception(errorMessage);
              ase.setErrorCode(errorCode);
              ase.setErrorType(ErrorType.Service);
              ase.setRequestId(requestId);
              ase.setExtendedRequestId(hostId);
              ase.setServiceName(request.getServiceName());
              ase.setStatusCode(200);
  
              throw ase;
          }
  
          CopyPartResult copyPartResult = new CopyPartResult();
          copyPartResult.setETag(copyObjectResultHandler.getETag());
          copyPartResult.setPartNumber(copyPartRequest.getPartNumber());
          copyPartResult.setLastModifiedDate(copyObjectResultHandler.getLastModified());
          copyPartResult.setVersionId(copyObjectResultHandler.getVersionId());
          copyPartResult.setSSEAlgorithm(copyObjectResultHandler.getSSEAlgorithm());
          copyPartResult.setSSECustomerAlgorithm(copyObjectResultHandler.getSSECustomerAlgorithm());
          copyPartResult.setSSECustomerKeyMd5(copyObjectResultHandler.getSSECustomerKeyMd5());
  
          return copyPartResult;
      }