public void handle(RestRequest restRequest, RestResponseChannel restResponseChannel, Callback<R> callback) {
    try {
      String path = ((RequestPath) restRequest.getArgs().get(REQUEST_PATH)).getOperationOrBlobId(true);
      if (!path.startsWith(Operations.NAMED_BLOB)) {
        throw new RuntimeException("S3 request handler can only handle named blob requests");
      }

      LOGGER.debug("{} {}", restRequest.getRestMethod(), path);

      doHandle(restRequest, restResponseChannel, CallbackUtils.chainCallback(callback, (r) -> {
        removeAmbryHeaders(restResponseChannel);
        callback.onCompletion(r, null);
      }));
    } catch (Throwable t) {
      Exception e = t instanceof Exception ? (Exception) t
          : new RestServiceException(t, RestServiceErrorCode.InternalServerError);
      callback.onCompletion(null, e);
    }
  }
Process the request and construct the response, internally the invocation is delegated
to method {@link #doHandle(RestRequest, RestResponseChannel, Callback)}, which is
implemented by subclasses.

@param restRequest the {@link RestRequest} that contains the request headers and body.
@param restResponseChannel the {@link RestResponseChannel} that contains the response headers and body.
@param callback the {@link Callback} to invoke when the response is ready (or if there is an exception).
@throws RestServiceException exception when the processing fails
ambry-frontend/src/main/java/com/github/ambry/frontend/s3/S3BaseHandler.java