TS-2690 Make the escalate plugin support URL and Host redirects TS-2690 Support multiple status codes for each rule, e.g. 403,404:
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/4b6c8e02 Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/4b6c8e02 Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/4b6c8e02 Branch: refs/heads/5.0.x Commit: 4b6c8e022de1b20371c334eec77d2ebd7c733ee2 Parents: a7b7d5f Author: Leif Hedstrom <[email protected]> Authored: Wed Apr 9 13:29:23 2014 -0600 Committer: Leif Hedstrom <[email protected]> Committed: Thu Apr 10 06:50:21 2014 -0600 ---------------------------------------------------------------------- plugins/experimental/escalate/escalate.cc | 172 +++++++++++++++---------- proxy/InkAPI.cc | 2 +- 2 files changed, 108 insertions(+), 66 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/4b6c8e02/plugins/experimental/escalate/escalate.cc ---------------------------------------------------------------------- diff --git a/plugins/experimental/escalate/escalate.cc b/plugins/experimental/escalate/escalate.cc index 2f71041..6768a70 100644 --- a/plugins/experimental/escalate/escalate.cc +++ b/plugins/experimental/escalate/escalate.cc @@ -1,6 +1,6 @@ /** @file - Escalation plugin. + This plugin allows retrying requests against different destinations. @section license License @@ -20,7 +20,6 @@ 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> @@ -32,94 +31,123 @@ #include <map> -// Constants +// Constants and some declarations const char PLUGIN_NAME[] = "escalate"; - - static int EscalateResponse(TSCont, TSEvent, void *); + +////////////////////////////////////////////////////////////////////////////////////////// +// Hold information about the escalation / retry states for a remap rule. +// struct EscalationState { - typedef std::map<unsigned, std::string> hostmap_type; + enum RetryType { + RETRY_URL, + RETRY_HOST + }; + + struct RetryInfo + { + RetryType type; + std::string target; + }; + + typedef std::map<unsigned, RetryInfo*> StatusMapType; EscalationState() { - handler = TSContCreate(EscalateResponse, NULL); - TSContDataSet(handler, this); + cont = TSContCreate(EscalateResponse, NULL); + TSContDataSet(cont, this); } ~EscalationState() { - TSContDestroy(handler); + for (StatusMapType::iterator iter = status_map.begin(); iter != status_map.end(); ++iter) { + delete(iter->second); + } + TSContDestroy(cont); } - TSCont handler; - hostmap_type hostmap; + TSCont cont; + StatusMapType status_map; }; +////////////////////////////////////////////////////////////////////////////////////////// +// Main continuation for the plugin, examining an origin response for a potential retry. +// static int EscalateResponse(TSCont cont, TSEvent event, void* edata) { + TSHttpTxn txn = (TSHttpTxn)edata; EscalationState* es = static_cast<EscalationState*>(TSContDataGet(cont)); - TSHttpTxn txn = (TSHttpTxn)edata; - TSMBuffer response; - TSMLoc resp_hdr; + EscalationState::StatusMapType::iterator entry; + TSMBuffer mbuf; + TSMLoc hdrp, url; + TSHttpStatus status; + char* url_str = NULL; + int url_len, tries; - TSReleaseAssert(event == TS_EVENT_HTTP_READ_RESPONSE_HDR); + TSAssert(event == TS_EVENT_HTTP_READ_RESPONSE_HDR); // First, we need the server response ... - if (TS_SUCCESS == TSHttpTxnServerRespGet(txn, &response, &resp_hdr)) { - int tries = TSHttpTxnRedirectRetries(txn); - - TSDebug(PLUGIN_NAME, "This is try %d", tries); - if (0 == tries) { // ToDo: Future support for more than one retry-URL - // Next, the response status ... - TSHttpStatus status = TSHttpHdrStatusGet(response, resp_hdr); - - // If we have an escalation URL for this response code, set the redirection URL and force it - // to be followed. - EscalationState::hostmap_type::iterator entry = es->hostmap.find((unsigned)status); - - if (entry != es->hostmap.end()) { - TSMBuffer request; - TSMLoc req_hdr; - - TSDebug(PLUGIN_NAME, "Found an entry for HTTP status %u", (unsigned)status); - if (TS_SUCCESS == TSHttpTxnClientReqGet(txn, &request, &req_hdr)) { - TSMLoc url; - - if (TS_SUCCESS == TSHttpHdrUrlGet(request, req_hdr, &url)) { - char* url_str; - int url_len; - - // Update the request URL with the new Host to try. - TSUrlHostSet(request, url, entry->second.c_str(), entry->second.size()); - url_str = TSUrlStringGet(request, url, &url_len); - - TSDebug(PLUGIN_NAME, "Setting new URL to %.*s", url_len, url_str); - TSHttpTxnRedirectUrlSet(txn, url_str, url_len); // Transfers ownership - } - // Release the request MLoc - TSHandleMLocRelease(request, TS_NULL_MLOC, req_hdr); - } + if (TS_SUCCESS != TSHttpTxnServerRespGet(txn, &mbuf, &hdrp)) { + goto no_action; + } + + tries = TSHttpTxnRedirectRetries(txn); + if (0 != tries) { // ToDo: Future support for more than one retry-URL + goto no_action; + } + TSDebug(PLUGIN_NAME, "This is try %d, proceeding", tries); + + // Next, the response status ... + status = TSHttpHdrStatusGet(mbuf, hdrp); + TSHandleMLocRelease(mbuf, TS_NULL_MLOC, hdrp); // Don't need this any more + + // See if we have an escalation retry config for this response code + entry = es->status_map.find((unsigned)status); + if (entry == es->status_map.end()) { + goto no_action; + } + + TSDebug(PLUGIN_NAME, "Found an entry for HTTP status %u", (unsigned)status); + if (EscalationState::RETRY_URL == entry->second->type) { + url_str = TSstrdup(entry->second->target.c_str()); + url_len = entry->second->target.size(); + TSDebug(PLUGIN_NAME, "Setting new URL to %.*s", url_len, url_str); + } else if (EscalationState::RETRY_HOST == entry->second->type) { + if (TS_SUCCESS == TSHttpTxnClientReqGet(txn, &mbuf, &hdrp)) { + if (TS_SUCCESS == TSHttpHdrUrlGet(mbuf, hdrp, &url)) { + // Update the request URL with the new Host to try. + TSUrlHostSet(mbuf, url, entry->second->target.c_str(), entry->second->target.size()); + url_str = TSUrlStringGet(mbuf, url, &url_len); + TSDebug(PLUGIN_NAME, "Setting new Host: to %.*s", url_len, url_str); } + // Release the request MLoc + TSHandleMLocRelease(mbuf, TS_NULL_MLOC, hdrp); } - // Release the response MLoc - TSHandleMLocRelease(response, TS_NULL_MLOC, resp_hdr); + } + + // Now update the Redirect URL, if set + if (url_str) { + TSHttpTxnRedirectUrlSet(txn, url_str, url_len); // Transfers ownership } // Set the transaction free ... + no_action: 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) { @@ -128,29 +156,41 @@ TSRemapNewInstance(int argc, char* argv[], void** instance, char* errbuf, int er // 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; - char* sep; + char *sep, *token, *save; // 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]); + snprintf(errbuf, errbuf_size, "malformed status:target config: %s", argv[i]); goto fail; } *sep = '\0'; - status = strtol(argv[i], NULL, 10); + ++sep; // Skip over the ':' (which is now \0) - if (status < 100 || status > 599) { - snprintf(errbuf, errbuf_size, "invalid status code: %.*s", (int)std::distance(argv[i], sep), argv[i]); - goto fail; + // OK, we have a valid status/URL pair. + EscalationState::RetryInfo* info = new EscalationState::RetryInfo(); + + info->target = sep; + if (std::string::npos != info->target.find('/')) { + info->type = EscalationState::RETRY_URL; + TSDebug(PLUGIN_NAME, "Creating Redirect rule with URL = %s", sep); + } else { + info->type = EscalationState::RETRY_HOST; + TSDebug(PLUGIN_NAME, "Creating Redirect rule with Host = %s", sep); } - ++sep; // Skip over the ':' + for (token = strtok_r(argv[i], ",", &save); token; token = strtok_r(NULL, ",", &save)) { + unsigned status = strtol(token, NULL, 10); - // OK, we have a valid status/URL pair. - TSDebug(PLUGIN_NAME, "Redirect of HTTP status %u to %s", status, sep); - es->hostmap[status] = sep; + if (status < 100 || status > 599) { + snprintf(errbuf, errbuf_size, "invalid status code: %.*s", (int)std::distance(argv[i], sep), argv[i]); + goto fail; + } + + TSDebug(PLUGIN_NAME, " added status = %d to rule", status); + es->status_map[status] = info; + } } *instance = es; @@ -161,17 +201,19 @@ fail: return TS_ERROR; } + void TSRemapDeleteInstance(void* instance) { - delete (EscalationState *)instance; + delete static_cast<EscalationState*>(instance); } + TSRemapStatus TSRemapDoRemap(void* instance, TSHttpTxn txn, TSRemapRequestInfo* /* rri */) { - EscalationState* es = static_cast<EscalationState *>(instance); + EscalationState* es = static_cast<EscalationState*>(instance); - TSHttpTxnHookAdd(txn, TS_HTTP_READ_RESPONSE_HDR_HOOK, es->handler); + TSHttpTxnHookAdd(txn, TS_HTTP_READ_RESPONSE_HDR_HOOK, es->cont); return TSREMAP_NO_REMAP; } http://git-wip-us.apache.org/repos/asf/trafficserver/blob/4b6c8e02/proxy/InkAPI.cc ---------------------------------------------------------------------- diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc index cc4c4dd..2aa4cb5 100644 --- a/proxy/InkAPI.cc +++ b/proxy/InkAPI.cc @@ -7129,7 +7129,7 @@ TSHttpTxnRedirectUrlSet(TSHttpTxn txnp, const char* url, const int url_len) HttpSM *sm = (HttpSM*) txnp; if (sm->redirect_url != NULL) { - ats_free((void*)sm->redirect_url); + ats_free(sm->redirect_url); sm->redirect_url = NULL; sm->redirect_url_len = 0; }
