From 2288382c8bde4a3924a9c8ef3c861e3aa068a918 Mon Sep 17 00:00:00 2001 From: xie Date: Fri, 17 Feb 2017 15:42:27 +0800 Subject: [PATCH] fix some 5.3.x bug --- .../background_fetch/background_fetch.cc | 2 + plugins/experimental/balancer/Makefile.am | 2 +- plugins/experimental/balancer/README.md | 32 ++ plugins/experimental/balancer/balancer.cc | 504 ++++++++++++------ plugins/experimental/balancer/balancer.h | 48 +- plugins/experimental/balancer/hash.cc | 226 -------- plugins/experimental/balancer/roundrobin.cc | 415 ++++++++++++-- plugins/experimental/balancer/roundrobin.h | 93 ++++ .../regex_revalidate/regex_revalidate.c | 9 +- plugins/header_rewrite/expander.cc | 1 + plugins/header_rewrite/header_rewrite.cc | 55 +- 11 files changed, 932 insertions(+), 455 deletions(-) create mode 100644 plugins/experimental/balancer/README.md delete mode 100644 plugins/experimental/balancer/hash.cc create mode 100644 plugins/experimental/balancer/roundrobin.h diff --git a/plugins/experimental/background_fetch/background_fetch.cc b/plugins/experimental/background_fetch/background_fetch.cc index 878183e3f81..d8344e26c97 100644 --- a/plugins/experimental/background_fetch/background_fetch.cc +++ b/plugins/experimental/background_fetch/background_fetch.cc @@ -450,7 +450,9 @@ BGFetchData::initialize(TSMBuffer request, TSMLoc req_hdr, TSHttpTxn txnp) char *url = TSUrlStringGet(mbuf, url_loc, &len); _url.append(url, len); // Save away the URL for later use when acquiring lock + TSfree(static_cast(url)); + TSHandleMLocRelease(request, TS_NULL_MLOC, purl); if (TS_SUCCESS == TSHttpHdrUrlSet(mbuf, hdr_loc, url_loc)) { // Make sure we have the correct Host: header for this request. diff --git a/plugins/experimental/balancer/Makefile.am b/plugins/experimental/balancer/Makefile.am index 02df2b9575c..f10287a844b 100644 --- a/plugins/experimental/balancer/Makefile.am +++ b/plugins/experimental/balancer/Makefile.am @@ -17,6 +17,6 @@ include $(top_srcdir)/build/plugins.mk pkglib_LTLIBRARIES = balancer.la -balancer_la_SOURCES = balancer.cc roundrobin.cc hash.cc balancer.h +balancer_la_SOURCES = balancer.cc roundrobin.cc balancer.h roundrobin.h balancer_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS) diff --git a/plugins/experimental/balancer/README.md b/plugins/experimental/balancer/README.md new file mode 100644 index 00000000000..4c6085e3d02 --- /dev/null +++ b/plugins/experimental/balancer/README.md @@ -0,0 +1,32 @@ +# balancer +具体用法:请参考https://docs.trafficserver.apache.org/en/latest/reference/plugins/balancer.en.html + +在roundrobin 模式下新增 backup、weight、max_fails、fail_timeout ,以及add path和开启https回源的功能: + +####backup=number + marks the server as a backup server. It will be passed requests when the primary servers are + unavailable.by default, 0 --(0/1) + +####weight=number + sets the weight of the server, by default, 1. + +####max_fails=number + sets the number of unsuccessful attempts to communicate with the server that should happen + in the duration set by the fail_timeout parameter to consider the server unavailable for + a duration also set by the fail_timeout parameter. By default, the number of unsuccessful + attempts is set to 3. The zero value disables the accounting of attempts. What is considered + an unsuccessful attempt is defined by the proxy_next_upstream, fastcgi_next_upstream, + uwsgi_next_upstream, scgi_next_upstream, and memcached_next_upstream directives. + +####fail_timeout=time + sets the time during which the specified number of unsuccessful attempts to communicate + with the server should happen to consider the server unavailable; and the period of time + the server will be considered unavailable. By default, the parameter is set to 10 seconds. + + +#For example: + map http://foo.com http://foo.com @plugin=balancer.so @pparam=--policy=roundrobin @pparam=--https @pparam=one.bar.com:80,0,1,3,10 @pparam=two.bar.com,0,1,3,10 + +#Add path: + map http://foo.com http://foo.com @plugin=balancer.so @pparam=--policy=roundrobin,0/ @pparam=one.bar.com:80,0,1,3,10 @pparam=two.bar.com,0,1,3,10
+ if client request http://cdnxxx.com/1.jpg then ATS will back to the source server request http://cdnxxx.com/0/1.jpg diff --git a/plugins/experimental/balancer/balancer.cc b/plugins/experimental/balancer/balancer.cc index 3af676de4e0..fe16fcd7e2b 100644 --- a/plugins/experimental/balancer/balancer.cc +++ b/plugins/experimental/balancer/balancer.cc @@ -1,27 +1,28 @@ /** @file - A brief file description + A brief file description - @section license License + @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 + 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 + 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. + 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 "balancer.h" +#include "roundrobin.h" #include #include #include @@ -30,161 +31,356 @@ #include // Using ink_inet API is cheating, but I was too lazy to write new IPv6 address parsing routines ;) -#include "ink_inet.h" -// The policy type is the first comma-separated token. -static BalancerInstance * -MakeBalancerInstance(const char *opt) -{ - const char *end = strchr(opt, ','); - size_t len = end ? std::distance(opt, end) : strlen(opt); - - if (len == lengthof("hash") && strncmp(opt, "hash", len) == 0) { - return MakeHashBalancer(end ? end + 1 : NULL); - } else if (len == lengthof("roundrobin") && strncmp(opt, "roundrobin", len) == 0) { - return MakeRoundRobinBalancer(end ? end + 1 : NULL); - } else { - TSError("balancer: invalid balancing policy '%.*s'", (int)len, opt); - return NULL; - } -} - -static BalancerTarget -MakeBalancerTarget(const char *strval) -{ - BalancerTarget target = BalancerTarget(); - - union { - struct sockaddr_storage storage; - struct sockaddr sa; - } address; - - memset(&address, 0, sizeof(address)); +static int arg_index = 0; - // First, check whether we have an address literal. - if (ats_ip_pton(strval, &address.sa) == 0) { - char namebuf[INET6_ADDRSTRLEN]; - target.port = ats_ip_port_host_order(&address.sa); - target.name = ats_ip_ntop(&address.sa, namebuf, sizeof(namebuf)); - } else { - const char *colon = strrchr(strval, ':'); - - if (colon) { - size_t len = std::distance(strval, colon); +// The policy type is the first comma-separated token. +static RoundRobinBalancer * +MakeBalancerInstance(const char *opt) { + const char *end = strchr(opt, ','); + size_t len = end ? std::distance(opt, end) : strlen(opt); + + if (len == lengthof("roundrobin") && strncmp(opt, "roundrobin", len) == 0) { + RoundRobinBalancer *roundrobin = new RoundRobinBalancer(); + roundrobin->hold(); + const char *options = end ? end + 1 : NULL; + if (options) { + if (strchr(options, ',')) { + TSError("[%s] Ignoring invalid round robin field '%s'", PLUGIN_NAME, options); + } + roundrobin->set_path(strdup(options)); + } + return roundrobin; + } else { + TSError("[%s] Invalid balancing policy '%.*s'", PLUGIN_NAME,(int) len, opt); + return NULL; + } +} - target.port = strtol(colon + 1, NULL, 10); - target.name = std::string(strval, len); - } else { - target.port = 0; - target.name = strval; - } - } +TSReturnCode TSRemapInit(TSRemapInterface * /* api */, char * /* errbuf */, int /* bufsz */) { + return TS_SUCCESS; +} - if (target.port > INT16_MAX) { - TSError("balancer: ignoring invalid port number for target '%s'", strval); - target.port = 0; - } +static TSReturnCode send_response_handle(TSHttpTxn txnp, BalancerTargetStatus *targetstatus) { + TSHttpStatus status; + + TSMBuffer bufp; + TSMLoc hdr_loc; + RoundRobinBalancer *balancer = (RoundRobinBalancer *)TSHttpTxnArgGet((TSHttpTxn)txnp, arg_index); + if ( NULL == targetstatus || balancer == NULL) { + return TS_SUCCESS; + } + + if(targetstatus && targetstatus->object_status < TS_CACHE_LOOKUP_MISS) { + return TS_SUCCESS; + } + + //回源check 包括down check + if ( targetstatus->target_id >= 0 && (!targetstatus->target_down or (targetstatus->target_down && targetstatus->is_down_check) )) { + //当源站没有正常返回的情况下,都会返回ts_error + status = TS_HTTP_STATUS_NONE; + //TODO 如果是回源304 check 的情况该如何处理? + //当前的ats ,当文件过期,正好源站不通的时候,返回旧文件,当源站有任务返回的时候,ats 将会返回该内容 + //TSHttpTxnServerRespNoStoreSet(txn, 1); + if(TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) { //ats内部处理,比如purge + status = TSHttpHdrStatusGet(bufp,hdr_loc); + TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); + } + + if(status > TS_HTTP_STATUS_NONE && targetstatus && balancer) { + TSDebug(PLUGIN_NAME, "handle_response (): Get status %d, do something.",status); + balancer->os_response_back_status(targetstatus->target_id, status); + } + + } else { + TSDebug(PLUGIN_NAME, " target.id == -1 or target_down == 1!"); + TSHttpTxnSetHttpRetStatus(txnp, TS_HTTP_STATUS_SOURCE_SERVICE_UNAVAILABLE); + + TSHttpTxnErrorBodySet(txnp, TSstrdup("553 Source Service Unavailable!"), sizeof("553 Source Service Unavailable!") - 1, NULL); + return TS_ERROR; + } + + return TS_SUCCESS; +} - return target; +//如果命中 +static TSReturnCode look_up_handle (TSCont contp, TSHttpTxn txnp, BalancerTargetStatus *targetstatus) { + + int obj_status; + RoundRobinBalancer *balancer = (RoundRobinBalancer *)TSHttpTxnArgGet((TSHttpTxn)txnp, arg_index); + if ( NULL == targetstatus || balancer == NULL) { + return TS_ERROR; + } + + if (TSHttpTxnCacheLookupStatusGet(txnp, &obj_status) == TS_ERROR) { + TSError("[%s] [%s] Couldn't get cache status of object",PLUGIN_NAME, __FUNCTION__); + return TS_ERROR; + } + TSDebug(PLUGIN_NAME, "look_up_handle obj_status = %d\n",obj_status); + targetstatus->object_status = obj_status; + //排除 hit_fresh 和 hit_stale的情况,不需要回源 + if (obj_status == TS_CACHE_LOOKUP_HIT_FRESH) { + return TS_ERROR; + } + + //修改成https请求 + if(balancer && balancer->get_https_backend_tag()) { + TSMBuffer req_bufp; + TSMLoc req_loc; + TSMLoc url_loc; + if (TSHttpTxnClientReqGet(txnp, &req_bufp, &req_loc) == TS_ERROR) { + TSDebug(PLUGIN_NAME, "Error while retrieving client request header\n"); + return TS_ERROR; + } + + if (TSHttpHdrUrlGet(req_bufp, req_loc, &url_loc) == TS_ERROR) { + TSDebug(PLUGIN_NAME, "Couldn't get the url\n"); + TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_loc); + return TS_ERROR; + } + TSUrlSchemeSet(req_bufp, url_loc,TS_URL_SCHEME_HTTPS,TS_URL_LEN_HTTPS); + TSHandleMLocRelease(req_bufp, req_loc, url_loc); + TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_loc); + } + + if(balancer && balancer->get_path() != NULL) { + TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, contp); + } + + //排除 hit_fresh 和 hit_stale的情况,不需要添加TS_HTTP_SEND_RESPONSE_HDR_HOOK钩子 + if (obj_status == TS_CACHE_LOOKUP_HIT_STALE) { + return TS_ERROR; + } + + TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp); + TSDebug(PLUGIN_NAME, "add TS_HTTP_SEND_RESPONSE_HDR_HOOK"); + + if (targetstatus && targetstatus->target_down && !targetstatus->is_down_check) + return TS_SUCCESS; + + return TS_ERROR; } -TSReturnCode -TSRemapInit(TSRemapInterface * /* api */, char * /* errbuf */, int /* bufsz */) +/** + * add by daemon.xie + * reason: we need modify origin request URL's path, if we need. + **/ +static TSReturnCode +rewrite_send_request_path(TSHttpTxn txnp, BalancerTargetStatus *targetstatus) { - return TS_SUCCESS; + RoundRobinBalancer *balancer = (RoundRobinBalancer *)TSHttpTxnArgGet((TSHttpTxn)txnp, arg_index); + if ( NULL == targetstatus || balancer == NULL) { + return TS_ERROR; + } + + TSMBuffer bufp; + TSMLoc hdr_loc,url_loc; + int len; + const char *old_path; + const char *add_path = balancer->get_path(); + +// TSDebug("balancer", "do TS_HTTP_POST_REMAP_HOOK event '%s' ",add_path); + if (add_path == NULL) { + return TS_SUCCESS; + } + if(TSHttpTxnServerReqGet(txnp,&bufp,&hdr_loc) != TS_SUCCESS ) { + TSError("[%s] couldn't retrieve request header",PLUGIN_NAME); + TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); + return TS_SUCCESS; + } + + if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) { + TSError("[%s] couldn't retrieve request url", PLUGIN_NAME); + TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); + TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); + return TS_SUCCESS; + } + + old_path = TSUrlPathGet(bufp, url_loc, &len); + if (!old_path) { + TSError("[%s] couldn't retrieve request path",PLUGIN_NAME); + TSHandleMLocRelease(bufp, hdr_loc, url_loc); + TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); + TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); + return TS_SUCCESS; + } +// TSDebug("balancer", "get old path %s", old_path); + int add_len = strlen(add_path); + int new_len = len + add_len; + char new_path[new_len]; + memcpy(new_path, add_path, add_len); + memcpy(&new_path[add_len], old_path , len); + if (TSUrlPathSet(bufp, url_loc, new_path, new_len) != TS_SUCCESS) { + TSError("[%s]: Set new Path field '%.*s'", PLUGIN_NAME,new_len, new_path); + } +// TSDebug("balancer", "new path '%.*s'", new_len, new_path); + TSHandleMLocRelease(bufp, hdr_loc, url_loc); + TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); + + return TS_SUCCESS; +} + + +/** + * Transaction event handler. + */ +static void balancer_handler(TSCont contp, TSEvent event, void *edata) { + TSHttpTxn txnp = static_cast(edata); + BalancerTargetStatus *targetstatus; + targetstatus = (struct BalancerTargetStatus *) TSContDataGet(contp); + RoundRobinBalancer *balancer = (RoundRobinBalancer *)TSHttpTxnArgGet((TSHttpTxn)txnp, arg_index); + TSEvent reenable = TS_EVENT_HTTP_CONTINUE; + + switch (event) { + case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE: + if (look_up_handle(contp, txnp, targetstatus) == TS_SUCCESS) { + reenable = TS_EVENT_HTTP_ERROR; + } + break; + case TS_EVENT_HTTP_SEND_REQUEST_HDR: + rewrite_send_request_path(txnp, targetstatus); + break; + case TS_EVENT_HTTP_SEND_RESPONSE_HDR://放在lookup 里添加 + if (send_response_handle(txnp, targetstatus) == TS_ERROR) { + reenable = TS_EVENT_HTTP_ERROR; + } + break; + case TS_EVENT_HTTP_TXN_CLOSE: + if (balancer) + balancer->release(); + if (targetstatus) + TSfree(targetstatus); + TSContDestroy(contp); + break; + default: + break; + } + TSHttpTxnReenable(txnp, reenable); } /////////////////////////////////////////////////////////////////////////////// // One instance per remap.config invocation. // -TSReturnCode -TSRemapNewInstance(int argc, char *argv[], void **instance, char *errbuf, int errbuf_size) -{ - static const struct option longopt[] = {{const_cast("policy"), required_argument, 0, 'p'}, {0, 0, 0, 0}}; - - BalancerInstance *balancer = NULL; - - // The first two arguments are the "from" and "to" URL string. We need to - // skip them, but we also require that there be an option to masquerade as - // argv[0], so we increment the argument indexes by 1 rather than by 2. - argc--; - argv++; - - optind = 0; - for (;;) { - int opt; - - opt = getopt_long(argc, (char *const *)argv, "", longopt, NULL); - switch (opt) { - case 'p': - balancer = MakeBalancerInstance(optarg); - break; - case -1: - break; - default: - snprintf(errbuf, errbuf_size, "invalid balancer option '%d'", opt); - delete balancer; - return TS_ERROR; - } - - if (opt == -1) { - break; - } - } - - if (!balancer) { - strncpy(errbuf, "missing balancer policy", errbuf_size); - return TS_ERROR; - } - - // Pick up the remaining options as balance targets. - for (int i = optind; i < argc; ++i) { - BalancerTarget target = MakeBalancerTarget(argv[i]); - - balancer->push_target(target); - if (target.port) { - TSDebug("balancer", "added target -> %s:%u", target.name.c_str(), target.port); - } else { - TSDebug("balancer", "added target -> %s", target.name.c_str()); - } - } - - *instance = balancer; - return TS_SUCCESS; +TSReturnCode TSRemapNewInstance(int argc, char *argv[], void **instance, + char *errbuf, int errbuf_size) { + static const struct option longopt[] = { { const_cast("policy"), + required_argument, 0, 'p' }, { const_cast("https"),no_argument, 0, 's' }, { 0, 0, 0, 0 } }; + + RoundRobinBalancer *balancer = NULL; + bool need_https_backend = false; + + // The first two arguments are the "from" and "to" URL string. We need to + // skip them, but we also require that there be an option to masquerade as + // argv[0], so we increment the argument indexes by 1 rather than by 2. + argc--; + argv++; + + optind = 0; + for (;;) { + int opt; + + opt = getopt_long(argc, (char * const *) argv, "", longopt, NULL); + switch (opt) { + case 'p': + balancer = MakeBalancerInstance(optarg); + break; + case 's': + need_https_backend = true; + break; + case -1: + break; + default: + snprintf(errbuf, errbuf_size, "invalid balancer option '%d'", opt); + delete balancer; + return TS_ERROR; + } + + if (opt == -1) { + break; + } + } + + if (!balancer) { + strncpy(errbuf, "missing balancer policy", errbuf_size); + return TS_ERROR; + } + + balancer->set_https_backend_tag(need_https_backend); + // Pick up the remaining options as balance targets. + uint s_count = 0; + int i; + for (i = optind; i < argc; ++i) { + BalancerTarget *target = balancer->MakeBalancerTarget(argv[i]); + target->id = s_count; + s_count ++; + balancer->push_target(target); + if (target->port) { + TSDebug(PLUGIN_NAME, "added target -> %s:%u", target->name.c_str(), target->port); + } else { + TSDebug(PLUGIN_NAME, "added target -> %s", target->name.c_str()); + } + } + + if(s_count == 0) { + TSDebug(PLUGIN_NAME, "no target have create!"); + return TS_ERROR; + } + *instance = balancer; + return TS_SUCCESS; } -void -TSRemapDeleteInstance(void *instance) -{ - delete (BalancerInstance *)instance; +void TSRemapDeleteInstance(void *instance) { + TSDebug(PLUGIN_NAME, "Delete Instance BalancerInstance!"); + static_cast(instance)->release(); } -TSRemapStatus -TSRemapDoRemap(void *instance, TSHttpTxn txn, TSRemapRequestInfo *rri) -{ - BalancerInstance *balancer = (BalancerInstance *)instance; - const BalancerTarget &target = balancer->balance(txn, rri); - - if (TSIsDebugTagSet("balancer")) { - char *url; - int len; - - url = TSHttpTxnEffectiveUrlStringGet(txn, &len); - if (target.port) { - TSDebug("balancer", "%s:%u <- %.*s", target.name.c_str(), target.port, len, url); - } else { - TSDebug("balancer", "%s <- %.*s", target.name.c_str(), len, url); - } - - TSfree(url); - } - - TSUrlHostSet(rri->requestBufp, rri->requestUrl, target.name.data(), target.name.size()); - - if (target.port) { - TSUrlPortSet(rri->requestBufp, rri->requestUrl, target.port); - } - - return TSREMAP_DID_REMAP; +TSRemapStatus TSRemapDoRemap(void *instance, TSHttpTxn txn,TSRemapRequestInfo *rri) { + TSCont txn_contp; + int method_len; + const char *method; + + method = TSHttpHdrMethodGet(rri->requestBufp, rri->requestHdrp, &method_len); + if (method == TS_HTTP_METHOD_PURGE) { + return TSREMAP_NO_REMAP; + } + RoundRobinBalancer *balancer = (RoundRobinBalancer *) instance; + if (balancer == NULL) { + return TSREMAP_NO_REMAP; + } + balancer->hold(); + const BalancerTarget *target = balancer->balance(txn, rri); + + TSUrlHostSet(rri->requestBufp, rri->requestUrl, target->name.data(),target->name.size()); + TSDebug(PLUGIN_NAME,"balancer target.name -> %s target.port -> %d ", target->name.c_str(), target->port); + if (target->port) { + TSUrlPortSet(rri->requestBufp, rri->requestUrl, target->port); + } + + BalancerTargetStatus *targetstatus; + targetstatus = (BalancerTargetStatus *) TSmalloc(sizeof(BalancerTargetStatus)); + targetstatus->target_id = target->id; + targetstatus->target_down = target->down; + targetstatus->is_down_check = false;//是否需要down check + targetstatus->object_status = -1;// < TS_CACHE_LOOKUP_MISS + + if (target->down ) { + time_t now = TShrtime() / TS_HRTIME_SECOND; + if ((now - target->accessed) > (target->timeout_fails * target->fail_timeout)) { + targetstatus->is_down_check = true; + } + } + + if (NULL == (txn_contp = TSContCreate((TSEventFunc) balancer_handler, NULL))) { + TSError("[%s] TSContCreate(): failed to create the transaction handler continuation.", PLUGIN_NAME); + balancer->release(); + TSfree(targetstatus); + } else { + TSContDataSet(txn_contp, targetstatus); + TSHttpTxnArgSet((TSHttpTxn)txn, arg_index, (void *) balancer); + TSHttpTxnHookAdd(txn, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, txn_contp); + TSHttpTxnHookAdd(txn, TS_HTTP_TXN_CLOSE_HOOK, txn_contp); + } + + return TSREMAP_DID_REMAP; } diff --git a/plugins/experimental/balancer/balancer.h b/plugins/experimental/balancer/balancer.h index b7bcd241de2..94af3b8d5eb 100644 --- a/plugins/experimental/balancer/balancer.h +++ b/plugins/experimental/balancer/balancer.h @@ -24,9 +24,14 @@ #ifndef BALANCER_H_29177589_32F1_4D93_AE4F_1E140EDCC273 #define BALANCER_H_29177589_32F1_4D93_AE4F_1E140EDCC273 +#include #include #include -#include +#include +#include +#include + +#define PLUGIN_NAME "balancer" // Return the length of a string literal. template @@ -36,18 +41,43 @@ lengthof(const char(&)[N]) return N - 1; } + struct BalancerTarget { + uint id; std::string name; - unsigned port; -}; + uint port; + + //add by daemon.xie + uint weight; //配置的权重 + int effective_weight; + int current_weight; //当前权重,ats 会在运行过程中调整次权重 -struct BalancerInstance { - virtual ~BalancerInstance() {} - virtual void push_target(const BalancerTarget &) = 0; - virtual const BalancerTarget &balance(TSHttpTxn, TSRemapRequestInfo *) = 0; + uint max_fails; //最大失败次数 + + time_t fail_timeout; //失败后,不再使用的时间 + uint down; //指定某个后端是否挂了 + uint backup; //是否为备份线路 + + uint fails; //已尝试失败次数 + uint timeout_fails;//当停用fail_timeout后,仍然是失败时+1,最大次数不能超过100 + time_t accessed; //检测失败时间,用于计算超时 + time_t checked; + + BalancerTarget():id(0),name(""),port(0),weight(1),effective_weight(1),current_weight(0),max_fails(10), + fail_timeout(30),down(0),backup(0),fails(0),timeout_fails(1),accessed(0),checked(0){ + } + + ~BalancerTarget() { + name = ""; + } }; -BalancerInstance *MakeHashBalancer(const char *); -BalancerInstance *MakeRoundRobinBalancer(const char *); +//用于存储target 状态,以备源站返回code 的做健康负载处理,new free +struct BalancerTargetStatus { + uint target_id; + uint target_down; + uint is_down_check; + int object_status; +}; #endif /* BALANCER_H_29177589_32F1_4D93_AE4F_1E140EDCC273 */ diff --git a/plugins/experimental/balancer/hash.cc b/plugins/experimental/balancer/hash.cc deleted file mode 100644 index 11734b6845c..00000000000 --- a/plugins/experimental/balancer/hash.cc +++ /dev/null @@ -1,226 +0,0 @@ -/** @file - * - * A brief file description - * - * @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 "balancer.h" -#include -#include -#include -#include -#include -#include -#include - -namespace -{ -size_t -sockaddrlen(const struct sockaddr *sa) -{ - switch (sa->sa_family) { - case AF_INET: - return sizeof(struct sockaddr_in); - case AF_INET6: - return sizeof(struct sockaddr_in6); - default: - TSReleaseAssert(0 && "unsupported socket type"); - } -} - -struct md5_key { - md5_key() {} - - md5_key(const BalancerTarget &target, unsigned i) - { - MD5_CTX ctx; - - MD5_Init(&ctx); - MD5_Update(&ctx, target.name.data(), target.name.size()); - MD5_Update(&ctx, &target.port, sizeof(target.port)); - MD5_Update(&ctx, &i, sizeof(i)); - MD5_Final(this->key, &ctx); - } - - bool operator<(const md5_key &rhs) const { return memcmp(this->key, rhs.key, sizeof(this->key)) < 0; } - - unsigned char key[MD5_DIGEST_LENGTH]; -}; - -typedef void (*HashComponent)(TSHttpTxn txn, TSRemapRequestInfo *, MD5_CTX *); - -// Hash on the source (client) IP address. -void -HashTxnSrcaddr(TSHttpTxn txn, TSRemapRequestInfo *, MD5_CTX *ctx) -{ - struct sockaddr const *sa; - - sa = TSHttpTxnClientAddrGet(txn); - if (sa) { - MD5_Update(ctx, sa, sockaddrlen(sa)); - TSDebug("balancer", "%s(addr[%zu]]", __func__, sockaddrlen(sa)); - } -} - -// Hash on the destination (server) IP address; -void -HashTxnDstaddr(TSHttpTxn txn, TSRemapRequestInfo *, MD5_CTX *ctx) -{ - struct sockaddr const *sa; - - sa = TSHttpTxnIncomingAddrGet(txn); - if (sa) { - MD5_Update(ctx, sa, sockaddrlen(sa)); - TSDebug("balancer", "%s(addr[%zu]]", __func__, sockaddrlen(sa)); - } -} - -// Hash on the request URL. -void -HashTxnUrl(TSHttpTxn txn, TSRemapRequestInfo *, MD5_CTX *ctx) -{ - char *url; - int len; - - url = TSHttpTxnEffectiveUrlStringGet(txn, &len); - if (url && len) { - MD5_Update(ctx, url, len); - TSDebug("balancer", "%s(%.*s)", __func__, len, url); - } - - TSfree(url); -} - -// Hash on the cache key. This is not typically set at remap time, unless by another plugin. -void -HashTxnKey(TSHttpTxn txn, TSRemapRequestInfo *rri, MD5_CTX *ctx) -{ - TSMLoc url = TS_NULL_MLOC; - char *str = NULL; - int len; - - if (TSUrlCreate(rri->requestBufp, &url) != TS_SUCCESS) { - goto done; - } - - if (TSHttpTxnCacheLookupUrlGet(txn, rri->requestBufp, url) != TS_SUCCESS) { - TSDebug("balancer", "no cache key"); - goto done; - } - - str = TSUrlStringGet(rri->requestBufp, url, &len); - if (str && len) { - TSDebug("balancer", "%s(%.*s)", __func__, len, str); - MD5_Update(ctx, str, len); - } - -done: - if (url != TS_NULL_MLOC) { - TSHandleMLocRelease(rri->requestBufp, TS_NULL_MLOC, url); - } - - TSfree(str); -} - -struct HashBalancer : public BalancerInstance { - typedef std::map hash_ring_type; - typedef std::vector hash_part_type; - - enum { - iterations = 10, - }; - - HashBalancer() { this->hash_parts.push_back(HashTxnUrl); } - - void - push_target(const BalancerTarget &target) - { - for (unsigned i = 0; i < iterations; ++i) { - this->hash_ring.insert(std::make_pair(md5_key(target, i), target)); - } - } - - const BalancerTarget & - balance(TSHttpTxn txn, TSRemapRequestInfo *rri) - { - md5_key key; - MD5_CTX ctx; - hash_ring_type::const_iterator loc; - - // We'd better have some hash functions set by now ... - TSReleaseAssert(!hash_parts.empty()); - - MD5_Init(&ctx); - - for (hash_part_type::const_iterator i = this->hash_parts.begin(); i != this->hash_parts.end(); ++i) { - (*i)(txn, rri, &ctx); - } - - MD5_Final(key.key, &ctx); - - // OK, now look up this hash in the hash ring. lower_bound() finds the first element that is not less than the - // target, so the element we find is the first key that is greater than our target. To visualize this in the - // hash ring, that means that each node owns the preceeding keyspace (ie. the node is at the end of each keyspace - // range). This means that when we wrap, the first node owns the wrapping portion of the keyspace. - loc = this->hash_ring.lower_bound(key); - if (loc == this->hash_ring.end()) { - loc = this->hash_ring.begin(); - } - - return loc->second; - } - - hash_ring_type hash_ring; - hash_part_type hash_parts; -}; - -} // namespace - -BalancerInstance * -MakeHashBalancer(const char *options) -{ - HashBalancer *hash = new HashBalancer(); - char *opt; - char *tmp; - - TSDebug("balancer", "making hash balancer with options '%s'", options); - - if (options) { - hash->hash_parts.clear(); // clear the default hash type if we have options - options = tmp = strdup(options); - while ((opt = strsep(&tmp, ",")) != NULL) { - if (strcmp(opt, "key") == 0) { - hash->hash_parts.push_back(HashTxnKey); - } else if (strcmp(opt, "url") == 0) { - hash->hash_parts.push_back(HashTxnUrl); - } else if (strcmp(opt, "srcaddr") == 0) { - hash->hash_parts.push_back(HashTxnSrcaddr); - } else if (strcmp(opt, "dstaddr") == 0) { - hash->hash_parts.push_back(HashTxnDstaddr); - } else { - TSError("balancer: ignoring invalid hash field '%s'", opt); - } - } - - free((void *)options); - } - - return hash; -} diff --git a/plugins/experimental/balancer/roundrobin.cc b/plugins/experimental/balancer/roundrobin.cc index ab9092101d4..3987653027e 100644 --- a/plugins/experimental/balancer/roundrobin.cc +++ b/plugins/experimental/balancer/roundrobin.cc @@ -21,53 +21,370 @@ * limitations under the License. */ -#include "balancer.h" -#include -#include -#include -#include -#include - -namespace -{ -struct RoundRobinBalancer : public BalancerInstance { - RoundRobinBalancer() : targets(), next(0) {} - - void - push_target(const BalancerTarget &target) - { - this->targets.push_back(target); - } - - const BalancerTarget & - balance(TSHttpTxn, TSRemapRequestInfo *) - { - return this->targets[++next % this->targets.size()]; - } - - std::vector targets; - unsigned next; -}; - -} // namespace - -BalancerInstance * -MakeRoundRobinBalancer(const char *options) -{ - RoundRobinBalancer *hash = new RoundRobinBalancer(); - char *opt; - char *tmp; - - TSDebug("balancer", "making round robin balancer with options '%s'", options); - - if (options) { - options = tmp = strdup(options); - while ((opt = strsep(&tmp, ",")) != NULL) { - TSError("balancer: ignoring invalid round robin field '%s'", opt); - } - - free((void *)options); - } - - return hash; +#include "roundrobin.h" + +RoundRobinBalancer::RoundRobinBalancer() : + targets_s(), targets_b(), _ref_count(0) { + this->next = 0; + this->peersS_number = 0; + this->peersB_number = 0; + this->path = NULL; + this->need_https_backend = false; +} + +RoundRobinBalancer::~RoundRobinBalancer() { + if (this->path != NULL) { + free((char *) this->path); + this->path = NULL; + } + uint i; + size_t t_len; + + t_len = targets_s.size(); + for (i = 0; i < t_len; i++) { + if (targets_s[i]) { + delete (targets_s[i]); + } + } + + t_len = targets_b.size(); + for (i = 0; i < t_len; i++) { + if (targets_b[i]) { + delete (targets_b[i]); + } + } + TSDebug(PLUGIN_NAME, "----------~RoundRobinBalancer---------------"); +} + +void RoundRobinBalancer::push_target(BalancerTarget *target) { + if (target->backup) { + this->targets_b.push_back(target); + this->peersB_number++; + } else { + this->targets_s.push_back(target); + this->peersS_number++; + } +} + +//获取一个后端 +BalancerTarget * RoundRobinBalancer::balance(TSHttpTxn, TSRemapRequestInfo *) { + BalancerTarget *peer; + time_t now; + now = TShrtime() / TS_HRTIME_SECOND; + + peer = get_down_timeout_peer(now); + + if (peer != NULL) { +// TSDebug(PLUGIN_NAME,"down timeout target is not NULL ! target id-> %d now-> %ld checked-> %ld down-> %d ", +// peer->id, now, peer->checked, peer->down); + return peer; + } + + if (this->peersS_number == OS_SINGLE) { + if (this->targets_s[0]->down) { + goto failed; + } + return this->targets_s[0]; + } else { +// TSDebug(PLUGIN_NAME, "go get_healthy_peer main targets !"); + peer = get_healthy_peer(targets_s, now); + if (peer == NULL) { + goto failed; + } + return peer; + } + + failed: if (!targets_b.empty()) { +// TSDebug(PLUGIN_NAME, "backup targets is not NULL !"); + if (peersB_number == OS_SINGLE) { + if (targets_b[0]->down) { + goto clear_fails; + } + return targets_b[0]; + } else { +// TSDebug(PLUGIN_NAME, "go get_healthy_peer backup targets !"); + peer = get_healthy_peer(targets_b, now); + if (peer == NULL) { + goto clear_fails; + } + return peer; + } + } + + clear_fails: clean_peer_status(); + //当所有服务都down的时候,进入轮询模式,(主备都需要轮询,尽快找出健康的os) + //该状态下的target 都不会回源(除了hit_stale) + ++next; + next = (next == UINT64_MAX ? 0 : next); + if (peersB_number && (next % 2)) { //主备选择 + return this->targets_b[next % this->targets_b.size()]; + } + + //防止主不存在 + if (this->peersS_number) + return this->targets_s[next % this->targets_s.size()]; + else + return this->targets_b[next % this->targets_b.size()]; +} + +//清除peer 的fails 和 timeout_fails状态 +void RoundRobinBalancer::clean_peer_status() { + uint i; + size_t t_len; + + t_len = targets_s.size(); + for (i = 0; i < t_len; i++) { + targets_s[i]->fails = 0; + targets_s[i]->timeout_fails = 1; + } + + t_len = targets_b.size(); + for (i = 0; i < t_len; i++) { + targets_b[i]->fails = 0; + targets_b[i]->timeout_fails = 1; + } +} + +//首先给down状态下的服务器一次机会 now - check >= fail_timeout * timeout_fails +//如果主还有存活的,就不用考虑down状态下冷却超时的备用,只有当主都不存活,才考虑 +BalancerTarget * RoundRobinBalancer::get_down_timeout_peer(time_t now) { + uint i; + size_t t_len; + BalancerTarget *check_peer; + check_peer = NULL; + + t_len = targets_s.size(); + for (i = 0; i < t_len; i++) { + if (targets_s[i]->down + && (now - targets_s[i]->checked) > (targets_s[i]->timeout_fails* targets_s[i]->fail_timeout)) { + targets_s[i]->checked = now; + return targets_s[i]; + } + } + + t_len = targets_b.size(); + for (i = 0; i < t_len; i++) { + if (targets_b[i]->down + && (now - targets_b[i]->checked) > (targets_b[i]->timeout_fails * targets_b[i]->fail_timeout)) { + targets_b[i]->checked = now; + return targets_b[i]; + } + } + + return check_peer; +} + +//获取最优的target 此处参考nginx rr 算法 +BalancerTarget * RoundRobinBalancer::get_healthy_peer( + std::vector &targets, time_t now) { + BalancerTarget *best; + int total; + uint i; + + best = NULL; + total = 0; + + size_t t_len = targets.size(); + + for (i = 0; i < t_len; i++) { + + if (targets[i]->down) { + continue; + } + //如果在fail_timeout内 失败次数fails >= max_fails 不可取 + if (targets[i]->max_fails && targets[i]->fails >= targets[i]->max_fails + && now - targets[i]->checked <= targets[i]->fail_timeout) { + continue; + } + + targets[i]->current_weight += targets[i]->effective_weight; + total += targets[i]->effective_weight; + + if (targets[i]->effective_weight < int(targets[i]->weight)) { + targets[i]->effective_weight++; + } + + if (best == NULL + || targets[i]->current_weight > best->current_weight) { + best = targets[i]; + } + } + + if (best == NULL) { + return NULL; + } + + best->current_weight -= total; + + if (now - best->checked > best->fail_timeout) { + best->checked = now; + } + + return best; +} + +//更改后端状态,后端返回5xx,就认为失败 +TSReturnCode RoundRobinBalancer::os_response_back_status(uint target_id, + TSHttpStatus status) { +// TSDebug(PLUGIN_NAME," os_response_back_status => target_id -> %d, status -> %d ",target_id, status); + BalancerTarget *peer; + size_t t_len; + uint i; + time_t now; + + peer = NULL; + t_len = 0; + + if (!targets_s.empty()) + t_len = targets_s.size(); + for (i = 0; i < t_len; i++) { + if (targets_s[i]->id == target_id) { + peer = targets_s[i]; + break; + } + } + if (peer == NULL && !targets_b.empty()) { + t_len = targets_b.size(); + for (i = 0; i < t_len; i++) { + if (targets_b[i]->id == target_id) { + peer = targets_b[i]; + break; + } + } + } + + if (peer == NULL) + return TS_SUCCESS; + +// TSDebug(PLUGIN_NAME, "os_response_back_status check time %ld accessed time %ld! ", peer->checked, peer->accessed); + + if (status >= FAIL_STATUS) { + now = TShrtime() / TS_HRTIME_SECOND; + peer->checked = now; + peer->accessed = now; + if (peer->down) { + peer->timeout_fails++; + peer->timeout_fails = + peer->timeout_fails > MAX_FAIL_TIME ? + MAX_FAIL_TIME : peer->timeout_fails; +// TSDebug(PLUGIN_NAME, " os_response_back_status target id-> %d is down again timeout_fails-> %d ", +// peer->id, peer->timeout_fails); + + } else { + peer->fails++; + if (peer->max_fails) { + peer->effective_weight -= peer->weight / peer->max_fails; + } + + if (peer->fails >= peer->max_fails) { + peer->down = 1; + peer->timeout_fails = 1; +// TSDebug(PLUGIN_NAME, " os_response_back_status target id-> %d is down ", peer->id); + } + } + + if (peer->effective_weight < 0) { + peer->effective_weight = 0; + } + + } else { + + if (peer->accessed < peer->checked) { + peer->fails = 0; + } + + //如果有一次探测正常,就将timeout_fail--, 直到为1,则将该后端服务down状态去掉,后续可以优化一下 + if (peer->down) { //可以不用防止并发的情况 + if (peer->timeout_fails <= 1) { + peer->down = 0; + peer->timeout_fails = 1; + peer->fails = 0; + peer->effective_weight = peer->weight; + peer->current_weight = 0; + peer->accessed = 0; + peer->checked = 0; + } else { + //当服务器状态从坏到好的时候,下降的基数稍微大点 + now = TShrtime() / TS_HRTIME_SECOND; + peer->timeout_fails = peer->timeout_fails / 2; + peer->timeout_fails = + peer->timeout_fails ? peer->timeout_fails : 1; + peer->checked = now; + peer->accessed = now; //因为peer 状态还是down ,所以这里accessed 还需要赋值 + } +// TSDebug(PLUGIN_NAME, " os_response_back_status target is down but return is OK, target->id %d", peer->id); + } + + } + peer = NULL; + return TS_SUCCESS; +} + +BalancerTarget *RoundRobinBalancer::MakeBalancerTarget(const char *strval) { + BalancerTarget *target = new BalancerTarget(); + + union { + struct sockaddr_storage storage; + struct sockaddr sa; + } address; + + memset(&address, 0, sizeof(address)); + + // First, check whether we have an address literal. + const char *is_address_literal = strrchr(strval, ','); + if ( NULL == is_address_literal && ats_ip_pton(strval, &address.sa) == 0) { + char namebuf[INET6_ADDRSTRLEN]; + + target->port = ats_ip_port_host_order(&address.sa); + target->name = ats_ip_ntop(&address.sa, namebuf, sizeof(namebuf)); + + } else { + //格式ip:port,是否为备用线路,权重,最大失败次数,禁用时间 + // 192.168.8.7:80,0,1,10,20 如果只有ip 后面几个参数都是默认值 + int target_array[4] = { 0, 1, 10, 20 }; + uint a_count = sizeof(target_array) / sizeof(target_array[0]); + uint s_count = 0; + const char *comma = strrchr(strval, ':'); + if (comma) { + target->name = std::string(strval, (comma - strval)); + target->port = strtol(comma + 1, NULL, 10); + + comma = strchr(comma + 1, ','); + while ( NULL != comma && s_count <= a_count) { + target_array[s_count] = strtol(comma + 1, NULL, 10); + s_count += 1; + comma = strchr(comma + 1, ','); + } + } else { + comma = strchr(strval, ','); + if (comma) { + target->name = std::string(strval, (comma - strval)); + while ( NULL != comma && s_count <= a_count) { + target_array[s_count] = strtol(comma + 1, NULL, 10); + s_count += 1; + comma = strchr(comma + 1, ','); + } + } else { + target->name = strval; + } + } + target->backup = target_array[0]; + target->weight = target_array[1]; + target->max_fails = target_array[2]; + target->fail_timeout = target_array[3]; + } + + if (target->port > INT16_MAX) { + TSError("[%s] Ignoring invalid port number for target '%s'",PLUGIN_NAME,strval); + target->port = 0; + } + + TSDebug(PLUGIN_NAME, + "balancer target -> %s target->name -> %s target->port -> %d target->backup ->%d target->weight -> %d target->max_fails ->%d target->fail_timeout -> %ld", + strval, target->name.c_str(), target->port, target->backup, + target->weight, target->max_fails, target->fail_timeout); + + return target; } + + diff --git a/plugins/experimental/balancer/roundrobin.h b/plugins/experimental/balancer/roundrobin.h new file mode 100644 index 00000000000..563d3f9e2a5 --- /dev/null +++ b/plugins/experimental/balancer/roundrobin.h @@ -0,0 +1,93 @@ +/* + * roundrobin.h + * + * Created on: 2016年5月6日 + * Author: xie + */ + +#ifndef PLUGINS_EXPERIMENTAL_BALANCER_ROUNDROBIN_H_ +#define PLUGINS_EXPERIMENTAL_BALANCER_ROUNDROBIN_H_ + +#include +#include +#include +#include +#include +#include "balancer.h" + + + +#define MAX_FAIL_TIME 30 +#define FAIL_STATUS 500 +#define OS_SINGLE 1 + +class RoundRobinBalancer { + +public: + RoundRobinBalancer(); + ~RoundRobinBalancer(); + + void hold() { + ink_atomic_increment(&_ref_count, 1); +// TSDebug(PLUGIN_NAME,"----------hold _ref_count---------------%d",_ref_count); + } + + void release() { + if (1 >= ink_atomic_decrement(&_ref_count, 1)) { +// TSDebug(PLUGIN_NAME,"----------release _ref_count---------------%d",_ref_count); + delete this; + } + + } + + void push_target(BalancerTarget *target); + + //获取一个后端 + BalancerTarget *balance(TSHttpTxn, TSRemapRequestInfo *); + + //清除peer 的fails 和 timeout_fails状态 + void clean_peer_status(); + + //首先给down状态下的服务器一次机会 now - check >= fail_timeout * timeout_fails + //如果主还有存活的,就不用考虑down状态下冷却超时的备用,只有当主都不存活,才考虑 + BalancerTarget *get_down_timeout_peer(time_t now); + + //获取最优的target 此处参考nginx rr 算法 + BalancerTarget *get_healthy_peer(std::vector &targets, time_t now); + + //更改后端状态,后端返回5xx,就认为失败 + TSReturnCode os_response_back_status(uint target_id, TSHttpStatus status); + + BalancerTarget * MakeBalancerTarget(const char *strval); + + void set_path(char *path) { + this->path = path; + } + + char *get_path() const { + return this->path; + } + + void set_https_backend_tag(bool is_need) { + this->need_https_backend = is_need; + } + + bool get_https_backend_tag() { + return this->need_https_backend; + } + +private: + std::vector targets_s; //主线路 + + std::vector targets_b; //备用线路 + uint peersS_number; + uint peersB_number; + unsigned next; + char *path; + bool need_https_backend; + volatile int _ref_count; +}; + + + +#endif /* PLUGINS_EXPERIMENTAL_BALANCER_ROUNDROBIN_H_ */ diff --git a/plugins/experimental/regex_revalidate/regex_revalidate.c b/plugins/experimental/regex_revalidate/regex_revalidate.c index f25c36f60d5..03c8d2a8edd 100644 --- a/plugins/experimental/regex_revalidate/regex_revalidate.c +++ b/plugins/experimental/regex_revalidate/regex_revalidate.c @@ -392,6 +392,8 @@ main_handler(TSCont cont, TSEvent event, void *edata) time_t date = 0, now = 0; char *url = NULL; int url_len = 0; + TSMBuffer buf; + TSMLoc url_loc; switch (event) { case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE: @@ -405,8 +407,11 @@ main_handler(TSCont cont, TSEvent event, void *edata) now = time(NULL); } if ((difftime(iptr->epoch, date) >= 0) && (difftime(iptr->expiry, now) >= 0)) { - if (!url) - url = TSHttpTxnEffectiveUrlStringGet(txn, &url_len); + if (!url && (TSHttpTxnPristineUrlGet(txn,&buf,&url_loc) == TS_SUCCESS)) { + url = TSUrlStringGet(buf,url_loc,&url_len); + TSHandleMLocRelease(buf, TS_NULL_MLOC, url_loc); + } + 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; diff --git a/plugins/header_rewrite/expander.cc b/plugins/header_rewrite/expander.cc index 5d867e3622c..8c33a2c404d 100644 --- a/plugins/header_rewrite/expander.cc +++ b/plugins/header_rewrite/expander.cc @@ -66,6 +66,7 @@ VariableExpander::expand(const Resources &res) if (TSHttpTxnPristineUrlGet(res.txnp, &bufp, &url_loc) == TS_SUCCESS) { int len; resolved_variable = TSUrlSchemeGet(bufp, url_loc, &len); + TSHandleMLocRelease(bufp, TS_NULL_MLOC, url_loc); } } else if (variable == "%") { // Original port of the incoming request diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc index ca85a24a097..4515b58643b 100644 --- a/plugins/header_rewrite/header_rewrite.cc +++ b/plugins/header_rewrite/header_rewrite.cc @@ -20,6 +20,7 @@ #include "ts/ts.h" #include "ts/remap.h" +#include "ts/ink_atomic.h" #include "parser.h" #include "ruleset.h" @@ -38,7 +39,7 @@ static int cont_rewrite_headers(TSCont, TSEvent, void *); class RulesConfig { public: - RulesConfig() + RulesConfig() : _ref_count(0) { memset(_rules, 0, sizeof(_rules)); memset(_resids, 0, sizeof(_resids)); @@ -47,15 +48,6 @@ class RulesConfig TSContDataSet(_cont, static_cast(this)); } - ~RulesConfig() - { - for (int i = TS_HTTP_READ_REQUEST_HDR_HOOK; i < TS_HTTP_LAST_HOOK; ++i) { - delete _rules[i]; - } - - TSContDestroy(_cont); - } - TSCont continuation() const { @@ -75,10 +67,31 @@ class RulesConfig bool parse_config(const std::string fname, TSHttpHookID default_hook); + void + hold() + { + ink_atomic_increment(&_ref_count, 1); + } + void + release() + { + if (1 >= ink_atomic_decrement(&_ref_count, 1)) + delete this; + } + private: + ~RulesConfig() + { + for (int i = TS_HTTP_READ_REQUEST_HDR_HOOK; i < TS_HTTP_LAST_HOOK; ++i) { + delete _rules[i]; + } + TSContDestroy(_cont); + } + bool add_rule(RuleSet *rule); TSCont _cont; + volatile int _ref_count; RuleSet *_rules[TS_HTTP_LAST_HOOK + 1]; ResourceIDs _resids[TS_HTTP_LAST_HOOK + 1]; }; @@ -236,6 +249,9 @@ cont_rewrite_headers(TSCont contp, TSEvent event, void *edata) case TS_EVENT_HTTP_SEND_RESPONSE_HDR: hook = TS_HTTP_SEND_RESPONSE_HDR_HOOK; break; + case TS_EVENT_HTTP_TXN_CLOSE: + conf->release(); + break; default: TSError("%s: unknown event for this plugin", PLUGIN_NAME); TSDebug(PLUGIN_NAME, "unknown event for this plugin"); @@ -288,6 +304,8 @@ TSPluginInit(int argc, const char *argv[]) RulesConfig *conf = new RulesConfig; bool got_config = false; + conf->hold(); + for (int i = 1; i < argc; ++i) { // Parse the config file(s). Note that multiple config files are // just appended to the configurations. @@ -313,7 +331,7 @@ TSPluginInit(int argc, const char *argv[]) } else { // Didn't get anything, nuke it. TSError("%s: failed to parse configuration file", PLUGIN_NAME); - delete conf; + conf->release(); } } @@ -357,6 +375,8 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE RulesConfig *conf = new RulesConfig; + conf->hold(); + for (int i = 2; i < argc; ++i) { TSDebug(PLUGIN_NAME, "Loading remap configuration file %s", argv[i]); if (!conf->parse_config(argv[i], TS_REMAP_PSEUDO_HOOK)) { @@ -384,9 +404,7 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE void TSRemapDeleteInstance(void *ih) { - RulesConfig *conf = static_cast(ih); - - delete conf; + static_cast(ih)->release(); } @@ -396,6 +414,8 @@ TSRemapDeleteInstance(void *ih) TSRemapStatus TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) { + bool hooked_p = false; + // Make sure things are properly setup (this should never happen) if (NULL == ih) { TSDebug(PLUGIN_NAME, "No Rules configured, falling back to default"); @@ -408,11 +428,18 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) // Go through all hooks we support, and setup the txn hook(s) as necessary for (int i = TS_HTTP_READ_REQUEST_HDR_HOOK; i < TS_HTTP_LAST_HOOK; ++i) { if (conf->rule(i)) { + hooked_p = true; TSHttpTxnHookAdd(rh, static_cast(i), conf->continuation()); TSDebug(PLUGIN_NAME, "Added remapped TXN hook=%s", TSHttpHookNameLookup((TSHttpHookID)i)); } } + // Two assumptions - configuration never uses this hook nor uses TS_HTTP_SSN_CLOSE_HOOK. + if (hooked_p) { + conf->hold(); // mark as in use. + TSHttpTxnHookAdd(rh, TS_HTTP_TXN_CLOSE_HOOK, conf->continuation()); // clean up after. + } + // Now handle the remap specific rules for the "remap hook" (which is not a real hook). // This is sufficiently differen than the normal cont_rewrite_headers() callback, and // we can't (shouldn't) schedule this as a TXN hook.