private FullHttpResponse getErrorResponse(Throwable cause) {
    HttpResponseStatus status;
    RestServiceErrorCode restServiceErrorCode = null;
    String errReason = null;
    Map<String, String> errHeaders = null;
    if (cause instanceof RestServiceException) {
      RestServiceException restServiceException = (RestServiceException) cause;
      restServiceErrorCode = restServiceException.getErrorCode();
      errorResponseStatus = ResponseStatus.getResponseStatus(restServiceErrorCode);
      status = getHttpResponseStatus(errorResponseStatus);
      if (shouldSendFailureReason(status, restServiceException)) {
        errReason = new String(
            Utils.getRootCause(cause).getMessage().replaceAll("[\n\t\r]", " ").getBytes(StandardCharsets.US_ASCII),
            StandardCharsets.US_ASCII);
      }
      if (restServiceException.shouldIncludeExceptionMetadataInResponse()) {
        errHeaders = restServiceException.getExceptionHeadersMap();
      }
    } else if (Utils.isPossibleClientTermination(cause)) {
      // Client closed the connection, it's likely that error response won't be able to reach client.
      // If that's the case, then set the status to client error. This would then be recorded as client error
      // in ContainerMetrics
      nettyMetrics.clientEarlyTerminationCount.inc();
      status = HttpResponseStatus.BAD_REQUEST;
      errorResponseStatus = ResponseStatus.BadRequest;
    } else {
      nettyMetrics.internalServerErrorCount.inc();
      status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
      errorResponseStatus = ResponseStatus.InternalServerError;
    }
    logger.trace("Constructed error response for the client - [{} - {}]", status, errReason);
    FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
    response.headers().set(HttpHeaderNames.DATE, new GregorianCalendar().getTime());
    HttpUtil.setContentLength(response, 0);
    if (errReason != null) {
      response.headers().set(FAILURE_REASON_HEADER, errReason);
    }
    if (errHeaders != null) {
      errHeaders.forEach((errHeaderKey, errHeaderVal) -> response.headers().set(errHeaderKey, errHeaderVal));
    }
    if (restServiceErrorCode != null && HttpStatusClass.CLIENT_ERROR.contains(status.code())) {
      response.headers().set(ERROR_CODE_HEADER, restServiceErrorCode.name());
    }
    response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
    // if there is an ALLOW header in the response so far constructed, copy it
    if (responseMetadata.headers().contains(HttpHeaderNames.ALLOW)) {
      response.headers().set(HttpHeaderNames.ALLOW, responseMetadata.headers().get(HttpHeaderNames.ALLOW));
    } else if (errorResponseStatus == ResponseStatus.MethodNotAllowed) {
      logger.warn("Response is {} but there is no value for {}", ResponseStatus.MethodNotAllowed,
          HttpHeaderNames.ALLOW);
    }
    copyTrackingHeaders(responseMetadata, response);
    HttpUtil.setKeepAlive(response, shouldKeepAlive(status));
    return response;
  }
Provided a cause, returns an error response with the right status and error message.
@param cause the cause of the error.
@return a {@link FullHttpResponse} with the error message that can be sent to the client.
ambry-rest/src/main/java/com/github/ambry/rest/NettyResponseChannel.java