@Override
  public void processResponse(RestRequest restRequest, RestResponseChannel responseChannel, BlobInfo blobInfo,
      Callback<Void> callback) {
    Exception exception = null;
    frontendMetrics.securityServiceProcessResponseRate.mark();
    long startTimeMs = System.currentTimeMillis();
    if (!isOpen) {
      exception = new RestServiceException("SecurityService is closed", RestServiceErrorCode.ServiceUnavailable);
    } else {
      if (restRequest == null || responseChannel == null) {
        throw new IllegalArgumentException("One of the required params is null");
      }
      RequestPath requestPath = RestUtils.getRequestPath(restRequest);
      RestMethod restMethod = restRequest.getRestMethod();
      if (blobInfo == null && !restMethod.equals(RestMethod.OPTIONS) && !restMethod.equals(RestMethod.PUT)
          && !restMethod.equals(RestMethod.DELETE)) {
        if (!requestPath.matchesOperation(Operations.GET_SIGNED_URL)) {
          throw new IllegalArgumentException("BlobInfo is null");
        }
      }
      try {
        GetBlobOptions options;
        responseChannel.setHeader(RestUtils.Headers.DATE, new GregorianCalendar().getTime());
        switch (restMethod) {
          case HEAD:
            options = RestUtils.buildGetBlobOptions(restRequest.getArgs(), null, GetOption.None, restRequest,
                NO_BLOB_SEGMENT_IDX_SPECIFIED);
            responseChannel.setStatus(options.getRange() == null ? ResponseStatus.Ok : ResponseStatus.PartialContent);
            responseChannel.setHeader(RestUtils.Headers.LAST_MODIFIED,
                new Date(blobInfo.getBlobProperties().getCreationTimeInMs()));
            setHeadResponseHeaders(blobInfo, options, restRequest, responseChannel);
            break;
          case GET:
            if (!requestPath.matchesOperation(Operations.GET_SIGNED_URL)) {
              options = RestUtils.buildGetBlobOptions(restRequest.getArgs(), null, GetOption.None, restRequest,
                  NO_BLOB_SEGMENT_IDX_SPECIFIED);
              responseChannel.setStatus(ResponseStatus.Ok);
              RestUtils.SubResource subResource = RestUtils.getRequestPath(restRequest).getSubResource();
              responseChannel.setHeader(RestUtils.Headers.LAST_MODIFIED,
                  new Date(blobInfo.getBlobProperties().getCreationTimeInMs()));
              Container container = RestUtils.getContainerFromArgs(restRequest.getArgs());
              if (subResource == null) {
                Long ifModifiedSinceMs = getIfModifiedSinceMs(restRequest);
                if (ifModifiedSinceMs != null
                    && RestUtils.toSecondsPrecisionInMs(blobInfo.getBlobProperties().getCreationTimeInMs())
                    <= ifModifiedSinceMs) {
                  responseChannel.setStatus(ResponseStatus.NotModified);
                } else {
                  if (options.getRange() != null) {
                    responseChannel.setStatus(ResponseStatus.PartialContent);
                  }
                  setGetBlobResponseHeaders(restRequest, blobInfo, options, responseChannel);
                  setBlobPropertiesHeaders(blobInfo.getBlobProperties(), responseChannel);
                  setAccountAndContainerHeaders(restRequest, responseChannel);
                  setUserMetadataHeaders(blobInfo.getUserMetadata(), responseChannel, container.getUserMetadataKeysToNotPrefixInResponse());
                  responseChannel.setHeader(RestUtils.Headers.LIFE_VERSION, blobInfo.getLifeVersion());
                  if (blobInfo.getBlobProperties().getReservedMetadataBlobId() != null) {
                    responseChannel.setHeader(
                        Headers.RESERVED_METADATA_ID, blobInfo.getBlobProperties().getReservedMetadataBlobId());
                  }
                }
                setCacheHeaders(restRequest, responseChannel);
              } else {
                if (subResource.equals(RestUtils.SubResource.BlobInfo)) {
                  setBlobInfoHeaders(blobInfo.getBlobProperties(), responseChannel);
                  setBlobPropertiesHeaders(blobInfo.getBlobProperties(), responseChannel);
                  setAccountAndContainerHeaders(restRequest, responseChannel);
                  responseChannel.setHeader(RestUtils.Headers.LIFE_VERSION, blobInfo.getLifeVersion());
                  if (blobInfo.getBlobProperties().getReservedMetadataBlobId() != null) {
                    responseChannel.setHeader(
                        Headers.RESERVED_METADATA_ID, blobInfo.getBlobProperties().getReservedMetadataBlobId());
                  }
                } else if (subResource.equals(SubResource.Segment)) {
                  setGetBlobResponseHeaders(restRequest, blobInfo, options, responseChannel);
                  setBlobPropertiesHeaders(blobInfo.getBlobProperties(), responseChannel);
                  setAccountAndContainerHeaders(restRequest, responseChannel);
                }
                if (!setUserMetadataHeaders(blobInfo.getUserMetadata(), responseChannel, container.getUserMetadataKeysToNotPrefixInResponse())) {
                  restRequest.setArg(InternalKeys.SEND_USER_METADATA_AS_RESPONSE_BODY, true);
                }
              }
            }
            break;
          case POST:
            responseChannel.setStatus(ResponseStatus.Created);
            responseChannel.setHeader(RestUtils.Headers.CONTENT_LENGTH, 0);
            responseChannel.setHeader(RestUtils.Headers.CREATION_TIME,
                new Date(blobInfo.getBlobProperties().getCreationTimeInMs()));
            break;
          case OPTIONS:
          case PUT:
            if (requestPath.matchesOperation(Operations.NAMED_BLOB)) {
              responseChannel.setStatus(ResponseStatus.Created);
              responseChannel.setHeader(RestUtils.Headers.CONTENT_LENGTH, 0);
              responseChannel.setHeader(RestUtils.Headers.CREATION_TIME,
                  new Date(blobInfo.getBlobProperties().getCreationTimeInMs()));
            }
            break;
          case DELETE:
            // There is nothing to do with delete
            break;
          default:
            exception = new RestServiceException("Cannot process response for request with method " + restMethod,
                RestServiceErrorCode.InternalServerError);
        }
      } catch (RestServiceException e) {
        exception = e;
      }
    }
    processRequestCharges(restRequest, responseChannel, blobInfo);
    frontendMetrics.securityServiceProcessResponseTimeInMs.update(System.currentTimeMillis() - startTimeMs);
    callback.onCompletion(null, exception);
  }
Default implementation of {@link SecurityService} for Ambry that doesn't do any validations, but just
sets the respective headers on response.
ambry-frontend/src/main/java/com/github/ambry/frontend/AmbrySecurityService.java