HttpTransact::handle_response_keep_alive_headers(State* s, HTTPVersion ver, HTTPHdr* heads)
{
  enum KA_Action_t
  { KA_UNKNOWN, KA_DISABLED, KA_CLOSE, KA_CONNECTION };
  KA_Action_t ka_action = KA_UNKNOWN;

  ink_assert(heads->type_get() == HTTP_TYPE_RESPONSE);

  // Since connection headers are hop-to-hop, strip the
  //  the ones we received from upstream
  heads->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
  heads->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);

  // Handle the upgrade cases
  if (s->is_upgrade_request  &&
      heads->status_get() == HTTP_STATUS_SWITCHING_PROTOCOL &&
      s->source == SOURCE_HTTP_ORIGIN_SERVER) {
    s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
    if (s->is_websocket) {
      DebugTxn("http_trans", "transaction successfully upgraded to websockets.");
      //s->transparent_passthrough = true;
      heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
      heads->value_set(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE, "websocket", 9);
    }

    // We set this state so that we can jump to our blind forwarding state once
    // the response is sent to the client.
    s->did_upgrade_succeed = true;
    return;
  }

  int c_hdr_field_len;
  const char *c_hdr_field_str;
  if (s->client_info.proxy_connect_hdr) {
    c_hdr_field_str = MIME_FIELD_PROXY_CONNECTION;
    c_hdr_field_len = MIME_LEN_PROXY_CONNECTION;
  } else {
    c_hdr_field_str = MIME_FIELD_CONNECTION;
    c_hdr_field_len = MIME_LEN_CONNECTION;
  }

  // Check pre-conditions for keep-alive
  if (HTTP_MAJOR(ver.m_version) == 0) {  /* No K-A for 0.9 apps */
    ka_action = KA_DISABLED;
  }
  else if (heads->status_get() == HTTP_STATUS_NO_CONTENT &&
      ((s->source == SOURCE_HTTP_ORIGIN_SERVER && s->current.server->transfer_encoding != NO_TRANSFER_ENCODING)
       || heads->get_content_length() != 0)) {
    // some systems hang until the connection closes when receiving a 204 regardless of the K-A headers
    // close if there is any body response from the origin
    ka_action = KA_CLOSE;
  } else {
    // Determine if we are going to send either a server-generated or
    // proxy-generated chunked response to the client. If we cannot
    // trust the content-length, we may be able to chunk the response
    // to the client to keep the connection alive.
    // Insert a Transfer-Encoding header in the response if necessary.

    // check that the client is HTTP 1.1 and the conf allows chunking
    if (s->client_info.http_version == HTTPVersion(1, 1) && s->txn_conf->chunking_enabled == 1 &&
        // if we're not sending a body, don't set a chunked header regardless of server response
        !is_response_body_precluded(s->hdr_info.client_response.status_get(), s->method) &&
        // we do not need chunked encoding for internal error messages
        // that are sent to the client if the server response is not valid.
        (((s->source == SOURCE_HTTP_ORIGIN_SERVER || s->source == SOURCE_TRANSFORM) &&
          s->hdr_info.server_response.valid() &&
          // if we receive a 304, we will serve the client from the
          // cache and thus do not need chunked encoding.
          s->hdr_info.server_response.status_get() != HTTP_STATUS_NOT_MODIFIED &&
          (s->current.server->transfer_encoding == HttpTransact::CHUNKED_ENCODING ||
           // we can use chunked encoding if we cannot trust the content
           // length (e.g. no Content-Length and Connection:close in HTTP/1.1 responses)
           s->hdr_info.trust_response_cl == false)) ||
         // handle serve from cache (read-while-write) case
         (s->source == SOURCE_CACHE && s->hdr_info.trust_response_cl == false) ||
         //any transform will potentially alter the content length. try chunking if possible
         (s->source == SOURCE_TRANSFORM && s->hdr_info.trust_response_cl == false ))) {
      s->client_info.receive_chunked_response = true;
      heads->value_append(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING, HTTP_VALUE_CHUNKED, HTTP_LEN_CHUNKED, true);
    } else {
      s->client_info.receive_chunked_response = false;
    }

    //make sure no content length header is send when transfer encoding is chunked
    if ( s->client_info.receive_chunked_response ) {
      s->hdr_info.trust_response_cl = false;

      // And delete the header if it's already been added...
      heads->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
    }

    // Close the connection if client_info is not keep-alive.
    // Otherwise, if we cannot trust the content length, we will close the connection
    // unless we are going to use chunked encoding or the client issued
    // a PUSH request
    if (s->client_info.keep_alive != HTTP_KEEPALIVE) {
       ka_action = KA_DISABLED;
    } else if (s->hdr_info.trust_response_cl == false &&
        !(s->client_info.receive_chunked_response == true ||
          (s->method == HTTP_WKSIDX_PUSH && s->client_info.keep_alive == HTTP_KEEPALIVE))) {
      ka_action = KA_CLOSE;
    } else {
      ka_action = KA_CONNECTION;
    }
  }

  // Insert K-A headers as necessary
  switch (ka_action) {
  case KA_CONNECTION:
    ink_assert(s->client_info.keep_alive != HTTP_NO_KEEPALIVE);
    // This is a hack, we send the keep-alive header for both 1.0
    // and 1.1, to be "compatible" with Akamai.
    // if (ver == HTTPVersion (1, 0)) {
    heads->value_set(c_hdr_field_str, c_hdr_field_len, "keep-alive", 10);
    // NOTE: if the version is 1.1 we don't need to do
    //  anything since keep-alive is assumed
    break;
  case KA_CLOSE:
  case KA_DISABLED:
    if (s->client_info.keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTPVersion(1, 1))) {
      heads->value_set(c_hdr_field_str, c_hdr_field_len, "close", 5);
      s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
    }
    // Note: if we are 1.1, we always need to send the close
    //  header since persistant connnections are the default
    break;
  case KA_UNKNOWN:
  default:
    ink_assert(0);
    break;
  }
}                               /* End HttpTransact::handle_response_keep_alive_headers */