private void processGetBlobInfoResponse(RequestInfo getRequestInfo, GetResponse getResponse)
      throws IOException, MessageFormatException {
    ServerErrorCode getError = getResponse.getError();
    if (getError == ServerErrorCode.NoError) {
      int partitionsInResponse = getResponse.getPartitionResponseInfoList().size();
      // Each get request issued by the router is for a single blob.
      if (partitionsInResponse != 1) {
        onErrorResponse(getRequestInfo.getReplicaId(), new RouterException(
            "Unexpected number of partition responses, expected: 1, " + "received: " + partitionsInResponse,
            RouterErrorCode.UnexpectedInternalError));
        // Again, no need to notify the responseHandler.
      } else {
        getError = getResponse.getPartitionResponseInfoList().get(0).getErrorCode();
        if (getError == ServerErrorCode.NoError) {
          PartitionResponseInfo partitionResponseInfo = getResponse.getPartitionResponseInfoList().get(0);
          int msgsInResponse = partitionResponseInfo.getMessageInfoList().size();
          if (msgsInResponse != 1) {
            onErrorResponse(getRequestInfo.getReplicaId(), new RouterException(
                "Unexpected number of messages in a partition response, expected: 1, " + "received: " + msgsInResponse,
                RouterErrorCode.UnexpectedInternalError));
          } else {
            MessageMetadata messageMetadata = partitionResponseInfo.getMessageMetadataList().get(0);
            MessageInfo messageInfo = partitionResponseInfo.getMessageInfoList().get(0);
            handleBody(getResponse.getInputStream(), messageMetadata, messageInfo);
            operationTracker.onResponse(getRequestInfo.getReplicaId(), TrackedRequestFinalState.SUCCESS);
            if (RouterUtils.isRemoteReplica(routerConfig, getRequestInfo.getReplicaId())) {
              logger.trace("Cross colo request successful for remote replica in {} ",
                  getRequestInfo.getReplicaId().getDataNodeId().getDatacenterName());
              routerMetrics.crossColoSuccessCount.inc();
            }
          }
        } else {
          // process and set the most relevant exception.
          logger.trace("Replica  {} returned error {} with response correlationId {} ",
              getRequestInfo.getReplicaId().getDataNodeId(), getError, getResponse.getCorrelationId());
          RouterErrorCode routerErrorCode = processServerError(getError);
          if (getError == ServerErrorCode.DiskUnavailable) {
            operationTracker.onResponse(getRequestInfo.getReplicaId(), TrackedRequestFinalState.DISK_DOWN);
            setOperationException(new RouterException("Server returned: " + getError, routerErrorCode));
            routerMetrics.routerRequestErrorCount.inc();
            routerMetrics.getDataNodeBasedMetrics(
                getRequestInfo.getReplicaId().getDataNodeId()).getBlobInfoRequestErrorCount.inc();
          } else {
            if (getError == ServerErrorCode.BlobDeleted || getError == ServerErrorCode.BlobExpired
                || getError == ServerErrorCode.BlobAuthorizationFailure) {
              // this is a successful response and one that completes the operation regardless of whether the
              // success target has been reached or not.
              operationCompleted = true;
            }
            // any server error code that is not equal to ServerErrorCode.No_Error, the onErrorResponse should be invoked
            // because the operation itself doesn't succeed although the response in some cases is successful (i.e. Blob_Deleted)
            onErrorResponse(getRequestInfo.getReplicaId(),
                new RouterException("Server returned: " + getError, routerErrorCode));
          }
        }
      }
    } else {
      logger.trace("Replica {} returned an error {} for a GetBlobInfoRequest with response correlationId : {} ",
          getRequestInfo.getReplicaId().getDataNodeId(), getError, getResponse.getCorrelationId());
      onErrorResponse(getRequestInfo.getReplicaId(),
          new RouterException("Server returned", processServerError(getError)));
    }
  }
Process the {@link GetResponse} extracted from a {@link ResponseInfo}
@param getRequestInfo the associated {@link RequestInfo} for which this response was received.
@param getResponse the {@link GetResponse} extracted from the {@link ResponseInfo}
@throws IOException if there is an error during deserialization of the GetResponse.
@throws MessageFormatException if there is an error during deserialization of the GetResponse.
ambry-router/src/main/java/com/github/ambry/router/GetBlobInfoOperation.java