private void processGetBlobResponse(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(), buildChunkException(
              "Unexpected number of partition responses, expected: 1, " + "received: " + partitionsInResponse,
              RouterErrorCode.UnexpectedInternalError));
        } else {
          getError = getResponse.getPartitionResponseInfoList().get(0).getErrorCode();
          if (getError == ServerErrorCode.NoError) {
            PartitionResponseInfo partitionResponseInfo = getResponse.getPartitionResponseInfoList().get(0);
            int objectsInPartitionResponse = partitionResponseInfo.getMessageInfoList().size();
            if (objectsInPartitionResponse != 1) {
              onErrorResponse(getRequestInfo.getReplicaId(), buildChunkException(
                  "Unexpected number of messages in a partition response, expected: 1, " + "received: "
                      + objectsInPartitionResponse, RouterErrorCode.UnexpectedInternalError));
            } else {
              MessageMetadata messageMetadata = partitionResponseInfo.getMessageMetadataList().get(0);
              MessageInfo messageInfo = partitionResponseInfo.getMessageInfoList().get(0);
              handleBody(getResponse.getInputStream(), messageMetadata, messageInfo);
              chunkOperationTracker.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.
            RouterErrorCode routerErrorCode = processServerError(getError);
            if (getError == ServerErrorCode.DiskUnavailable) {
              chunkOperationTracker.onResponse(getRequestInfo.getReplicaId(), TrackedRequestFinalState.DISK_DOWN);
              setChunkException(buildChunkException("Server returned: " + getError, routerErrorCode));
              routerMetrics.routerRequestErrorCount.inc();
              routerMetrics.getDataNodeBasedMetrics(
                  getRequestInfo.getReplicaId().getDataNodeId()).getRequestErrorCount.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.
                chunkCompleted = true;
                chunkException = buildChunkException("Server returned: " + getError, routerErrorCode);
              }
              // 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(),
                  buildChunkException("Server returned: " + getError, routerErrorCode));
            }
          }
        }
      } else {
        logger.trace("Replica {} returned an error {} for a GetBlobRequest with response correlationId : {} ",
            getRequestInfo.getReplicaId().getDataNodeId(), getError, getResponse.getCorrelationId());
        // process and set the most relevant exception.
        onErrorResponse(getRequestInfo.getReplicaId(),
            buildChunkException("Server returned", processServerError(getError)));
      }
    }
Process the 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/GetBlobOperation.java