HttpTransact::build_response_from_cache(State* s, HTTPWarningCode warning_code)
{
  HTTPHdr *client_request = &s->hdr_info.client_request;
  HTTPHdr *cached_response = NULL;
  HTTPHdr *to_warn = &s->hdr_info.client_response;
  CacheHTTPInfo *obj;

  if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
    obj = &s->cache_info.object_store;
    ink_assert(obj->valid());
  } else
    obj = s->cache_info.object_read;
  cached_response = obj->response_get();

  // If the client request is conditional, and the cached copy meets
  // the conditions, do not need to send back the full document,
  // just a NOT_MODIFIED response.
  // If the request is not conditional,
  // the function match_response_to_request_conditionals() returns
  // the code of the cached response, which means that we should send
  // back the full document.
  HTTPStatus client_response_code =
    HttpTransactCache::match_response_to_request_conditionals(client_request, cached_response);

  switch (client_response_code) {
  case HTTP_STATUS_NOT_MODIFIED:
    // A IMS or INM GET client request with conditions being met
    // by the cached response.  Send back a NOT MODIFIED response.
    DebugTxn("http_trans", "[build_response_from_cache] Not modified");
    SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_HIT_CONDITIONAL);

    build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
    s->cache_info.action = CACHE_DO_NO_ACTION;
    s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
    break;

  case HTTP_STATUS_PRECONDITION_FAILED:
    // A conditional request with conditions not being met by the cached
    // response.  Send back a PRECONDITION FAILED response.
    DebugTxn("http_trans", "[build_response_from_cache] Precondition Failed");
    SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_CONDITIONAL);

    build_response(s, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
    s->cache_info.action = CACHE_DO_NO_ACTION;
    s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
    break;

  case HTTP_STATUS_RANGE_NOT_SATISFIABLE:
    // Check if cached response supports Range. If it does, append
    // Range transformation plugin
    // A little misnomer. HTTP_STATUS_RANGE_NOT_SATISFIABLE
    // acutally means If-Range match fails here.
    // fall through
  default:
    SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_HIT_SERVED);
    if (s->method == HTTP_WKSIDX_GET || s->api_resp_cacheable == true) {
      // send back the full document to the client.
      DebugTxn("http_trans", "[build_response_from_cache] Match! Serving full document.");
      s->cache_info.action = CACHE_DO_SERVE;

      // Check if cached response supports Range. If it does, append
      // Range transformation plugin
      // only if the cached response is a 200 OK
      if (client_response_code == HTTP_STATUS_OK && client_request->presence(MIME_PRESENCE_RANGE)) {
        s->state_machine->do_range_setup_if_necessary();
        if (s->range_setup == RANGE_NOT_SATISFIABLE) {
          build_error_response(s, HTTP_STATUS_RANGE_NOT_SATISFIABLE, "Requested Range Not Satisfiable", "default", NULL);
          s->cache_info.action = CACHE_DO_NO_ACTION;
          s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
          break;
        } else if ((s->range_setup == RANGE_NOT_HANDLED) || !s->range_in_cache) {
          // we switch to tunneling for Range requests if it is out of order.
          // or if the range can't be satisfied from the cache
          // In that case we fetch the entire source so it's OK to switch
          // this late.
          DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] Out-of-order Range request - tunneling");
          s->cache_info.action = CACHE_DO_NO_ACTION;
          if (s->force_dns) {
            HandleCacheOpenReadMiss(s); // DNS is already completed no need of doing DNS
          } else {
            TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);   // DNS not done before need to be done now as we are connecting to OS
          }
          return;
        }
      }

      if (s->state_machine->do_transform_open()) {
        set_header_for_transform(s, cached_response);
        to_warn = &s->hdr_info.transform_response;
      } else {
        build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version);
      }
      s->next_action = SM_ACTION_SERVE_FROM_CACHE;
    }
    // If the client request is a HEAD, then serve the header from cache.
    else if (s->method == HTTP_WKSIDX_HEAD) {
      DebugTxn("http_trans", "[build_response_from_cache] Match! Serving header only.");

      build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version);
      s->cache_info.action = CACHE_DO_NO_ACTION;
      s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
    } else {
      // We handled the request but it's not GET or HEAD (eg. DELETE),
      // and server is not reacheable: 502
      //
      DebugTxn("http_trans", "[build_response_from_cache] No match! Connection failed.");
      build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Connection Failed", "connect#failed_connect", NULL);
      s->cache_info.action = CACHE_DO_NO_ACTION;
      s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
      warning_code = HTTP_WARNING_CODE_NONE;
    }
    break;
  }

  // After building the client response, add the given warning if provided.
  if (warning_code != HTTP_WARNING_CODE_NONE) {
    delete_warning_value(to_warn, warning_code);
    HttpTransactHeaders::insert_warning_header(s->http_config_param, to_warn, warning_code);
  }
}