TS-2508: escalation plugin The escalation plugin is intended to allow Traffic Server to handle origin errors by rerouting to a different URL. This doesn't currently work, since the APIs it uses cause crashes. In it's current form, it should be useful as a test case to work on fixing the APIs.
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/9fda6972 Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/9fda6972 Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/9fda6972 Branch: refs/heads/5.0.x Commit: 9fda6972b6c36a8075e7f780521a589b19c22aff Parents: 6af333d Author: James Peach <[email protected]> Authored: Fri Jan 10 10:48:37 2014 -0800 Committer: James Peach <[email protected]> Committed: Fri Jan 17 11:51:05 2014 -0800 ---------------------------------------------------------------------- CHANGES | 2 + configure.ac | 1 + plugins/experimental/Makefile.am | 1 + plugins/experimental/escalate/Makefile.am | 22 ++++ plugins/experimental/escalate/escalate.cc | 166 +++++++++++++++++++++++++ 5 files changed, 192 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9fda6972/CHANGES ---------------------------------------------------------------------- diff --git a/CHANGES b/CHANGES index a0d6ffd..be6efa7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -*- coding: utf-8 -*- Changes with Apache Traffic Server 4.2.0 + *) [TS-2508] Add a *highly* experimental escalation plugin. + *) [TS-2507] Fix the state transition logging in HttpSM::handle_server_setup_error. *) [TS-1648] Segmentation fault in dir_clear_range() http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9fda6972/configure.ac ---------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index ebffa6f..a71f578 100644 --- a/configure.ac +++ b/configure.ac @@ -1940,6 +1940,7 @@ AC_CONFIG_FILES([ plugins/experimental/buffer_upload/Makefile plugins/experimental/channel_stats/Makefile plugins/experimental/custom_redirect/Makefile + plugins/experimental/escalate/Makefile plugins/experimental/esi/Makefile plugins/experimental/geoip_acl/Makefile plugins/experimental/lua/Makefile http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9fda6972/plugins/experimental/Makefile.am ---------------------------------------------------------------------- diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am index fae383d..d2d14ab 100644 --- a/plugins/experimental/Makefile.am +++ b/plugins/experimental/Makefile.am @@ -19,6 +19,7 @@ if BUILD_EXPERIMENTAL_PLUGINS SUBDIRS = \ authproxy \ balancer \ + escalate \ buffer_upload \ channel_stats \ custom_redirect \ http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9fda6972/plugins/experimental/escalate/Makefile.am ---------------------------------------------------------------------- diff --git a/plugins/experimental/escalate/Makefile.am b/plugins/experimental/escalate/Makefile.am new file mode 100644 index 0000000..1537847 --- /dev/null +++ b/plugins/experimental/escalate/Makefile.am @@ -0,0 +1,22 @@ +# 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 = escalate.la +escalate_la_SOURCES = escalate.cc +escalate_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS) + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9fda6972/plugins/experimental/escalate/escalate.cc ---------------------------------------------------------------------- diff --git a/plugins/experimental/escalate/escalate.cc b/plugins/experimental/escalate/escalate.cc new file mode 100644 index 0000000..7181111 --- /dev/null +++ b/plugins/experimental/escalate/escalate.cc @@ -0,0 +1,166 @@ +/** @file + + Escalation plugin. + + @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 <ts/ts.h> +#include <ts/remap.h> +#include <ts/experimental.h> +#include <stdio.h> +#include <getopt.h> +#include <string.h> +#include <string> +#include <sstream> +#include <iterator> +#include <map> + +struct EscalationState +{ + typedef std::map<unsigned, TSMLoc> urlmap_type; + + EscalationState() { + this->mbuf = TSMBufferCreate(); + } + + ~EscalationState() { + TSMBufferDestroy(this->mbuf); + } + + TSCont handler; + urlmap_type urlmap; + TSMBuffer mbuf; +}; + +static unsigned +toint(const std::string& str) +{ + std::istringstream istr(str); + unsigned val; + + istr >> val; + return val; +} + +static int +EscalateResponse(TSCont cont, TSEvent event, void * edata) +{ + EscalationState * es = (EscalationState *)TSContDataGet(cont); + TSHttpTxn txn = (TSHttpTxn)edata; + TSMBuffer buffer; + TSMLoc hdr; + TSHttpStatus status; + + TSDebug("escalate", "hit escalation hook with event %d", (int)event); + TSReleaseAssert(event == TS_EVENT_HTTP_READ_RESPONSE_HDR); + + // First, we need the server response ... + TSReleaseAssert( + TSHttpTxnServerRespGet(txn, &buffer, &hdr) == TS_SUCCESS + ); + + // Next, the respose status ... + status = TSHttpHdrStatusGet(buffer, hdr); + + // If we have an escalation URL for this response code, set the redirection URL and force it + // to be followed. + EscalationState::urlmap_type::iterator entry = es->urlmap.find((unsigned)status); + if (entry != es->urlmap.end()) { + TSDebug("escalate", "found an escalation entry for HTTP status %u", (unsigned)status); + TSHttpTxnRedirectRequest(txn, es->mbuf, entry->second); + TSHttpTxnFollowRedirect(txn, 1 /* on */); + } + + // Set the transaction free ... + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + return TS_EVENT_NONE; +} + +TSReturnCode +TSRemapInit(TSRemapInterface * /* api */, char * /* errbuf */, int /* bufsz */) +{ + return TS_SUCCESS; +} + +TSReturnCode +TSRemapNewInstance(int argc, char * argv[], void ** instance, char * errbuf, int errbuf_size) +{ + EscalationState * es((EscalationState *)instance); + + es = new EscalationState(); + es->handler = TSContCreate(EscalateResponse, NULL); + TSContDataSet(es->handler, es); + + // The first two arguments are the "from" and "to" URL string. We can just + // skip those, since we only ever remap on the error path. + for (int i = 2; i < argc; ++i) { + unsigned status; + TSMLoc url; + char * sep; + + // Each token should be a status code then a URL, separated by '='. + sep = strchr(argv[i], '='); + if (sep == NULL) { + snprintf(errbuf, errbuf_size, "missing status code: %s", argv[i]); + goto fail; + } + + status = toint(std::string(argv[i], std::distance(argv[i], sep))); + if (status < 100 || status > 599) { + snprintf(errbuf, errbuf_size, "invalid status code: %.*s", (int)std::distance(argv[i], sep), argv[i]); + goto fail; + } + + TSReleaseAssert(TSUrlCreate(es->mbuf, &url) == TS_SUCCESS); + + ++sep; // Skip over the '='. + + TSDebug("escalate", "escalating HTTP status %u to %s", status, sep); + if (TSUrlParse(es->mbuf, url, (const char **)&sep, argv[i] + strlen(argv[i])) != TS_PARSE_DONE) { + snprintf(errbuf, errbuf_size, "invalid target URL: %s", sep); + goto fail; + } + + // OK, we have a valid status/URL pair. + es->urlmap[status] = url; + } + + *instance = es; + return TS_SUCCESS; + +fail: + delete es; + return TS_ERROR; +} + +void +TSRemapDeleteInstance(void * instance) +{ + delete (EscalationState *)instance; +} + +TSRemapStatus +TSRemapDoRemap(void * instance, TSHttpTxn txn, TSRemapRequestInfo * /* rri */) +{ + EscalationState * es((EscalationState *)instance); + + TSHttpTxnHookAdd(txn, TS_HTTP_READ_RESPONSE_HDR_HOOK, es->handler); + return TSREMAP_NO_REMAP; +}
