@SuppressWarnings("unchecked")
  static <R extends Response> R extractResponseAndNotifyResponseHandler(ResponseHandler responseHandler,
      NonBlockingRouterMetrics routerMetrics, ResponseInfo responseInfo, Deserializer<R> deserializer,
      Function<R, ServerErrorCode> errorExtractor) {
    R response = null;
    if (responseInfo.isQuotaRejected()) {
      return response;
    }
    ReplicaId replicaId = responseInfo.getRequestInfo().getReplicaId();
    NetworkClientErrorCode networkClientErrorCode = responseInfo.getError();
    if (networkClientErrorCode == null) {
      try {
        if (responseInfo.getResponse() != null) {
          // If this responseInfo already has the deserialized java object, we can reference it directly. This is
          // applicable when we are receiving responses from Azure APIs in frontend. The responses from Azure are
          // handled in {@code AmbryRequest} class methods using a thread pool running with in the frontend. These
          // responses are then sent using local queues in {@code LocalRequestResponseChannel}.
          response = (R) mapToReceivedResponse((Response) responseInfo.getResponse());
        } else {
          DataInputStream dis = new NettyByteBufDataInputStream(responseInfo.content());
          response = deserializer.readFrom(dis);
        }
        responseHandler.onEvent(replicaId, errorExtractor.apply(response));
      } catch (Exception e) {
        // Ignore. There is no value in notifying the response handler.
        logger.error("Response deserialization received unexpected error", e);
        routerMetrics.responseDeserializationErrorCount.inc();
      }
    } else {
      responseHandler.onEvent(replicaId, networkClientErrorCode);
    }
    return response;
  }
Extract the {@link Response} from the given {@link ResponseInfo}
@param <R> the {@link Response} type.
@param responseHandler the {@link ResponseHandler} instance to use.
@param routerMetrics the {@link NonBlockingRouterMetrics} instance to use.
@param responseInfo the {@link ResponseInfo} from which the {@link Response} is to be extracted.
@param deserializer the {@link Deserializer} to use.
@param errorExtractor extract the {@link ServerErrorCode} to send to {@link ResponseHandler#onEvent}.
@return the extracted {@link Response} if there is one; null otherwise.
ambry-router/src/main/java/com/github/ambry/router/RouterUtils.java