You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2017/01/13 18:42:25 UTC
[05/10] incubator-trafficcontrol git commit: Removed ATS patches.
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/0d874bf1/traffic_server/patches/trafficserver-5.3.2-089d585.diff
----------------------------------------------------------------------
diff --git a/traffic_server/patches/trafficserver-5.3.2-089d585.diff b/traffic_server/patches/trafficserver-5.3.2-089d585.diff
deleted file mode 100644
index ede76cc..0000000
--- a/traffic_server/patches/trafficserver-5.3.2-089d585.diff
+++ /dev/null
@@ -1,5157 +0,0 @@
-diff --git a/README b/README
-index 78cce71..769f85a 100644
---- a/README
-+++ b/README
-@@ -1,5 +1,6 @@
- Apache Traffic Server
-
-+
- Traffic Server is a high-performance building block for cloud services.
- It's more than just a caching proxy server; it also has support for
- plugins to build large scale web applications.
-diff --git a/configure.ac b/configure.ac
-index fe2af20..45c47fe 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -1948,6 +1948,7 @@ AS_IF([test "x$enable_experimental_plugins" = xyes], [
- plugins/experimental/background_fetch/Makefile
- plugins/experimental/balancer/Makefile
- plugins/experimental/buffer_upload/Makefile
-+ plugins/experimental/cache_range_requests/Makefile
- plugins/experimental/channel_stats/Makefile
- plugins/experimental/collapsed_connection/Makefile
- plugins/experimental/custom_redirect/Makefile
-diff --git a/doc/reference/api/TSHttpOverridableConfig.en.rst b/doc/reference/api/TSHttpOverridableConfig.en.rst
-index 837df9f..e53d371 100644
---- a/doc/reference/api/TSHttpOverridableConfig.en.rst
-+++ b/doc/reference/api/TSHttpOverridableConfig.en.rst
-@@ -57,6 +57,7 @@ Configurations
- The following configurations (from ``records.config``) are overridable: ::
-
- proxy.config.url_remap.pristine_host_hdr
-+ proxy.config.url_remap.remap_required
- proxy.config.http.chunking_enabled
- proxy.config.http.negative_caching_enabled
- proxy.config.http.negative_caching_lifetime
-@@ -100,6 +101,7 @@ The following configurations (from ``records.config``) are overridable: ::
- proxy.config.http.keep_alive_no_activity_timeout_out
- proxy.config.http.transaction_no_activity_timeout_in
- proxy.config.http.transaction_no_activity_timeout_out
-+ proxy.config.http.transaction_active_timeout_in
- proxy.config.http.transaction_active_timeout_out
- proxy.config.http.origin_max_connections
- proxy.config.http.connect_attempts_max_retries
-@@ -135,7 +137,12 @@ The following configurations (from ``records.config``) are overridable: ::
- proxy.config.http.accept_encoding_filter_enabled
- proxy.config.http.cache.range.write
- proxy.config.http.global_user_agent_header
--
-+ proxy.config.http.parent_proxy.per_parent_connect_attempts
-+ proxy.config.http.parent_proxy.total_connect_attempts
-+ proxy.config.http.parent_origin.simple_retry_enabled
-+ proxy.config.http.parent_origin.simple_retry_response_codes
-+ proxy.config.http.parent_origin.dead_server_retry_enabled
-+ proxy.config.http.parent_origin.dead_server_retry_response_codes
-
- Examples
- ========
-diff --git a/iocore/cache/Cache.cc b/iocore/cache/Cache.cc
-index 2879e82..fae560d 100644
---- a/iocore/cache/Cache.cc
-+++ b/iocore/cache/Cache.cc
-@@ -38,6 +38,8 @@
- #include "P_CacheBC.h"
- #endif
-
-+#include "hugepages.h"
-+
- // Compilation Options
- #define USELESS_REENABLES // allow them for now
- // #define VERIFY_JTEST_DATA
-@@ -1295,7 +1297,13 @@ Vol::init(char *s, off_t blocks, off_t dir_skip, bool clear)
-
- Debug("cache_init", "allocating %zu directory bytes for a %lld byte volume (%lf%%)", vol_dirlen(this), (long long)this->len,
- (double)vol_dirlen(this) / (double)this->len * 100.0);
-- raw_dir = (char *)ats_memalign(ats_pagesize(), vol_dirlen(this));
-+
-+ raw_dir = NULL;
-+ if (ats_hugepage_enabled())
-+ raw_dir = (char *)ats_alloc_hugepage(vol_dirlen(this));
-+ if (raw_dir == NULL)
-+ raw_dir = (char *)ats_memalign(ats_pagesize(), vol_dirlen(this));
-+
- dir = (Dir *)(raw_dir + vol_headerlen(this));
- header = (VolHeaderFooter *)raw_dir;
- footer = (VolHeaderFooter *)(raw_dir + vol_dirlen(this) - ROUND_TO_STORE_BLOCK(sizeof(VolHeaderFooter)));
-@@ -2179,7 +2187,7 @@ CacheProcessor::mark_storage_offline(CacheDisk *d ///< Target disk
- Warning("All storage devices offline, cache disabled");
- CacheProcessor::cache_ready = 0;
- } else { // check cache types specifically
-- if (theCache && !theCache->hosttable->gen_host_rec.vol_hash_table) {
-+ if (theCache && !theCache->getHosttable(__func__)->gen_host_rec.vol_hash_table) {
- unsigned int caches_ready = 0;
- caches_ready = caches_ready | (1 << CACHE_FRAG_TYPE_HTTP);
- caches_ready = caches_ready | (1 << CACHE_FRAG_TYPE_NONE);
-@@ -2187,7 +2195,7 @@ CacheProcessor::mark_storage_offline(CacheDisk *d ///< Target disk
- CacheProcessor::cache_ready &= caches_ready;
- Warning("all volumes for http cache are corrupt, http cache disabled");
- }
-- if (theStreamCache && !theStreamCache->hosttable->gen_host_rec.vol_hash_table) {
-+ if (theStreamCache && !theStreamCache->getHosttable(__func__)->gen_host_rec.vol_hash_table) {
- unsigned int caches_ready = 0;
- caches_ready = caches_ready | (1 << CACHE_FRAG_TYPE_RTSP);
- caches_ready = ~caches_ready;
-@@ -3366,9 +3374,10 @@ create_volume(int volume_number, off_t size_in_blocks, int scheme, CacheVol *cp)
- void
- rebuild_host_table(Cache *cache)
- {
-- build_vol_hash_table(&cache->hosttable->gen_host_rec);
-- if (cache->hosttable->m_numEntries != 0) {
-- CacheHostMatcher *hm = cache->hosttable->getHostMatcher();
-+ CacheHostTable *hosttable = cache->getHosttable(__func__);
-+ build_vol_hash_table(&hosttable->gen_host_rec);
-+ if (hosttable->m_numEntries != 0) {
-+ CacheHostMatcher *hm = hosttable->getHostMatcher();
- CacheHostRecord *h_rec = hm->getDataArray();
- int h_rec_len = hm->getNumElements();
- int i;
-@@ -3383,8 +3392,9 @@ Vol *
- Cache::key_to_vol(CacheKey *key, char const *hostname, int host_len)
- {
- uint32_t h = (key->slice32(2) >> DIR_TAG_WIDTH) % VOL_HASH_TABLE_SIZE;
-+ CacheHostTable *hosttable = getHosttable(__func__);
- unsigned short *hash_table = hosttable->gen_host_rec.vol_hash_table;
-- CacheHostRecord *host_rec = &hosttable->gen_host_rec;
-+ CacheHostRecord *host_rec = &(hosttable->gen_host_rec);
-
- if (hosttable->m_numEntries > 0 && host_len) {
- CacheHostResult res;
-@@ -3392,21 +3402,13 @@ Cache::key_to_vol(CacheKey *key, char const *hostname, int host_len)
- if (res.record) {
- unsigned short *host_hash_table = res.record->vol_hash_table;
- if (host_hash_table) {
-- if (is_debug_tag_set("cache_hosting")) {
-- char format_str[50];
-- snprintf(format_str, sizeof(format_str), "Volume: %%xd for host: %%.%ds", host_len);
-- Debug("cache_hosting", format_str, res.record, hostname);
-- }
-+ Debug("cache_hosting", "Volume: %p for host: %.*s", res.record, host_len, hostname);
- return res.record->vols[host_hash_table[h]];
- }
- }
- }
- if (hash_table) {
-- if (is_debug_tag_set("cache_hosting")) {
-- char format_str[50];
-- snprintf(format_str, sizeof(format_str), "Generic volume: %%xd for host: %%.%ds", host_len);
-- Debug("cache_hosting", format_str, host_rec, hostname);
-- }
-+ Debug("cache_hosting", "Generic volume: %p for host: %.*s", host_rec, host_len, hostname);
- return host_rec->vols[hash_table[h]];
- } else
- return host_rec->vols[0];
-diff --git a/iocore/cache/CacheDir.cc b/iocore/cache/CacheDir.cc
-index 779c16b..e54fbfb 100644
---- a/iocore/cache/CacheDir.cc
-+++ b/iocore/cache/CacheDir.cc
-@@ -24,6 +24,8 @@
-
- #include "P_Cache.h"
-
-+#include "hugepages.h"
-+
- // #define LOOP_CHECK_MODE 1
- #ifdef LOOP_CHECK_MODE
- #define DIR_LOOP_THRESHOLD 1000
-@@ -1011,6 +1013,7 @@ sync_cache_dir_on_shutdown(void)
- Debug("cache_dir_sync", "sync started");
- char *buf = NULL;
- size_t buflen = 0;
-+ bool buf_huge = false;
-
- EThread *t = (EThread *)0xdeadbeef;
- for (int i = 0; i < gnvol; i++) {
-@@ -1077,10 +1080,22 @@ sync_cache_dir_on_shutdown(void)
- #endif
-
- if (buflen < dirlen) {
-- if (buf)
-- ats_memalign_free(buf);
-- buf = (char *)ats_memalign(ats_pagesize(), dirlen);
-+ if (buf) {
-+ if (buf_huge)
-+ ats_free_hugepage(buf, buflen);
-+ else
-+ ats_memalign_free(buf);
-+ buf = NULL;
-+ }
- buflen = dirlen;
-+ if (ats_hugepage_enabled()) {
-+ buf = (char *)ats_alloc_hugepage(buflen);
-+ buf_huge = true;
-+ }
-+ if (buf == NULL) {
-+ buf = (char *)ats_memalign(ats_pagesize(), buflen);
-+ buf_huge = false;
-+ }
- }
-
- if (!d->dir_sync_in_progress) {
-@@ -1104,8 +1119,13 @@ sync_cache_dir_on_shutdown(void)
- Debug("cache_dir_sync", "done syncing dir for vol %s", d->hash_text.get());
- }
- Debug("cache_dir_sync", "sync done");
-- if (buf)
-- ats_memalign_free(buf);
-+ if (buf) {
-+ if (buf_huge)
-+ ats_free_hugepage(buf, buflen);
-+ else
-+ ats_memalign_free(buf);
-+ buf = NULL;
-+ }
- }
-
-
-@@ -1120,11 +1140,6 @@ CacheSync::mainEvent(int event, Event *e)
- Lrestart:
- if (vol_idx >= gnvol) {
- vol_idx = 0;
-- if (buf) {
-- ats_memalign_free(buf);
-- buf = 0;
-- buflen = 0;
-- }
- Debug("cache_dir_sync", "sync done");
- if (event == EVENT_INTERVAL)
- trigger = e->ethread->schedule_in(this, HRTIME_SECONDS(cache_config_dir_sync_frequency));
-@@ -1196,10 +1211,22 @@ Lrestart:
- Debug("cache_dir_sync", "pos: %" PRIu64 " Dir %s dirty...syncing to disk", vol->header->write_pos, vol->hash_text.get());
- vol->header->dirty = 0;
- if (buflen < dirlen) {
-- if (buf)
-- ats_memalign_free(buf);
-- buf = (char *)ats_memalign(ats_pagesize(), dirlen);
-+ if (buf) {
-+ if (buf_huge)
-+ ats_free_hugepage(buf, buflen);
-+ else
-+ ats_memalign_free(buf);
-+ buf = NULL;
-+ }
- buflen = dirlen;
-+ if (ats_hugepage_enabled()) {
-+ buf = (char *)ats_alloc_hugepage(buflen);
-+ buf_huge = true;
-+ }
-+ if (buf == NULL) {
-+ buf = (char *)ats_memalign(ats_pagesize(), buflen);
-+ buf_huge = false;
-+ }
- }
- vol->header->sync_serial++;
- vol->footer->sync_serial = vol->header->sync_serial;
-diff --git a/iocore/cache/CacheVol.cc b/iocore/cache/CacheVol.cc
-index 47f5775..fb82742 100644
---- a/iocore/cache/CacheVol.cc
-+++ b/iocore/cache/CacheVol.cc
-@@ -57,10 +57,11 @@ CacheVC::scanVol(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
- Debug("cache_scan_truss", "inside %p:scanVol", this);
- if (_action.cancelled)
- return free_CacheVC(this);
-- CacheHostRecord *rec = &theCache->hosttable->gen_host_rec;
-+ CacheHostTable *hosttable = theCache->getHosttable(__func__);
-+ CacheHostRecord *rec = &hosttable->gen_host_rec;
- if (host_len) {
- CacheHostResult res;
-- theCache->hosttable->Match(hostname, host_len, &res);
-+ hosttable->Match(hostname, host_len, &res);
- if (res.record)
- rec = res.record;
- }
-diff --git a/iocore/cache/P_CacheDir.h b/iocore/cache/P_CacheDir.h
-index 0a31c32..f5c83d5 100644
---- a/iocore/cache/P_CacheDir.h
-+++ b/iocore/cache/P_CacheDir.h
-@@ -293,6 +293,7 @@ struct CacheSync : public Continuation {
- int vol_idx;
- char *buf;
- size_t buflen;
-+ bool buf_huge;
- off_t writepos;
- AIOCallbackInternal io;
- Event *trigger;
-@@ -300,7 +301,8 @@ struct CacheSync : public Continuation {
- int mainEvent(int event, Event *e);
- void aio_write(int fd, char *b, int n, off_t o);
-
-- CacheSync() : Continuation(new_ProxyMutex()), vol_idx(0), buf(0), buflen(0), writepos(0), trigger(0), start_time(0)
-+ CacheSync()
-+ : Continuation(new_ProxyMutex()), vol_idx(0), buf(0), buflen(0), buf_huge(false), writepos(0), trigger(0), start_time(0)
- {
- SET_HANDLER(&CacheSync::mainEvent);
- }
-diff --git a/iocore/cache/P_CacheHosting.h b/iocore/cache/P_CacheHosting.h
-index 21d2b1a..c874f64 100644
---- a/iocore/cache/P_CacheHosting.h
-+++ b/iocore/cache/P_CacheHosting.h
-@@ -163,7 +163,8 @@ struct CacheHostTableConfig : public Continuation {
- (void)e;
- (void)event;
- CacheHostTable *t = new CacheHostTable((*ppt)->cache, (*ppt)->type);
-- CacheHostTable *old = (CacheHostTable *)ink_atomic_swap(&t, *ppt);
-+ CacheHostTable *old = (CacheHostTable *)ink_atomic_swap(ppt, t);
-+ Debug("cache_hosting", "swapped: old=%p, new=%p", old, t);
- new_Deleter(old, CACHE_MEM_FREE_TIMEOUT);
- return EVENT_DONE;
- }
-diff --git a/iocore/cache/P_CacheInternal.h b/iocore/cache/P_CacheInternal.h
-index d06c8d3..d27a752 100644
---- a/iocore/cache/P_CacheInternal.h
-+++ b/iocore/cache/P_CacheInternal.h
-@@ -1058,6 +1058,12 @@ struct Cache {
- hosttable(NULL), total_initialized_vol(0), scheme(CACHE_NONE_TYPE)
- {
- }
-+ CacheHostTable *
-+ getHosttable(const char *callfunc)
-+ {
-+ Debug("cache_hosting", "getHosttable() from: %s", callfunc);
-+ return hosttable;
-+ }
- };
-
- extern Cache *theCache;
-diff --git a/lib/records/RecCore.cc b/lib/records/RecCore.cc
-index 5babfad..9c75006 100644
---- a/lib/records/RecCore.cc
-+++ b/lib/records/RecCore.cc
-@@ -789,7 +789,7 @@ RecRegisterStat(RecT rec_type, const char *name, RecDataT data_type, RecData dat
- // new default value.
- if ((r->stat_meta.persist_type == RECP_NULL || r->stat_meta.persist_type == RECP_PERSISTENT) &&
- persist_type == RECP_NON_PERSISTENT) {
-- RecDebug(DL_Debug, "resetting default value for formerly persisted stat '%s'", r->name);
-+ RecDebug(DL_Debug, "resetting default value for formerly persisted stat id:%d '%s'", r->rsb_id, r->name);
- RecDataSet(r->data_type, &(r->data), &(data_default));
- }
-
-@@ -955,6 +955,8 @@ RecDumpRecords(RecT rec_type, RecDumpEntryCb callback, void *edata)
- {
- int i, num_records;
-
-+ ink_rwlock_rdlock(&g_records_rwlock);
-+
- num_records = g_num_records;
- for (i = 0; i < num_records; i++) {
- RecRecord *r = &(g_records[i]);
-@@ -964,6 +966,8 @@ RecDumpRecords(RecT rec_type, RecDumpEntryCb callback, void *edata)
- rec_mutex_release(&(r->lock));
- }
- }
-+
-+ ink_rwlock_unlock(&g_records_rwlock);
- }
-
- void
-diff --git a/lib/records/RecProcess.cc b/lib/records/RecProcess.cc
-index f0c59bb..9a44ee6 100644
---- a/lib/records/RecProcess.cc
-+++ b/lib/records/RecProcess.cc
-@@ -173,12 +173,18 @@ raw_stat_sync_to_global(RecRawStatBlock *rsb, int id)
- tlp = ((RecRawStat *)((char *)(eventProcessor.all_ethreads[i]) + rsb->ethr_stat_offset)) + id;
- total.sum += tlp->sum;
- total.count += tlp->count;
-+ // Debug("stats","raw_stat_sync_to_global(): ethread: %d, id: %d, total.sum: %" PRId64 ", total.count: %" PRId64 ", tlp->sum: %"
-+ // PRId64 ", tlp->count: %" PRId64,
-+ // i, id, total.sum, total.count, tlp->sum, tlp->count);
- }
-
- for (i = 0; i < eventProcessor.n_dthreads; i++) {
- tlp = ((RecRawStat *)((char *)(eventProcessor.all_dthreads[i]) + rsb->ethr_stat_offset)) + id;
- total.sum += tlp->sum;
- total.count += tlp->count;
-+ // Debug("stats","raw_stat_sync_to_global(): dthread: %d, id: %d, total.sum: %" PRId64 ", total.count: %" PRId64 ", tlp->sum: %"
-+ // PRId64 ", tlp->count: %" PRId64,
-+ // i, id, total.sum, total.count, tlp->sum, tlp->count);
- }
-
- if (total.sum < 0) { // Assure that we stay positive
-@@ -194,9 +200,9 @@ raw_stat_sync_to_global(RecRawStatBlock *rsb, int id)
- delta.count = total.count - rsb->global[id]->last_count;
-
- // This is too verbose now, so leaving it out / leif
-- // Debug("stats", "raw_stat_sync_to_global(): rsb pointer:%p id:%d delta:%" PRId64 " total:%" PRId64 " last:%" PRId64 " global:%"
-- // PRId64 "\n",
-- // rsb, id, delta.sum, total.sum, rsb->global[id]->last_sum, rsb->global[id]->sum);
-+ Debug("stats.verbose", "raw_stat_sync_to_global(): rsb pointer:%p rsb data pointer:%p id:%d delta:%" PRId64 " total:%" PRId64
-+ " last:%" PRId64 " global:%" PRId64 "\n",
-+ rsb, rsb->global[id], id, delta.sum, total.sum, rsb->global[id]->last_sum, rsb->global[id]->sum);
-
- // increment the global values by the delta
- ink_atomic_increment(&(rsb->global[id]->sum), delta.sum);
-@@ -540,7 +546,7 @@ int
- _RecRegisterRawStat(RecRawStatBlock *rsb, RecT rec_type, const char *name, RecDataT data_type, RecPersistT persist_type, int id,
- RecRawStatSyncCb sync_cb)
- {
-- Debug("stats", "RecRawStatSyncCb(%s): rsb pointer:%p id:%d\n", name, rsb, id);
-+ Debug("stats", "_RecRegisterRawStat(%s): rsb pointer:%p id:%d\n", name, rsb, id);
-
- // check to see if we're good to proceed
- ink_assert(id < rsb->max_stats);
-@@ -556,6 +562,11 @@ _RecRegisterRawStat(RecRawStatBlock *rsb, RecT rec_type, const char *name, RecDa
- err = REC_ERR_FAIL;
- goto Ldone;
- }
-+ if (r->rsb_id > 0 && r->rsb_id != id) {
-+ Warning("_RecRegisterRawStat(): Created and reusing a stat with id = %d for new stat named %s", r->rsb_id, name);
-+ } else {
-+ Warning("_RecRegisterStat(): Stat created, name: %s, id: %d", name, id);
-+ }
- r->rsb_id = id; // This is the index within the RSB raw block for this stat, used for lookups by name.
- if (i_am_the_record_owner(r->rec_type)) {
- r->sync_required = r->sync_required | REC_PEER_SYNC_REQUIRED;
-@@ -570,6 +581,7 @@ _RecRegisterRawStat(RecRawStatBlock *rsb, RecT rec_type, const char *name, RecDa
-
- // setup the periodic sync callback
- RecRegisterRawStatSyncCb(name, sync_cb, rsb, id);
-+ Warning("_RecRegisterRawStat(): Stat created, id:%d name:%s, data address:%p", id, name, &r->stat_meta.data_raw);
-
- Ldone:
- return err;
-diff --git a/lib/ts/ConsistentHash.cc b/lib/ts/ConsistentHash.cc
-index c983ccb..912bc74 100644
---- a/lib/ts/ConsistentHash.cc
-+++ b/lib/ts/ConsistentHash.cc
-@@ -67,13 +67,19 @@ ATSConsistentHash::insert(ATSConsistentHashNode *node, float weight, ATSHash64 *
- }
-
- ATSConsistentHashNode *
--ATSConsistentHash::lookup(const char *url, ATSConsistentHashIter *i, bool *w, ATSHash64 *h)
-+ATSConsistentHash::lookup(const char *url, size_t url_len, ATSConsistentHashIter *i, bool *w, ATSHash64 *h)
- {
- uint64_t url_hash;
- ATSConsistentHashIter NodeMapIterUp, *iter;
- ATSHash64 *thash;
- bool *wptr, wrapped = false;
-
-+ if (url_len <= 0 && url) {
-+ url_len = strlen(url);
-+ } else {
-+ url_len = 0;
-+ }
-+
- if (h) {
- thash = h;
- } else if (hash) {
-@@ -95,7 +101,7 @@ ATSConsistentHash::lookup(const char *url, ATSConsistentHashIter *i, bool *w, AT
- }
-
- if (url) {
-- thash->update(url, strlen(url));
-+ thash->update(url, url_len);
- thash->final();
- url_hash = thash->get();
- thash->clear();
-@@ -124,13 +130,19 @@ ATSConsistentHash::lookup(const char *url, ATSConsistentHashIter *i, bool *w, AT
- }
-
- ATSConsistentHashNode *
--ATSConsistentHash::lookup_available(const char *url, ATSConsistentHashIter *i, bool *w, ATSHash64 *h)
-+ATSConsistentHash::lookup_available(const char *url, size_t url_len, ATSConsistentHashIter *i, bool *w, ATSHash64 *h)
- {
- uint64_t url_hash;
- ATSConsistentHashIter NodeMapIterUp, *iter;
- ATSHash64 *thash;
- bool *wptr, wrapped = false;
-
-+ if (url_len <= 0 && url) {
-+ url_len = strlen(url);
-+ } else {
-+ url_len = 0;
-+ }
-+
- if (h) {
- thash = h;
- } else if (hash) {
-@@ -152,7 +164,7 @@ ATSConsistentHash::lookup_available(const char *url, ATSConsistentHashIter *i, b
- }
-
- if (url) {
-- thash->update(url, strlen(url));
-+ thash->update(url, url_len);
- thash->final();
- url_hash = thash->get();
- thash->clear();
-@@ -179,6 +191,34 @@ ATSConsistentHash::lookup_available(const char *url, ATSConsistentHashIter *i, b
- return (*iter)->second;
- }
-
-+ATSConsistentHashNode *
-+ATSConsistentHash::lookup_by_hashval(uint64_t hashval, ATSConsistentHashIter *i, bool *w)
-+{
-+ ATSConsistentHashIter NodeMapIterUp, *iter;
-+ bool *wptr, wrapped = false;
-+
-+ if (w) {
-+ wptr = w;
-+ } else {
-+ wptr = &wrapped;
-+ }
-+
-+ if (i) {
-+ iter = i;
-+ } else {
-+ iter = &NodeMapIterUp;
-+ }
-+
-+ *iter = NodeMap.lower_bound(hashval);
-+
-+ if (*iter == NodeMap.end()) {
-+ *wptr = true;
-+ *iter = NodeMap.begin();
-+ }
-+
-+ return (*iter)->second;
-+}
-+
- ATSConsistentHash::~ATSConsistentHash()
- {
- if (hash) {
-diff --git a/lib/ts/ConsistentHash.h b/lib/ts/ConsistentHash.h
-index 6406a6c6..49822ad 100644
---- a/lib/ts/ConsistentHash.h
-+++ b/lib/ts/ConsistentHash.h
-@@ -49,9 +49,11 @@ typedef std::map<uint64_t, ATSConsistentHashNode *>::iterator ATSConsistentHashI
- struct ATSConsistentHash {
- ATSConsistentHash(int r = 1024, ATSHash64 *h = NULL);
- void insert(ATSConsistentHashNode *node, float weight = 1.0, ATSHash64 *h = NULL);
-- ATSConsistentHashNode *lookup(const char *url = NULL, ATSConsistentHashIter *i = NULL, bool *w = NULL, ATSHash64 *h = NULL);
-- ATSConsistentHashNode *lookup_available(const char *url = NULL, ATSConsistentHashIter *i = NULL, bool *w = NULL,
-- ATSHash64 *h = NULL);
-+ ATSConsistentHashNode *lookup(const char *url = NULL, size_t url_len = 0, ATSConsistentHashIter *i = NULL, bool *w = NULL,
-+ ATSHash64 *h = NULL);
-+ ATSConsistentHashNode *lookup_available(const char *url = NULL, size_t url_len = 0, ATSConsistentHashIter *i = NULL,
-+ bool *w = NULL, ATSHash64 *h = NULL);
-+ ATSConsistentHashNode *lookup_by_hashval(uint64_t hashval, ATSConsistentHashIter *i = NULL, bool *w = NULL);
- ~ATSConsistentHash();
-
- private:
-diff --git a/lib/ts/Makefile.am b/lib/ts/Makefile.am
-index d5ca4ac..3f5eee5 100644
---- a/lib/ts/Makefile.am
-+++ b/lib/ts/Makefile.am
-@@ -107,6 +107,8 @@ libtsutil_la_SOURCES = \
- defalloc.h \
- fastlz.c \
- fastlz.h \
-+ hugepages.cc \
-+ hugepages.h \
- ink_aiocb.h \
- ink_align.h \
- ink_apidefs.h \
-diff --git a/lib/ts/apidefs.h.in b/lib/ts/apidefs.h.in
-index 04f4dd9..31cb807 100644
---- a/lib/ts/apidefs.h.in
-+++ b/lib/ts/apidefs.h.in
-@@ -758,6 +758,14 @@ extern "C"
- TS_CONFIG_HTTP_POST_CHECK_CONTENT_LENGTH_ENABLED,
- TS_CONFIG_HTTP_GLOBAL_USER_AGENT_HEADER,
- TS_CONFIG_HTTP_AUTH_SERVER_SESSION_PRIVATE,
-+ TS_CONFIG_HTTP_TRANSACTION_ACTIVE_TIMEOUT_IN,
-+ TS_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS,
-+ TS_CONFIG_HTTP_PARENT_TOTAL_CONNECT_ATTEMPTS,
-+ TS_CONFIG_HTTP_SIMPLE_RETRY_ENABLED,
-+ TS_CONFIG_HTTP_SIMPLE_RETRY_RESPONSE_CODES,
-+ TS_CONFIG_HTTP_DEAD_SERVER_RETRY_ENABLED,
-+ TS_CONFIG_HTTP_DEAD_SERVER_RETRY_RESPONSE_CODES,
-+ TS_CONFIG_HTTP_URL_REMAP_REQUIRED,
- TS_CONFIG_LAST_ENTRY
- } TSOverridableConfigKey;
-
-diff --git a/lib/ts/hugepages.cc b/lib/ts/hugepages.cc
-new file mode 100644
-index 0000000..d7d94a4
---- /dev/null
-+++ b/lib/ts/hugepages.cc
-@@ -0,0 +1,147 @@
-+/** @file
-+
-+ @section license License
-+
-+ Licensed to the Apache Software Foundation (ASF) under one
-+ or more contributor license agreements. See the NOTICE file
-+ distributed with this work for additional information
-+ regarding copyright ownership. The ASF licenses this file
-+ to you under the Apache License, Version 2.0 (the
-+ "License"); you may not use this file except in compliance
-+ with the License. You may obtain a copy of the License at
-+
-+ http://www.apache.org/licenses/LICENSE-2.0
-+
-+ Unless required by applicable law or agreed to in writing, software
-+ distributed under the License is distributed on an "AS IS" BASIS,
-+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+ See the License for the specific language governing permissions and
-+ limitations under the License.
-+ */
-+
-+#include <cstdio>
-+#include <sys/mman.h>
-+#include "Diags.h"
-+#include "ink_align.h"
-+
-+#define DEBUG_TAG "hugepages"
-+
-+#ifdef MAP_HUGETLB
-+#define MEMINFO_PATH "/proc/meminfo"
-+#define LINE_SIZE 256
-+#define TOKEN "Hugepagesize:"
-+#define TOKEN_SIZE (strlen(TOKEN))
-+
-+static int hugepage_size = -1;
-+static bool hugepage_enabled;
-+#endif
-+
-+size_t
-+ats_hugepage_size(void)
-+{
-+#ifdef MAP_HUGETLB
-+ return hugepage_size;
-+#else
-+ Debug(DEBUG_TAG, "MAP_HUGETLB not defined");
-+ return 0;
-+#endif
-+}
-+
-+bool
-+ats_hugepage_enabled(void)
-+{
-+#ifdef MAP_HUGETLB
-+ return hugepage_enabled;
-+#else
-+ return false;
-+#endif
-+}
-+
-+void
-+ats_hugepage_init(int enabled)
-+{
-+#ifdef MAP_HUGETLB
-+ FILE *fp;
-+ char line[LINE_SIZE];
-+ char *p, *ep;
-+
-+ hugepage_size = 0;
-+
-+ if (!enabled) {
-+ Debug(DEBUG_TAG, "hugepages not enabled");
-+ return;
-+ }
-+
-+ fp = fopen(MEMINFO_PATH, "r");
-+
-+ if (fp == NULL) {
-+ Debug(DEBUG_TAG, "Cannot open file %s", MEMINFO_PATH);
-+ return;
-+ }
-+
-+ while (fgets(line, sizeof(line), fp)) {
-+ if (strncmp(line, TOKEN, TOKEN_SIZE) == 0) {
-+ p = line + TOKEN_SIZE;
-+ while (*p == ' ') {
-+ p++;
-+ }
-+ hugepage_size = strtol(p, &ep, 10);
-+ // What other values can this be?
-+ if (strncmp(ep, " kB", 4)) {
-+ hugepage_size *= 1024;
-+ }
-+ break;
-+ }
-+ }
-+
-+ fclose(fp);
-+
-+ if (hugepage_size) {
-+ hugepage_enabled = true;
-+ }
-+
-+ Debug(DEBUG_TAG, "Hugepage size = %d", hugepage_size);
-+#else
-+ Debug(DEBUG_TAG, "MAP_HUGETLB not defined");
-+#endif
-+}
-+
-+void *
-+ats_alloc_hugepage(size_t s)
-+{
-+#ifdef MAP_HUGETLB
-+ size_t size;
-+ void *mem;
-+
-+ size = INK_ALIGN(s, ats_hugepage_size());
-+
-+ mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
-+
-+ if (mem == MAP_FAILED) {
-+ Debug(DEBUG_TAG, "Could not allocate hugepages size = %zu", size);
-+ return NULL;
-+ }
-+
-+ return mem;
-+#else
-+ (void)s;
-+ Debug(DEBUG_TAG, "MAP_HUGETLB not defined");
-+ return NULL;
-+#endif
-+}
-+
-+bool
-+ats_free_hugepage(void *ptr, size_t s)
-+{
-+#ifdef MAP_HUGETLB
-+ size_t size;
-+
-+ size = INK_ALIGN(s, ats_hugepage_size());
-+ return (munmap(ptr, size) == 0);
-+#else
-+ (void)ptr;
-+ (void)s;
-+ Debug(DEBUG_TAG, "MAP_HUGETLB not defined");
-+ return false;
-+#endif
-+}
-diff --git a/lib/ts/hugepages.h b/lib/ts/hugepages.h
-new file mode 100644
-index 0000000..812542b
---- /dev/null
-+++ b/lib/ts/hugepages.h
-@@ -0,0 +1,32 @@
-+/** @file
-+
-+ @section license License
-+
-+ Licensed to the Apache Software Foundation (ASF) under one
-+ or more contributor license agreements. See the NOTICE file
-+ distributed with this work for additional information
-+ regarding copyright ownership. The ASF licenses this file
-+ to you under the Apache License, Version 2.0 (the
-+ "License"); you may not use this file except in compliance
-+ with the License. You may obtain a copy of the License at
-+
-+ http://www.apache.org/licenses/LICENSE-2.0
-+
-+ Unless required by applicable law or agreed to in writing, software
-+ distributed under the License is distributed on an "AS IS" BASIS,
-+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+ See the License for the specific language governing permissions and
-+ limitations under the License.
-+ */
-+#ifndef _hugepages_h_
-+#define _hugepages_h_
-+
-+#include <cstring>
-+
-+size_t ats_hugepage_size(void);
-+bool ats_hugepage_enabled(void);
-+void ats_hugepage_init(int);
-+void *ats_alloc_hugepage(size_t);
-+bool ats_free_hugepage(void *, size_t);
-+
-+#endif
-diff --git a/lib/ts/ink_queue.cc b/lib/ts/ink_queue.cc
-index e718b3f..0f14b68 100644
---- a/lib/ts/ink_queue.cc
-+++ b/lib/ts/ink_queue.cc
-@@ -50,6 +50,7 @@
- #include "ink_assert.h"
- #include "ink_queue_ext.h"
- #include "ink_align.h"
-+#include "hugepages.h"
-
- inkcoreapi volatile int64_t fastalloc_mem_in_use = 0;
- inkcoreapi volatile int64_t fastalloc_mem_total = 0;
-@@ -100,9 +101,13 @@ ink_freelist_init(InkFreeList **fl, const char *name, uint32_t type_size, uint32
- /* quick test for power of 2 */
- ink_assert(!(alignment & (alignment - 1)));
- f->alignment = alignment;
-- f->chunk_size = chunk_size;
- // Make sure we align *all* the objects in the allocation, not just the first one
- f->type_size = INK_ALIGN(type_size, alignment);
-+ if (ats_hugepage_enabled()) {
-+ f->chunk_size = INK_ALIGN(chunk_size * f->type_size, ats_hugepage_size()) / f->type_size;
-+ } else {
-+ f->chunk_size = chunk_size;
-+ }
- SET_FREELIST_POINTER_VERSION(f->head, FROM_PTR(0), 0);
-
- f->used = 0;
-@@ -171,12 +176,16 @@ ink_freelist_new(InkFreeList *f)
- #ifdef DEBUG
- char *oldsbrk = (char *)sbrk(0), *newsbrk = NULL;
- #endif
-- if (f->alignment)
-- newp = ats_memalign(f->alignment, f->chunk_size * type_size);
-- else
-- newp = ats_malloc(f->chunk_size * type_size);
-+ if (ats_hugepage_enabled())
-+ newp = ats_alloc_hugepage(f->chunk_size * type_size);
-+
-+ if (newp == NULL) {
-+ if (f->alignment)
-+ newp = ats_memalign(f->alignment, f->chunk_size * type_size);
-+ else
-+ newp = ats_malloc(f->chunk_size * type_size);
-+ }
- ats_madvise((caddr_t)newp, f->chunk_size * type_size, f->advice);
--
- fl_memadd(f->chunk_size * type_size);
- #ifdef DEBUG
- newsbrk = (char *)sbrk(0);
-diff --git a/lib/ts/libts.h b/lib/ts/libts.h
-index d244158..20f85b2 100644
---- a/lib/ts/libts.h
-+++ b/lib/ts/libts.h
-@@ -41,6 +41,7 @@
- #define std *** _FIXME_REMOVE_DEPENDENCY_ON_THE_STL_ ***
- */
-
-+#include "hugepages.h"
- #include "ink_config.h"
- #include "ink_platform.h"
- #include "ink_align.h"
-diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
-index f520f4a..0e229c2 100644
---- a/mgmt/RecordsConfig.cc
-+++ b/mgmt/RecordsConfig.cc
-@@ -506,6 +506,17 @@ static const RecordElement RecordsConfig[] =
- ,
-
- // ###################################
-+ // # parent origin configuration #
-+ // ###################################
-+ {RECT_CONFIG, "proxy.config.http.parent_origin.simple_retry_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
-+ ,
-+ {RECT_CONFIG, "proxy.config.http.parent_origin.simple_retry_response_codes", RECD_STRING, "404", RECU_DYNAMIC, RR_NULL, RECC_STR, "^([0-9]+,)$", RECA_NULL}
-+ ,
-+ {RECT_CONFIG, "proxy.config.http.parent_origin.dead_server_retry_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
-+ ,
-+ {RECT_CONFIG, "proxy.config.http.parent_origin.dead_server_retry_response_codes", RECD_STRING, "503", RECU_DYNAMIC, RR_NULL, RECC_STR, "^([0-9]+,)$", RECA_NULL}
-+ ,
-+ // ###################################
- // # NO DNS DOC IN CACHE #
- // ###################################
- {RECT_CONFIG, "proxy.config.http.doc_in_cache_skip_dns", RECD_INT, "1", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
-@@ -647,6 +658,13 @@ static const RecordElement RecordsConfig[] =
- ,
- {RECT_CONFIG, "proxy.config.http.cache.max_open_write_retries", RECD_INT, "1", RECU_DYNAMIC, RR_NULL, RECC_NULL, NULL, RECA_NULL}
- ,
-+ // # open_write_fail_action has 3 options:
-+ // #
-+ // # 0 - default. disable cache and goto origin
-+ // # 1 - return error if cache miss
-+ // # 2 - serve stale until proxy.config.http.cache.max_stale_age, then goto origin, if refresh_miss
-+ {RECT_CONFIG, "proxy.config.http.cache.open_write_fail_action", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_NULL, NULL, RECA_NULL}
-+ ,
- // # when_to_revalidate has 4 options:
- // #
- // # 0 - default. use use cache directives or heuristic
-@@ -2067,6 +2085,8 @@ static const RecordElement RecordsConfig[] =
- ,
- {RECT_CONFIG, "proxy.config.allocator.debug_filter", RECD_INT, "0", RECU_NULL, RR_NULL, RECC_NULL, "[0-3]", RECA_NULL}
- ,
-+ {RECT_CONFIG, "proxy.config.allocator.hugepages", RECD_INT, "0", RECU_RESTART_TS, RR_NULL, RECC_NULL, "[0-1]", RECA_NULL}
-+ ,
-
- //############
- //#
-diff --git a/plugins/cacheurl/cacheurl.cc b/plugins/cacheurl/cacheurl.cc
-index 5f03598..52565fc 100644
---- a/plugins/cacheurl/cacheurl.cc
-+++ b/plugins/cacheurl/cacheurl.cc
-@@ -42,6 +42,7 @@
- #define TOKENCOUNT 10
- #define OVECOUNT 30
- #define PLUGIN_NAME "cacheurl"
-+#define DEFAULT_CONFIG "cacheurl.config"
-
- struct regex_info {
- pcre *re; /* Compiled regular expression */
-@@ -213,10 +214,10 @@ load_config_file(const char *config_file)
- regex_info *info = 0;
-
- if (config_file == NULL) {
-- /* Default config file of plugins/cacheurl.config */
-- path = TSPluginDirGet();
-- path += "/cacheurl.config";
-- } else if (*config_file != '/') {
-+ config_file = DEFAULT_CONFIG;
-+ }
-+
-+ if (*config_file != '/') {
- // Relative paths are relative to the config directory
- path = TSConfigDirGet();
- path += "/";
-diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am
-index 2ef296c..52fef44 100644
---- a/plugins/experimental/Makefile.am
-+++ b/plugins/experimental/Makefile.am
-@@ -19,6 +19,7 @@ SUBDIRS = \
- background_fetch \
- balancer \
- buffer_upload \
-+ cache_range_requests \
- channel_stats \
- collapsed_connection \
- custom_redirect \
-diff --git a/plugins/experimental/background_fetch/background_fetch.cc b/plugins/experimental/background_fetch/background_fetch.cc
-index 878183e..fd0af7a 100644
---- a/plugins/experimental/background_fetch/background_fetch.cc
-+++ b/plugins/experimental/background_fetch/background_fetch.cc
-@@ -84,8 +84,13 @@ read_config(char *config_file, BgFetchRuleMap *ri)
- snprintf(file_path, sizeof(file_path), "%s/%s", TSInstallDirGet(), config_file);
- file = TSfopen(file_path, "r");
- if (file == NULL) {
-- TSError("%s: invalid config file", PLUGIN_NAME);
-- return false;
-+ TSDebug(PLUGIN_NAME, "Failed to open config file %s, trying config path", config_file);
-+ snprintf(file_path, sizeof(file_path), "%s/%s", TSConfigDirGet(), config_file);
-+ file = TSfopen(file_path, "r");
-+ if (file == NULL) {
-+ TSError("%s: invalid config file", PLUGIN_NAME);
-+ return false;
-+ }
- }
- }
-
-diff --git a/plugins/experimental/cache_range_requests/Makefile.am b/plugins/experimental/cache_range_requests/Makefile.am
-new file mode 100644
-index 0000000..5a27cac
---- /dev/null
-+++ b/plugins/experimental/cache_range_requests/Makefile.am
-@@ -0,0 +1,21 @@
-+# Licensed to the Apache Software Foundation (ASF) under one
-+# or more contributor license agreements. See the NOTICE file
-+# distributed with this work for additional information
-+# regarding copyright ownership. The ASF licenses this file
-+# to you under the Apache License, Version 2.0 (the
-+# "License"); you may not use this file except in compliance
-+# with the License. You may obtain a copy of the License at
-+#
-+# http://www.apache.org/licenses/LICENSE-2.0
-+#
-+# Unless required by applicable law or agreed to in writing, software
-+# distributed under the License is distributed on an "AS IS" BASIS,
-+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+# See the License for the specific language governing permissions and
-+# limitations under the License.
-+
-+include $(top_srcdir)/build/plugins.mk
-+
-+pkglib_LTLIBRARIES = cache_range_requests.la
-+cache_range_requests_la_SOURCES = cache_range_requests.cc
-+cache_range_requests_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)
-diff --git a/plugins/experimental/cache_range_requests/README b/plugins/experimental/cache_range_requests/README
-new file mode 100644
-index 0000000..faddeb5
---- /dev/null
-+++ b/plugins/experimental/cache_range_requests/README
-@@ -0,0 +1,36 @@
-+
-+Thousands of range requests for a very large object in the traffic server cache
-+are likely to increase system load averages due to I/O wait as objects are stored
-+on a single stripe or disk drive.
-+
-+This plugin allows you to remap individual range requests so that they are stored
-+as individual objects in the ATS cache when subsequent range requests are likely
-+to use the same range. This spreads range requests over multiple stripes thereby
-+reducing I/O wait and system load averages.
-+
-+This plugin reads the range request header byte range value and then creates a
-+new cache key url using the original request url with the range value appended
-+to it. The range header is removed where appropriate from the requests and the
-+origin server response code is changed from a 206 to a 200 to insure that the
-+object is written to cache using the new cache key url. The response code sent
-+to the client will be changed back to a 206 and all requests to the origin server
-+will contain the range header so that the correct response is received.
-+
-+Installation:
-+
-+ make
-+ sudo make install
-+
-+If you don't have the traffic server binaries in your path, then you will need
-+to specify the path to tsxs manually:
-+
-+ make TSXS=/opt/trafficserver/bin/tsxs
-+ sudo make TSXS=/opt/trafficserver/bin/tsxs install
-+
-+Configuration:
-+
-+ Add @plugin=cache_range_requests.so to your remap.config rules.
-+
-+ Or for a global plugin where all range requests are processed,
-+ Add cache_range_requests.so to the plugin.config
-+
-diff --git a/plugins/experimental/cache_range_requests/cache_range_requests.cc b/plugins/experimental/cache_range_requests/cache_range_requests.cc
-new file mode 100644
-index 0000000..8722279
---- /dev/null
-+++ b/plugins/experimental/cache_range_requests/cache_range_requests.cc
-@@ -0,0 +1,411 @@
-+/*
-+ * Licensed to the Apache Software Foundation (ASF) under one
-+ * or more contributor license agreements. See the NOTICE file
-+ * distributed with this work for additional information
-+ * regarding copyright ownership. The ASF licenses this file
-+ * to you under the Apache License, Version 2.0 (the "License");
-+ * you may not use this file except in compliance with the
-+ * License. You may obtain a copy of the License at
-+ *
-+ * http://www.apache.org/licenses/LICENSE-2.0
-+ *
-+ * Unless required by applicable law or agreed to in writing,
-+ * software distributed under the License is distributed on an
-+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-+ * KIND, either express or implied. See the License for the
-+ * specific language governing permissions and limitations
-+ * under the License.
-+ */
-+
-+/*
-+ * This plugin looks for range requests and then creates a new
-+ * cache key url so that each individual range requests is written
-+ * to the cache as a individual object so that subsequent range
-+ * requests are read accross different disk drives reducing I/O
-+ * wait and load averages when there are large numbers of range
-+ * requests.
-+ */
-+
-+#include <stdio.h>
-+#include <string.h>
-+#include "ts/ts.h"
-+#include "ts/remap.h"
-+
-+#define PLUGIN_NAME "cache_range_requests"
-+#define DEBUG_LOG(fmt, ...) TSDebug(PLUGIN_NAME, "[%s:%d] %s(): " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
-+#define ERROR_LOG(fmt, ...) TSError("[%s:%d] %s(): " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
-+
-+struct txndata {
-+ char *range_value;
-+};
-+
-+static void handle_read_request_header(TSCont, TSEvent, void *);
-+static void range_header_check(TSHttpTxn txnp);
-+static void handle_send_origin_request(TSCont, TSHttpTxn, struct txndata *);
-+static void handle_client_send_response(TSHttpTxn, struct txndata *);
-+static void handle_server_read_response(TSHttpTxn, struct txndata *);
-+static int remove_header(TSMBuffer, TSMLoc, const char *, int);
-+static bool set_header(TSMBuffer, TSMLoc, const char *, int, const char *, int);
-+static void transaction_handler(TSCont, TSEvent, void *);
-+
-+/**
-+ * Entry point when used as a global plugin.
-+ *
-+ */
-+static void
-+handle_read_request_header(TSCont txn_contp, TSEvent event, void *edata)
-+{
-+ TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
-+
-+ range_header_check(txnp);
-+
-+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
-+}
-+
-+/**
-+ * Reads the client request header and if this is a range request:
-+ *
-+ * 1. creates a new cache key url using the range request information.
-+ * 2. Saves the range request information and then removes the range
-+ * header so that the response retrieved from the origin will
-+ * be written to cache.
-+ * 3. Schedules TS_HTTP_SEND_REQUEST_HDR_HOOK, TS_HTTP_SEND_RESPONSE_HDR_HOOK,
-+ * and TS_HTTP_TXN_CLOSE_HOOK for further processing.
-+ */
-+static void
-+range_header_check(TSHttpTxn txnp)
-+{
-+ char cache_key_url[8192] = {0};
-+ char *req_url;
-+ int length, url_length;
-+ struct txndata *txn_state;
-+ TSMBuffer hdr_bufp;
-+ TSMLoc req_hdrs = NULL;
-+ TSMLoc loc = NULL;
-+ TSCont txn_contp;
-+
-+ if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &hdr_bufp, &req_hdrs)) {
-+ loc = TSMimeHdrFieldFind(hdr_bufp, req_hdrs, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE);
-+ if (TS_NULL_MLOC != loc) {
-+ const char *hdr_value = TSMimeHdrFieldValueStringGet(hdr_bufp, req_hdrs, loc, 0, &length);
-+ if (!hdr_value || length <= 0) {
-+ DEBUG_LOG("Not a range request.");
-+ } else {
-+ if (NULL == (txn_contp = TSContCreate((TSEventFunc)transaction_handler, NULL))) {
-+ ERROR_LOG("failed to create the transaction handler continuation.");
-+ } else {
-+ txn_state = (struct txndata *)TSmalloc(sizeof(struct txndata));
-+ txn_state->range_value = TSstrndup(hdr_value, length);
-+ DEBUG_LOG("length: %d, txn_state->range_value: %s", length, txn_state->range_value);
-+ txn_state->range_value[length] = '\0'; // workaround for bug in core
-+
-+ req_url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_length);
-+ snprintf(cache_key_url, 8192, "%s-%s", req_url, txn_state->range_value);
-+ DEBUG_LOG("Rewriting cache URL for %s to %s", req_url, cache_key_url);
-+ if (req_url != NULL)
-+ TSfree(req_url);
-+
-+ // set the cache key.
-+ if (TS_SUCCESS != TSCacheUrlSet(txnp, cache_key_url, strlen(cache_key_url))) {
-+ DEBUG_LOG("failed to change the cache url to %s.", cache_key_url);
-+ }
-+ // remove the range request header.
-+ if (remove_header(hdr_bufp, req_hdrs, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE) > 0) {
-+ DEBUG_LOG("Removed the Range: header from the request.");
-+ }
-+
-+ TSContDataSet(txn_contp, txn_state);
-+ TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, txn_contp);
-+ TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, txn_contp);
-+ TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp);
-+ DEBUG_LOG("Added TS_HTTP_SEND_REQUEST_HDR_HOOK, TS_HTTP_SEND_RESPONSE_HDR_HOOK, and TS_HTTP_TXN_CLOSE_HOOK");
-+ }
-+ }
-+ TSHandleMLocRelease(hdr_bufp, req_hdrs, loc);
-+ } else {
-+ DEBUG_LOG("no range request header.");
-+ }
-+ TSHandleMLocRelease(hdr_bufp, req_hdrs, NULL);
-+ } else {
-+ DEBUG_LOG("failed to retrieve the server request");
-+ }
-+}
-+
-+/**
-+ * Restores the range request header if the request must be
-+ * satisfied from the origin and schedules the TS_READ_RESPONSE_HDR_HOOK.
-+ */
-+static void
-+handle_send_origin_request(TSCont contp, TSHttpTxn txnp, struct txndata *txn_state)
-+{
-+ TSMBuffer hdr_bufp;
-+ TSMLoc req_hdrs = NULL;
-+
-+ if (TS_SUCCESS == TSHttpTxnServerReqGet(txnp, &hdr_bufp, &req_hdrs) && txn_state->range_value != NULL) {
-+ if (set_header(hdr_bufp, req_hdrs, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE, txn_state->range_value,
-+ strlen(txn_state->range_value))) {
-+ DEBUG_LOG("Added range header: %s", txn_state->range_value);
-+ TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
-+ }
-+ }
-+ TSHandleMLocRelease(hdr_bufp, req_hdrs, NULL);
-+}
-+
-+/**
-+ * Changes the response code back to a 206 Partial content before
-+ * replying to the client that requested a range.
-+ */
-+static void
-+handle_client_send_response(TSHttpTxn txnp, struct txndata *txn_state)
-+{
-+ bool partial_content_reason = false;
-+ char *p;
-+ int length;
-+ TSMBuffer response, hdr_bufp;
-+ TSMLoc resp_hdr, req_hdrs = NULL;
-+
-+ TSReturnCode result = TSHttpTxnClientRespGet(txnp, &response, &resp_hdr);
-+ DEBUG_LOG("result: %d", result);
-+ if (TS_SUCCESS == result) {
-+ TSHttpStatus status = TSHttpHdrStatusGet(response, resp_hdr);
-+ // a cached result will have a TS_HTTP_OK with a 'Partial Content' reason
-+ if ((p = (char *)TSHttpHdrReasonGet(response, resp_hdr, &length)) != NULL) {
-+ if ((length == 15) && (0 == strncasecmp(p, "Partial Content", length))) {
-+ partial_content_reason = true;
-+ }
-+ }
-+ DEBUG_LOG("%d %.*s", status, length, p);
-+ if (TS_HTTP_STATUS_OK == status && partial_content_reason) {
-+ DEBUG_LOG("Got TS_HTTP_STATUS_OK.");
-+ TSHttpHdrStatusSet(response, resp_hdr, TS_HTTP_STATUS_PARTIAL_CONTENT);
-+ DEBUG_LOG("Set response header to TS_HTTP_STATUS_PARTIAL_CONTENT.");
-+ }
-+ }
-+ // add the range request header back in so that range requests may be logged.
-+ if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &hdr_bufp, &req_hdrs) && txn_state->range_value != NULL) {
-+ if (set_header(hdr_bufp, req_hdrs, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE, txn_state->range_value,
-+ strlen(txn_state->range_value))) {
-+ DEBUG_LOG("added range header: %s", txn_state->range_value);
-+ } else {
-+ DEBUG_LOG("set_header() failed.");
-+ }
-+ } else {
-+ DEBUG_LOG("failed to get Request Headers");
-+ }
-+ TSHandleMLocRelease(response, resp_hdr, NULL);
-+ TSHandleMLocRelease(hdr_bufp, req_hdrs, NULL);
-+}
-+
-+/**
-+ * After receiving a range request response from the origin, change
-+ * the response code from a 206 Partial content to a 200 OK so that
-+ * the response will be written to cache.
-+ */
-+static void
-+handle_server_read_response(TSHttpTxn txnp, struct txndata *txn_state)
-+{
-+ TSMBuffer response;
-+ TSMLoc resp_hdr;
-+ TSHttpStatus status;
-+
-+ if (TS_SUCCESS == TSHttpTxnServerRespGet(txnp, &response, &resp_hdr)) {
-+ status = TSHttpHdrStatusGet(response, resp_hdr);
-+ if (TS_HTTP_STATUS_PARTIAL_CONTENT == status) {
-+ DEBUG_LOG("Got TS_HTTP_STATUS_PARTIAL_CONTENT.");
-+ TSHttpHdrStatusSet(response, resp_hdr, TS_HTTP_STATUS_OK);
-+ DEBUG_LOG("Set response header to TS_HTTP_STATUS_OK.");
-+ bool cacheable = TSHttpTxnIsCacheable(txnp, NULL, response);
-+ DEBUG_LOG("range is cacheable: %d", cacheable);
-+ } else if (TS_HTTP_STATUS_OK == status) {
-+ DEBUG_LOG("The origin does not support range requests, attempting to disable cache write.");
-+ if (TS_SUCCESS == TSHttpTxnServerRespNoStoreSet(txnp, 1)) {
-+ DEBUG_LOG("Cache write has been disabled for this transaction.");
-+ } else {
-+ DEBUG_LOG("Unable to disable cache write for this transaction.");
-+ }
-+ }
-+ }
-+ TSHandleMLocRelease(response, resp_hdr, NULL);
-+}
-+
-+/**
-+ * Remove a header (fully) from an TSMLoc / TSMBuffer. Return the number
-+ * of fields (header values) we removed.
-+ *
-+ * From background_fetch.cc
-+ */
-+static int
-+remove_header(TSMBuffer bufp, TSMLoc hdr_loc, const char *header, int len)
-+{
-+ TSMLoc field = TSMimeHdrFieldFind(bufp, hdr_loc, header, len);
-+ int cnt = 0;
-+
-+ while (field) {
-+ TSMLoc tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field);
-+
-+ ++cnt;
-+ TSMimeHdrFieldDestroy(bufp, hdr_loc, field);
-+ TSHandleMLocRelease(bufp, hdr_loc, field);
-+ field = tmp;
-+ }
-+
-+ return cnt;
-+}
-+
-+/**
-+ * Set a header to a specific value. This will avoid going to through a
-+ * remove / add sequence in case of an existing header.
-+ * but clean.
-+ *
-+ * From background_fetch.cc
-+ */
-+static bool
-+set_header(TSMBuffer bufp, TSMLoc hdr_loc, const char *header, int len, const char *val, int val_len)
-+{
-+ if (!bufp || !hdr_loc || !header || len <= 0 || !val || val_len <= 0) {
-+ return false;
-+ }
-+
-+ DEBUG_LOG("header: %s, len: %d, val: %s, val_len: %d", header, len, val, val_len);
-+ bool ret = false;
-+ TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, header, len);
-+
-+ if (!field_loc) {
-+ // No existing header, so create one
-+ if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(bufp, hdr_loc, header, len, &field_loc)) {
-+ if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, val, val_len)) {
-+ TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
-+ ret = true;
-+ }
-+ TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-+ }
-+ } else {
-+ TSMLoc tmp = NULL;
-+ bool first = true;
-+
-+ while (field_loc) {
-+ if (first) {
-+ first = false;
-+ if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, val, val_len)) {
-+ ret = true;
-+ }
-+ } else {
-+ TSMimeHdrFieldDestroy(bufp, hdr_loc, field_loc);
-+ }
-+ tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field_loc);
-+ TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-+ field_loc = tmp;
-+ }
-+ }
-+
-+ return ret;
-+}
-+
-+/**
-+ * Remap initialization.
-+ */
-+TSReturnCode
-+TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
-+{
-+ if (!api_info) {
-+ strncpy(errbuf, "[tsremap_init] - Invalid TSRemapInterface argument", errbuf_size - 1);
-+ return TS_ERROR;
-+ }
-+
-+ if (api_info->tsremap_version < TSREMAP_VERSION) {
-+ snprintf(errbuf, errbuf_size - 1, "[TSRemapInit] - Incorrect API version %ld.%ld", api_info->tsremap_version >> 16,
-+ (api_info->tsremap_version & 0xffff));
-+ return TS_ERROR;
-+ }
-+
-+ DEBUG_LOG("cache_range_requests remap is successfully initialized.");
-+ return TS_SUCCESS;
-+}
-+
-+/**
-+ * not used.
-+ */
-+TSReturnCode
-+TSRemapNewInstance(int argc, char *argv[], void **ih, char * /*errbuf */, int /* errbuf_size */)
-+{
-+ return TS_SUCCESS;
-+}
-+
-+/**
-+ * not used.
-+ */
-+void
-+TSRemapDeleteInstance(void *ih)
-+{
-+ DEBUG_LOG("no op");
-+}
-+
-+/**
-+ * Remap entry point.
-+ */
-+TSRemapStatus
-+TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo * /* rri */)
-+{
-+ range_header_check(txnp);
-+ return TSREMAP_NO_REMAP;
-+}
-+
-+/**
-+ * Global plugin initialization.
-+ */
-+void
-+TSPluginInit(int argc, const char *argv[])
-+{
-+ TSPluginRegistrationInfo info;
-+ TSCont txnp_cont;
-+
-+ info.plugin_name = (char *)PLUGIN_NAME;
-+ info.vendor_name = (char *)"Comcast";
-+ info.support_email = (char *)"John_Rushford@cable.comcast.com";
-+
-+ if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
-+ ERROR_LOG("Plugin registration failed.\n");
-+ ERROR_LOG("Unable to initialize plugin (disabled).");
-+ return;
-+ }
-+
-+ if (NULL == (txnp_cont = TSContCreate((TSEventFunc)handle_read_request_header, NULL))) {
-+ ERROR_LOG("failed to create the transaction continuation handler.");
-+ return;
-+ } else {
-+ TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, txnp_cont);
-+ }
-+}
-+
-+/**
-+ * Transaction event handler.
-+ */
-+static void
-+transaction_handler(TSCont contp, TSEvent event, void *edata)
-+{
-+ TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
-+ struct txndata *txn_state = (struct txndata *)TSContDataGet(contp);
-+
-+ switch (event) {
-+ case TS_EVENT_HTTP_READ_RESPONSE_HDR:
-+ handle_server_read_response(txnp, txn_state);
-+ break;
-+ case TS_EVENT_HTTP_SEND_REQUEST_HDR:
-+ handle_send_origin_request(contp, txnp, txn_state);
-+ break;
-+ case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
-+ handle_client_send_response(txnp, txn_state);
-+ break;
-+ case TS_EVENT_HTTP_TXN_CLOSE:
-+ if (txn_state != NULL && txn_state->range_value != NULL)
-+ TSfree(txn_state->range_value);
-+ if (txn_state != NULL)
-+ TSfree(txn_state);
-+ TSContDestroy(contp);
-+ break;
-+ default:
-+ TSAssert(!"Unexpected event");
-+ break;
-+ }
-+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
-+}
-diff --git a/plugins/experimental/regex_revalidate/regex_revalidate.c b/plugins/experimental/regex_revalidate/regex_revalidate.c
-index f25c36f..d261308 100644
---- a/plugins/experimental/regex_revalidate/regex_revalidate.c
-+++ b/plugins/experimental/regex_revalidate/regex_revalidate.c
-@@ -40,8 +40,35 @@
- #include <pcre.h>
- #endif
-
--#define LOG_PREFIX "regex_revalidate"
--#define CONFIG_TMOUT 60000
-+typedef struct invalidate_t {
-+ const char *regex_text;
-+ pcre *regex;
-+ pcre_extra *regex_extra;
-+ time_t epoch;
-+ time_t expiry;
-+ struct invalidate_t *volatile next;
-+} invalidate_t;
-+
-+typedef invalidate_t config_t;
-+
-+typedef struct {
-+ char *config_path;
-+ volatile time_t last_load;
-+ config_t *config;
-+ TSTextLogObject log;
-+} config_holder_t;
-+
-+static int free_handler(TSCont cont, TSEvent event, void *edata);
-+static int config_handler(TSCont cont, TSEvent event, void *edata);
-+static config_t *get_config(TSCont cont);
-+static config_holder_t *new_config_holder();
-+static config_holder_t *init_config_holder(config_holder_t *config_holder, const char *path);
-+static void free_config_holder_t(config_holder_t *config_holder);
-+static void schedule_free_invalidate_t(invalidate_t *iptr);
-+
-+#define PLUGIN_TAG "regex_revalidate"
-+#define DEFAULT_CONFIG_NAME "regex_revalidate.config"
-+#define PRUNE_TMOUT 60000
- #define FREE_TMOUT 300000
- #define OVECTOR_SIZE 30
- #define LOG_ROLL_INTERVAL 86400
-@@ -59,22 +86,6 @@ ts_free(void *s)
- return TSfree(s);
- }
-
--typedef struct invalidate_t {
-- const char *regex_text;
-- pcre *regex;
-- pcre_extra *regex_extra;
-- time_t epoch;
-- time_t expiry;
-- struct invalidate_t *next;
--} invalidate_t;
--
--typedef struct {
-- invalidate_t *volatile invalidate_list;
-- char *config_file;
-- volatile time_t last_load;
-- TSTextLogObject log;
--} plugin_state_t;
--
- static invalidate_t *
- init_invalidate_t(invalidate_t *i)
- {
-@@ -111,65 +122,6 @@ free_invalidate_t_list(invalidate_t *i)
- free_invalidate_t(i);
- }
-
--static plugin_state_t *
--init_plugin_state_t(plugin_state_t *pstate)
--{
-- pstate->invalidate_list = NULL;
-- pstate->config_file = NULL;
-- pstate->last_load = 0;
-- pstate->log = NULL;
-- return pstate;
--}
--
--static void
--free_plugin_state_t(plugin_state_t *pstate)
--{
-- if (pstate->invalidate_list)
-- free_invalidate_t_list(pstate->invalidate_list);
-- if (pstate->config_file)
-- TSfree(pstate->config_file);
-- if (pstate->log)
-- TSTextLogObjectDestroy(pstate->log);
-- TSfree(pstate);
--}
--
--static invalidate_t *
--copy_invalidate_t(invalidate_t *i)
--{
-- invalidate_t *iptr;
-- const char *errptr;
-- int erroffset;
--
-- iptr = (invalidate_t *)TSmalloc(sizeof(invalidate_t));
-- iptr->regex_text = TSstrdup(i->regex_text);
-- iptr->regex = pcre_compile(iptr->regex_text, 0, &errptr, &erroffset, NULL); // There is no pcre_copy :-(
-- iptr->regex_extra = pcre_study(iptr->regex, 0, &errptr); // Assuming no errors since this worked before :-/
-- iptr->epoch = i->epoch;
-- iptr->expiry = i->expiry;
-- iptr->next = NULL;
-- return iptr;
--}
--
--static invalidate_t *
--copy_config(invalidate_t *old_list)
--{
-- invalidate_t *new_list = NULL;
-- invalidate_t *iptr_old, *iptr_new;
--
-- if (old_list) {
-- new_list = copy_invalidate_t(old_list);
-- iptr_old = old_list->next;
-- iptr_new = new_list;
-- while (iptr_old) {
-- iptr_new->next = copy_invalidate_t(iptr_old);
-- iptr_new = iptr_new->next;
-- iptr_old = iptr_old->next;
-- }
-- }
--
-- return new_list;
--}
--
- static bool
- prune_config(invalidate_t **i)
- {
-@@ -184,14 +136,20 @@ prune_config(invalidate_t **i)
- ilast = NULL;
- while (iptr) {
- if (difftime(iptr->expiry, now) < 0) {
-- TSDebug(LOG_PREFIX, "Removing %s expiry: %d now: %d", iptr->regex_text, (int)iptr->expiry, (int)now);
-+ TSDebug(PLUGIN_TAG, "Removing %s expiry: %d now: %d", iptr->regex_text, (int)iptr->expiry, (int)now);
-+ TSError(PLUGIN_TAG " - Removing %s expiry: %d now: %d", iptr->regex_text, (int)iptr->expiry, (int)now);
- if (ilast) {
-+ // jlaue: TODO is this right?
-+ // iptr = __sync_val_compare_and_swap(&(ilast->next), ilast->next, iptr->next);
- ilast->next = iptr->next;
-- free_invalidate_t(iptr);
-+ // free_invalidate_t(iptr);
-+ schedule_free_invalidate_t(iptr);
- iptr = ilast->next;
-+
- } else {
- *i = iptr->next;
-- free_invalidate_t(iptr);
-+ // free_invalidate_t(iptr);
-+ schedule_free_invalidate_t(iptr);
- iptr = *i;
- }
- pruned = true;
-@@ -204,161 +162,44 @@ prune_config(invalidate_t **i)
- return pruned;
- }
-
--static bool
--load_config(plugin_state_t *pstate, invalidate_t **ilist)
--{
-- FILE *fs;
-- struct stat s;
-- size_t path_len;
-- char *path;
-- char line[LINE_MAX];
-- time_t now;
-- pcre *config_re;
-- const char *errptr;
-- int erroffset, ovector[OVECTOR_SIZE], rc;
-- int ln = 0;
-- invalidate_t *iptr, *i;
--
-- if (pstate->config_file[0] != '/') {
-- path_len = strlen(TSConfigDirGet()) + strlen(pstate->config_file) + 2;
-- path = alloca(path_len);
-- snprintf(path, path_len, "%s/%s", TSConfigDirGet(), pstate->config_file);
-- } else
-- path = pstate->config_file;
-- if (stat(path, &s) < 0) {
-- TSDebug(LOG_PREFIX, "Could not stat %s", path);
-- return false;
-- }
-- if (s.st_mtime > pstate->last_load) {
-- now = time(NULL);
-- if (!(fs = fopen(path, "r"))) {
-- TSDebug(LOG_PREFIX, "Could not open %s for reading", path);
-- return false;
-- }
-- config_re = pcre_compile("^([^#].+?)\\s+(\\d+)\\s*$", 0, &errptr, &erroffset, NULL);
-- while (fgets(line, LINE_MAX, fs) != NULL) {
-- ln++;
-- TSDebug(LOG_PREFIX, "Processing: %d %s", ln, line);
-- rc = pcre_exec(config_re, NULL, line, strlen(line), 0, 0, ovector, OVECTOR_SIZE);
-- if (rc == 3) {
-- i = (invalidate_t *)TSmalloc(sizeof(invalidate_t));
-- init_invalidate_t(i);
-- pcre_get_substring(line, ovector, rc, 1, &i->regex_text);
-- i->epoch = now;
-- i->expiry = atoi(line + ovector[4]);
-- i->regex = pcre_compile(i->regex_text, 0, &errptr, &erroffset, NULL);
-- if (i->expiry <= i->epoch) {
-- TSDebug(LOG_PREFIX, "Rule is already expired!");
-- free_invalidate_t(i);
-- } else if (i->regex == NULL) {
-- TSDebug(LOG_PREFIX, "%s did not compile", i->regex_text);
-- free_invalidate_t(i);
-- } else {
-- i->regex_extra = pcre_study(i->regex, 0, &errptr);
-- if (!*ilist) {
-- *ilist = i;
-- TSDebug(LOG_PREFIX, "Created new list and Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-- } else {
-- iptr = *ilist;
-- while (1) {
-- if (strcmp(i->regex_text, iptr->regex_text) == 0) {
-- if (iptr->expiry != i->expiry) {
-- TSDebug(LOG_PREFIX, "Updating duplicate %s", i->regex_text);
-- iptr->epoch = i->epoch;
-- iptr->expiry = i->expiry;
-- }
-- free_invalidate_t(i);
-- i = NULL;
-- break;
-- } else if (!iptr->next)
-- break;
-- else
-- iptr = iptr->next;
-- }
-- if (i) {
-- iptr->next = i;
-- TSDebug(LOG_PREFIX, "Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-- }
-- }
-- }
-- } else
-- TSDebug(LOG_PREFIX, "Skipping line %d", ln);
-- }
-- pcre_free(config_re);
-- fclose(fs);
-- pstate->last_load = s.st_mtime;
-- return true;
-- } else
-- TSDebug(LOG_PREFIX, "File mod time is not newer: %d >= %d", (int)pstate->last_load, (int)s.st_mtime);
-- return false;
--}
-
- static void
--list_config(plugin_state_t *pstate, invalidate_t *i)
-+list_config(config_holder_t *config_holder, invalidate_t *i)
- {
- invalidate_t *iptr;
-
-- TSDebug(LOG_PREFIX, "Current config:");
-- if (pstate->log)
-- TSTextLogObjectWrite(pstate->log, "Current config:");
-+ TSDebug(PLUGIN_TAG, "Current config:");
-+ if (config_holder->log)
-+ TSTextLogObjectWrite(config_holder->log, "Current config:");
- if (i) {
- iptr = i;
- while (iptr) {
-- TSDebug(LOG_PREFIX, "%s epoch: %d expiry: %d", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry);
-- if (pstate->log)
-- TSTextLogObjectWrite(pstate->log, "%s epoch: %d expiry: %d", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry);
-+ TSDebug(PLUGIN_TAG, "%s epoch: %d expiry: %d", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry);
-+ if (config_holder->log)
-+ TSTextLogObjectWrite(config_holder->log, "%s epoch: %d expiry: %d", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry);
- iptr = iptr->next;
- }
- } else {
-- TSDebug(LOG_PREFIX, "EMPTY");
-- if (pstate->log)
-- TSTextLogObjectWrite(pstate->log, "EMPTY");
-+ TSDebug(PLUGIN_TAG, "EMPTY");
-+ if (config_holder->log)
-+ TSTextLogObjectWrite(config_holder->log, "EMPTY");
- }
- }
-
- static int
--free_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+config_pruner(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
- {
-- invalidate_t *iptr;
-+ invalidate_t *i;
-
-- TSDebug(LOG_PREFIX, "Freeing old config");
-- iptr = (invalidate_t *)TSContDataGet(cont);
-- free_invalidate_t_list(iptr);
-- TSContDestroy(cont);
-- return 0;
--}
-+ TSDebug(PLUGIN_TAG, "config_pruner");
-+ config_holder_t *configh = (config_holder_t *)TSContDataGet(cont);
-+ i = configh->config;
-
--static int
--config_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
--{
-- plugin_state_t *pstate;
-- invalidate_t *i, *iptr;
-- TSCont free_cont;
-- bool updated;
-+ prune_config(&i);
-
-- TSDebug(LOG_PREFIX, "In config Handler");
-- pstate = (plugin_state_t *)TSContDataGet(cont);
-- i = copy_config(pstate->invalidate_list);
-+ configh->config = i;
-
-- updated = prune_config(&i);
-- updated = load_config(pstate, &i) || updated;
--
-- if (updated) {
-- list_config(pstate, i);
-- iptr = __sync_val_compare_and_swap(&(pstate->invalidate_list), pstate->invalidate_list, i);
--
-- if (iptr) {
-- free_cont = TSContCreate(free_handler, NULL);
-- TSContDataSet(free_cont, (void *)iptr);
-- TSContSchedule(free_cont, FREE_TMOUT, TS_THREAD_POOL_TASK);
-- }
-- } else {
-- TSDebug(LOG_PREFIX, "No Changes");
-- if (i)
-- free_invalidate_t_list(i);
-- }
--
-- TSContSchedule(cont, CONFIG_TMOUT, TS_THREAD_POOL_TASK);
-+ TSContSchedule(cont, PRUNE_TMOUT, TS_THREAD_POOL_TASK);
- return 0;
- }
-
-@@ -387,7 +228,6 @@ main_handler(TSCont cont, TSEvent event, void *edata)
- TSHttpTxn txn = (TSHttpTxn)edata;
- int status;
- invalidate_t *iptr;
-- plugin_state_t *pstate;
-
- time_t date = 0, now = 0;
- char *url = NULL;
-@@ -397,8 +237,7 @@ main_handler(TSCont cont, TSEvent event, void *edata)
- case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
- if (TSHttpTxnCacheLookupStatusGet(txn, &status) == TS_SUCCESS) {
- if (status == TS_CACHE_LOOKUP_HIT_FRESH) {
-- pstate = (plugin_state_t *)TSContDataGet(cont);
-- iptr = pstate->invalidate_list;
-+ iptr = get_config(cont);
- while (iptr) {
- if (!date) {
- date = get_date_from_cached_hdr(txn);
-@@ -410,7 +249,7 @@ main_handler(TSCont cont, TSEvent event, void *edata)
- if (pcre_exec(iptr->regex, iptr->regex_extra, url, url_len, 0, 0, NULL, 0) >= 0) {
- TSHttpTxnCacheLookupStatusSet(txn, TS_CACHE_LOOKUP_HIT_STALE);
- iptr = NULL;
-- TSDebug(LOG_PREFIX, "Forced revalidate - %.*s", url_len, url);
-+ TSDebug(PLUGIN_TAG, "Forced revalidate - %.*s", url_len, url);
- }
- }
- if (iptr)
-@@ -457,13 +296,12 @@ TSPluginInit(int argc, const char *argv[])
- {
- TSPluginRegistrationInfo info;
- TSCont main_cont, config_cont;
-- plugin_state_t *pstate;
-- invalidate_t *iptr = NULL;
-+ config_holder_t *config_holder;
-+ char *path = NULL;
-
-- TSDebug(LOG_PREFIX, "Starting plugin init.");
-+ TSDebug(PLUGIN_TAG, "Starting plugin init.");
-
-- pstate = (plugin_state_t *)TSmalloc(sizeof(plugin_state_t));
-- init_plugin_state_t(pstate);
-+ config_holder = new_config_holder();
-
- int c;
- optind = 1;
-@@ -473,46 +311,48 @@ TSPluginInit(int argc, const char *argv[])
- while ((c = getopt_long(argc, (char *const *)argv, "c:l:", longopts, NULL)) != -1) {
- switch (c) {
- case 'c':
-- pstate->config_file = TSstrdup(optarg);
-+ path = TSstrdup(optarg);
- break;
- case 'l':
-- TSTextLogObjectCreate(optarg, TS_LOG_MODE_ADD_TIMESTAMP, &pstate->log);
-- TSTextLogObjectRollingEnabledSet(pstate->log, 1);
-- TSTextLogObjectRollingIntervalSecSet(pstate->log, LOG_ROLL_INTERVAL);
-- TSTextLogObjectRollingOffsetHrSet(pstate->log, LOG_ROLL_OFFSET);
-+ TSTextLogObjectCreate(optarg, TS_LOG_MODE_ADD_TIMESTAMP, &config_holder->log);
-+ TSTextLogObjectRollingEnabledSet(config_holder->log, 1);
-+ TSTextLogObjectRollingIntervalSecSet(config_holder->log, LOG_ROLL_INTERVAL);
-+ TSTextLogObjectRollingOffsetHrSet(config_holder->log, LOG_ROLL_OFFSET);
- break;
- default:
- break;
- }
- }
-+ config_holder = init_config_holder(config_holder, path);
-
-- if (!pstate->config_file) {
-+ if (!config_holder->config_path) {
- TSError("Plugin requires a --config option along with a config file name.");
-- free_plugin_state_t(pstate);
-+ free_config_holder_t(config_holder);
- return;
- }
-
-- if (!load_config(pstate, &iptr))
-- TSDebug(LOG_PREFIX, "Problem loading config from file %s", pstate->config_file);
-+ // if (!load_config(free_config_holder_t, &iptr))
-+ if (config_holder->config)
-+ TSDebug(PLUGIN_TAG, "Problem loading config from file %s", config_holder->config_path);
- else {
-- pstate->invalidate_list = iptr;
-- list_config(pstate, iptr);
-+ // config_holder->config = iptr;
-+ list_config(config_holder, config_holder->config);
- }
-
-- info.plugin_name = LOG_PREFIX;
-+ info.plugin_name = PLUGIN_TAG;
- info.vendor_name = "Apache Software Foundation";
- info.support_email = "dev@trafficserver.apache.org";
-
- if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
- TSError("Plugin registration failed.");
-- free_plugin_state_t(pstate);
-+ free_config_holder_t(config_holder);
- return;
- } else
-- TSDebug(LOG_PREFIX, "Plugin registration succeeded.");
-+ TSDebug(PLUGIN_TAG, "Plugin registration succeeded.");
-
- if (!check_ts_version()) {
- TSError("Plugin requires Traffic Server %d.%d.%d", TS_VERSION_MAJOR, TS_VERSION_MINOR, TS_VERSION_MICRO);
-- free_plugin_state_t(pstate);
-+ free_config_holder_t(config_holder);
- return;
- }
-
-@@ -520,12 +360,235 @@ TSPluginInit(int argc, const char *argv[])
- pcre_free = &ts_free;
-
- main_cont = TSContCreate(main_handler, NULL);
-- TSContDataSet(main_cont, (void *)pstate);
-+ TSContDataSet(main_cont, (void *)config_holder);
- TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, main_cont);
-
-+ config_cont = TSContCreate(config_pruner, TSMutexCreate());
-+ TSContDataSet(config_cont, (void *)config_holder);
-+ TSContSchedule(config_cont, PRUNE_TMOUT, TS_THREAD_POOL_TASK);
-+
- config_cont = TSContCreate(config_handler, TSMutexCreate());
-- TSContDataSet(config_cont, (void *)pstate);
-- TSContSchedule(config_cont, CONFIG_TMOUT, TS_THREAD_POOL_TASK);
-+ TSContDataSet(config_cont, (void *)config_holder);
-+ TSMgmtUpdateRegister(config_cont, PLUGIN_TAG);
-+
-+ TSDebug(PLUGIN_TAG, "Plugin Init Complete.");
-+}
-+
-+static config_t *
-+new_config(TSFile fs)
-+{
-+ char line[LINE_MAX];
-+ time_t now;
-+ pcre *config_re;
-+ const char *errptr;
-+ int erroffset, ovector[OVECTOR_SIZE], rc;
-+ int ln = 0;
-+ invalidate_t *iptr, *i, *config = 0;
-+
-+ now = time(NULL);
-+
-+ config_re = pcre_compile("^([^#].+?)\\s+(\\d+)\\s*$", 0, &errptr, &erroffset, NULL);
-+ while (TSfgets(fs, line, LINE_MAX - 1) != NULL) {
-+ ln++;
-+ TSDebug(PLUGIN_TAG, "Processing: %d %s", ln, line);
-+ rc = pcre_exec(config_re, NULL, line, strlen(line), 0, 0, ovector, OVECTOR_SIZE);
-+ if (rc == 3) {
-+ i = (invalidate_t *)TSmalloc(sizeof(invalidate_t));
-+ init_invalidate_t(i);
-+ pcre_get_substring(line, ovector, rc, 1, &i->regex_text);
-+ i->epoch = now;
-+ i->expiry = atoi(line + ovector[4]);
-+ i->regex = pcre_compile(i->regex_text, 0, &errptr, &erroffset, NULL);
-+ if (i->expiry <= i->epoch) {
-+ TSDebug(PLUGIN_TAG, "NOT Loaded, already expired! %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+ TSError(PLUGIN_TAG " - NOT Loaded, already expired: %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+ free_invalidate_t(i);
-+ } else if (i->regex == NULL) {
-+ TSDebug(PLUGIN_TAG, "%s did not compile", i->regex_text);
-+ free_invalidate_t(i);
-+ } else {
-+ i->regex_extra = pcre_study(i->regex, 0, &errptr);
-+ if (!config) {
-+ config = i;
-+ TSDebug(PLUGIN_TAG, "Created new list and Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+ TSError(PLUGIN_TAG " - New Revalidate: %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+ } else {
-+ iptr = config;
-+ while (1) {
-+ if (strcmp(i->regex_text, iptr->regex_text) == 0) {
-+ if (iptr->expiry != i->expiry) {
-+ TSDebug(PLUGIN_TAG, "Updating duplicate %s", i->regex_text);
-+ iptr->epoch = i->epoch;
-+ iptr->expiry = i->expiry;
-+ }
-+ free_invalidate_t(i);
-+ i = NULL;
-+ break;
-+ } else if (!iptr->next)
-+ break;
-+ else
-+ iptr = iptr->next;
-+ }
-+ if (i) {
-+ iptr->next = i;
-+ TSDebug(PLUGIN_TAG, "Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+ }
-+ }
-+ }
-+ } else
-+ TSDebug(PLUGIN_TAG, "Skipping line %d", ln);
-+ }
-+ pcre_free(config_re);
-+
-+ return config;
-+}
-+
-+static void
-+delete_config(config_t *config)
-+{
-+ TSDebug(PLUGIN_TAG, "Freeing config");
-+ free_invalidate_t_list(config);
-+}
-+
-+static int
-+free_invalidate_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+ invalidate_t *i = (invalidate_t *)TSContDataGet(cont);
-+ free_invalidate_t(i);
-+ TSContDestroy(cont);
-+ return 0;
-+}
-+
-+static void
-+schedule_free_invalidate_t(invalidate_t *iptr)
-+{
-+ TSCont free_cont;
-+ free_cont = TSContCreate(free_invalidate_handler, NULL);
-+ TSContDataSet(free_cont, (void *)iptr);
-+ TSContSchedule(free_cont, FREE_TMOUT, TS_THREAD_POOL_TASK);
-+ return;
-+}
-
-- TSDebug(LOG_PREFIX, "Plugin Init Complete.");
-+static config_t *
-+get_config(TSCont cont)
-+{
-+ config_holder_t *configh = (config_holder_t *)TSContDataGet(cont);
-+ if (!configh) {
-+ return 0;
-+ }
-+ return configh->config;
-+}
-+
-+static void
-+load_config_file(config_holder_t *config_holder)
-+{
-+ TSFile fh;
-+ struct stat s;
-+
-+ config_t *newconfig, *oldconfig;
-+ TSCont free_cont;
-+
-+ // check date
-+ if (stat(config_holder->config_path, &s) < 0) {
-+ TSDebug(PLUGIN_TAG, "Could not stat %s", config_holder->config_path);
-+ if (config_holder->config) {
-+ return;
-+ }
-+ } else {
-+ TSDebug(PLUGIN_TAG, "s.st_mtime=%lu, last_load=%lu", s.st_mtime, config_holder->last_load);
-+ if (s.st_mtime < config_holder->last_load) {
-+ return;
-+ }
-+ }
-+
-+ TSDebug(PLUGIN_TAG, "Opening config file: %s", config_holder->config_path);
-+ fh = TSfopen(config_holder->config_path, "r");
-+ TSError(PLUGIN_TAG " - Reading config: %s", config_holder->config_path);
-+
-+ if (!fh) {
-+ TSError("[%s] Unable to open config: %s.\n", PLUGIN_TAG, config_holder->config_path);
-+ return;
-+ }
-+
-+ newconfig = 0;
-+ newconfig = new_config(fh);
-+ if (newconfig) {
-+ config_holder->last_load = time(NULL);
-+ config_t **confp = &(config_holder->config);
-+ oldconfig = __sync_lock_test_and_set(confp, newconfig);
-+ if (oldconfig) {
-+ TSDebug(PLUGIN_TAG, "scheduling free: %p (%p)", oldconfig, newconfig);
-+ free_cont = TSContCreate(free_handler, NULL);
-+ TSContDataSet(free_cont, (void *)oldconfig);
-+ TSContSchedule(free_cont, FREE_TMOUT, TS_THREAD_POOL_TASK);
-+ }
-+ }
-+ if (fh)
-+ TSfclose(fh);
-+ return;
-+}
-+
-+static config_holder_t *
-+new_config_holder(void)
-+{
-+ config_holder_t *config_holder = TSmalloc(sizeof(config_holder_t));
-+ return config_holder;
-+}
-+
-+static config_holder_t *
-+init_config_holder(config_holder_t *config_holder, const char *path)
-+{
-+ int path_len = 0;
-+ config_holder->config_path = 0;
-+ config_holder->config = 0;
-+ config_holder->last_load = 0;
-+ config_holder->log = 0;
-+
-+ if (!path)
-+ path = DEFAULT_CONFIG_NAME;
-+ if (path[0] != '/') {
-+ path_len = strlen(TSConfigDirGet()) + strlen(path) + 2;
-+ config_holder->config_path = ts_malloc(path_len);
-+ snprintf(config_holder->config_path, path_len, "%s/%s", TSConfigDirGet(), path);
-+ TSDebug(PLUGIN_TAG, "path: '%s' len=%d", config_holder->config_path, path_len);
-+ } else
-+ config_holder->config_path = TSstrdup(path);
-+
-+ load_config_file(config_holder);
-+ return config_holder;
-+}
-+
-+static void
-+free_config_holder_t(config_holder_t *config_holder)
-+{
-+ if (config_holder->config)
-+ free_invalidate_t_list(config_holder->config);
-+ if (config_holder->config_path)
-+ TSfree(config_holder->config_path);
-+ if (config_holder->log)
-+ TSTextLogObjectDestroy(config_holder->log);
-+ TSfree(config_holder);
-+}
-+
-+static int
-+free_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+ config_t *config;
-+
-+ TSDebug(PLUGIN_TAG, "Freeing old config");
-+ config = (config_t *)TSContDataGet(cont);
-+ delete_config(config);
-+ TSContDestroy(cont);
-+ return 0;
-+}
-+
-+static int
-+config_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+ config_holder_t *config_holder;
-+
-+ TSDebug(PLUGIN_TAG, "In config Handler");
-+ config_holder = (config_holder_t *)TSContDataGet(cont);
-+ load_config_file(config_holder);
-+ return 0;
- }
-diff --git a/plugins/experimental/remap_stats/remap_stats.c b/plugins/experimental/remap_stats/remap_stats.c
-index e231ec2..8d3f987 100644
---- a/plugins/experimental/remap_stats/remap_stats.c
-+++ b/plugins/experimental/remap_stats/remap_stats.c
-@@ -23,12 +23,14 @@
- #include "ink_defs.h"
-
- #include "ts/ts.h"
-+
- #include <stdint.h>
- #include <stdbool.h>
- #include <string.h>
- #include <stdio.h>
- #include <getopt.h>
- #include <search.h>
-+#include <time.h>
-
- #define PLUGIN_NAME "remap_stats"
- #define DEBUG_TAG PLUGIN_NAME
-@@ -36,18 +38,52 @@
- #define MAX_STAT_LENGTH (1 << 8)
-
- typedef struct {
-+ time_t last_update;
- bool post_remap_host;
-- int txn_slot;
-+ int txn_slot, schedule_delay;
- TSStatPersistence persist_type;
- TSMutex stat_creation_mutex;
- } config_t;
-
-+typedef struct {
-+ int stat_id;
-+ time_t last_update;
-+} value_t;
-+
-+static int
-+lookup_stat(char *name, TSStatPersistence persist_type, TSMutex create_mutex)
-+{
-+ int stat_id = -1;
-+
-+ 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
-+ TSError("[%s:%d] Created stat_name: %s stat_id: %d", __FILE__, __LINE__, name, stat_id);
-+ }
-+ TSMutexUnlock(create_mutex);
-+
-+ return stat_id;
-+}
-+
-+static inline time_t
-+now()
-+{
-+ struct timespec ts;
-+
-+ clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
-+
-+ return ts.tv_sec;
-+}
-
- static void
--stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex create_mutex)
-+stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex create_mutex, time_t config_last_update)
- {
- int stat_id = -1;
- ENTRY search, *result = NULL;
-+ value_t *val;
- static __thread struct hsearch_data stat_cache;
- static __thread bool hash_init = false;
-
-@@ -65,32 +101,43 @@ stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex c
- // 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);
-+ stat_id = lookup_stat(name, persist_type, create_mutex);
-
- if (stat_id >= 0) {
- search.key = TSstrdup(name);
-- search.data = (void *)((intptr_t)stat_id);
-+ val = (value_t *)TSmalloc(sizeof(value_t));
-+ val->stat_id = stat_id;
-+ val->last_update = now();
-+ search.data = (void *)val;
- 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);
-+ } else {
-+ val = (value_t *)result->data;
-+
-+ // Stat expiry should be rare, so lets assume this is unlikely
-+ if (unlikely(val->last_update < config_last_update)) {
-+ stat_id = lookup_stat(name, persist_type, create_mutex);
-+ TSDebug(DEBUG_TAG, "stat_add(): running stat expiry check for stat_id:%d named: %s", stat_id, name);
-+ // Stat changes should not happen so lets assume this is unlikely
-+ if (unlikely(val->stat_id != stat_id)) {
-+ TSError("[%s:%d] Found difference stat_name: %s old stat_id: %d new stat_id: %d", __FILE__, __LINE__, name, val->stat_id,
-+ stat_id);
-+ val->stat_id = stat_id;
-+ val->last_update = now();
-+ }
-+ } else {
-+ stat_id = val->stat_id;
-+ }
-+ }
-
-- if (likely(stat_id >= 0))
-+ if (likely(stat_id >= 0)) {
-+ TSDebug(DEBUG_TAG, "stat_add(): preparing to increment, name=%s, stat_id=%d, amount=%" PRId64, name, stat_id, amount);
- TSStatIntIncrement(stat_id, amount);
-- else
-+ } else
- TSDebug(DEBUG_TAG, "stat error! stat_name: %s stat_id: %d", name, stat_id);
- }
-
--
- static char *
- get_effective_host(TSHttpTxn txn)
- {
-@@ -113,7 +160,6 @@ get_effective_host(TSHttpTxn txn)
- return tmp;
- }
-
--
- static int
- handle_read_req_hdr(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- {
-@@ -130,7 +176,6 @@ handle_read_req_hdr(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- return 0;
- }
-
--
- static int
- handle_post_remap(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- {
-@@ -148,14 +193,12 @@ handle_post_remap(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- }
-
- TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
-- TSDebug(DEBUG_TAG, "Post Remap Handler Finished");
-+ TSDebug(DEBUG_TAG, "Post Remap Handler Finished.");
- return 0;
- }
-
--
- #define CREATE_STAT_NAME(s, h, b) snprintf(s, MAX_STAT_LENGTH, "plugin.%s.%s.%s", PLUGIN_NAME, h, b)
-
--
- static int
- handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- {
-@@ -175,6 +218,8 @@ handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
-
- hostname = (char *)((uintptr_t)txnd & (~((uintptr_t)0x01))); // Get hostname
-
-+ TSDebug(DEBUG_TAG, "handle_txn_close(): hostname: %s", hostname);
-+
- if (txnd) {
- if ((uintptr_t)txnd & 0x01) // remap succeeded?
- {
-@@ -190,13 +235,13 @@ handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- in_bytes += TSHttpTxnClientReqBodyBytesGet(txn);
-
- CREATE_STAT_NAME(stat_name, remap, "in_bytes");
-- stat_add(stat_name, (TSMgmtInt)in_bytes, config->persist_type, config->stat_creation_mutex);
-+ stat_add(stat_name, (TSMgmtInt)in_bytes, config->persist_type, config->stat_creation_mutex, config->last_update);
-
- out_bytes = TSHttpTxnClientRespHdrBytesGet(txn);
- out_bytes += TSHttpTxnClientRespBodyBytesGet(txn);
-
- CREATE_STAT_NAME(stat_name, remap, "out_bytes");
-- stat_add(stat_name, (TSMgmtInt)out_bytes, config->persist_type, config->stat_creation_mutex);
-+ stat_add(stat_name, (TSMgmtInt)out_bytes, config->persist_type, config->stat_creation_mutex, config->last_update);
-
- if (TSHttpTxnClientRespGet(txn, &buf, &hdr_loc) == TS_SUCCESS) {
- status_code = (int)TSHttpHdrStatusGet(buf, hdr_loc);
-@@ -215,10 +260,10 @@ handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- else
- CREATE_STAT_NAME(stat_name, remap, "status_other");
-
-- stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex);
-+ stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex, config->last_update);
- } else {
- CREATE_STAT_NAME(stat_name, remap, "status_unknown");
-- stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex);
-+ stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex, config->last_update);
- }
-
- if (remap != unknown)
-@@ -232,11 +277,43 @@ handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- return 0;
- }
-
-+static int
-+do_time_update(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+ config_t *config = (config_t *)TSContDataGet(cont);
-+ TSDebug(DEBUG_TAG, "do_time_update() called, updating config->last_update.");
-+
-+
-+ while (!__sync_bool_compare_and_swap(&(config->last_update), config->last_update, now()))
-+ ;
-+
-+ TSContDestroy(cont);
-+ return 0;
-+}
-+
-+static int
-+handle_config_update(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+ config_t *config = (config_t *)TSContDataGet(cont);
-+
-+ TSDebug(DEBUG_TAG, "handle_config_update() called due to management update.");
-+
-+ if (config->schedule_delay > 0) {
-+ TSCont do_update_cont = TSContCreate(do_time_update, NULL);
-+ TSContDataSet(do_update_cont, (void *)config);
-+ TSContSchedule(do_update_cont, config->schedule_delay * 1000, TS_THREAD_POOL_TASK);
-+ } else {
-+ while (!__sync_bool_compare_and_swap(&(config->last_update), config->last_update, now()))
-+ ;
-+ }
-+ return 0;
-+}
-+
- void
- TSPluginInit(int argc, const char *argv[])
- {
- TSPluginRegistrationInfo info;
-- TSCont pre_remap_cont, post_remap_cont, global_cont;
-+ TSCont pre_remap_cont, post_remap_cont, global_cont, update_cont;
- config_t *config;
-
- info.plugin_name = PLUGIN_NAME;
-@@ -253,14 +330,18 @@ TSPluginInit(int argc, const char *argv[])
- config->post_remap_host = false;
- config->persist_type = TS_STAT_NON_PERSISTENT;
- config->stat_creation_mutex = TSMutexCreate();
-+ config->schedule_delay = 0;
-+ config->last_update = 0;
-
- if (argc > 1) {
- int c;
- optind = 1;
-- static const struct option longopts[] = {
-- {"post-remap-host", no_argument, NULL, 'P'}, {"persistent", no_argument, NULL, 'p'}, {NULL, 0, NULL, 0}};
-+ static const struct option longopts[] = {{"post-remap-host", no_argument, NULL, 'P'},
-+ {"persistent", no_argument, NULL, 'p'},
-+ {"delay", required_argument, NULL, 'd'},
-+ {NULL, 0, NULL, 0}};
-
-- while ((c = getopt_long(argc, (char *const *)argv, "Pp", longopts, NULL)) != -1) {
-+ while ((c = getopt_long(argc, (char *const *)argv, "Ppd:", longopts, NULL)) != -1) {
- switch (c) {
- case 'P':
- config->post_remap_host = true;
-@@ -270,6 +351,10 @@ TSPluginInit(int argc, const char *argv[])
- config->persist_type = TS_STAT_PERSISTENT;
- TSDebug(DEBUG_TAG, "Using persistent stats");
- break;
-+ case 'd':
-+ config->schedule_delay = atoi(optarg);
-+ TSDebug(DEBUG_TAG, "Setting scheduling delay to %d", config->schedule_delay);
-+ break;
- default:
- break;
- }
-@@ -292,5 +377,9 @@ TSPluginInit(int argc, const char *argv[])
- TSContDataSet(global_cont, (void *)config);
- TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, global_cont);
-
-+ update_cont = TSContCreate(handle_config_update, NULL);
-+ TSContDataSet(update_cont, (void *)config);
-+ TSMgmtUpdateRegister(update_cont, PLUGIN_NAME);
-+
- TSDebug(DEBUG_TAG, "Init complete");
- }
-diff --git a/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c b/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
-index c3cd9bc..dd45572 100644
---- a/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
-+++ b/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
-@@ -268,6 +268,7 @@ consume_resource(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
- case TS_EVENT_VCONN_WRITE_READY:
- // We shouldn't get here because we specify the exact size of the buffer.
- TSDebug(PLUGIN_NAME, "Write Ready");
-+ break;
- case TS_EVENT_VCONN_WRITE_COMPLETE:
- TSDebug(PLUGIN_NAME, "Write Complete");
- // TSDebug(PLUGIN_NAME, "TSVConnShutdown()");
-diff --git a/plugins/experimental/ts_lua/ts_lua_http_config.c b/plugins/experimental/ts_lua/ts_lua_http_config.c
-index f840625..be81bd6 100644
---- a/plugins/experimental/ts_lua/ts_lua_http_config.c
-+++ b/plugins/experimental/ts_lua/ts_lua_http_config.c
-@@ -81,6 +81,13 @@ typedef enum {
- TS_LUA_CONFIG_HTTP_CACHE_FUZZ_PROBABILITY = TS_CONFIG_HTTP_CACHE_FUZZ_PROBABILITY,
- TS_LUA_CONFIG_NET_SOCK_PACKET_MARK_OUT = TS_CONFIG_NET_SOCK_PACKET_MARK_OUT,
- TS_LUA_CONFIG_NET_SOCK_PACKET_TOS_OUT = TS_CONFIG_NET_SOCK_PACKET_TOS_OUT,
-+ TS_LUA_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS = TS_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS,
-+ TS_LUA_CONFIG_HTTP_PARENT_TOTAL_CONNECT_ATTEMPTS = TS_CONFIG_HTTP_PARENT_TOTAL_CONNECT_ATTEMPTS,
-+ TS_LUA_CONFIG_HTTP_SIMPLE_RETRY_ENABLED = TS_CONFIG_HTTP_SIMPLE_RETRY_ENABLED,
-+ TS_LUA_CONFIG_HTTP_SIMPLE_RETRY_RESPONSE_CODES = TS_CONFIG_HTTP_SIMPLE_RETRY_RESPONSE_CODES,
-+ TS_LUA_CONFIG_HTTP_DEAD_SERVER_RETRY_ENABLED = TS_CONFIG_HTTP_DEAD_SERVER_RETRY_ENABLED,
-+ TS_LUA_CONFIG_HTTP_DEAD_SERVER_RETRY_RESPONSE_CODES = TS_CONFIG_HTTP_SIMPLE_RETRY_RESPONSE_CODES,
-+ TS_LUA_CONFIG_HTTP_URL_REMAP_REQUIRED = TS_CONFIG_HTTP_URL_REMAP_REQUIRED,
- TS_LUA_CONFIG_LAST_ENTRY = TS_CONFIG_LAST_ENTRY
- } TSLuaOverridableConfigKey;
-
-diff --git a/plugins/experimental/url_sig/url_sig.c b/plugins/experimental/url_sig/url_sig.c
-index 672f2c5..2418f21 100644
---- a/plugins/experimental/url_sig/url_sig.c
-+++ b/plugins/experimental/url_sig/url_sig.c
-@@ -32,6 +32,12 @@
- #include <limits.h>
- #include <ctype.h>
-
-+#ifdef HAVE_PCRE_PCRE_H
-+#include <pcre/pcre.h>
-+#else
-+#include <pcre.h>
-+#endif
-+
- #include <ts/ts.h>
- #include <ts/remap.h>
-
-@@ -41,13 +47,26 @@ struct config {
- TSHttpStatus err_status;
- char *err_url;
- char keys[MAX_KEY_NUM][MAX_KEY_LEN];
-+ pcre *regex;
-+ pcre_extra *regex_extra;
- };
-
--void
-+static void
- free_cfg(struct config *cfg)
- {
- TSError("Cleaning up...");
- TSfree(cfg->err_url);
-+
-+ if (cfg->regex_extra)
-+#ifndef PCRE_STUDY_JIT_COMPILE
-+ pcre_free(cfg->regex_extra);
-+#else
-+ pcre_free_study(cfg->regex_extra);
-+#endif
-+
-+ if (cfg->regex)
-+ pcre_free(cfg->regex);
-+
- TSfree(cfg);
- }
-
-@@ -150,16 +169,30 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s
- value += 3;
-
<TRUNCATED>