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 2014/03/19 01:45:06 UTC

[1/2] git commit: TS-2646 regex_remap: Add an option for @caseless, case insentive regexes.

Repository: trafficserver
Updated Branches:
  refs/heads/master ee82dd80a -> df0938afc


TS-2646 regex_remap: Add an option for @caseless, case insentive regexes.

Cleanup, and allow (some) config to work without parameters. Also adds docs.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/ba7d394e
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/ba7d394e
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/ba7d394e

Branch: refs/heads/master
Commit: ba7d394ea8c6edf5b7a0e2294bdc71316b5e87c1
Parents: ee82dd8
Author: Leif Hedstrom <zw...@apache.org>
Authored: Fri Mar 14 21:26:46 2014 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Tue Mar 18 18:18:55 2014 -0600

----------------------------------------------------------------------
 CHANGES                                  |  2 +
 doc/reference/plugins/regex_remap.en.rst |  5 ++
 plugins/regex_remap/regex_remap.cc       | 86 +++++++++++++++++----------
 3 files changed, 62 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ba7d394e/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index ed049c7..5de3a18 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 5.0.0
 
+  *) [TS-2646] regex_remap: Add a new option, @caseless.
+
   *) [TS-2647] atscppapi: Bug fixes in headers and atscppapi
 
   *) [TS-2598] Expose HttpDebugNames to public plugin APIs.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ba7d394e/doc/reference/plugins/regex_remap.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/plugins/regex_remap.en.rst b/doc/reference/plugins/regex_remap.en.rst
index e9093ce..cb9d9f0 100644
--- a/doc/reference/plugins/regex_remap.en.rst
+++ b/doc/reference/plugins/regex_remap.en.rst
@@ -123,8 +123,13 @@ remap.config. The following options are available ::
     @no_activity_timeout=<nnn>  - No activity timeout (in ms)
     @connect_timeout=<nnn>      - Connect timeouts (in ms)
     @dns_timeout=<nnn>          - Connect timeouts (in ms)
+
     @overridable-config=<value> - see :ref:`ts-overridable-config`
 
