HttpTransact::HandleCacheOpenReadHit(State* s)
{
  bool needs_revalidate, needs_authenticate = false;
  bool needs_cache_auth = false;
  bool server_up = true;
  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;

  // do we have to authenticate with the server before
  // sending back the cached response to the client?
  Authentication_t authentication_needed =
    AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, obj->response_get());

  switch (authentication_needed) {
  case AUTHENTICATION_SUCCESS:
    DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication not needed");
    needs_authenticate = false;
    break;
  case AUTHENTICATION_MUST_REVALIDATE:
    SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
    DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed");
    needs_authenticate = true;
    break;
  case AUTHENTICATION_MUST_PROXY:
    DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed");
    HandleCacheOpenReadMiss(s);
    return;
  case AUTHENTICATION_CACHE_AUTH:
    DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed for cache_auth_content");
    needs_authenticate = false;
    needs_cache_auth = true;
    break;
  default:
    ink_assert(!("AuthenticationNeeded has returned unsupported code."));
    break;
  }

  ink_assert(s->cache_lookup_result == CACHE_LOOKUP_HIT_FRESH ||
             s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING ||
             s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE);
  if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE &&
      s->api_update_cached_object != HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
    needs_revalidate = true;
    SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
  } else
    needs_revalidate = false;

  // the response may not be directly returnable to the client. there
  // are several reasons for this: config may force revalidation or
  // client may have forced a refresh by sending a Pragma:no-cache
  // or a Cache-Control:no-cache, or the client may have sent a
  // non-GET/HEAD request for a document that is cached. an example
  // of a situation for this is when a client sends a DELETE, PUT
  // or POST request for a url that is cached. except for DELETE,
  // we may actually want to update the cached copy with the contents
  // of the PUT/POST, but the easiest, safest and most robust solution
  // is to simply delete the cached copy (in order to maintain cache
  // consistency). this is particularly true if the server does not
  // accept or conditionally accepts the PUT/POST requests.
  // anyhow, this is an overloaded function and will return false
  // if the origin server still has to be looked up.
  bool response_returnable = is_cache_response_returnable(s);

  // do we need to revalidate. in other words if the response
  // has to be authorized, is stale or can not be returned, do
  // a revalidate.
  bool send_revalidate = ((needs_authenticate == true) || (needs_revalidate == true) || (response_returnable == false));

  if (needs_cache_auth == true) {
    SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
    s->www_auth_content = send_revalidate ? CACHE_AUTH_STALE : CACHE_AUTH_FRESH;
    send_revalidate = true;
  }

  DebugTxn("http_trans", "CacheOpenRead --- needs_auth          = %d", needs_authenticate);
  DebugTxn("http_trans", "CacheOpenRead --- needs_revalidate    = %d", needs_revalidate);
  DebugTxn("http_trans", "CacheOpenRead --- response_returnable = %d", response_returnable);
  DebugTxn("http_trans", "CacheOpenRead --- needs_cache_auth    = %d", needs_cache_auth);
  DebugTxn("http_trans", "CacheOpenRead --- send_revalidate    = %d", send_revalidate);
  if (send_revalidate) {
    DebugTxn("http_trans", "CacheOpenRead --- HIT-STALE");
    s->dns_info.attempts = 0;

    DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Revalidate document with server");

    if (s->http_config_param->icp_enabled && icp_dynamic_enabled && s->http_config_param->stale_icp_enabled &&
        needs_authenticate == false && needs_cache_auth == false &&
        !s->hdr_info.client_request.is_pragma_no_cache_set() &&
        !s->hdr_info.client_request.is_cache_control_set(HTTP_VALUE_NO_CACHE)) {
      DebugTxn("http_trans", "[HandleCacheOpenReadHit] ICP is configured" " and no no-cache in request; checking ICP for a STALE hit");

      s->stale_icp_lookup = true;

      // we haven't done the ICP lookup yet. The following is to
      // fake an icp_info to cater for build_request's needs
      s->icp_info.http_version.set(1, 0);
      if (!s->txn_conf->keep_alive_enabled_out) {
        s->icp_info.keep_alive = HTTP_NO_KEEPALIVE;
      } else {
        s->icp_info.keep_alive = HTTP_KEEPALIVE;
      }

      update_current_info(&s->current, &s->icp_info, HttpTransact::ICP_SUGGESTED_HOST, 1);
    }

    if (s->stale_icp_lookup == false) {
      find_server_and_update_current_info(s);

      // We do not want to try to revalidate documents if we think
      //  the server is down due to the something report problem
      //
      // Note: we only want to skip origin servers because 1)
      //  parent proxies have their own negative caching
      //  scheme & 2) If we skip down parents, every page
      //  we serve is potentially stale
      //
      if (s->current.request_to == ORIGIN_SERVER &&
          is_server_negative_cached(s) && response_returnable == true &&
          is_stale_cache_response_returnable(s) == true) {
        server_up = false;
        update_current_info(&s->current, NULL, UNDEFINED_LOOKUP, 0);
        DebugTxn("http_trans", "CacheOpenReadHit - server_down, returning stale document");
      }
    }

    if (server_up || s->stale_icp_lookup) {
      if (!s->stale_icp_lookup && !ats_is_ip(&s->current.server->addr)) {
//        ink_release_assert(s->current.request_to == PARENT_PROXY ||
//                    s->http_config_param->no_dns_forward_to_parent != 0);

        // Set ourselves up to handle pending revalidate issues
        //  after the PP DNS lookup
        ink_assert(s->pending_work == NULL);
        s->pending_work = issue_revalidate;

        // We must be going a PARENT PROXY since so did
        //  origin server DNS lookup right after state Start
        //
        // If we end up here in the release case just fall
        //  through.  The request will fail because of the
        //  missing ip but we won't take down the system
        //
        if (s->current.request_to == PARENT_PROXY) {
          TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
        } else if (s->current.request_to == ORIGIN_SERVER) {
          TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);
        } else {
          handle_parent_died(s);
          return;
        }
      }

      build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);

      issue_revalidate(s);

      // this can not be anything but a simple origin server connection.
      // in other words, we would not have looked up the cache for a
      // connect request, so the next action can not be origin_server_raw_open.
      s->next_action = how_to_open_connection(s);
      if (s->stale_icp_lookup && s->next_action == SM_ACTION_ORIGIN_SERVER_OPEN) {
        s->next_action = SM_ACTION_ICP_QUERY;
      }

      ink_release_assert(s->next_action != SM_ACTION_ORIGIN_SERVER_RAW_OPEN);
      return;
    } else {                    // server is down but stale response is returnable
      SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
    }
  }
  // cache hit, document is fresh, does not authorization,
  // is valid, etc. etc. send it back to the client.
  //
  // the important thing to keep in mind is that if we are
  // here then we found a match in the cache and the document
  // is fresh and we have enough authorization for it to send
  // it back to the client without revalidating first with the
  // origin server. we are, therefore, allowed to behave as the
  // origin server. we can, therefore, make the claim that the
  // document has not been modified since or has not been unmodified
  // since the time requested by the client. this may not be
  // the case in reality, but since the document is fresh in
  // the cache, we can make the claim that this is the truth.
  //
  // so, any decision we make at this point can be made with authority.
  // realistically, if we can not make this claim, then there
  // is no reason to cache anything.
  //
  ink_assert((send_revalidate == true && server_up == false) || (send_revalidate == false && server_up == true));

  DebugTxn("http_trans", "CacheOpenRead --- HIT-FRESH");
  DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Serve from cache");

  if (s->cache_info.is_ram_cache_hit) {
    SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_RAM_CACHE_FRESH);
  } else {
    SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_FRESH);
  }

  if (s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING) {
    build_response_from_cache(s, HTTP_WARNING_CODE_HERUISTIC_EXPIRATION);
  } else if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE) {
    ink_assert(server_up == false);
    build_response_from_cache(s, HTTP_WARNING_CODE_REVALIDATION_FAILED);
  } else {
    build_response_from_cache(s, HTTP_WARNING_CODE_NONE);
  }

  if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
    s->saved_update_next_action = s->next_action;
    s->saved_update_cache_action = s->cache_info.action;
    s->next_action = SM_ACTION_CACHE_PREPARE_UPDATE;
  }
}