You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2012/06/13 22:18:14 UTC

[9/11] Moved these plugins from the separate git repo

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/header_rewrite.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/header_rewrite.cc b/plugins/experimental/header_rewrite/header_rewrite.cc
new file mode 100644
index 0000000..aba03ef
--- /dev/null
+++ b/plugins/experimental/header_rewrite/header_rewrite.cc
@@ -0,0 +1,331 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// header_rewrite: YTS plugin to do header rewrites
+// --------------
+//
+//
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__header_rewrite_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <fstream>
+#include <string>
+#include <boost/algorithm/string.hpp>
+
+#include <ts/ts.h>
+#include <ts/remap.h>
+
+#include "parser.h"
+#include "ruleset.h"
+#include "resources.h"
+
+// "Defines"
+static const char* DEFAULT_CONF_PATH = "/usr/local/etc/header_rewrite/";
+
+
+// Global holding the rulesets and resource IDs
+static RuleSet* all_rules[TS_HTTP_LAST_HOOK+1];
+static ResourceIDs all_resids[TS_HTTP_LAST_HOOK+1];
+
+// Helper function to add a rule to the rulesets
+static bool
+add_rule(RuleSet* rule) {
+  if (rule && rule->has_operator()) {
+    TSDebug(PLUGIN_NAME, "Adding rule to hook=%d\n", rule->get_hook());
+    if (NULL == all_rules[rule->get_hook()]) {
+      all_rules[rule->get_hook()] = rule;
+    } else {
+      all_rules[rule->get_hook()]->append(rule);
+    }
+    return true;
+  }
+
+  return false;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Simple "config" parser, this modifies the global above. ToDo: What happens
+// on a "config" reload?
+//
+// Note that this isn't particularly efficient, but it's a startup time cost
+// anyways (or reload for remap.config), so not really in the critical path.
+//
+bool
+parse_config(const std::string fname, TSHttpHookID default_hook)
+{
+  RuleSet* rule = NULL;
+  std::string filename = fname;
+  std::ifstream f;
+  int lineno = 0;
+
+  // Try appending the default conf path if the fname doesn't exist.
+  if (0 != access(filename.c_str(), R_OK)) {
+    filename = DEFAULT_CONF_PATH;
+    filename += fname;
+  }
+
+  f.open(filename.c_str(), std::ios::in);
+  if (!f.is_open()) {
+    TSError("header_rewrite: unable to open %s", filename.c_str());
+    return false;
+  }
+
+  TSDebug(PLUGIN_NAME, "Loading header_rewrite config from %s", filename.c_str());
+
+  while (!f.eof()) {
+    std::string line;
+
+    getline(f, line);
+    ++lineno; // ToDo: we should probably use this for error messages ...
+
+    boost::trim(line);
+    if (line.empty() || (line[0] == '#'))
+      continue;
+
+    Parser p(line);  // Tokenize and parse this line
+    if (p.empty())
+      continue;
+
+    // If we are at the beginning of a new condition, save away the previous rule (but only if it has operators).
+    if (p.is_cond() && add_rule(rule))
+      rule = NULL;
+
+    if (NULL == rule) {
+      rule = new RuleSet();
+      rule->set_hook(default_hook);
+
+      // Special case for specifying the HOOK this rule applies to.
+      // These can only be at the beginning of a rule, and have an implicit [AND].
+      if (p.cond_op_is("READ_RESPONSE_HDR_HOOK")) {
+        rule->set_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+        continue;
+      } else if (p.cond_op_is("READ_REQUEST_HDR_HOOK")) {
+        rule->set_hook(TS_HTTP_READ_REQUEST_HDR_HOOK);
+        continue;
+      } else if (p.cond_op_is("READ_REQUEST_PRE_REMAP_HOOK")) {
+        rule->set_hook(TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK);
+        continue;
+      } else if (p.cond_op_is("SEND_REQUEST_HDR_HOOK")) {
+        rule->set_hook(TS_HTTP_SEND_REQUEST_HDR_HOOK);
+        continue;
+      } else if (p.cond_op_is("SEND_RESPONSE_HDR_HOOK")) {
+        rule->set_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+        continue;
+      }
+    }
+
+    if (p.is_cond()) {
+      rule->add_condition(p);
+    } else {
+      rule->add_operator(p);
+    }
+  }
+
+  // Add the last rule (possibly the only rule)
+  add_rule(rule);
+
+  // Collect all resource IDs that we need
+  for (int i=TS_HTTP_READ_REQUEST_HDR_HOOK; i<TS_HTTP_LAST_HOOK; ++i)
+    if (all_rules[i])
+      all_resids[i] = all_rules[i]->get_all_resource_ids();
+
+  return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Continuation
+//
+static int
+cont_rewrite_headers(TSCont contp, TSEvent event, void *edata)
+{
+  TSDebug(PLUGIN_NAME, "plugin: %d", event);
+
+  TSHttpTxn txnp = (TSHttpTxn) edata;
+  Resources res(txnp, contp);
+  const RuleSet* rule = NULL;
+  TSHttpHookID hook = TS_HTTP_LAST_HOOK;
+
+  // Get the resources necessary to process this event
+  switch (event) {
+  case TS_EVENT_HTTP_READ_RESPONSE_HDR:
+    hook = TS_HTTP_READ_RESPONSE_HDR_HOOK;
+    break;
+  case TS_EVENT_HTTP_READ_REQUEST_HDR:
+    hook = TS_HTTP_READ_REQUEST_HDR_HOOK;
+    break;
+  case TS_EVENT_HTTP_READ_REQUEST_PRE_REMAP:
+    hook = TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK;
+    break;
+  case TS_EVENT_HTTP_SEND_REQUEST_HDR:
+    hook = TS_HTTP_SEND_REQUEST_HDR_HOOK;
+    break;
+  case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
+    hook = TS_HTTP_SEND_RESPONSE_HDR_HOOK;
+    break;
+  default:
+    TSError("header_rewrite: unknown event for this plugin");
+    TSDebug(PLUGIN_NAME, "unknown event for this plugin");
+    break;
+  }
+
+  if (hook != TS_HTTP_LAST_HOOK) {
+    res.gather(all_resids[hook], hook);
+    rule = all_rules[hook];
+
+    // Evaluation
+    while (rule) {
+      if (rule->eval(res)) {
+        OperModifiers rt = rule->exec(res);
+
+        if (rule->last() || (rt & OPER_LAST)) {
+          break; // Conditional break, force a break with [L]
+        }
+      }
+      rule = rule->next;
+    }
+  }
+
+  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+  return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Initialize the InkAPI plugin for the global hooks we support.
+//
+void
+TSPluginInit(int argc, const char *argv[])
+{
+  TSPluginRegistrationInfo info;
+
+  info.plugin_name = (char*)PLUGIN_NAME;
+  info.vendor_name = (char*)"";
+  info.support_email = (char*)"";
+
+  if (TS_SUCCESS != TSPluginRegister(TS_SDK_VERSION_3_0 , &info)) {
+    TSError("header_rewrite: plugin registration failed.\n"); 
+  }
+
+  if (argc != 2) {
+    TSError("usage: %s <config-file>\n", argv[0] );
+  }
+
+  // Initialize the globals
+  for (int i=TS_HTTP_READ_REQUEST_HDR_HOOK; i<TS_HTTP_LAST_HOOK; ++i) {
+    all_rules[i] = NULL;
+    all_resids[i] = RSRC_NONE;
+  }
+
+  // Parse the config file
+  if (parse_config(argv[1], TS_HTTP_READ_RESPONSE_HDR_HOOK)) {
+    for (int i=TS_HTTP_READ_REQUEST_HDR_HOOK; i<TS_HTTP_LAST_HOOK; ++i) {
+      if (all_rules[i]) {
+        TSDebug(PLUGIN_NAME, "adding hook: %d", i);
+        TSHttpHookAdd(static_cast<TSHttpHookID>(i), TSContCreate(cont_rewrite_headers, NULL));
+      }
+    }
+  } else {
+    TSError("header_rewrite: failed to parse configuration file");
+  }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Initialize the plugin as a remap plugin.
+//
+TSReturnCode
+TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
+{
+  if (!api_info) {
+    strncpy(errbuf, "[TSRemapInit] - Invalid TSRemapInterface argument", errbuf_size - 1);
+    return TS_ERROR;
+  }
+
+  if (api_info->size < sizeof(TSRemapInterface)) {
+    strncpy(errbuf, "[TSRemapInit] - Incorrect size of TSRemapInterface structure", errbuf_size - 1);
+    return TS_ERROR;
+  }
+
+  if (api_info->tsremap_version < TSREMAP_VERSION) {
+    snprintf(errbuf, errbuf_size - 1, "[TSRemapInit] - Incorrect API version %ld.%ld",
+             api_info->tsremap_version >> 16, (api_info->tsremap_version & 0xffff));
+    return TS_ERROR;
+  }
+
+  TSDebug(PLUGIN_NAME, "remap plugin is succesfully initialized");
+  return TS_SUCCESS;
+}
+
+
+TSReturnCode
+TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_size)
+{
+  TSDebug(PLUGIN_NAME, "initializing the remap plugin header_rewrite");
+
+  if (argc < 3) {
+    TSError("Unable to create remap instance, need config file");
+    return TS_ERROR;
+  }
+
+  // TODO: this is a big ugly, we use a pseudo hook for parsing the config for a 
+  // remap instantiation.
+  all_rules[TS_REMAP_PSEUDO_HOOK] = NULL;
+  if (!parse_config(argv[2], TS_REMAP_PSEUDO_HOOK)) {
+    TSError("Unable to create remap instance");
+    return TS_ERROR;
+  }
+
+  *ih = all_rules[TS_REMAP_PSEUDO_HOOK];
+  all_rules[TS_REMAP_PSEUDO_HOOK] = NULL;
+
+  TSDebug(PLUGIN_NAME, "successfully initialize the header_rewrite plugin");
+  return TS_SUCCESS;
+}
+
+void
+TSRemapDeleteInstance(void *ih)
+{
+  RuleSet* rule = static_cast<RuleSet*>(ih);
+
+  delete rule;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// This is the main "entry" point for the plugin, called for every request.
+//
+TSRemapStatus
+TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
+{
+  TSRemapStatus rval = TSREMAP_NO_REMAP;
+
+  if (NULL == ih) {
+    TSDebug(PLUGIN_NAME, "No Rules configured, falling back to default");
+    return rval;
+  } else {
+    RuleSet* rule = (RuleSet*)ih;
+    Resources res((TSHttpTxn)rh, rri);
+
+    // TODO: This might be suboptimal, do we always need the client request
+    // headers in a remap plugin?
+    res.gather(RSRC_CLIENT_REQUEST_HEADERS, TS_REMAP_PSEUDO_HOOK);
+
+    // Evaluation
+    while (rule) {
+      if (rule->eval(res)) {
+        OperModifiers rt = rule->exec(res);
+        if (res.changed_url == true)
+          rval = TSREMAP_DID_REMAP;
+
+        if (rule->last() || (rt & OPER_LAST)) {
+          break; // Conditional break, force a break with [L]
+        }
+      }
+      rule = rule->next;
+    }
+
+  }
+
+  return rval;
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/header_rewrite.config
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/header_rewrite.config b/plugins/experimental/header_rewrite/header_rewrite.config
new file mode 100644
index 0000000..8be269a
--- /dev/null
+++ b/plugins/experimental/header_rewrite/header_rewrite.config
@@ -0,0 +1,77 @@
+Operators
+---------
+rm-header Header matcher
+add-header Header value
+set-header Header value
+
+set-status value
+set-status-reason value
+
+
+Conditions
+-----------
+%{TRUE}  # Default condition if none specified
+%{FALSE}
+%{HEADER:string}
+%{YCOOKIE:id}
+%{METHOD}
+%{PROTOCOL}
+%{PORT}
+%{HOST}
+%{TOHOST}
+%{FROMHOST}
+%{PATH}
+%{PARAMS}
+%{QUERY}
+%{STATUS}
+
+# These can only be used as the first condition of a new ruleset,
+# and have no run-time evaluation effects (only config parse time).
+%{READ_RESPONSE_HDR_HOOK} # Default
+%{READ_REQUEST_HDR_HOOK}
+
+
+Operator Flags
+--------------
+L    # Last rule (stop evaluations)
+
+
+Cond Flags
+----------
+NC   # Not case sensitive condition (when applicable)
+NOT  # Negate the cond
+OR   # Local OR between conds
+AND  # Logical AND betwen conds
+
+
+Matcher
+--------
+
+/string/  # regular expression
+<string   # lexically lower
+>string   # lexically greater
+=string   # lexically equal
+
+(The absense of a "matcher" means value exists).
+
+
+Values
+------
+
+Any of the cond definitions, that extracts a value from the request
+$N 0 <= N <= 9, as grouped in a regular expression
+string (which can contain the above)
+null
+
+
+Examples
+--------
+
+cond %{HEADER:X-Y-Foobar}
+cond %{METHOD} =GET [OR]
+cond %{METHOD} =POST
+set-header X-Y-Fiefum %{HEADER:X-Y-Foobar}
+rm-header X-Y-Foobar
+rm-header Set-Cookie
+
+cond %{HEADER:X-Y-Foobar} "Some string" [AND,NC]

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/lulu.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/lulu.h b/plugins/experimental/header_rewrite/lulu.h
new file mode 100644
index 0000000..7452e49
--- /dev/null
+++ b/plugins/experimental/header_rewrite/lulu.h
@@ -0,0 +1,42 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// 
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __LULU_H__
+#define __LULU_H__ 1
+
+// Define UNUSED properly.
+#if ((__GNUC__ >= 3) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 7)))
+#define UNUSED __attribute__ ((unused))
+#else
+#define UNUSED
+#endif /* #if ((__GNUC__ >= 3) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 7))) */
+
+static char UNUSED rcsId__lulu_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <sys/types.h>
+
+// Memory barriers on i386 / linux / gcc
+#if defined(__i386__)
+#define mb()  __asm__ __volatile__ ( "lock; addl $0,0(%%esp)" : : : "memory" )
+#define rmb() __asm__ __volatile__ ( "lock; addl $0,0(%%esp)" : : : "memory" )
+#define wmb() __asm__ __volatile__ ( "" : : : "memory")
+#elif defined(__x86_64__)
+#define mb()  __asm__ __volatile__ ( "mfence" : : : "memory")
+#define rmb() __asm__ __volatile__ ( "lfence" : : : "memory")
+#define wmb() __asm__ __volatile__ ( "" : : : "memory")
+#else
+#error "Define barriers"
+#endif
+
+static const char* PLUGIN_NAME UNUSED = "header_rewrite";
+static const char* PLUGIN_NAME_DBG UNUSED = "header_rewrite_dbg";
+
+
+// From google styleguide: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
+#define DISALLOW_COPY_AND_ASSIGN(TypeName)      \
+  TypeName(const TypeName&);                    \
+  void operator=(const TypeName&)
+
+
+#endif // __LULU_H__

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/matcher.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/matcher.cc b/plugins/experimental/header_rewrite/matcher.cc
new file mode 100644
index 0000000..b520ab7
--- /dev/null
+++ b/plugins/experimental/header_rewrite/matcher.cc
@@ -0,0 +1,12 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// matcher.cc: Implementation of the condition classes
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__matcher_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+
+#include "matcher.h"

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/matcher.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/matcher.h b/plugins/experimental/header_rewrite/matcher.h
new file mode 100644
index 0000000..3676a52
--- /dev/null
+++ b/plugins/experimental/header_rewrite/matcher.h
@@ -0,0 +1,159 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// 
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __MATCHER_H__
+#define __MATCHER_H__ 1
+
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__matcher_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+#include "regex_helper.h"
+#include <iostream> // For debugging
+
+#include "lulu.h"
+
+
+// Possible operators that we support (at least partially)
+enum MatcherOps {
+  MATCH_EQUAL,
+  MATCH_LESS_THEN,
+  MATCH_GREATER_THEN,
+  MATCH_REGULAR_EXPRESSION
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Base class for all Matchers (this is also the interface)
+//
+class Matcher
+{
+public:
+  explicit Matcher(const MatcherOps op)
+    : _pdata(NULL), _op(op)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Matcher");
+  }
+
+  virtual ~Matcher() {
+    TSDebug(PLUGIN_NAME_DBG, "Calling DTOR for Matcher");
+    free_pdata();
+  }
+
+  void set_pdata(void* pdata) { _pdata = pdata; }
+  void* get_pdata() const { return _pdata; }
+  virtual void free_pdata() { TSfree(_pdata); _pdata = NULL; }
+
+protected:
+  void* _pdata;
+  const MatcherOps _op;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(Matcher);
+};
+
+// Template class to match on various types of data
+template<class T>
+class Matchers : public Matcher
+{
+public:
+  explicit Matchers<T>(const MatcherOps op)
+    : Matcher(op)
+  { }
+
+  // Getters / setters
+  const T get() const { return _data; };
+ 
+ void setRegex(const std::string data)
+ {
+     if(!helper.setRegexMatch(_data))
+     {
+         std::cout<<"Invalid regex:failed to precompile"<<std::endl;
+         abort();
+     }
+     TSDebug(PLUGIN_NAME,"Regex precompiled successfully");
+
+ }
+
+ void setRegex(const unsigned int t)
+ {
+     return;
+ }
+
+ void setRegex(const TSHttpStatus t)
+ {
+     return;
+ }
+
+  void set (const T d)
+  { 
+      _data = d;
+      if(_op == MATCH_REGULAR_EXPRESSION)
+        setRegex(d);
+  }
+
+  // Evaluate this matcher
+  bool
+  test(const T t) const {
+    switch (_op) {
+    case MATCH_EQUAL:
+      return test_eq(t);
+      break;
+    case MATCH_LESS_THEN:
+      return test_lt(t);
+      break;
+    case MATCH_GREATER_THEN:
+      return test_gt(t);
+      break;
+    case MATCH_REGULAR_EXPRESSION:
+      return test_reg(t);
+      break;
+    default:
+      // ToDo: error
+      break;
+    }
+    return false;
+  }
+
+private:
+  // For basic types
+  bool test_eq(const T t) const {
+    // std::cout << "Testing: " << t << " == " << _data << std::endl;
+    return t == _data;
+  }
+  bool test_lt(const T t) const {
+    // std::cout << "Testing: " << t << " < " << _data << std::endl;
+    return t < _data;
+  }
+  bool test_gt(const T t) const {
+    // std::cout << "Testing: " << t << " > " << _data << std::endl;
+    return t > _data;
+  }
+
+ bool test_reg(const unsigned int t) const {
+    // Not support
+    return false;
+  }
+
+  bool test_reg(const TSHttpStatus t) const {
+    // Not support
+    return false;
+  }
+  
+  bool test_reg(const std::string t) const {
+      TSDebug(PLUGIN_NAME, "Test regular expression %s : %s", _data.c_str(), t.c_str());
+          int ovector[OVECCOUNT];
+          if (helper.regexMatch(t.c_str(), t.length(), ovector) > 0) {
+              TSDebug(PLUGIN_NAME, "Successfully found regular expression match");
+              return true;
+    }
+      return false;
+  }
+  T _data;
+  regexHelper helper;
+};
+
+#endif // __MATCHER_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/operator.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/operator.cc b/plugins/experimental/header_rewrite/operator.cc
new file mode 100644
index 0000000..d68cab3
--- /dev/null
+++ b/plugins/experimental/header_rewrite/operator.cc
@@ -0,0 +1,32 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// operator.cc: Implementation of the operator base class
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__operator_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <ts/ts.h>
+#include "operator.h"
+
+const OperModifiers
+Operator::get_oper_modifiers() const {
+  if (_next)
+    return static_cast<OperModifiers>(_mods | static_cast<Operator*>(_next)->get_oper_modifiers());
+
+  return _mods;
+}
+
+void
+Operator::initialize(Parser& p) {
+  Statement::initialize(p);
+
+  if (p.mod_exist("L")) {
+    _mods = static_cast<OperModifiers>(_mods | OPER_LAST);
+  }
+
+  if (p.mod_exist("QSA")) {
+    _mods = static_cast<OperModifiers>(_mods | OPER_QSA);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/operator.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/operator.h b/plugins/experimental/header_rewrite/operator.h
new file mode 100644
index 0000000..4323f15
--- /dev/null
+++ b/plugins/experimental/header_rewrite/operator.h
@@ -0,0 +1,59 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Public interface for creating all operators. Don't user the operator.h interface
+// directly!
+//
+#ifndef __OPERATOR_H__
+#define __OPERATOR_H__ 1
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__operator_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+
+#include "resources.h"
+#include "statement.h"
+#include "parser.h"
+
+
+// Operator modifiers
+enum OperModifiers {
+  OPER_NONE = 0,
+  OPER_LAST = 1,
+  OPER_NEXT = 2,
+  OPER_QSA=4
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Base class for all Operators (this is also the interface)
+//
+class Operator : public Statement
+{
+public:
+  Operator()
+    : _mods(OPER_NONE)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Operator");
+  }
+
+  void do_exec(const Resources& res) const {
+    exec(res);
+    if (NULL != _next)
+      static_cast<Operator*>(_next)->do_exec(res);
+  }
+
+  const OperModifiers get_oper_modifiers() const;
+
+  virtual void initialize(Parser& p);
+
+protected:
+  virtual void exec(const Resources& res) const = 0;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(Operator);
+
+  OperModifiers _mods;
+};
+
+#endif // __OPERATOR_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/operators.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/operators.cc b/plugins/experimental/header_rewrite/operators.cc
new file mode 100644
index 0000000..c5db93a
--- /dev/null
+++ b/plugins/experimental/header_rewrite/operators.cc
@@ -0,0 +1,366 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// operators.cc: implementation of the operator classes
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__operators_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <arpa/inet.h>
+#include <ts/ts.h>
+#include <string.h>
+
+#include "operators.h"
+
+// OperatorRMHeader
+void
+OperatorRMHeader::initialize(Parser& p) {
+  Operator::initialize(p);
+
+  _header = p.get_arg();
+
+  require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+  require_resources(RSRC_SERVER_REQUEST_HEADERS);
+  require_resources(RSRC_CLIENT_REQUEST_HEADERS);
+  require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+}
+
+
+void
+OperatorRMHeader::exec(const Resources& res) const
+{
+  TSMLoc field_loc, tmp;
+
+  if (res.bufp && res.hdr_loc) {
+    TSDebug(PLUGIN_NAME, "OperatorRMHeader::exec() invoked on header %s", _header.c_str());
+    field_loc = TSMimeHdrFieldFind(res.bufp, res.hdr_loc, _header.c_str(), _header.size());
+    while (field_loc) {
+      TSDebug(PLUGIN_NAME, "\tdeleting header %s", _header.c_str());
+      tmp = TSMimeHdrFieldNextDup(res.bufp, res.hdr_loc, field_loc);
+      TSMimeHdrFieldDestroy(res.bufp, res.hdr_loc, field_loc);
+      TSHandleMLocRelease(res.bufp, res.hdr_loc, field_loc);
+      field_loc = tmp;
+    }
+  }
+}
+
+
+// OperatorSetStatus
+void
+OperatorSetStatus::initialize(Parser& p) {
+  Operator::initialize(p);
+
+  _status.set_value(p.get_arg());
+
+  if (NULL == (_reason = TSHttpHdrReasonLookup((TSHttpStatus)_status.get_int_value()))) {
+    TSError("header_rewrite: unknown status %d", _status.get_int_value());
+    _reason_len = 0;
+  } else {
+    _reason_len = strlen(_reason);
+  }
+
+  require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+  require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+  require_resources(RSRC_RESPONSE_STATUS);
+}
+
+
+void
+OperatorSetStatus::initialize_hooks() {
+  add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+  add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+}
+
+
+void
+OperatorSetStatus::exec(const Resources& res) const
+{
+  if (res.bufp && res.hdr_loc) {
+    TSHttpHdrStatusSet(res.bufp, res.hdr_loc, (TSHttpStatus)_status.get_int_value());
+    if (_reason && _reason_len > 0)
+      TSHttpHdrReasonSet(res.bufp, res.hdr_loc, _reason, _reason_len);
+  }
+}
+
+
+// OperatorSetStatusReason
+void
+OperatorSetStatusReason::initialize(Parser& p) {
+  Operator::initialize(p);
+
+  _reason.set_value(p.get_arg());
+  require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+  require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+}
+
+
+void
+OperatorSetStatusReason::initialize_hooks() {
+  add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+  add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+}
+
+void
+OperatorSetStatusReason::exec(const Resources& res) const {
+  if (res.bufp && res.hdr_loc) {
+    std::string reason;
+
+    _reason.append_value(reason, res);
+    if (reason.size() > 0) {
+      TSDebug(PLUGIN_NAME, "Setting Status Reason to %s", reason.c_str());
+      TSHttpHdrReasonSet(res.bufp, res.hdr_loc, reason.c_str(), reason.size());
+    }
+  }
+}
+
+
+// OperatorAddHeader
+void
+OperatorAddHeader::initialize(Parser& p) {
+  Operator::initialize(p);
+
+  _header = p.get_arg();
+  _value.set_value(p.get_value());
+  
+  require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+  require_resources(RSRC_SERVER_REQUEST_HEADERS);
+  require_resources(RSRC_CLIENT_REQUEST_HEADERS);
+  require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+}
+
+
+void
+OperatorAddHeader::exec(const Resources& res) const
+{
+//  int IP = TSHttpTxnServerIPGet(res.txnp);
+//  inet_ntop(AF_INET, &IP, buf, sizeof(buf));
+  std::string value;
+
+  _value.append_value(value, res);
+
+  // Never set an empty header (I don't think that ever makes sense?)
+  if (value.empty()) {
+    TSDebug(PLUGIN_NAME, "Would set header %s to an empty value, skipping", _header.c_str());
+    return;
+  }
+  
+  if (res.bufp && res.hdr_loc) {
+    TSDebug(PLUGIN_NAME, "OperatorAddHeader::exec() invoked on header %s: %s", _header.c_str(), value.c_str());
+    TSMLoc field_loc;
+    
+    if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(res.bufp, res.hdr_loc, _header.c_str(), _header.size(), &field_loc)) {
+      if (TS_SUCCESS == TSMimeHdrFieldValueStringInsert(res.bufp, res.hdr_loc, field_loc, -1, value.c_str(), value.size())) {
+        TSDebug(PLUGIN_NAME, "   adding header %s", _header.c_str());
+        //INKHttpHdrPrint(res.bufp, res.hdr_loc, reqBuff);
+        TSMimeHdrFieldAppend(res.bufp, res.hdr_loc, field_loc);
+      }
+      TSHandleMLocRelease(res.bufp, res.hdr_loc, field_loc);
+    }
+      
+  }
+}
+
+
+/// TODO and XXX: These currently only support when running as remap plugin.
+// OperatorSetDestination
+void
+OperatorSetDestination::initialize(Parser& p) {
+  Operator::initialize(p);
+
+  _url_qual = parse_url_qualifier(p.get_arg());
+  _value.set_value(p.get_value());
+  // TODO: What resources would we require here?
+}
+
+
+void
+OperatorSetDestination::exec(const Resources& res) const
+{
+  if (res._rri) {
+    std::string value;
+
+    // Never set an empty destination value (I don't think that ever makes sense?)
+    switch (_url_qual) {
+
+    case URL_QUAL_HOST:
+      _value.append_value(value, res);
+      if (value.empty()) {
+        TSDebug(PLUGIN_NAME, "Would set destination HOST to an empty value, skipping");
+      } else {
+        const_cast<Resources&>(res).changed_url = true;
+        TSUrlHostSet(res._rri->requestBufp, res._rri->requestUrl, value.c_str(), value.size());
+        TSDebug(PLUGIN_NAME, "OperatorSetHost::exec() invoked with HOST: %s", value.c_str());
+      }
+      break;
+
+    case URL_QUAL_PATH:
+      _value.append_value(value, res);
+      if (value.empty()) {
+        TSDebug(PLUGIN_NAME, "Would set destination PATH to an empty value, skipping");
+      } else {
+        const_cast<Resources&>(res).changed_url = true;
+        TSUrlHostSet(res._rri->requestBufp, res._rri->requestUrl, value.c_str(), value.size());
+        TSDebug(PLUGIN_NAME, "OperatorSetHost::exec() invoked with PATH: %s", value.c_str());
+      }
+      break;
+
+    case URL_QUAL_QUERY:
+      _value.append_value(value, res);
+      if (value.empty()) {
+        TSDebug(PLUGIN_NAME, "Would set destination QUERY to an empty value, skipping");
+      } else {
+        //1.6.4--Support for preserving QSA in case of set-destination
+        if (get_oper_modifiers() & OPER_QSA) {
+          int query_len = 0;
+          const char* query = TSUrlHttpQueryGet(res._rri->requestBufp, res._rri->requestUrl, &query_len);
+          TSDebug(PLUGIN_NAME, "QSA mode, append original query string: %.*s", query_len, query);
+          //std::string connector = (value.find("?") == std::string::npos)? "?" : "&";
+          value.append("&");
+          value.append(query, query_len);
+        }
+
+        const_cast<Resources&>(res).changed_url = true;
+        TSUrlHttpQuerySet(res._rri->requestBufp, res._rri->requestUrl, value.c_str(), value.size());
+        TSDebug(PLUGIN_NAME, "OperatorSetHost::exec() invoked with QUERY: %s", value.c_str());
+      }
+      break;
+
+    case URL_QUAL_PORT:
+      if (_value.get_int_value() <= 0) {
+        TSDebug(PLUGIN_NAME, "Would set destination PORT to an invalid range, skipping");
+      } else {
+        const_cast<Resources&>(res).changed_url = true;
+        TSUrlPortSet(res._rri->requestBufp, res._rri->requestUrl, _value.get_int_value());
+        TSDebug(PLUGIN_NAME, "OperatorSetHost::exec() invoked with PORT: %d", _value.get_int_value());
+      }
+      break;
+    case URL_QUAL_URL:
+      // TODO: Implement URL parser.
+      break;
+    default:
+      break;
+    }
+  } else {
+    // TODO: Handle the non-remap case here (InkAPI hooks)
+  }
+}
+
+
+/// TODO and XXX: These currently only support when running as remap plugin.
+// OperatorSetRedirect
+void
+OperatorSetRedirect::initialize(Parser& p) {
+  Operator::initialize(p);
+
+  _status.set_value(p.get_arg());
+  _location.set_value(p.get_value());
+
+  if ((_status.get_int_value() != (int)TS_HTTP_STATUS_MOVED_PERMANENTLY) &&
+      (_status.get_int_value() != (int)TS_HTTP_STATUS_MOVED_TEMPORARILY)) {
+    TSError("header_rewrite: unsupported redirect status %d", _status.get_int_value());
+  }
+
+  require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+  require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+  require_resources(RSRC_RESPONSE_STATUS);
+}
+
+
+void
+OperatorSetRedirect::exec(const Resources& res) const
+{
+  if (res._rri) {
+    if (res.bufp && res.hdr_loc) {
+      std::string value;
+
+      _location.append_value(value, res);
+         
+      // Replace %{PATH} to original path
+      size_t pos_path = 0;
+      
+      if ((pos_path = value.find("%{PATH}")) != std::string::npos) {
+          value.erase(pos_path, 7); // erase %{PATH} from the rewritten to url
+          int path_len = 0;
+          const char *path = TSUrlPathGet(res._rri->requestBufp, res._rri->requestUrl, &path_len);
+          if (path_len > 0) {
+            TSDebug(PLUGIN_NAME, "Find %%{PATH} in redirect url, replace it with: %.*s", path_len, path);
+            value.insert(pos_path, path, path_len);
+          }
+      }
+       
+      // Append the original query string
+      int query_len = 0;
+      const char *query = TSUrlHttpQueryGet(res._rri->requestBufp, res._rri->requestUrl, &query_len);
+      if ((get_oper_modifiers() & OPER_QSA) && (query_len > 0)) {
+          TSDebug(PLUGIN_NAME, "QSA mode, append original query string: %.*s", query_len, query);
+          std::string connector = (value.find("?") == std::string::npos)? "?" : "&";
+          value.append(connector);
+          value.append(query, query_len);
+      }
+
+      TSHttpTxnSetHttpRetStatus(res.txnp,(TSHttpStatus)_status.get_int_value());
+      //TSHttpHdrStatusSet(res.bufp, res.hdr_loc, (TSHttpStatus)_status.get_int_value());
+      const char *start = value.c_str();
+      const char *end = value.size() + start;
+      TSUrlParse(res._rri->requestBufp, res._rri->requestUrl, &start, end);
+      TSDebug(PLUGIN_NAME, "OperatorSetRedirect::exec() invoked with destination=%s and status code=%d", 
+              value.c_str(), _status.get_int_value());
+    }
+    
+  } else {
+    // TODO: Handle the non-remap case here (InkAPI hooks)
+  }
+}
+
+
+// OperatorSetTimeoutOut
+void
+OperatorSetTimeoutOut::initialize(Parser& p) {
+  Operator::initialize(p);
+
+  if (p.get_arg() == "active") {
+    _type = TO_OUT_ACTIVE;
+  } else if (p.get_arg() == "inactive") {
+    _type = TO_OUT_INACTIVE;
+  } else if (p.get_arg() == "connect") {
+    _type = TO_OUT_CONNECT;
+  } else if (p.get_arg() == "dns") {
+    _type = TO_OUT_DNS;
+  } else {
+    _type = TO_OUT_UNDEFINED;
+    TSError("header_rewrite: unsupported timeout qualifier: %s", p.get_arg().c_str());
+  }
+
+  _timeout.set_value(p.get_value());
+}
+
+
+void
+OperatorSetTimeoutOut::exec(const Resources& res) const
+{
+  switch (_type) {
+  case TO_OUT_ACTIVE:
+    TSDebug(PLUGIN_NAME, "OperatorSetTimeoutOut::exec(active, %d)", _timeout.get_int_value());
+    TSHttpTxnActiveTimeoutSet(res.txnp, _timeout.get_int_value());
+    break;
+
+  case TO_OUT_INACTIVE:
+    TSDebug(PLUGIN_NAME, "OperatorSetTimeoutOut::exec(inactive, %d)", _timeout.get_int_value());
+    TSHttpTxnNoActivityTimeoutSet(res.txnp, _timeout.get_int_value());
+    break;
+
+  case TO_OUT_CONNECT:
+    TSDebug(PLUGIN_NAME, "OperatorSetTimeoutOut::exec(connect, %d)", _timeout.get_int_value());
+    TSHttpTxnConnectTimeoutSet(res.txnp, _timeout.get_int_value());
+    break;
+
+  case TO_OUT_DNS:
+    TSDebug(PLUGIN_NAME, "OperatorSetTimeoutOut::exec(dns, %d)", _timeout.get_int_value());
+    TSHttpTxnDNSTimeoutSet(res.txnp, _timeout.get_int_value());
+    break;
+  default:
+    TSError("header_rewrite: unsupported timeout");
+    break;
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/operators.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/operators.h b/plugins/experimental/header_rewrite/operators.h
new file mode 100644
index 0000000..f85112c
--- /dev/null
+++ b/plugins/experimental/header_rewrite/operators.h
@@ -0,0 +1,192 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// 
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __OPERATORS_H__
+#define __OPERATORS_H__ 1
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__operators_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+
+#include "operator.h"
+#include "resources.h"
+#include "value.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Operator declarations.
+//
+class OperatorRMHeader : public Operator
+{
+public:
+  OperatorRMHeader()
+    : _header("")
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorRMHeader");
+  }
+  void initialize(Parser& p);
+
+protected:
+  void exec(const Resources& res) const;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(OperatorRMHeader);
+
+  std::string _header;
+};
+
+
+class OperatorSetStatus : public Operator
+{
+public:
+  OperatorSetStatus()
+    : _reason(NULL), _reason_len(0)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetStatus");
+  }
+  void initialize(Parser& p);
+
+protected:
+  void initialize_hooks();
+  void exec(const Resources& res) const;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(OperatorSetStatus);
+
+  Value _status;
+  const char* _reason;
+  int _reason_len;
+};
+
+
+class OperatorSetStatusReason : public Operator
+{
+public:
+  OperatorSetStatusReason()
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetStatusReason");
+  }
+  void initialize(Parser& p);
+
+protected:
+  void initialize_hooks();
+  void exec(const Resources& res) const;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(OperatorSetStatusReason);
+
+  Value _reason;
+};
+
+
+class OperatorAddHeader : public Operator
+{
+public:
+  OperatorAddHeader()
+    : _header("")
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorAddHeader");
+  }
+  void initialize(Parser& p);
+
+protected:
+  void exec(const Resources& res) const;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(OperatorAddHeader);
+
+  std::string _header;
+  Value _value;
+};
+
+
+class OperatorSetDestination : public Operator
+{
+public:
+  OperatorSetDestination()
+    : _url_qual(URL_QUAL_NONE)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetDestination");
+  }
+  void initialize(Parser& p);
+
+protected:
+  void exec(const Resources& res) const;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(OperatorSetDestination);
+
+  UrlQualifiers _url_qual;
+  Value _value;
+};
+
+
+class OperatorSetRedirect : public Operator
+{
+public:
+  OperatorSetRedirect()
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetRedirect");
+  }
+  void initialize(Parser& p);
+
+protected:
+  void exec(const Resources& res) const;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(OperatorSetRedirect);
+
+  Value _status;
+  Value _location;
+};
+
+
+class OperatorNoOp : public Operator
+{
+public:
+  OperatorNoOp()
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorNoOp");
+  }
+
+protected:
+  void exec(const Resources& res) const { };
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(OperatorNoOp);
+};
+
+
+class OperatorSetTimeoutOut : public Operator
+{
+public:
+  OperatorSetTimeoutOut()
+    : _type(TO_OUT_UNDEFINED)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetTimeoutOut");
+  }
+  void initialize(Parser& p);
+
+protected:
+  void exec(const Resources& res) const;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(OperatorSetTimeoutOut);
+
+  enum TimeoutOutType {
+    TO_OUT_UNDEFINED,
+    TO_OUT_ACTIVE,
+    TO_OUT_INACTIVE,
+    TO_OUT_CONNECT,
+    TO_OUT_DNS
+  };
+
+  TimeoutOutType _type;
+  Value _timeout;
+};
+
+
+#endif // __OPERATORS_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/parser.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/parser.cc b/plugins/experimental/header_rewrite/parser.cc
new file mode 100644
index 0000000..b0289d6
--- /dev/null
+++ b/plugins/experimental/header_rewrite/parser.cc
@@ -0,0 +1,107 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// parser.cc: implementation of the config parser
+//
+//
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__parser_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <utility>
+#include <boost/tokenizer.hpp>
+
+#include <ts/ts.h>
+
+#include "parser.h"
+
+// Tokenizer separators
+static boost::escaped_list_separator<char> token_sep('\\', ' ', '\"');
+static boost::char_separator<char> comma_sep(",");
+
+
+// This is the core "parser", parsing rule sets
+void
+Parser::preprocess(std::vector<std::string>& tokens)
+{
+  // Special case for "conditional" values
+  if (tokens[0].substr(0, 2) == "%{") {
+    _cond = true;
+  } else if (tokens[0] == "cond") {
+    _cond = true;
+    tokens.erase(tokens.begin());
+  }
+
+  // Is it a condition or operator?
+  if (_cond) {
+    if ((tokens[0].substr(0, 2) == "%{") && (tokens[0][tokens[0].size() - 1] == '}')) {
+      std::string s = tokens[0].substr(2, tokens[0].size() - 3);
+
+      _op = s;
+      if (tokens.size() > 1)
+        _arg = tokens[1];
+      else
+        _arg = "";
+    } else {
+      TSError("header_rewrite: conditions must be embraced in %%{}");
+      return;
+    }
+  } else {
+    // Operator has no qualifiers, but could take an optional second argumetn
+    _op = tokens[0];
+    if (tokens.size() > 1) {
+      _arg = tokens[1];
+      if (tokens.size() > 2)
+        _val = tokens[2];
+      else
+        _val = "";
+    } else {
+      _arg = "";
+      _val = "";
+    }
+  }
+
+  // The last token might be the "flags" section
+  if (tokens.size() > 0) {
+    std::string m  = tokens[tokens.size() - 1];
+
+    if (!m.empty() && (m[0] == '[')) {
+      if (m[m.size() - 1] == ']') {
+        m = m.substr(1, m.size() - 2);
+        if (m.find_first_of(',') != std::string::npos) {
+          boost::tokenizer<boost::char_separator<char> > mods(m, comma_sep);
+          std::copy(mods.begin(), mods.end(), std::back_inserter(_mods));
+        } else {
+          _mods.push_back(m);
+        }
+      } else {
+        // Syntax error
+        TSError("header_rewrite: mods have to be embraced in []");
+        return;
+      }
+    }
+  }
+}
+
+
+Parser::Parser(const std::string& line) :
+  _cond(false), _empty(false)
+{
+  TSDebug("header_rewrite_dbg", "Calling CTOR for Parser");
+
+  if (line[0] == '#') {
+    _empty = true;
+  } else {
+    boost::tokenizer<boost::escaped_list_separator<char> > elem(line, token_sep);
+    std::vector<std::string> tokens;
+
+    for (boost::tokenizer<boost::escaped_list_separator<char> >::iterator it = elem.begin(); it != elem.end(); it++) {
+      // Skip "empty" tokens (tokenizer is a little brain dead IMO)
+      if ((*it) != "")
+        tokens.push_back(*it);
+    }
+
+    if (tokens.empty()) {
+      _empty = true;
+    } else {
+      preprocess(tokens);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/parser.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/parser.h b/plugins/experimental/header_rewrite/parser.h
new file mode 100644
index 0000000..0d0aa46
--- /dev/null
+++ b/plugins/experimental/header_rewrite/parser.h
@@ -0,0 +1,53 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// 
+// Interface for the config line parser
+//
+#ifndef __PARSER_H__
+#define __PARSER_H__ 1
+
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__parser_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <vector>
+#include <algorithm>
+
+#include "lulu.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class Parser
+{
+public:
+  explicit Parser(const std::string& line);
+
+  bool empty() const { return _empty; }
+  bool is_cond() const { return _cond; }
+
+  bool cond_op_is(const std::string s) const { return _cond && (_op == s); }
+  bool oper_op_is(const std::string s) const { return !_cond && (_op == s); }
+
+  const std::string& get_op() const { return _op; }
+  std::string& get_arg() { return _arg; }
+  const std::string& get_value() const { return _val; }
+
+  bool mod_exist(const std::string m) const {
+    return (std::find(_mods.begin(), _mods.end(), m) != _mods.end());
+  }
+
+private:
+  void preprocess(std::vector<std::string>& tokens);
+  DISALLOW_COPY_AND_ASSIGN(Parser);
+
+  bool _cond;
+  bool _empty;
+  std::vector<std::string> _mods;
+  std::string _op;
+  std::string _arg;
+  std::string _val;
+};
+
+
+#endif // __PARSER_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/regex_helper.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/regex_helper.cc b/plugins/experimental/header_rewrite/regex_helper.cc
new file mode 100644
index 0000000..578253c
--- /dev/null
+++ b/plugins/experimental/header_rewrite/regex_helper.cc
@@ -0,0 +1,52 @@
+#include "regex_helper.h"
+
+bool
+regexHelper::setRegexMatch(const std::string& s) 
+{
+
+    const char* errorComp = NULL;
+    const char* errorStudy = NULL;
+    int erroffset;
+
+    regexString = s;
+    regex = pcre_compile(regexString.c_str(), 0, &errorComp, &erroffset, NULL);
+
+    if (regex == NULL) 
+    {
+        return false;
+    }
+    regexExtra = pcre_study(regex, 0, &errorStudy);
+    if ((regexExtra == NULL) && (errorStudy != 0)) 
+    {
+        return false;
+    }
+    if (pcre_fullinfo(regex, regexExtra, PCRE_INFO_CAPTURECOUNT, &regexCcount) != 0)
+        return false;
+    return true;
+  }
+
+const std::string&
+regexHelper::getRegexString() const 
+{
+    return regexString;
+}
+
+int
+regexHelper::getRegexCcount() const 
+{
+    return regexCcount;
+}
+
+int
+regexHelper::regexMatch(const char* str, int len, int ovector[]) const {
+    return pcre_exec(regex,                // the compiled pattern
+          regexExtra,          // Extra data from study (maybe)
+          str,                  // the subject std::string
+          len,                  // the length of the subject
+          0,                    // start at offset 0 in the subject
+          0,                    // default options
+          ovector,              // output vector for substring information
+          OVECCOUNT);           // number of elements in the output vector
+  };
+
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/regex_helper.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/regex_helper.h b/plugins/experimental/header_rewrite/regex_helper.h
new file mode 100644
index 0000000..6b43c31
--- /dev/null
+++ b/plugins/experimental/header_rewrite/regex_helper.h
@@ -0,0 +1,44 @@
+#ifndef REGEX_HELPER_H
+#define REGEX_HELPER_H
+
+#include <pcre.h>
+
+#include <string>
+
+
+const int OVECCOUNT = 30; // We support $1 - $9 only, and this needs to be 3x that
+
+class regexHelper{
+public:
+    regexHelper():
+        regex(NULL),regexExtra(NULL),regexCcount(0)
+    {
+    
+    }
+
+  ~regexHelper() 
+  {
+      if (regex)
+          pcre_free(regex);
+
+      if (regexExtra)
+          pcre_free(regexExtra);
+  }
+
+
+
+bool setRegexMatch(const std::string &s);
+const std::string& getRegexString() const;
+int getRegexCcount() const;
+int regexMatch(const char*,int,int ovector[]) const;
+
+private:
+  pcre* regex;
+  pcre_extra* regexExtra;
+  std::string regexString;
+  int regexCcount;
+
+};
+
+
+#endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/resources.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/resources.cc b/plugins/experimental/header_rewrite/resources.cc
new file mode 100644
index 0000000..1e1233e
--- /dev/null
+++ b/plugins/experimental/header_rewrite/resources.cc
@@ -0,0 +1,110 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// resources.cc: Implementation of the resources class.
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__resources_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <ts/ts.h>
+
+#include "resources.h"
+#include "lulu.h"
+
+void
+Resources::gather(const ResourceIDs ids, TSHttpHookID hook)
+{
+  TSDebug(PLUGIN_NAME, "Building resource structure for hook (%d)", hook);
+
+  // If we need the client request headers, make sure it's also available in the client vars.
+  if (ids & RSRC_CLIENT_REQUEST_HEADERS) {
+    TSDebug(PLUGIN_NAME, "\tAdding TXN client request header buffers");
+    if (TSHttpTxnClientReqGet(txnp, &client_bufp, &client_hdr_loc) != TS_SUCCESS) {
+      TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for request");
+      return;
+    }
+  }
+
+  switch (hook) {
+  case TS_HTTP_READ_RESPONSE_HDR_HOOK:
+    // Read response headers from server
+    if (ids & RSRC_SERVER_RESPONSE_HEADERS) {
+      TSDebug(PLUGIN_NAME, "\tAdding TXN server response header buffers");
+      if (TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+        TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for response");
+        return;
+      }
+    }
+    if (ids & RSRC_RESPONSE_STATUS) {
+      TSDebug(PLUGIN_NAME, "\tAdding TXN server response status resource");
+      resp_status = TSHttpHdrStatusGet(bufp, hdr_loc);
+    }
+    break;
+
+  case TS_HTTP_SEND_REQUEST_HDR_HOOK:
+    // Read request headers to server
+    if (ids & RSRC_SERVER_REQUEST_HEADERS) {
+      TSDebug(PLUGIN_NAME, "\tAdding TXN server request header buffers");
+      if (!TSHttpTxnServerReqGet(txnp, &bufp, &hdr_loc)) {
+        TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for request");
+        return;
+      }
+    }
+    break;
+
+  case TS_HTTP_READ_REQUEST_HDR_HOOK:
+  case TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK:
+    // Read request from client
+    if (ids & RSRC_CLIENT_REQUEST_HEADERS) {
+      bufp = client_bufp;
+      hdr_loc = client_hdr_loc;
+    }
+    break;
+
+  case TS_HTTP_SEND_RESPONSE_HDR_HOOK:
+    // Send response headers to client
+    if (ids & RSRC_CLIENT_RESPONSE_HEADERS) {
+      TSDebug(PLUGIN_NAME, "\tAdding TXN client response header buffers");
+      if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+        TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for request");
+        return;
+      }
+      if (ids & RSRC_RESPONSE_STATUS) {
+        TSDebug(PLUGIN_NAME, "\tAdding TXN client esponse status resource");
+        resp_status = TSHttpHdrStatusGet(bufp, hdr_loc);
+      }
+    }
+    break;
+
+  case TS_REMAP_PSEUDO_HOOK:
+    // Pseudo-hook for a remap instance
+    if (client_bufp && client_hdr_loc) {
+      TSDebug(PLUGIN_NAME, "\tAdding TXN client request header buffers for remap instance");
+      bufp = client_bufp;
+      hdr_loc = client_hdr_loc;
+    }
+    break;
+    
+    
+  default:
+    break;
+  }
+
+    _ready  = true;
+}
+
+void
+Resources::destroy()
+{
+  if (bufp) {
+    if (hdr_loc)
+      TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+  }
+
+  if (client_bufp && (client_bufp != bufp)) {
+    if (client_hdr_loc && (client_hdr_loc != hdr_loc)) // TODO: Is this check really necessary?
+      TSHandleMLocRelease(client_bufp, TS_NULL_MLOC, client_hdr_loc);
+  }
+
+  _ready = false;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/resources.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/resources.h b/plugins/experimental/header_rewrite/resources.h
new file mode 100644
index 0000000..db29170
--- /dev/null
+++ b/plugins/experimental/header_rewrite/resources.h
@@ -0,0 +1,75 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// 
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __RESOURCES_H__
+#define __RESOURCES_H__ 1
+
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__resources_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+#include <ts/remap.h>
+
+#include "lulu.h"
+
+
+#define TS_REMAP_PSEUDO_HOOK TS_HTTP_LAST_HOOK // Ugly, but use the "last hook" for remap instances.
+
+enum ResourceIDs {
+  RSRC_NONE = 0,
+  RSRC_SERVER_RESPONSE_HEADERS = 1,
+  RSRC_SERVER_REQUEST_HEADERS = 2,
+  RSRC_CLIENT_REQUEST_HEADERS = 4,
+  RSRC_CLIENT_RESPONSE_HEADERS = 8,
+  RSRC_RESPONSE_STATUS = 16,
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Resources holds the minimum resources required to process a request.
+//
+class Resources
+{
+public:
+  explicit Resources(TSHttpTxn txnptr, TSCont contptr)
+    : txnp(txnptr), contp(contptr), bufp(NULL), hdr_loc(NULL), client_bufp(NULL), client_hdr_loc(NULL),
+      resp_status(TS_HTTP_STATUS_NONE), _rri(NULL), changed_url(false), _ready(false)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Resources (InkAPI)");
+  }
+
+  Resources(TSHttpTxn txnptr, TSRemapRequestInfo *rri) :
+    txnp(txnptr), contp(NULL),
+    bufp(NULL), hdr_loc(NULL), client_bufp(NULL), client_hdr_loc(NULL), resp_status(TS_HTTP_STATUS_NONE),
+    _rri(rri), _ready(false)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Resources (RemapAPI)");
+  }
+
+  ~Resources() { destroy(); }
+
+  void gather(const ResourceIDs ids, TSHttpHookID hook);
+  bool ready() const { return _ready; }
+
+  TSHttpTxn txnp;
+  TSCont contp;
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+  TSMBuffer client_bufp;
+  TSMLoc client_hdr_loc;
+  TSHttpStatus resp_status;
+  TSRemapRequestInfo *_rri;
+  bool changed_url;
+
+private:
+  void destroy();
+  DISALLOW_COPY_AND_ASSIGN(Resources);
+
+  bool _ready;
+};
+
+
+#endif // __RESOURCES_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/ruleset.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/ruleset.cc b/plugins/experimental/header_rewrite/ruleset.cc
new file mode 100644
index 0000000..6dce0fb
--- /dev/null
+++ b/plugins/experimental/header_rewrite/ruleset.cc
@@ -0,0 +1,75 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// ruleset.cc: implementation of the ruleset class
+//
+//
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__ruleset_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+
+#include "ruleset.h"
+#include "factory.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Class implementation (no reason to have these inline)
+//
+void
+RuleSet::append(RuleSet* rule) {
+  RuleSet* tmp = this;
+
+  TSReleaseAssert(rule->next == NULL);
+
+  while (tmp->next)
+    tmp = tmp->next;
+  tmp->next = rule;
+}
+
+
+void
+RuleSet::add_condition(Parser& p) {
+  Condition* c = condition_factory(p.get_op());
+
+  if (NULL != c) {
+    TSDebug(PLUGIN_NAME, "Adding condition: %%{%s} with arg: %s\n", p.get_op().c_str(), p.get_arg().c_str());
+    c->initialize(p);
+    if (!c->set_hook(_hook)) {
+      TSError("header_rewrite: can't use this condition in this hook");
+      return;
+    }
+    if (NULL == _cond) {
+      _cond = c;
+    } else {
+      _cond->append(c);
+    }
+
+    // Update some ruleset state based on this new condition
+    _last |= c->last();
+    _ids = static_cast<ResourceIDs>(_ids | _cond->get_resource_ids());
+  }
+}
+
+
+void
+RuleSet::add_operator(Parser& p) {
+  Operator* o = operator_factory(p.get_op());
+
+  if (NULL != o) {
+    // TODO: This should be extended to show both the "argument" and the "value" (if both are used)
+    TSDebug(PLUGIN_NAME, "Adding operator: %s(%s)\n", p.get_op().c_str(), p.get_arg().c_str());
+    o->initialize(p);
+    if (!o->set_hook(_hook)) {
+      TSError("header_rewrite: can't use this operator in this hook");
+      return;
+    }
+    if (NULL == _oper) {
+      _oper = o;
+    } else {
+      _oper->append(o);
+    }
+
+    // Update some ruleset state based on this new operator
+    _opermods = static_cast<OperModifiers>(_opermods | _oper->get_oper_modifiers());
+    _ids = static_cast<ResourceIDs>(_ids | _oper->get_resource_ids());
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/ruleset.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/ruleset.h b/plugins/experimental/header_rewrite/ruleset.h
new file mode 100644
index 0000000..f0e13ad
--- /dev/null
+++ b/plugins/experimental/header_rewrite/ruleset.h
@@ -0,0 +1,81 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// 
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __RULESET_H__
+#define __RULESET_H__ 1
+
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__ruleset_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+
+#include "matcher.h"
+#include "factory.h"
+#include "resources.h"
+#include "parser.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Class holding one ruleset. A ruleset is one (or more) pre-conditions, and
+// one (or more) operators.
+//
+class RuleSet
+{
+public:
+  RuleSet()
+    : next(NULL), _cond(NULL), _oper(NULL), _hook(TS_HTTP_READ_RESPONSE_HDR_HOOK), _ids(RSRC_NONE),
+      _opermods(OPER_NONE), _last(false)
+  { };
+
+  // No reason to inline these
+  void append(RuleSet* rule);
+
+  void add_condition(Parser& p);
+  void add_operator(Parser& p);
+  bool has_operator() const { return NULL != _oper; }
+  bool has_condition() const { return NULL != _cond; }
+
+  void set_hook(TSHttpHookID hook) { _hook = hook; }
+  const TSHttpHookID get_hook() const { return _hook; }
+
+  // Inline
+  const ResourceIDs get_all_resource_ids() const {
+    return _ids;
+  }
+
+  bool eval(const Resources& res) const {
+    if (NULL == _cond) {
+      return true;
+    } else {
+      return _cond->do_eval(res);
+    }
+  }
+
+  bool last() const {
+    return _last;
+  }
+
+  OperModifiers exec(const Resources& res) const {
+    _oper->do_exec(res);
+    return _opermods;
+  }
+
+  RuleSet* next; // Linked list
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(RuleSet);
+
+  Condition* _cond; // First pre-condition (linked list)
+  Operator* _oper; // First operator (linked list)
+  TSHttpHookID _hook; // Which hook is this rule for
+
+  // State values (updated when conds / operators are added)
+  ResourceIDs _ids;
+  OperModifiers _opermods;
+  bool _last;
+};
+
+
+#endif // __RULESET_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/statement.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/statement.cc b/plugins/experimental/header_rewrite/statement.cc
new file mode 100644
index 0000000..918c910
--- /dev/null
+++ b/plugins/experimental/header_rewrite/statement.cc
@@ -0,0 +1,84 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// statement.cc: Implementation of the statement base class.
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__statement_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <ts/ts.h>
+
+#include "statement.h"
+
+void
+Statement::append(Statement* stmt)
+{
+  Statement* tmp = this;
+
+  TSReleaseAssert(stmt->_next == NULL);
+  while (tmp->_next)
+    tmp = tmp->_next;
+  tmp->_next = stmt;
+}
+
+
+const ResourceIDs
+Statement::get_resource_ids() const
+{
+  const Statement* stmt = this;
+  ResourceIDs ids = RSRC_NONE;
+
+  while (stmt) {
+    ids = static_cast<ResourceIDs>(ids | stmt->_rsrc);
+    stmt = stmt->_next;
+  }
+
+  return ids;
+}
+
+
+bool
+Statement::set_hook(TSHttpHookID hook) {
+  bool ret = std::find(_allowed_hooks.begin(), _allowed_hooks.end(), hook) != _allowed_hooks.end();
+
+  if (ret)
+    _hook = hook;
+
+  return ret;
+}
+
+
+// This should be overridden for any Statement which only supports some hooks
+void
+Statement::initialize_hooks() {
+  add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+  add_allowed_hook(TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK);
+  add_allowed_hook(TS_HTTP_READ_REQUEST_HDR_HOOK);
+  add_allowed_hook(TS_HTTP_SEND_REQUEST_HDR_HOOK);
+  add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+  add_allowed_hook(TS_REMAP_PSEUDO_HOOK);
+}
+
+
+// Parse URL qualifiers
+UrlQualifiers
+Statement::parse_url_qualifier(const std::string& q) {
+  UrlQualifiers qual = URL_QUAL_NONE;
+
+  if (q == "HOST")
+    qual = URL_QUAL_HOST;
+  else if (q == "PORT")
+    qual = URL_QUAL_PORT;
+  else if (q == "PATH")
+    qual = URL_QUAL_PATH;
+  else if (q == "QUERY")
+    qual = URL_QUAL_QUERY;
+  else if (q == "MATRIX")
+    qual = URL_QUAL_MATRIX;
+  else if (q == "SCHEME")
+    qual = URL_QUAL_SCHEME;
+  else if (q == "URL")
+    qual = URL_QUAL_URL;
+
+  return qual;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/statement.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/statement.h b/plugins/experimental/header_rewrite/statement.h
new file mode 100644
index 0000000..e42149b
--- /dev/null
+++ b/plugins/experimental/header_rewrite/statement.h
@@ -0,0 +1,92 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// 
+// Base class for all Conditions and Operations. We share the "linked" list, and the
+// resource management / requirements.
+//
+#ifndef __STATEMENT_H__
+#define __STATEMENT_H__ 1
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__statement_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <vector>
+#include <ts/ts.h>
+
+#include "resources.h"
+#include "parser.h"
+#include "lulu.h"
+
+
+// URL data (both client and server)
+enum UrlQualifiers {
+  URL_QUAL_NONE,
+  URL_QUAL_HOST,
+  URL_QUAL_PORT,
+  URL_QUAL_PATH,
+  URL_QUAL_QUERY,
+  URL_QUAL_MATRIX,
+  URL_QUAL_SCHEME,
+  URL_QUAL_URL
+};
+
+
+class Statement
+{
+ public:
+  Statement()
+    : _next(NULL), _pdata(NULL), _rsrc(RSRC_NONE), _initialized(false), _hook(TS_HTTP_READ_RESPONSE_HDR_HOOK)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Statement");
+  }
+
+  virtual ~Statement() {
+    TSDebug(PLUGIN_NAME_DBG, "Calling DTOR for Statement");
+    free_pdata();
+  }
+
+  // Private data
+  void set_pdata(void* pdata) { _pdata = pdata; }
+  void* get_pdata() const { return(_pdata); }
+  virtual void free_pdata() { TSfree(_pdata); _pdata = NULL; }
+
+  // Which hook are we adding this statement to?
+  bool set_hook(TSHttpHookID hook);
+  const TSHttpHookID get_hook() const { return _hook; }
+
+  // Which hooks are this "statement" applicable for? Used during parsing only.
+  void add_allowed_hook(const TSHttpHookID hook) { _allowed_hooks.push_back(hook); }
+
+  // Linked list.
+  void append(Statement* stmt);
+
+  const ResourceIDs get_resource_ids() const;
+
+  virtual void initialize(Parser& p) {
+    TSReleaseAssert(_initialized == false);
+    initialize_hooks();
+    _initialized = true;
+  }
+  bool initialized() const { return _initialized; }
+
+ protected:
+  virtual void initialize_hooks();
+
+  UrlQualifiers parse_url_qualifier(const std::string& q);
+  void require_resources(const ResourceIDs ids) { _rsrc = static_cast<ResourceIDs>(_rsrc | ids); }
+
+  Statement* _next; // Linked list
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(Statement);
+
+  void* _pdata;
+  ResourceIDs _rsrc;
+  bool _initialized;
+  std::vector<TSHttpHookID> _allowed_hooks;
+  TSHttpHookID _hook;
+};
+
+
+#endif // __STATEMENT_H
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/value.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/value.h b/plugins/experimental/header_rewrite/value.h
new file mode 100644
index 0000000..81d7792
--- /dev/null
+++ b/plugins/experimental/header_rewrite/value.h
@@ -0,0 +1,74 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Public interface for creating all values.
+// 
+//
+#ifndef __VALUE_H__
+#define __VALUE_H__ 1
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__value_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+
+#include "resources.h"
+#include "statement.h"
+#include "condition.h"
+#include "factory.h"
+#include "parser.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Base class for all Values (this is also the interface).
+//
+// TODO: This is very incomplete, we need to support linked lists of these,
+// which evaluate each component and create a "joined" final string.
+//
+class Value : Statement
+{
+public:
+  Value()
+    : _value(""), _int_value(-1), _cond_val(NULL)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Value");
+  };
+
+  void
+  set_value(const std::string& val)
+  {
+    _value = val;
+    if (_value.substr(0,2) == "%{") {
+      Parser parser(_value);
+
+      _cond_val = condition_factory(parser.get_op());
+      if (_cond_val) {
+        _cond_val->initialize(parser);
+      }
+    }
+    _int_value = strtol(_value.c_str(), NULL, 10);
+  }
+
+  void
+  append_value(std::string& s, const Resources& res) const {
+    if (_cond_val) {
+      _cond_val->append_value(s, res);
+    } else {
+      s += _value;
+    }
+  }
+
+  const std::string& get_value() const { return _value; }
+  int get_int_value() const { return _int_value; }
+
+  bool empty() const { return _value.empty(); }
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(Value);
+
+  std::string _value;
+  int _int_value;
+  Condition* _cond_val;
+};
+
+
+#endif // __VALUE_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/hipes/README
----------------------------------------------------------------------
diff --git a/plugins/experimental/hipes/README b/plugins/experimental/hipes/README
new file mode 100644
index 0000000..b12f722
--- /dev/null
+++ b/plugins/experimental/hipes/README
@@ -0,0 +1,25 @@
+This plugin implements the HIPES system.
+
+Plugin configuration
+--------------------
+
+   server:<host>	Name of the server to send this request to
+   urlp:<name>		Name of the query parameter for the service URL [url]
+   path:<path>		Path to use for the service URL [/]
+   ssl			Use SSL when connecting to the service [no]
+   service		Service server, host[:port]  [no default]
+   server		Name of HIPES server, host[:port]   [hipes.yimg.com]
+   active_timeout	The active connection timeout in ms [no default]
+   no_activity_timeout	The no activity timeout in ms [no default]
+   connect_timeout	The connect timeout in ms [no default]
+   dns_timeout		The DNS timeout [no default]
+
+
+The timeout options override the server defaults (from records.config),
+and only apply to the connection to the specific "service" host.
+
+RELEASES
+--------
+
+Version 1.0 (??/??/10)
+  - First release.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/hipes/gen_escape.c
----------------------------------------------------------------------
diff --git a/plugins/experimental/hipes/gen_escape.c b/plugins/experimental/hipes/gen_escape.c
new file mode 100644
index 0000000..e137372
--- /dev/null
+++ b/plugins/experimental/hipes/gen_escape.c
@@ -0,0 +1,49 @@
+/** @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 <stdio.h>
+#include <string.h>
+
+
+int main() {
+  unsigned char codes[32];
+  unsigned char hex;
+  int c;
+
+  memset(codes, 0, sizeof(codes));
+
+  for (c = 0; c <= 255; ++c) {
+    if ( ( (c >= '0') && (c <= '9') ) ||
+         ( (c >= 'A') && (c <= 'Z') ) ||
+         ( (c >= 'a') && (c <= 'z') ) ||
+         ( (c == '_') ) ||
+         ( (c == '-') ) ||
+         ( (c == '.') ) ) {
+    } else {
+      codes[c / 8] |= (1 << (7 - c % 8));
+    }
+  }
+
+  for (hex = 0; hex < 32; ++hex) {
+    printf("0x%02lX, ", codes[hex]);
+    if (!((hex+1) % 4))
+      printf("\n");
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/hipes/hipes.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/hipes/hipes.cc b/plugins/experimental/hipes/hipes.cc
new file mode 100644
index 0000000..1bbdbee
--- /dev/null
+++ b/plugins/experimental/hipes/hipes.cc
@@ -0,0 +1,536 @@
+/** @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.
+*/
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__url_remap_cc[] = "@(#) $Id: yfor_remap.cc 218 2009-04-25 01:29:16Z leifh $ built on " __DATE__ " " __TIME__;
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string>
+
+#include <ts/remap.h>
+#include <ts/ts.h>
+
+static const char* PLUGIN_NAME = "hipes";
+static const char* HIPES_SERVER_NAME = "hipes.example.com";
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Escape a URL string.
+//
+int
+escapify_url(const char *src, int src_len, char* dst, int dst_len)
+{
+  // This bitmap is generated using the gen_escape.c prog.
+  static unsigned char codes_to_escape[32] = {
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xF9, 0x00, 0x3F, 
+    0x80, 0x00, 0x00, 0x1E, 
+    0x80, 0x00, 0x00, 0x1F, 
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF
+  };
+
+  static char hex_digit[16] = {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
+    'D', 'E', 'F'
+  };
+
+  const char* from = src;
+  char* to = dst;
+  int len = 0;
+
+  // Sanity check
+  if (!src)
+    return -1;
+
+  while (from < (src + src_len)) {
+    register unsigned char c = *from;
+
+    if (len >= dst_len)
+      return -1; // Does not fit.... abort!
+
+    if (codes_to_escape[c / 8] & (1 << (7 - c % 8))) {
+      *to++ = '%';
+      *to++ = hex_digit[c / 16];
+      *to++ = hex_digit[c % 16];
+      len += 3;
+    } else {
+      *to++ = *from;
+      ++len;
+    }
+    ++from;
+  }
+  *to = '\0';
+
+  return len;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Unescape a string. Have to make sure the destination buffer is at least as
+// long as the source buffer.
+//
+char*
+unescapify(const char* src, char* dst, int len) {
+  const char* cur = src;
+  char* next;
+  char subStr[3];
+  int size;
+
+  subStr[2] = '\0';
+  while ((next = (char*)memchr(cur, '%', len))) {
+    size = next - cur;
+    if (size > 0) {
+      memcpy(dst, cur, size);
+      dst += size;
+      cur += size;
+      len -= size;
+    }
+
+    if (len > 2  && (*cur+1) != '\0' && *(cur+2) != '\0') {
+      subStr[0] = *(++cur);
+      subStr[1] = *(++cur);
+      len -= 2;
+      *dst = (char)strtol(subStr, (char**)NULL, 16);
+    } else {
+      *dst = *cur;
+    }
+    ++dst;
+    ++cur;
+    --len;
+  }
+
+  if (len > 0) {
+    memcpy(dst, cur, len);
+    dst += len;
+  }
+
+  return dst;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Class encapsulating one service configuration
+//
+struct HIPESService
+{
+  HIPESService()
+    : url_param("url"), path(""), svc_server(""), svc_port(80), ssl(false), hipes_server(HIPES_SERVER_NAME),
+      hipes_port(80), default_redirect_flag(1), x_hipes_header("X-HIPES-Redirect"),
+      active_timeout(-1), no_activity_timeout(-1), connect_timeout(-1), dns_timeout(-1)
+  { };
+
+  std::string url_param;
+  std::string path;
+  std::string svc_server;
+  int svc_port;
+  bool ssl;
+  std::string hipes_server;
+  int hipes_port;
+  unsigned int default_redirect_flag;
+  std::string x_hipes_header;
+  int active_timeout;
+  int no_activity_timeout;
+  int connect_timeout;
+  int dns_timeout;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Initialize the plugin.
+//
+int
+TSRemapInit(TSREMAP_INTERFACE *api_info, char *errbuf, int errbuf_size)
+{
+  if (!api_info) {
+    strncpy(errbuf, "[tsremap_init] - Invalid TSREMAP_INTERFACE argument", errbuf_size - 1);
+    return TS_ERROR;
+  }
+
+  if (api_info->tsremap_version < TSREMAP_VERSION) {
+    snprintf(errbuf, errbuf_size - 1, "[tsremap_init] - Incorrect API version %ld.%ld",
+             api_info->tsremap_version >> 16, (api_info->tsremap_version & 0xffff));
+    return TS_ERROR;
+  }
+
+  INKDebug("hipes", "plugin is succesfully initialized");
+  return TS_SUCCESS;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// One instance per remap.config invocation.
+//
+int
+tsremap_new_instance(int argc, char *argv[], ihandle *ih, char *errbuf, int errbuf_size)
+{
+  HIPESService* ri = new HIPESService;
+
+  *ih = static_cast<ihandle>(ri);
+
+  if (ri == NULL) {
+    INKError("Unable to create remap instance");
+    return -5;
+  }
+
+  for (int ix=2; ix < argc; ++ix) {
+    std::string arg = argv[ix];
+    std::string::size_type sep = arg.find_first_of(":");
+
+    if (sep == std::string::npos) {
+      INKError("Malformed options in url_remap: %s", argv[ix]);
+    } else {
+      std::string arg_val = arg.substr(sep + 1, std::string::npos);
+
+      if (arg.compare(0, 4, "urlp") == 0) {
+        ri->url_param = arg_val;
+      } else if (arg.compare(0, 4, "path") == 0) {
+        ri->path = arg_val;
+        if (arg_val[0] == '/')
+          ri->path = arg_val.substr(1);
+        else
+          ri->path = arg_val;
+      } else if (arg.compare(0, 3, "ssl") == 0) {
+        ri->ssl = true;
+      } else if (arg.compare(0, 7, "service") == 0) {
+        std::string::size_type port = arg_val.find_first_of(":");
+
+        if (port == std::string::npos) {
+          ri->svc_server = arg_val;
+        } else {
+          ri->svc_server = arg_val.substr(0, port);
+          ri->svc_port = atoi(arg_val.substr(port+1).c_str());
+        }
+      } else if (arg.compare(0, 6, "server") == 0) {
+        std::string::size_type port = arg_val.find_first_of(":");
+
+        if (port == std::string::npos) {
+          ri->hipes_server = arg_val;
+        } else {
+          ri->hipes_server = arg_val.substr(0, port);
+          ri->hipes_port = atoi(arg_val.substr(port+1).c_str());
+        }
+      } else if (arg.compare(0, 14, "active_timeout") == 0) {
+        ri->active_timeout = atoi(arg_val.c_str());
+      } else if (arg.compare(0, 19, "no_activity_timeout") == 0) {
+        ri->no_activity_timeout = atoi(arg_val.c_str());
+      } else if (arg.compare(0, 15, "connect_timeout") == 0) {
+        ri->connect_timeout = atoi(arg_val.c_str());
+      } else if (arg.compare(0, 11, "dns_timeout") == 0) {
+        ri->dns_timeout = atoi(arg_val.c_str());
+      } else {
+        INKError("Unknown url_remap option: %s", argv[ix]);
+      }
+    }
+  }
+
+  return 0;
+}
+
+void
+tsremap_delete_instance(ihandle ih)
+{
+  HIPESService* ri = static_cast<HIPESService*>(ih);
+
+  delete ri;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// This is the main "entry" point for the plugin, called for every request.
+//
+int
+tsremap_remap(ihandle ih, rhandle rh, REMAP_REQUEST_INFO *rri)
+{
+  const char* slash;
+  char* ptr;
+  HIPESService* h_conf = static_cast<HIPESService*>(ih);
+
+  if (NULL == h_conf) {
+    INKDebug("hipes", "Falling back to default URL on URL remap without rules");
+    return 0;
+  }
+
+  // Make sure we have a matrix parameter, anything without is a bogus request.
+  if (rri->request_matrix_size <= 0) {
+    INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_BAD_REQUEST);
+    return 0;
+  }
+
+  // Don't think this can/should happen, but safety first ...
+  if (rri->request_matrix_size > TSREMAP_RRI_MAX_PATH_SIZE) {
+    INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_REQUEST_URI_TOO_LONG);
+    return 0;
+  }
+
+  // If there is a '/' in the matrix parameters, we know there are multiple service requests in
+  // the incoming URL, so nibble off the first one, and pass the rest as a HIPES URL to the service.
+  if ((slash = static_cast<const char*>(memchr(rri->request_matrix, '/', rri->request_matrix_size)))) {
+    char svc_url[TSREMAP_RRI_MAX_PATH_SIZE + 1];
+    char svc_url_esc[TSREMAP_RRI_MAX_PATH_SIZE + 1];
+    int len, query_len;
+
+    // Create the escaped URL, which gets passed over to the service as a url= param.
+    len = 8 + h_conf->hipes_server.size() + (rri->request_matrix_size - (slash - rri->request_matrix) - 1);
+    if (len > TSREMAP_RRI_MAX_PATH_SIZE) {
+      INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_REQUEST_URI_TOO_LONG);
+      return 0;
+    }
+    snprintf(svc_url, TSREMAP_RRI_MAX_PATH_SIZE, "http://%s/%.*s", h_conf->hipes_server.c_str(), len, slash + 1);
+    INKDebug("hipes", "Service URL is %s", svc_url);
+
+    len = escapify_url(svc_url, len, svc_url_esc, TSREMAP_RRI_MAX_PATH_SIZE);
+    if (len < 0) {
+      return 0;
+    }
+    INKDebug("hipes", "Escaped service URL is %s(%d)", svc_url_esc, len);
+
+    // Prepare the new query arguments, make sure it fits
+    if (( (slash - rri->request_matrix) + 2 + h_conf->url_param.size() + len) > TSREMAP_RRI_MAX_PATH_SIZE) {
+      INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_REQUEST_URI_TOO_LONG);
+      return 0;
+    }
+
+    query_len = (slash - rri->request_matrix);
+    memcpy(rri->new_query, rri->request_matrix, query_len);
+    ptr = rri->new_query;
+    while ((ptr = static_cast<char*>(memchr(ptr, ';', (rri->new_query + query_len) - ptr))))
+      *ptr = '&';
+
+    rri->new_query[query_len++] = '&';
+    memcpy(rri->new_query + query_len, h_conf->url_param.c_str(), h_conf->url_param.size());
+    query_len += h_conf->url_param.size();
+    rri->new_query[query_len++] = '=';
+
+    memcpy(rri->new_query + query_len, svc_url_esc, len);
+    rri->new_query_size = query_len + len;
+    INKDebug("hipes", "New query is %.*s(%d)", rri->new_query_size, rri->new_query, rri->new_query_size);
+  } else {
+    // This is the "final" step in this HIPES URL, so don't point back to HIPES (or we'll never leave)
+    rri->new_query_size = rri->request_matrix_size;
+    memcpy(rri->new_query, rri->request_matrix, rri->request_matrix_size);
+    ptr = rri->new_query;
+    while ((ptr = static_cast<char*>(memchr(ptr, ';', (rri->new_query + rri->new_query_size) - ptr))))
+      *ptr = '&';
+
+    INKDebug("hipes", "New query is %.*s(%d)", rri->new_query_size, rri->new_query, rri->new_query_size);
+  }
+
+  // Test if we should redirect or not
+  bool do_redirect = false;
+  int redirect_flag = h_conf->default_redirect_flag;
+  char* pos = rri->new_query;
+
+  while (pos && (pos = (char*)memchr(pos, '_', rri->new_query_size - (pos - rri->new_query)))) {
+    if (pos) {
+      ++pos;
+      if ((rri->new_query_size - (pos - rri->new_query)) < 10) { // redirect=n
+        pos = NULL;
+      } else {
+        if ((*pos == 'r') && (!strncmp(pos, "redirect=", 9))) {
+          redirect_flag = *(pos + 9) - '0';
+          if ((redirect_flag < 0) || (redirect_flag > 2))
+            redirect_flag = h_conf->default_redirect_flag;
+          INKDebug("hipes", "Found _redirect flag in URL: %d\n", redirect_flag);
+          pos = NULL;
+        }
+      }
+    }
+  }
+
+  if (redirect_flag > 0) {
+    // Now check the incoming request header, and match up.
+    INKMBuffer bufp;
+    INKMLoc hdr_loc, field_loc;
+    bool has_error = false;
+
+    if (INKHttpTxnClientReqGet((INKHttpTxn)rh, &bufp, &hdr_loc)) {
+      field_loc = INKMimeHdrFieldFind(bufp, hdr_loc, h_conf->x_hipes_header.c_str(), h_conf->x_hipes_header.size());
+      if (field_loc) {
+        int hdr_flag;
+
+        if (INKMimeHdrFieldValueIntGet(bufp, hdr_loc, field_loc, 0, &hdr_flag) == INK_SUCCESS) {
+          // Alright, now match up this header flag with the request (or default) flag
+          INKDebug("hipes", "Extracted %s header with value %d", h_conf->x_hipes_header.c_str(), hdr_flag);
+          switch (redirect_flag) {
+          case 0:
+            if (hdr_flag == 2) {
+              INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_BAD_REQUEST);
+              has_error = true;
+            } // Everything else is a "no"
+            break;
+          case 1:
+            if (hdr_flag == 2) {
+              do_redirect = true;
+            } // Everything else is a "no"
+            break;
+          case 2:
+            if (hdr_flag == 2) {
+              do_redirect = true;
+            } else {
+              INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_BAD_REQUEST);
+              has_error = true;
+            }
+            break;
+          default:
+            INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_BAD_REQUEST);
+            has_error = true;
+            break;
+          }
+        }
+        INKHandleMLocRelease(bufp, hdr_loc, field_loc);
+      }
+      INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
+    }
+    if (has_error)
+      return 1;
+  }
+
+  // If we redirect, just generate a 302 URL, otherwise update the RRI struct properly.
+  if (do_redirect) {
+    int len; 
+
+    if (h_conf->ssl) {
+      // https://<host>:<port>/<path>?<query?\0
+      len = 5 + 3 + h_conf->svc_server.size() + 6 + 1 + h_conf->path.size() + 1 + rri->new_query_size + 1;
+    } else {
+      // http://<host>:<port>/<path>?<query?\0
+      len = 4 + 3 + h_conf->svc_server.size() + 6 + 1 + h_conf->path.size() + 1 + rri->new_query_size + 1;
+    }
+
+    if (len > TSREMAP_RRI_MAX_REDIRECT_URL) {
+      INKError("Redirect in HIPES URL too long");
+      INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, (INKHttpStatus)414);
+    } else {
+      int port = -1;
+
+      pos = rri->redirect_url;
+
+      // HTTP vs HTTPS
+      if (h_conf->ssl) {
+        memcpy(pos, "https://", 8);
+        pos += 8;
+        if (h_conf->svc_port != 443)
+          port = h_conf->svc_port;
+      } else {
+        memcpy(pos, "http://", 7);
+        pos += 7;
+        if (h_conf->svc_port != 80)
+          port = h_conf->svc_port;
+      }
+
+      // Server
+      memcpy(pos, h_conf->svc_server.c_str(), h_conf->svc_server.size());
+      pos += h_conf->svc_server.size();
+
+      // Port
+      if (port != -1)
+        pos += snprintf(pos, 6, ":%d", port);
+
+      // Path
+      *(pos++) = '/';
+      if (h_conf->path.size() > 0) {
+        memcpy(pos, h_conf->path.c_str(), h_conf->path.size());
+        pos += h_conf->path.size();
+      }
+
+      // Query
+      if (rri->new_query_size > 0) {
+        *(pos++) = '?';
+        memcpy(pos, rri->new_query, rri->new_query_size);
+        pos += rri->new_query_size;
+      }
+
+      // NULL terminate the URL.
+      *pos = '\0';
+
+      rri->redirect_url_size = pos - rri->redirect_url + 1;
+      INKDebug("hipes", "Redirecting to %.*s", rri->redirect_url_size, rri->redirect_url);
+      *(rri->new_query) = '\0';
+      rri->new_query_size = 0;
+      INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_MOVED_TEMPORARILY);
+    }
+  } else { // Not a redirect, so proceed normally
+    // Set timeouts (if requested)
+    if (h_conf->active_timeout > -1) {
+      INKDebug("hipes", "Setting active timeout to %d", h_conf->active_timeout);
+      INKHttpTxnActiveTimeoutSet((INKHttpTxn)rh, h_conf->active_timeout);
+    }
+    if (h_conf->no_activity_timeout > -1) {
+      INKDebug("hipes", "Setting no activity timeout to %d", h_conf->no_activity_timeout);
+      INKHttpTxnNoActivityTimeoutSet((INKHttpTxn)rh, h_conf->no_activity_timeout);
+    }
+    if (h_conf->connect_timeout > -1) {
+      INKDebug("hipes", "Setting connect timeout to %d", h_conf->connect_timeout);
+      INKHttpTxnConnectTimeoutSet((INKHttpTxn)rh, h_conf->connect_timeout);
+    }
+    if (h_conf->dns_timeout > -1) {
+      INKDebug("hipes", "Setting DNS timeout to %d", h_conf->dns_timeout);
+      INKHttpTxnDNSTimeoutSet((INKHttpTxn)rh, h_conf->dns_timeout);
+    }
+
+    // Set server ...
+    rri->new_host_size = h_conf->svc_server.size();
+    memcpy(rri->new_host, h_conf->svc_server.c_str(), rri->new_host_size);
+    INKDebug("hipes", "New server is %.*s", rri->new_host_size, rri->new_host);
+
+    // ... and port
+    rri->new_port = h_conf->svc_port;
+    INKDebug("hipes", "New port is %d", rri->new_port);
+
+    // Update the path
+    rri->new_path_size = h_conf->path.size();
+    memcpy(rri->new_path, h_conf->path.c_str(), rri->new_path_size);
+    INKDebug("hipes", "New path is %.*s", rri->new_path_size, rri->new_path);
+
+    // Enable SSL?
+    if (h_conf->ssl)
+      rri->require_ssl = 1;
+
+    // Clear previous matrix params
+    rri->new_matrix_size = -1;
+  }
+
+  // Step 3: Profit
+  return 1;
+}
+
+
+
+/*
+  local variables:
+  mode: C++
+  indent-tabs-mode: nil
+  c-basic-offset: 2
+  c-comment-only-line-offset: 0
+  c-file-offsets: ((statement-block-intro . +)
+  (label . 0)
+  (statement-cont . +)
+
+  end:
+
+  Indent with: /usr/bin/indent -ncs -nut -npcs -l 120 logstats.cc
+*/

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/memcached_remap/AUTHORS
----------------------------------------------------------------------
diff --git a/plugins/experimental/memcached_remap/AUTHORS b/plugins/experimental/memcached_remap/AUTHORS
new file mode 100644
index 0000000..945968f
--- /dev/null
+++ b/plugins/experimental/memcached_remap/AUTHORS
@@ -0,0 +1,7 @@
+Author: opensource@navyaprabha.com
+
+memcached_remap plugin
+
+========= CREDITS ===============
+Eric Balsa <er...@apache.org>
+Original concept via mysql_remap plugin

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/memcached_remap/Changelog
----------------------------------------------------------------------
diff --git a/plugins/experimental/memcached_remap/Changelog b/plugins/experimental/memcached_remap/Changelog
new file mode 100644
index 0000000..3d16c80
--- /dev/null
+++ b/plugins/experimental/memcached_remap/Changelog
@@ -0,0 +1,2 @@
+1.0.0 20-May-2011
+	* Initial Release for 2.1.8-unstable apache release

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/memcached_remap/NOTICE
----------------------------------------------------------------------
diff --git a/plugins/experimental/memcached_remap/NOTICE b/plugins/experimental/memcached_remap/NOTICE
new file mode 100644
index 0000000..c19f064
--- /dev/null
+++ b/plugins/experimental/memcached_remap/NOTICE
@@ -0,0 +1,5 @@
+Apache Traffic Server
+Copyright 2010 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).