On Apr 10, 2014, at 5:54 AM, zw...@apache.org wrote:

> 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/master
> Commit: 4b6c8e022de1b20371c334eec77d2ebd7c733ee2
> Parents: a7b7d5f
> Author: Leif Hedstrom <zw...@apache.org>
> Authored: Wed Apr 9 13:29:23 2014 -0600
> Committer: Leif Hedstrom <zw...@apache.org>
> 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;

It would be simpler to map RetryInfo by value so you don't need to manually 
destroy it:
        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)) {

You should copy the argument before whacking it with strtok.

> +      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) {

You don't need the NULL check here.

> -    ats_free((void*)sm->redirect_url);
> +    ats_free(sm->redirect_url);
>     sm->redirect_url = NULL;
>     sm->redirect_url_len = 0;
>   }
> 

Reply via email to