TS-2316: header_rewrite: added cookie-based conditions

Submitted by: Alexey Ivanov <aiva...@linkedin.com>
Sponsored by: LinkedIn


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

Branch: refs/heads/5.0.x
Commit: 3d37f51d1b1be4222dbb33afd2692e4a6ab11de7
Parents: b79c907
Author: Alexey Ivanov <aiva...@linkedin.com>
Authored: Tue Nov 5 12:55:10 2013 -0800
Committer: Alexey Ivanov <aiva...@linkedin.com>
Committed: Sun Nov 10 18:07:53 2013 -0800

----------------------------------------------------------------------
 plugins/header_rewrite/conditions.cc | 65 +++++++++++++++++++++++++++++++
 plugins/header_rewrite/conditions.h  | 60 ++++++++++++++++++++++++++++
 plugins/header_rewrite/factory.cc    |  2 +
 3 files changed, 127 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3d37f51d/plugins/header_rewrite/conditions.cc
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/conditions.cc 
b/plugins/header_rewrite/conditions.cc
index fdbb309..25bbae6 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -385,3 +385,68 @@ ConditionDBM::eval(const Resources& res)
 
   return static_cast<const Matchers<std::string>*>(_matcher)->test(s);
 }
+
+
+// ConditionCookie: request or response header
+void ConditionCookie::initialize(Parser& p)
+{
+  Condition::initialize(p);
+
+  Matchers<std::string>* match = new Matchers<std::string>(_cond_op);
+  match->set(p.get_arg());
+
+  _matcher = match;
+
+  require_resources(RSRC_CLIENT_REQUEST_HEADERS);
+}
+
+void ConditionCookie::append_value(std::string& s, const Resources& res)
+{
+  TSMBuffer bufp = res.client_bufp;
+  TSMLoc hdr_loc = res.client_hdr_loc;
+  TSMLoc field_loc;
+  int error;
+  int cookies_len;
+  int cookie_value_len;
+  const char *cookies;
+  const char *cookie_value;
+  const char * const cookie_name = _qualifier.c_str();
+  const int cookie_name_len = _qualifier.length();
+
+  // Sanity
+  if (bufp == NULL || hdr_loc == NULL)
+    return;
+
+  // Find Cookie
+  field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_COOKIE, 
TS_MIME_LEN_COOKIE);
+  if (field_loc == NULL)
+    return;
+
+  // Get all cookies
+  // NB! Cookie field does not support commas, so we use index == 0
+  cookies = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, 0, 
&cookies_len);
+  if (cookies == NULL || cookies_len <= 0)
+    goto out_release_field;
+
+  // Find particular cookie's value
+  error = get_cookie_value(cookies, cookies_len, cookie_name, cookie_name_len, 
&cookie_value, &cookie_value_len);
+  if (error == TS_ERROR)
+    goto out_release_field;
+
+  TSDebug(PLUGIN_NAME, "Appending COOKIE(%s) to evaluation value -> %.*s", 
cookie_name, cookie_value_len, cookie_value);
+  s.append(cookie_value, cookie_value_len);
+
+  // Unwind
+out_release_field:
+  TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+}
+
+bool ConditionCookie::eval(const Resources& res)
+{
+  std::string s;
+
+  append_value(s, res);
+  bool rval = static_cast<const Matchers<std::string>*>(_matcher)->test(s);
+  TSDebug(PLUGIN_NAME, "Evaluating COOKIE(%s): %s: rval: %d", 
_qualifier.c_str(), s.c_str(), rval);
+  return rval;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3d37f51d/plugins/header_rewrite/conditions.h
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/conditions.h 
b/plugins/header_rewrite/conditions.h
index 96d1db4..2f4d093 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -25,6 +25,7 @@
 #include <string>
 #include <ts/ts.h>
 #include <boost/lexical_cast.hpp>
+#include <cstring>
 
 #include "condition.h"
 #include "matcher.h"
@@ -146,6 +147,65 @@ private:
 };
 
 
+// cookie(name)
+class ConditionCookie: public Condition
+{
+public:
+  ConditionCookie()
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionCookie");
+  }
+  void initialize(Parser& p);
+  void append_value(std::string& s, const Resources& res);
+
+protected:
+  bool eval(const Resources& res);
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(ConditionCookie);
+
+  // Nginx-style cookie parsing:
+  //   nginx/src/http/ngx_http_parse.c:ngx_http_parse_multi_header_lines()
+  inline int
+  get_cookie_value(const char *buf, int buf_len, const char *name, int 
name_len,
+        const char **value, int *value_len)
+  {
+    const char *start, *last, *end;
+
+    // Sanity
+    if (buf == NULL || name == NULL || value == NULL || value_len == NULL)
+      return TS_ERROR;
+
+    start = buf;
+    end = buf + buf_len;
+
+    while (start < end) {
+      if (strncasecmp(start, name, name_len) != 0)
+        goto skip;
+
+      for (start += name_len; start < end && *start == ' '; start++);
+
+      if (start == end || *start++ != '=')
+        goto skip;
+
+      while (start < end && *start == ' ') { start++; }
+      for (last = start; last < end && *last != ';'; last++);
+
+      *value_len = last - start;
+      *value = start;
+      return TS_SUCCESS;
+skip:
+      while (start < end) {
+        char ch = *start++;
+        if (ch == ';' || ch == ',')
+          break;
+      }
+      while (start < end && *start == ' ') { start++; }
+    }
+    return TS_ERROR;
+  };
+};
+
 // header
 class ConditionHeader : public Condition
 {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3d37f51d/plugins/header_rewrite/factory.cc
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/factory.cc 
b/plugins/header_rewrite/factory.cc
index 9af8f25..208d42a 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -89,6 +89,8 @@ condition_factory(const std::string& cond)
     c = new ConditionRandom();
   } else if (c_name == "ACCESS") {
     c = new ConditionAccess();
+  } else if (c_name == "COOKIE") {
+    c = new ConditionCookie();
   } else if (c_name == "HEADER") { // This condition adapts to the hook
     c = new ConditionHeader();
   } else if (c_name == "PATH") {

Reply via email to