TS-2291: Add remap_state plugin to experimental.
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/c3518560 Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/c3518560 Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/c3518560 Branch: refs/heads/5.0.x Commit: c3518560e3ca80b870cd24a99b1bb54179cfa980 Parents: 2c3f7c5 Author: Phil Sorber <sor...@apache.org> Authored: Sun Oct 20 13:22:56 2013 -0600 Committer: Phil Sorber <sor...@apache.org> Committed: Sun Oct 20 13:22:56 2013 -0600 ---------------------------------------------------------------------- CHANGES | 2 + NOTICE | 8 +- configure.ac | 1 + plugins/experimental/Makefile.am | 3 +- plugins/experimental/remap_stats/Makefile.am | 21 ++ plugins/experimental/remap_stats/remap_stats.c | 329 ++++++++++++++++++++ 6 files changed, 362 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c3518560/CHANGES ---------------------------------------------------------------------- diff --git a/CHANGES b/CHANGES index 1446547..30c1354 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -*- coding: utf-8 -*- Changes with Apache Traffic Server 4.1.0 + *) [TS-2291] Add remap_state plugin to experimental. + *) [TS-2242] Update core plugins' support_email and vendor_name for consistency. http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c3518560/NOTICE ---------------------------------------------------------------------- diff --git a/NOTICE b/NOTICE index b486b5e..55084b6 100644 --- a/NOTICE +++ b/NOTICE @@ -6,6 +6,7 @@ This product includes software developed at - Yahoo! Inc - Network Geographics (http://network-geographics.com) - OmniTI + - Comcast ~~~ @@ -42,4 +43,9 @@ Copyright (C) 2012 GoDaddy. ~~~ lib/atscppapi developed by LinkedIn -Copyright (c) 2013 LinkedIn \ No newline at end of file +Copyright (c) 2013 LinkedIn + +~~~ + +remap_stats plugin developed by Comcast. +Copyright (C) 2013 Comcast http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c3518560/configure.ac ---------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index bb2f373..439a6e1 100644 --- a/configure.ac +++ b/configure.ac @@ -1896,6 +1896,7 @@ AC_CONFIG_FILES([ plugins/experimental/spdy/Makefile plugins/experimental/tcp_info/Makefile plugins/experimental/healthchecks/Makefile + plugins/experimental/remap_stats/Makefile plugins/gzip/Makefile plugins/libloader/Makefile plugins/header_filter/Makefile http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c3518560/plugins/experimental/Makefile.am ---------------------------------------------------------------------- diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am index bb97af1..eb807f8 100644 --- a/plugins/experimental/Makefile.am +++ b/plugins/experimental/Makefile.am @@ -27,5 +27,6 @@ SUBDIRS = \ channel_stats \ authproxy \ geoip_acl \ - healthchecks + healthchecks \ + remap_stats endif http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c3518560/plugins/experimental/remap_stats/Makefile.am ---------------------------------------------------------------------- diff --git a/plugins/experimental/remap_stats/Makefile.am b/plugins/experimental/remap_stats/Makefile.am new file mode 100644 index 0000000..4033fa4 --- /dev/null +++ b/plugins/experimental/remap_stats/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 = remap_stats.la +remap_stats_la_SOURCES = remap_stats.c +remap_stats_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS) http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c3518560/plugins/experimental/remap_stats/remap_stats.c ---------------------------------------------------------------------- diff --git a/plugins/experimental/remap_stats/remap_stats.c b/plugins/experimental/remap_stats/remap_stats.c new file mode 100644 index 0000000..23c93cd --- /dev/null +++ b/plugins/experimental/remap_stats/remap_stats.c @@ -0,0 +1,329 @@ +/** @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 "ink_config.h" +#include "ink_defs.h" + +#include "ts/ts.h" +#include <stdbool.h> +#include <string.h> +#include <stdio.h> +#include <getopt.h> +#include <search.h> + +#define PLUGIN_NAME "remap_stats" +#define DEBUG_TAG PLUGIN_NAME + +typedef struct +{ + bool post_remap_host; + int txn_slot; + TSStatPersistence persist_type; + TSMutex stat_creation_mutex; +} config_t; + +typedef struct +{ + char *hostname; + bool remap_success; +} txn_data_t; + +static void +stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex create_mutex) +{ + int stat_id = -1, *statp; + 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; + 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); + + search.key = TSstrdup(name); + statp = TSmalloc(sizeof(int)); + *statp = stat_id; + search.data = (void *) statp; + hsearch_r(search, ENTER, &result, &stat_cache); + TSDebug(DEBUG_TAG, "Cached stat_name: %s stat_id: %d", name, stat_id); + } + else + stat_id = *((int *) 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 char * +get_effective_host(TSHttpTxn txn) +{ + char *effective_url, *tmp; + const char *host; + int len; + TSMBuffer buf; + TSMLoc url_loc; + + effective_url = TSHttpTxnEffectiveUrlStringGet(txn, &len); + buf = TSMBufferCreate(); + TSUrlCreate(buf, &url_loc); + tmp = effective_url; + TSUrlParse(buf, url_loc, (const char **) (&tmp), (const char *) (effective_url + len)); + TSfree(effective_url); + host = TSUrlHostGet(buf, url_loc, &len); + tmp = TSstrndup(host, len); + TSHandleMLocRelease(buf, TS_NULL_MLOC, url_loc); + TSMBufferDestroy(buf); + return tmp; +} + +static char * +create_stat_name(char *hostname, char *basename) +{ + char *stat_name; + size_t stat_len; + + stat_len = strlen(hostname) + strlen(basename) + strlen(PLUGIN_NAME) + 3 + strlen("plugin."); + stat_name = TSmalloc(stat_len * sizeof(char)); + snprintf(stat_name, stat_len, "plugin.%s.%s.%s", PLUGIN_NAME, hostname, basename); + return stat_name; +} + +static int +handle_read_req_hdr(TSCont cont, TSEvent event, void *edata) +{ + TSHttpTxn txn = (TSHttpTxn) edata; + config_t *config; + txn_data_t *txnd; + + config = (config_t *) TSContDataGet(cont); + txnd = TSmalloc(sizeof(txn_data_t)); + txnd->remap_success = false; + txnd->hostname = get_effective_host(txn); + TSHttpTxnArgSet(txn, config->txn_slot, (void *) txnd); + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + TSDebug(DEBUG_TAG, "Read Req Handler Finished"); + return 0; +} + +static int +handle_post_remap(TSCont cont, TSEvent event, void *edata) +{ + TSHttpTxn txn = (TSHttpTxn) edata; + config_t *config; + txn_data_t *txnd; + + config = (config_t *) TSContDataGet(cont); + + if (config->post_remap_host) + { + txnd = TSmalloc(sizeof(txn_data_t)); + txnd->remap_success = true; + txnd->hostname = NULL; + TSHttpTxnArgSet(txn, config->txn_slot, (void *) txnd); + } + else + { + txnd = (txn_data_t *) TSHttpTxnArgGet(txn, config->txn_slot); + txnd->remap_success = true; + } + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + TSDebug(DEBUG_TAG, "Post Remap Handler Finished"); + return 0; +} + +static int +handle_txn_close(TSCont cont, TSEvent event, void *edata) +{ + TSHttpTxn txn = (TSHttpTxn) edata; + config_t *config; + txn_data_t *txnd; + TSHttpStatus status_code = 0; + TSMBuffer buf; + TSMLoc hdr_loc; + uint64_t out_bytes, in_bytes; + char *remap, *stat_name; + + config = (config_t *) TSContDataGet(cont); + txnd = (txn_data_t *) TSHttpTxnArgGet(txn, config->txn_slot); + + if (txnd) + { + if (txnd->remap_success) + { + if (!config->post_remap_host) + remap = txnd->hostname; + else + remap = get_effective_host(txn); + + if (!remap) + remap = TSstrdup("unknown"); + + in_bytes = TSHttpTxnClientReqHdrBytesGet(txn); + in_bytes += TSHttpTxnClientReqBodyBytesGet(txn); + + stat_name = create_stat_name(remap, "in_bytes"); + stat_add(stat_name, (TSMgmtInt) in_bytes, config->persist_type, config->stat_creation_mutex); + TSfree(stat_name); + + out_bytes = TSHttpTxnClientRespHdrBytesGet(txn); + out_bytes += TSHttpTxnClientRespBodyBytesGet(txn); + + stat_name = create_stat_name(remap, "out_bytes"); + stat_add(stat_name, (TSMgmtInt) out_bytes, config->persist_type, config->stat_creation_mutex); + TSfree(stat_name); + + if (TSHttpTxnClientRespGet(txn, &buf, &hdr_loc) == TS_SUCCESS) + { + status_code = TSHttpHdrStatusGet(buf, hdr_loc); + TSHandleMLocRelease(buf, TS_NULL_MLOC, hdr_loc); + + if ((status_code >= 200) && (status_code <= 299)) + stat_name = create_stat_name(remap, "status_2xx"); + else if ((status_code >= 300) && (status_code <= 399)) + stat_name = create_stat_name(remap, "status_3xx"); + else if ((status_code >= 400) && (status_code <= 499)) + stat_name = create_stat_name(remap, "status_4xx"); + else if ((status_code >= 500) && (status_code <= 599)) + stat_name = create_stat_name(remap, "status_5xx"); + else + stat_name = create_stat_name(remap, "status_other"); + + stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex); + TSfree(stat_name); + } + else + { + stat_name = create_stat_name(remap, "status_unknown"); + stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex); + TSfree(stat_name); + } + + TSfree(remap); + + } + else if (txnd->hostname) + TSfree(txnd->hostname); + + TSfree(txnd); + } + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + TSDebug(DEBUG_TAG, "Handler Finished"); + return 0; +} + +void +TSPluginInit (int argc, const char *argv[]) +{ + TSPluginRegistrationInfo info; + TSCont pre_remap_cont, post_remap_cont, global_cont; + config_t *config; + + info.plugin_name = PLUGIN_NAME; + info.vendor_name = "Apache Software Foundation"; + info.support_email = "d...@trafficserver.apache.org"; + + if (TSPluginRegister(TS_SDK_VERSION_3_0 , &info) != TS_SUCCESS) + { + TSError("Plugin registration failed."); + return; + } + else + TSDebug(DEBUG_TAG, "Plugin registration succeeded."); + + config = TSmalloc(sizeof(config_t)); + config->post_remap_host = false; + config->persist_type = TS_STAT_NON_PERSISTENT; + config->stat_creation_mutex = TSMutexCreate(); + + 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 } + }; + + while ((c = getopt_long(argc, (char * const*) argv, "Pp", longopts, NULL)) != -1) + { + switch (c) + { + case 'P': + config->post_remap_host = true; + TSDebug(DEBUG_TAG, "Using post remap hostname"); + break; + case 'p': + config->persist_type = TS_STAT_PERSISTENT; + TSDebug(DEBUG_TAG, "Using persistent stats"); + break; + default: + break; + } + } + } + + TSHttpArgIndexReserve(PLUGIN_NAME, "txn data", &(config->txn_slot)); + + if (!config->post_remap_host) + { + pre_remap_cont = TSContCreate(handle_read_req_hdr, NULL); + TSContDataSet(pre_remap_cont, (void *) config); + TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, pre_remap_cont); + } + + post_remap_cont = TSContCreate(handle_post_remap, NULL); + TSContDataSet(post_remap_cont, (void *) config); + TSHttpHookAdd(TS_HTTP_POST_REMAP_HOOK, post_remap_cont); + + global_cont = TSContCreate(handle_txn_close, NULL); + TSContDataSet(global_cont, (void *) config); + TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, global_cont); + + TSDebug(DEBUG_TAG, "Init complete"); +}