/*
 *	This function is only called for redirected requests.  It illustrates
 *	several different ways of updating INT64 stats.  Some may consider
 *	the particular use of TSDecrementStat() shown below somewhat contrived.
 */
void
update_redirected_method_stats(TSMBuffer bufp, TSMLoc hdr_loc)
{
  const char *txn_method;
  int length;
  int64_t tempint;

  txn_method = TSHttpHdrMethodGet(bufp, hdr_loc, &length);

  if (NULL != txn_method) {
    if (0 == strncmp(txn_method, TS_HTTP_METHOD_CONNECT, length)) {
      TSStatIntIncrement(redirect_count_connect, 1);
    } else if (0 == strncmp(txn_method, TS_HTTP_METHOD_DELETE, length)) {
      TSStatIntIncrement(redirect_count_delete, 1);
    } else if (0 == strncmp(txn_method, TS_HTTP_METHOD_GET, length)) {
      TSStatIntIncrement(redirect_count_get, 1);

    } else if (0 == strncmp(txn_method, TS_HTTP_METHOD_HEAD, length)) {
      TSStatIntIncrement(redirect_count_head, 1);

    } else if (0 == strncmp(txn_method, TS_HTTP_METHOD_OPTIONS, length)) {
      // This is a bad idea in a real plugin because it causes a race condition
      // with other transactions, but is here for illustrative purposes.
      tempint = TSStatIntGet(redirect_count_options);
      ++tempint;
      TSStatIntSet(redirect_count_options, tempint);
    } else if (0 == strncmp(txn_method, TS_HTTP_METHOD_POST, length)) {
      // Illustrative only.
      TSStatIntDecrement(redirect_count_post, 1);
      TSStatIntIncrement(redirect_count_post, 2);
    }

    else if (0 == strncmp(txn_method, TS_HTTP_METHOD_PURGE, length)) {
      TSStatIntIncrement(redirect_count_purge, 1);
    } else if (0 == strncmp(txn_method, TS_HTTP_METHOD_PUT, length)) {
      TSStatIntIncrement(redirect_count_put, 1);
    } else if (0 == strncmp(txn_method, TS_HTTP_METHOD_TRACE, length)) {
      TSStatIntIncrement(redirect_count_trace, 1);
    } else {
      TSStatIntIncrement(redirect_count_unknown, 1);
    }
  }
}
static void
stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex create_mutex)
{
int stat_id = -1;
ENTRY search, *result = NULL;
static __thread struct hsearch_data stat_cache;
static __thread bool hash_init = false;

if (unlikely(!hash_init)) {
    hcreate_r(TS_MAX_API_STATS << 1, &stat_cache);
    hash_init = true;
    TSDebug(DEBUG_TAG, "stat cache hash init");
}

search.key  = name;
search.data = 0;
hsearch_r(search, FIND, &result, &stat_cache);

if (unlikely(result == NULL)) {
    // This is an unlikely path because we most likely have the stat cached
    // so this mutex won't be much overhead and it fixes a race condition
    // in the RecCore. Hopefully this can be removed in the future.
    TSMutexLock(create_mutex);
    if (TS_ERROR == TSStatFindName((const char *)name, &stat_id)) {
        stat_id = TSStatCreate((const char *)name, TS_RECORDDATATYPE_INT, persist_type, TS_STAT_SYNC_SUM);
        if (stat_id == TS_ERROR) {
            TSDebug(DEBUG_TAG, "Error creating stat_name: %s", name);
        } else {
            TSDebug(DEBUG_TAG, "Created stat_name: %s stat_id: %d", name, stat_id);
        }
    }
    TSMutexUnlock(create_mutex);

    if (stat_id >= 0) {
        search.key  = TSstrdup(name);
        search.data = (void *)((intptr_t)stat_id);
        hsearch_r(search, ENTER, &result, &stat_cache);
        TSDebug(DEBUG_TAG, "Cached stat_name: %s stat_id: %d", name, stat_id);
    }
} else {
    stat_id = (int)((intptr_t)result->data);
}

if (likely(stat_id >= 0)) {
    TSStatIntIncrement(stat_id, amount);
} else {
    TSDebug(DEBUG_TAG, "stat error! stat_name: %s stat_id: %d", name, stat_id);
}
}
static void
handle_client_lookup(TSHttpTxn txnp, TSCont contp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc, url_loc;
  int host_length;

  in_addr_t clientip = 0;

  const char *host;

  if (TSIsDebugTagSet("redirect")) {
    struct sockaddr const *addr = TSHttpTxnClientAddrGet(txnp);

    if (addr) {
      socklen_t addr_size = 0;

      if (addr->sa_family == AF_INET) {
        addr_size = sizeof(struct sockaddr_in);
      } else if (addr->sa_family == AF_INET6) {
        addr_size = sizeof(struct sockaddr_in6);
      }
      if (addr_size > 0) {
        char clientstring[INET6_ADDRSTRLEN];

        if (NULL != inet_ntop(addr->sa_family, addr, clientstring, addr_size)) {
          TSDebug(PLUGIN_NAME, "clientip is %s and block_ip is %s", clientstring, block_ip);
        }
      }
    }
  }

  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("[%s] Couldn't retrieve client request header", PLUGIN_NAME);
    goto done;
  }

  if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) {
    TSError("[%s] Couldn't retrieve request url", PLUGIN_NAME);
    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    goto done;
  }

  host = TSUrlHostGet(bufp, url_loc, &host_length);
  if (!host) {
    TSError("[%s] Couldn't retrieve request hostname", PLUGIN_NAME);
    TSHandleMLocRelease(bufp, hdr_loc, url_loc);
    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    goto done;
  }

  /*
   *   Check to see if the client is already headed to the redirect site.
   */
  if (strncmp(host, url_redirect, host_length) == 0) {
    TSHandleMLocRelease(bufp, hdr_loc, url_loc);
    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    goto done;
  }

  /* TODO: This is odd, clientip is never set ... */
  if (ip_deny == clientip) {
    TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);

    update_redirected_method_stats(bufp, hdr_loc);

    TSHandleMLocRelease(bufp, hdr_loc, url_loc);
    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);

    /*
     *   Increment the local redirect stat and do global update:
     */
    TSStatIntIncrement(requests_redirects, 1);

    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return;
  }

done:
  // Increment the local number unchanged stat and do global update:
  TSStatIntIncrement(requests_unchanged, 1);

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
}