+    @caseless                   - Make regular expressions case insensitive
+    @lowercase_substitutions    - Turn on (enable) lower case substitutions
+
+
 This can be useful to force a particular response for some URLs, e.g. ::
 
     ^/(ogre.*)/bad      http://www.examle.com/  @status=404

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/ba7d394e/plugins/regex_remap/regex_remap.cc
----------------------------------------------------------------------
diff --git a/plugins/regex_remap/regex_remap.cc b/plugins/regex_remap/regex_remap.cc
index 519ba25..1f98442 100644
--- a/plugins/regex_remap/regex_remap.cc
+++ b/plugins/regex_remap/regex_remap.cc
@@ -117,7 +117,8 @@ class RemapRegex
 {
  public:
   RemapRegex(const std::string& reg, const std::string& sub, const std::string& opt) :
-    _num_subs(-1), _rex(NULL), _extra(NULL), _order(-1), _simple(false), _lowercase_substitutions(false),
+    _num_subs(-1), _rex(NULL), _extra(NULL), _options(0), _order(-1),
+    _simple(false), _lowercase_substitutions(false),
     _active_timeout(-1), _no_activity_timeout(-1), _connect_timeout(-1), _dns_timeout(-1),
     _first_override(NULL)
   {
@@ -158,29 +159,38 @@ class RemapRegex
 
       ++start;
       pos1 = opt.find_first_of("=", start);
-      if (pos1 == std::string::npos) {
+      pos2 = opt.find_first_of(" \t\n", pos1);
+      if (pos2 == std::string::npos) {
+        pos2 = opt.length();
+      }
+
+      if (pos1 != std::string::npos) {
+        // Get the value as well
+        ++pos1;
+        opt_val = opt.substr(pos1, pos2-pos1);
+      }
+
+      // These take an option 0|1 value, without value it implies 1
+      if (opt.compare(start, 8, "caseless") == 0) {
+        _options |= PCRE_CASELESS;
+      } else if (opt.compare(start, 23, "lowercase_substitutions") == 0) {
+        _lowercase_substitutions = true;
+      } else if (opt_val.size() <= 0) {
+        // All other options have a required value
         TSError("Malformed options: %s", opt.c_str());
         break;
       }
-      ++pos1;
-      pos2 = opt.find_first_of(" \t\n", pos1);
-      if (pos2 == std::string::npos)
-        pos2 = opt.length();
-      opt_val = opt.substr(pos1, pos2-pos1);
 
       if (opt.compare(start, 6, "status") == 0) {
-        _status = static_cast<TSHttpStatus>(atoi(opt_val.c_str()));
+        _status = static_cast<TSHttpStatus>(strtol(opt_val.c_str(), NULL, 10));
       } else if (opt.compare(start, 14, "active_timeout") == 0) {
-        _active_timeout = atoi(opt_val.c_str());
+        _active_timeout = strtol(opt_val.c_str(), NULL, 10);
       } else if (opt.compare(start, 19, "no_activity_timeout") == 0) {
-        _no_activity_timeout = atoi(opt_val.c_str());
+        _no_activity_timeout = strtol(opt_val.c_str(), NULL, 10);
       } else if (opt.compare(start, 15, "connect_timeout") == 0) {
-        _connect_timeout = atoi(opt_val.c_str());
+        _connect_timeout = strtol(opt_val.c_str(), NULL, 10);
       } else if (opt.compare(start, 11, "dns_timeout") == 0) {
-        _dns_timeout = atoi(opt_val.c_str());
-      } else if (opt.compare(start, 23, "lowercase_substitutions") == 0) {
-        _lowercase_substitutions = atoi(opt_val.c_str());
-        TSDebug(PLUGIN_NAME, "Lowercasing %d", _lowercase_substitutions);
+        _dns_timeout = strtol(opt_val.c_str(), NULL, 10);
       } else {
         TSOverridableConfigKey key;
         TSRecordDataType type;
@@ -229,15 +239,20 @@ class RemapRegex
   ~RemapRegex()
   {
     TSDebug(PLUGIN_NAME, "Calling destructor");
-    if (_rex_string)
+
+    if (_rex_string) {
       TSfree(_rex_string);
-    if (_subst)
+    }
+    if (_subst) {
       TSfree(_subst);
+    }
 
-    if (_rex)
+    if (_rex) {
       pcre_free(_rex);
-    if (_extra)
+    }
+    if (_extra) {
       pcre_free(_extra);
+    }
   }
 
   // For profiling information
@@ -261,20 +276,23 @@ class RemapRegex
     int ccount;
 
     _rex = pcre_compile(_rex_string,          // the pattern
-                        0,                    // default options
+                        _options,             // options
                         error,                // for error message
                         erroffset,            // for error offset
                         NULL);                // use default character tables
 
-    if (NULL == _rex)
+    if (NULL == _rex) {
       return -1;
+    }
 
     _extra = pcre_study(_rex, 0, error);
-    if ((_extra == NULL) && (*error != 0))
+    if ((_extra == NULL) && (*error != 0)) {
       return -1;
+    }
 
-    if (pcre_fullinfo(_rex, _extra, PCRE_INFO_CAPTURECOUNT, &ccount) != 0)
+    if (pcre_fullinfo(_rex, _extra, PCRE_INFO_CAPTURECOUNT, &ccount) != 0) {
       return -1;
+    }
 
     // Get some info for the string substitutions
     str = _subst;
@@ -550,6 +568,7 @@ class RemapRegex
 
   pcre* _rex;
   pcre_extra* _extra;
+  int _options;
   int _sub_pos[MAX_SUBS];
   int _sub_ix[MAX_SUBS];
   RemapRegex* _next;
@@ -706,11 +725,13 @@ TSRemapNewInstance(int argc, char* argv[], void** ih, char* /* errbuf ATS_UNUSED
 
     getline(f, line);
     ++lineno;
-    if (line.empty())
+    if (line.empty()) {
       continue;
+    }
     pos1 = line.find_first_not_of(" \t\n");
-    if (line[pos1] == '#')
+    if (line[pos1] == '#') {
       continue;  // Skip comment lines
+    }
 
     if (pos1 != std::string::npos) {
       pos2 = line.find_first_of(" \t\n", pos1);
@@ -760,10 +781,11 @@ TSRemapNewInstance(int argc, char* argv[], void** ih, char* /* errbuf ATS_UNUSED
       TSDebug(PLUGIN_NAME, "Added regex=%s with subs=%s and options `%s'",
                regex.c_str(), subst.c_str(), options.c_str());
       cur->set_order(++count);
-      if (ri->first == NULL)
+      if (ri->first == NULL) {
         ri->first = cur;
-      else
+      } else {
         ri->last->set_next(cur);
+      }
       ri->last = cur;
     }
 
@@ -790,9 +812,9 @@ TSRemapDeleteInstance(void* ih)
     char now[64];
     const ink_time_t tim = time(NULL);
 
-    if (ink_ctime_r(&tim, now))
+    if (ink_ctime_r(&tim, now)) {
       now[strlen(now) - 1] = '\0';
-    else {
+    } else {
       memcpy(now, "unknown time", 12);
       *(now + 12) = '\0';
     }
@@ -870,8 +892,9 @@ TSRemapDoRemap(void* ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
     if (TS_SUCCESS == TSHttpTxnClientReqGet(static_cast<TSHttpTxn>(txnp), &mBuf, &reqHttpHdrLoc)) {
       method = TSHttpHdrMethodGet(mBuf, reqHttpHdrLoc, &match_len);
       if (method && (match_len > 0)) {
-        if (match_len > 16)
+        if (match_len > 16) {
           match_len = 16;
+        }
         memcpy(match_buf, method, match_len);
       }
     }
@@ -997,8 +1020,9 @@ TSRemapDoRemap(void* ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
     re = re->next();
     if (re == NULL) {
       retval = TSREMAP_NO_REMAP; // No match
-      if (ri->profile)
+      if (ri->profile) {
         ink_atomic_increment(&(ri->misses), 1);
+      }
     }
   }
 


[2/2] git commit: TS-2646 Make the overall code slightly less hideous

Posted by zw...@apache.org.
TS-2646 Make the overall code slightly less hideous


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/df0938af
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/df0938af
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/df0938af

Branch: refs/heads/master
Commit: df0938afc11fe2b792202b24208ffa04d876cee3
Parents: ba7d394
Author: Leif Hedstrom <zw...@apache.org>
Authored: Tue Mar 18 18:44:40 2014 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Tue Mar 18 18:44:40 2014 -0600

----------------------------------------------------------------------
 plugins/regex_remap/regex_remap.cc | 748 ++++++++++++++++----------------
 1 file changed, 377 insertions(+), 371 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/df0938af/plugins/regex_remap/regex_remap.cc
----------------------------------------------------------------------
diff --git a/plugins/regex_remap/regex_remap.cc b/plugins/regex_remap/regex_remap.cc
index 1f98442..416f90c 100644
--- a/plugins/regex_remap/regex_remap.cc
+++ b/plugins/regex_remap/regex_remap.cc
@@ -116,136 +116,20 @@ struct UrlComponents
 class RemapRegex
 {
  public:
-  RemapRegex(const std::string& reg, const std::string& sub, const std::string& opt) :
+  RemapRegex() :
     _num_subs(-1), _rex(NULL), _extra(NULL), _options(0), _order(-1),
     _simple(false), _lowercase_substitutions(false),
     _active_timeout(-1), _no_activity_timeout(-1), _connect_timeout(-1), _dns_timeout(-1),
     _first_override(NULL)
   {
     TSDebug(PLUGIN_NAME, "Calling constructor");
-
-    _status = static_cast<TSHttpStatus>(0);
-
-    if (!reg.empty()) {
-      if (reg == ".") {
-        TSDebug(PLUGIN_NAME, "Rule is simple, and fast!");
-        _simple = true;
-      }
-      _rex_string = TSstrdup(reg.c_str());
-    } else
-      _rex_string = NULL;
-
-    if (!sub.empty()) {
-      _subst = TSstrdup(sub.c_str());
-      _subst_len = sub.length();
-    } else {
-      _subst = NULL;
-      _subst_len = 0;
-    }
-
-    _hits = 0;
-
-    memset(_sub_pos, 0, sizeof(_sub_pos));
-    memset(_sub_ix, 0, sizeof(_sub_ix));
-    _next = NULL;
-
-    // Parse options
-    std::string::size_type start = opt.find_first_of("@");
-    std::string::size_type pos1, pos2;
-    Override* last_override = NULL;
-
-    while (start != std::string::npos) {
-      std::string opt_val;
-
-      ++start;
-      pos1 = opt.find_first_of("=", start);
-      pos2 = opt.find_first_of(" \t\n", pos1);
-      if (pos2 == std::string::npos) {
-        pos2 = opt.length();
-      }
-
-      if (pos1 != std::string::npos) {
-        // Get the value as well
-        ++pos1;
-        opt_val = opt.substr(pos1, pos2-pos1);
-      }
-
-      // These take an option 0|1 value, without value it implies 1
-      if (opt.compare(start, 8, "caseless") == 0) {
-        _options |= PCRE_CASELESS;
-      } else if (opt.compare(start, 23, "lowercase_substitutions") == 0) {
-        _lowercase_substitutions = true;
-      } else if (opt_val.size() <= 0) {
-        // All other options have a required value
-        TSError("Malformed options: %s", opt.c_str());
-        break;
-      }
-
-      if (opt.compare(start, 6, "status") == 0) {
-        _status = static_cast<TSHttpStatus>(strtol(opt_val.c_str(), NULL, 10));
-      } else if (opt.compare(start, 14, "active_timeout") == 0) {
-        _active_timeout = strtol(opt_val.c_str(), NULL, 10);
-      } else if (opt.compare(start, 19, "no_activity_timeout") == 0) {
-        _no_activity_timeout = strtol(opt_val.c_str(), NULL, 10);
-      } else if (opt.compare(start, 15, "connect_timeout") == 0) {
-        _connect_timeout = strtol(opt_val.c_str(), NULL, 10);
-      } else if (opt.compare(start, 11, "dns_timeout") == 0) {
-        _dns_timeout = strtol(opt_val.c_str(), NULL, 10);
-      } else {
-        TSOverridableConfigKey key;
-        TSRecordDataType type;
-        std::string opt_name = opt.substr(start, pos1-start-1);
-
-        if (TS_SUCCESS == TSHttpTxnConfigFind(opt_name.c_str(), opt_name.length(), &key, &type)) {
-          Override* cur = new Override;
-
-          switch (type) {
-          case TS_RECORDDATATYPE_INT:
-            cur->data.rec_int = strtoll(opt_val.c_str(), NULL, 10);
-            break;
-          case TS_RECORDDATATYPE_FLOAT:
-            cur->data.rec_float = strtof(opt_val.c_str(), NULL);
-            break;
-          case TS_RECORDDATATYPE_STRING:
-            cur->data.rec_string = TSstrdup(opt_val.c_str());
-            cur->data_len = opt_val.size();
-            break;
-          default:
-            delete cur;
-            cur = NULL;
-            TSError("%s: configuration variable '%s' is of an unsupported type", PLUGIN_NAME, opt_name.c_str());
-            break;
-          }
-          if (cur) {
-            TSDebug(PLUGIN_NAME, "Overridable config %s=%s", opt_name.c_str(), opt_val.c_str());
-            cur->key = key;
-            cur->type = type;
-            cur->next = NULL;
-            if (NULL == last_override) {
-              _first_override = cur;
-            } else {
-              last_override->next = cur;
-            }
-            last_override = cur;
-          }
-        } else {
-          TSError("Unknown options: %s", opt.c_str());
-        }
-      }
-      start = opt.find_first_of("@", pos2);
-    }
   }
 
   ~RemapRegex()
   {
     TSDebug(PLUGIN_NAME, "Calling destructor");
-
-    if (_rex_string) {
-      TSfree(_rex_string);
-    }
-    if (_subst) {
-      TSfree(_subst);
-    }
+    TSfree(_rex_string);
+    TSfree(_subst);
 
     if (_rex) {
       pcre_free(_rex);
@@ -255,111 +139,16 @@ class RemapRegex
     }
   }
 
+  bool initialize(const std::string& reg, const std::string& sub, const std::string& opt);
+
   // For profiling information
-  inline void
-  print(int ix, int max, const char* now)
+  void increment() { ink_atomic_increment(&(_hits), 1); }
+  void print(int ix, int max, const char* now)
   {
     fprintf(stderr, "[%s]:    Regex %d ( %s ): %.2f%%\n", now, ix, _rex_string, 100.0 * _hits / max);
   }
 
-  inline void
-  increment()
-  {
-    ink_atomic_increment(&(_hits), 1);
-  }
-
-  // Compile and study the regular expression.
-  int
-  compile(const char** error, int* erroffset)
-  {
-    char* str;
-    int ccount;
-
-    _rex = pcre_compile(_rex_string,          // the pattern
-                        _options,             // options
-                        error,                // for error message
-                        erroffset,            // for error offset
-                        NULL);                // use default character tables
-
-    if (NULL == _rex) {
-      return -1;
-    }
-
-    _extra = pcre_study(_rex, 0, error);
-    if ((_extra == NULL) && (*error != 0)) {
-      return -1;
-    }
-
-    if (pcre_fullinfo(_rex, _extra, PCRE_INFO_CAPTURECOUNT, &ccount) != 0) {
-      return -1;
-    }
-
-    // Get some info for the string substitutions
-    str = _subst;
-    _num_subs = 0;
-
-    while (str && *str) {
-      if ('$' == *str) {
-        int ix = -1;
-
-        if (isdigit(*(str+1))) {
-          ix = *(str + 1) - '0';
-        } else {
-          switch (*(str + 1)) {
-          case 'h':
-            ix = SUB_HOST;
-            break;
-          case 'f':
-            ix = SUB_FROM_HOST;
-            break;
-          case 't':
-            ix = SUB_TO_HOST;
-            break;
-          case 'p':
-            ix = SUB_PORT;
-            break;
-          case 's':
-            ix = SUB_SCHEME;
-            break;
-          case 'P':
-            ix = SUB_PATH;
-            break;
-          case 'l':
-            ix = SUB_LOWER_PATH;
-            break;
-          case 'q':
-            ix = SUB_QUERY;
-            break;
-          case 'm':
-            ix = SUB_MATRIX;
-            break;
-          case 'i':
-            ix = SUB_CLIENT_IP;
-            break;
-          default:
-            break;
-          }
-        }
-
-        if (ix > -1) {
-          if ((ix < 10) && (ix > ccount)) {
-            TSDebug(PLUGIN_NAME, "Trying to use unavailable substitution, check the regex!");
-            return -1; // No substitutions available other than $0
-          }
-
-          _sub_ix[_num_subs] = ix;
-          _sub_pos[_num_subs] = (str - _subst);
-          str += 2;
-          ++_num_subs;
-        } else { // Not a valid substitution character, so just ignore it
-          ++str;
-        }
-      } else {
-        ++str;
-      }
-    }
-    return 0;
-  }
+  int compile(const char** error, int* erroffset);
 
   // Perform the regular expression matching against a string.
   int
@@ -375,156 +164,10 @@ class RemapRegex
                      OVECCOUNT);           // number of elements in the output vector
   }
 
-  // Get the lengths of the matching string(s), taking into account variable substitutions.
-  // We also calculate a total length for the new string, which is the max length the
-  // substituted string can have (use it to allocate a buffer before calling substitute() ).
-  int
-  get_lengths(const int ovector[], int lengths[], TSRemapRequestInfo *rri, UrlComponents *req_url)
-  {
-    int len = _subst_len + 1;   // Bigger then necessary
-
-    for (int i=0; i < _num_subs; i++) {
-      int ix = _sub_ix[i];
-
-      if (ix < 10) {
-        lengths[ix] = ovector[2*ix+1] - ovector[2*ix]; // -1 - -1 == 0
-        len += lengths[ix];
-      } else {
-        int tmp_len;
-
-        switch (ix) {
-        case SUB_HOST:
-          len += req_url->host_len;
-          break;
-        case SUB_FROM_HOST:
-          TSUrlHostGet(rri->requestBufp, rri->mapFromUrl, &tmp_len);
-          len += tmp_len;
-          break;
-        case SUB_TO_HOST:
-          TSUrlHostGet(rri->requestBufp, rri->mapToUrl, &tmp_len);
-          len += tmp_len;
-          break;
-        case SUB_PORT:
-          len += 6; // One extra for snprintf()
-          break;
-        case SUB_SCHEME:
-          len += req_url->scheme_len;
-          break;
-        case SUB_PATH:
-        case SUB_LOWER_PATH:
-          len += req_url->path_len;
-          break;
-        case SUB_QUERY:
-          len += req_url->query_len;
-          break;
-        case SUB_MATRIX:
-          len += req_url->matrix_len;
-          break;
-        case SUB_CLIENT_IP:
-          len += 15; // Allow for 255.255.255.255
-          break;
-        default:
-          break;
-        }
-      }
-    }
-
-    return len;
-  }
-
-  // Perform substitution on the $0 - $9 variables in the "src" string. $0 is the entire
-  // regex that was matches, while $1 - $9 are the corresponding groups. Return the final
-  // length of the string as written to dest (not including the trailing '0').
-  int
-  substitute(char dest[], const char *src, const int ovector[], const int lengths[],
-             TSRemapRequestInfo *rri, UrlComponents *req_url, bool lowercase_substitutions)
-  {
-    if (_num_subs > 0) {
-      char* p1 = dest;
-      char* p2 = _subst;
-      int prev = 0;
-
-      for (int i=0; i < _num_subs; i++) {
-        char *start = p1;
-        int ix = _sub_ix[i];
-
-        memcpy(p1, p2, _sub_pos[i] - prev);
-        p1 += (_sub_pos[i] - prev);
-        if (ix < 10) {
-          memcpy(p1, src + ovector[2*ix], lengths[ix]);
-          p1 += lengths[ix];
-        } else {
-          const char* str = NULL;
-          int len = 0;
-
-          switch (ix) {
-          case SUB_HOST:
-            str = req_url->host;
-            len = req_url->host_len;
-            break;
-          case SUB_FROM_HOST:
-            str = TSUrlHostGet(rri->requestBufp, rri->mapFromUrl, &len);
-            break;
-          case SUB_TO_HOST:
-            str = TSUrlHostGet(rri->requestBufp, rri->mapToUrl, &len);
-            break;
-          case SUB_PORT:
-            p1 += snprintf(p1, 6, "%u", req_url->port);
-            break;
-          case SUB_SCHEME:
-            str = req_url->scheme;
-            len = req_url->scheme_len;
-            break;
-          case SUB_PATH:
-          case SUB_LOWER_PATH:
-            str = req_url->path;
-            len = req_url->path_len;
-            break;
-          case SUB_QUERY:
-            str = req_url->query;
-            len = req_url->query_len;
-            break;
-          case SUB_MATRIX:
-            str = req_url->matrix;
-            len = req_url->matrix_len;
-            break;
-          case SUB_CLIENT_IP:
-            {
-              // TODO: Finish implementing with the addr from above
-              // p1 += snprintf(p1, 15, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
-            }
-            break;
-          default:
-            break;
-          }
-          // If one of the rules fetched a read-only string, copy it in.
-          if (str && len > 0) {
-            memcpy(p1, str, len);
-            p1 += len;
-          }
-        }
-        p2 += (_sub_pos[i] - prev + 2);
-        prev = _sub_pos[i] + 2;
-
-        if (lowercase_substitutions == true || ix == SUB_LOWER_PATH) {
-           while (start < p1) {
-             *start = tolower(*start);
-             start++;
-            }
-         }
-      }
-
-      memcpy(p1, p2, _subst_len - (p2 - _subst));
-      p1 += _subst_len - (p2 - _subst);
-      *p1 = 0; // Make sure it's NULL terminated (for safety).
-      return p1 - dest;
-    } else {
-      memcpy(dest, _subst, _subst_len + 1); // No substitutions in the string, copy it all
-      return _subst_len;
-    }
-
-    return 0; // Shouldn't happen.
-  }
+  // Substitutions
+  int get_lengths(const int ovector[], int lengths[], TSRemapRequestInfo *rri, UrlComponents *req_url);
+  int substitute(char dest[], const char *src, const int ovector[], const int lengths[],
+                 TSRemapRequestInfo *rri, UrlComponents *req_url, bool lowercase_substitutions);
 
   // setter / getters for members the linked list.
   inline void set_next(RemapRegex* next) { _next = next; }
@@ -584,6 +227,368 @@ class RemapRegex
   Override* _first_override;
 };
 
+bool
+RemapRegex::initialize(const std::string& reg, const std::string& sub, const std::string& opt)
+{
+  _status = static_cast<TSHttpStatus>(0);
+
+  if (!reg.empty()) {
+    if (reg == ".") {
+      TSDebug(PLUGIN_NAME, "Rule is simple, and fast!");
+      _simple = true;
+    }
+    _rex_string = TSstrdup(reg.c_str());
+  } else
+    _rex_string = NULL;
+
+  if (!sub.empty()) {
+    _subst = TSstrdup(sub.c_str());
+    _subst_len = sub.length();
+  } else {
+    _subst = NULL;
+    _subst_len = 0;
+  }
+
+  _hits = 0;
+
+  memset(_sub_pos, 0, sizeof(_sub_pos));
+  memset(_sub_ix, 0, sizeof(_sub_ix));
+  _next = NULL;
+
+  // Parse options
+  std::string::size_type start = opt.find_first_of("@");
+  std::string::size_type pos1, pos2;
+  Override* last_override = NULL;
+
+  while (start != std::string::npos) {
+    std::string opt_val;
+
+    ++start;
+    pos1 = opt.find_first_of("=", start);
+    pos2 = opt.find_first_of(" \t\n", pos1);
+    if (pos2 == std::string::npos) {
+      pos2 = opt.length();
+    }
+
+    if (pos1 != std::string::npos) {
+      // Get the value as well
+      ++pos1;
+      opt_val = opt.substr(pos1, pos2-pos1);
+    }
+
+    // These take an option 0|1 value, without value it implies 1
+    if (opt.compare(start, 8, "caseless") == 0) {
+      _options |= PCRE_CASELESS;
+    } else if (opt.compare(start, 23, "lowercase_substitutions") == 0) {
+      _lowercase_substitutions = true;
+    } else if (opt_val.size() <= 0) {
+      // All other options have a required value
+      TSError("Malformed options: %s", opt.c_str());
+      break;
+    }
+
+    if (opt.compare(start, 6, "status") == 0) {
+      _status = static_cast<TSHttpStatus>(strtol(opt_val.c_str(), NULL, 10));
+    } else if (opt.compare(start, 14, "active_timeout") == 0) {
+      _active_timeout = strtol(opt_val.c_str(), NULL, 10);
+    } else if (opt.compare(start, 19, "no_activity_timeout") == 0) {
+      _no_activity_timeout = strtol(opt_val.c_str(), NULL, 10);
+    } else if (opt.compare(start, 15, "connect_timeout") == 0) {
+      _connect_timeout = strtol(opt_val.c_str(), NULL, 10);
+    } else if (opt.compare(start, 11, "dns_timeout") == 0) {
+      _dns_timeout = strtol(opt_val.c_str(), NULL, 10);
+    } else {
+      TSOverridableConfigKey key;
+      TSRecordDataType type;
+      std::string opt_name = opt.substr(start, pos1-start-1);
+
+      if (TS_SUCCESS == TSHttpTxnConfigFind(opt_name.c_str(), opt_name.length(), &key, &type)) {
+        Override* cur = new Override;
+
+        switch (type) {
+        case TS_RECORDDATATYPE_INT:
+          cur->data.rec_int = strtoll(opt_val.c_str(), NULL, 10);
+          break;
+        case TS_RECORDDATATYPE_FLOAT:
+          cur->data.rec_float = strtof(opt_val.c_str(), NULL);
+          break;
+        case TS_RECORDDATATYPE_STRING:
+          cur->data.rec_string = TSstrdup(opt_val.c_str());
+          cur->data_len = opt_val.size();
+          break;
+        default:
+          TSError("%s: configuration variable '%s' is of an unsupported type", PLUGIN_NAME, opt_name.c_str());
+          return false;
+          break;
+        }
+        if (cur) {
+          TSDebug(PLUGIN_NAME, "Overridable config %s=%s", opt_name.c_str(), opt_val.c_str());
+          cur->key = key;
+          cur->type = type;
+          cur->next = NULL;
+          if (NULL == last_override) {
+            _first_override = cur;
+          } else {
+            last_override->next = cur;
+          }
+          last_override = cur;
+        }
+      } else {
+        TSError("Unknown options: %s", opt.c_str());
+      }
+    }
+    start = opt.find_first_of("@", pos2);
+  }
+
+  return true;
+}
+
+// Compile and study the regular expression.
+int
+RemapRegex::compile(const char** error, int* erroffset)
+{
+  char* str;
+  int ccount;
+
+  _rex = pcre_compile(_rex_string,          // the pattern
+                      _options,             // options
+                      error,                // for error message
+                      erroffset,            // for error offset
+                      NULL);                // use default character tables
+
+  if (NULL == _rex) {
+    return -1;
+  }
+
+  _extra = pcre_study(_rex, 0, error);
+  if ((_extra == NULL) && (*error != 0)) {
+    return -1;
+  }
+
+  if (pcre_fullinfo(_rex, _extra, PCRE_INFO_CAPTURECOUNT, &ccount) != 0) {
+    return -1;
+  }
+
+  // Get some info for the string substitutions
+  str = _subst;
+  _num_subs = 0;
+
+  while (str && *str) {
+    if ('$' == *str) {
+      int ix = -1;
+
+      if (isdigit(*(str+1))) {
+        ix = *(str + 1) - '0';
+      } else {
+        switch (*(str + 1)) {
+        case 'h':
+          ix = SUB_HOST;
+          break;
+        case 'f':
+          ix = SUB_FROM_HOST;
+          break;
+        case 't':
+          ix = SUB_TO_HOST;
+          break;
+        case 'p':
+          ix = SUB_PORT;
+          break;
+        case 's':
+          ix = SUB_SCHEME;
+          break;
+        case 'P':
+          ix = SUB_PATH;
+          break;
+        case 'l':
+          ix = SUB_LOWER_PATH;
+          break;
+        case 'q':
+          ix = SUB_QUERY;
+          break;
+        case 'm':
+          ix = SUB_MATRIX;
+          break;
+        case 'i':
+          ix = SUB_CLIENT_IP;
+          break;
+        default:
+          break;
+        }
+      }
+
+      if (ix > -1) {
+        if ((ix < 10) && (ix > ccount)) {
+          TSDebug(PLUGIN_NAME, "Trying to use unavailable substitution, check the regex!");
+          return -1; // No substitutions available other than $0
+        }
+
+        _sub_ix[_num_subs] = ix;
+        _sub_pos[_num_subs] = (str - _subst);
+        str += 2;
+        ++_num_subs;
+      } else { // Not a valid substitution character, so just ignore it
+        ++str;
+      }
+    } else {
+      ++str;
+    }
+  }
+  return 0;
+}
+
+// Get the lengths of the matching string(s), taking into account variable substitutions.
+// We also calculate a total length for the new string, which is the max length the
+// substituted string can have (use it to allocate a buffer before calling substitute() ).
+int
+RemapRegex::get_lengths(const int ovector[], int lengths[], TSRemapRequestInfo *rri, UrlComponents *req_url)
+{
+  int len = _subst_len + 1;   // Bigger then necessary
+
+  for (int i=0; i < _num_subs; i++) {
+    int ix = _sub_ix[i];
+
+    if (ix < 10) {
+      lengths[ix] = ovector[2*ix+1] - ovector[2*ix]; // -1 - -1 == 0
+      len += lengths[ix];
+    } else {
+      int tmp_len;
+
+      switch (ix) {
+      case SUB_HOST:
+        len += req_url->host_len;
+        break;
+      case SUB_FROM_HOST:
+        TSUrlHostGet(rri->requestBufp, rri->mapFromUrl, &tmp_len);
+        len += tmp_len;
+        break;
+      case SUB_TO_HOST:
+        TSUrlHostGet(rri->requestBufp, rri->mapToUrl, &tmp_len);
+        len += tmp_len;
+        break;
+      case SUB_PORT:
+        len += 6; // One extra for snprintf()
+        break;
+      case SUB_SCHEME:
+        len += req_url->scheme_len;
+        break;
+      case SUB_PATH:
+      case SUB_LOWER_PATH:
+        len += req_url->path_len;
+        break;
+      case SUB_QUERY:
+        len += req_url->query_len;
+        break;
+      case SUB_MATRIX:
+        len += req_url->matrix_len;
+        break;
+      case SUB_CLIENT_IP:
+        len += 15; // Allow for 255.255.255.255
+        break;
+      default:
+        break;
+      }
+    }
+  }
+
+  return len;
+}
+
+// Perform substitution on the $0 - $9 variables in the "src" string. $0 is the entire
+// regex that was matches, while $1 - $9 are the corresponding groups. Return the final
+// length of the string as written to dest (not including the trailing '0').
+int
+RemapRegex::substitute(char dest[], const char *src, const int ovector[], const int lengths[],
+                       TSRemapRequestInfo *rri, UrlComponents *req_url, bool lowercase_substitutions)
+{
+  if (_num_subs > 0) {
+    char* p1 = dest;
+    char* p2 = _subst;
+    int prev = 0;
+
+    for (int i=0; i < _num_subs; i++) {
+      char *start = p1;
+      int ix = _sub_ix[i];
+
+      memcpy(p1, p2, _sub_pos[i] - prev);
+      p1 += (_sub_pos[i] - prev);
+      if (ix < 10) {
+        memcpy(p1, src + ovector[2*ix], lengths[ix]);
+        p1 += lengths[ix];
+      } else {
+        const char* str = NULL;
+        int len = 0;
+
+        switch (ix) {
+        case SUB_HOST:
+          str = req_url->host;
+          len = req_url->host_len;
+          break;
+        case SUB_FROM_HOST:
+          str = TSUrlHostGet(rri->requestBufp, rri->mapFromUrl, &len);
+          break;
+        case SUB_TO_HOST:
+          str = TSUrlHostGet(rri->requestBufp, rri->mapToUrl, &len);
+          break;
+        case SUB_PORT:
+          p1 += snprintf(p1, 6, "%u", req_url->port);
+          break;
+        case SUB_SCHEME:
+          str = req_url->scheme;
+          len = req_url->scheme_len;
+          break;
+        case SUB_PATH:
+        case SUB_LOWER_PATH:
+          str = req_url->path;
+          len = req_url->path_len;
+          break;
+        case SUB_QUERY:
+          str = req_url->query;
+          len = req_url->query_len;
+          break;
+        case SUB_MATRIX:
+          str = req_url->matrix;
+          len = req_url->matrix_len;
+          break;
+        case SUB_CLIENT_IP:
+          {
+            // TODO: Finish implementing with the addr from above
+            // p1 += snprintf(p1, 15, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+          }
+          break;
+        default:
+          break;
+        }
+        // If one of the rules fetched a read-only string, copy it in.
+        if (str && len > 0) {
+          memcpy(p1, str, len);
+          p1 += len;
+        }
+      }
+      p2 += (_sub_pos[i] - prev + 2);
+      prev = _sub_pos[i] + 2;
+
+      if (lowercase_substitutions == true || ix == SUB_LOWER_PATH) {
+        while (start < p1) {
+          *start = tolower(*start);
+          start++;
+        }
+      }
+    }
+
+    memcpy(p1, p2, _subst_len - (p2 - _subst));
+    p1 += _subst_len - (p2 - _subst);
+    *p1 = 0; // Make sure it's NULL terminated (for safety).
+    return p1 - dest;
+  } else {
+    memcpy(dest, _subst, _subst_len + 1); // No substitutions in the string, copy it all
+    return _subst_len;
+  }
+
+  return 0; // Shouldn't happen.
+}
+
+
+// Hold one remap instance
 struct RemapInstance
 {
   RemapInstance() :
@@ -766,10 +771,11 @@ TSRemapNewInstance(int argc, char* argv[], void** ih, char* /* errbuf ATS_UNUSED
     }
 
     // Got a regex and substitution string
-    RemapRegex* cur = new RemapRegex(regex, subst, options);
+    RemapRegex* cur = new RemapRegex();
 
-    if (cur == NULL) {
+    if (!cur->initialize(regex, subst, options)) {
       TSError("%s: can't create a new regex remap rule", PLUGIN_NAME);
+      delete cur;
       continue;
     }