Repository: trafficserver
Updated Branches:
  refs/heads/master 370ad860d -> 8c148c9e8


TS-3956: Header_rewrite applies strange logic with = operator, this
closes #300


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

Branch: refs/heads/master
Commit: 8c148c9e885ea28ead4d46f49350256602b84d82
Parents: 370ad86
Author: Brian Geffon <bri...@apache.org>
Authored: Tue Oct 6 00:06:49 2015 -0700
Committer: Brian Geffon <bri...@apache.org>
Committed: Tue Oct 6 00:15:20 2015 -0700

----------------------------------------------------------------------
 plugins/header_rewrite/Makefile.am            |   6 +-
 plugins/header_rewrite/header_rewrite_test.cc | 225 +++++++++++++++++++++
 plugins/header_rewrite/parser.cc              | 141 ++++++++-----
 plugins/header_rewrite/parser.h               |   5 +-
 4 files changed, 323 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8c148c9e/plugins/header_rewrite/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/Makefile.am 
b/plugins/header_rewrite/Makefile.am
index 8a1abf6..5a7acd3 100644
--- a/plugins/header_rewrite/Makefile.am
+++ b/plugins/header_rewrite/Makefile.am
@@ -31,5 +31,9 @@ header_rewrite_la_SOURCES = \
   resources.cc \
   ruleset.cc \
   statement.cc
-
+  
 header_rewrite_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)
+
+bin_PROGRAMS = header_rewrite_test
+header_rewrite_test_SOURCES = parser.cc header_rewrite_test.cc
+header_rewrite_test_CXXFLAGS = $(AM_CXXFLAGS)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8c148c9e/plugins/header_rewrite/header_rewrite_test.cc
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/header_rewrite_test.cc 
b/plugins/header_rewrite/header_rewrite_test.cc
new file mode 100644
index 0000000..ebc9e2b
--- /dev/null
+++ b/plugins/header_rewrite/header_rewrite_test.cc
@@ -0,0 +1,225 @@
+/*
+  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.
+*/
+
+/*
+ * These are misc unit tests for header rewrite
+ */
+
+#include <cstdio>
+#include <cstdarg>
+#include <parser.h>
+
+const char PLUGIN_NAME[] = "TEST_header_rewrite";
+const char PLUGIN_NAME_DBG[] = "TEST_dbg_header_rewrite";
+
+extern "C" void TSError(const char* fmt, ...) {
+  char buf[2048];
+  int bytes = 0;
+  va_list args;
+  va_start (args, fmt);
+  if((bytes = vsnprintf (buf, sizeof(buf), fmt, args)) > 0) {
+    fprintf(stderr, "TSError: %s: %.*s\n", PLUGIN_NAME, bytes, buf);
+  }
+}
+
+extern "C" void TSDebug(const char *tag, const char* fmt, ...) {
+  char buf[2048];
+  int bytes = 0;
+  va_list args;
+  va_start (args, fmt);
+  if((bytes = vsnprintf (buf, sizeof(buf), fmt, args)) > 0) {
+    fprintf(stdout, "TSDebug: %s: %.*s\n", PLUGIN_NAME, bytes, buf);
+  }
+}
+
+#define CHECK_EQ(x, y) \
+  do { \
+   if ( (x) != (y) ) { \
+    fprintf(stderr, "CHECK FAILED " #x " != " #y "\n"); \
+    return 1; \
+   } \
+  } while (false);
+
+class ParserTest : public Parser {
+public:
+  ParserTest(std::string line) : Parser(line) { }
+
+  std::vector<std::string> getTokens() {
+    return _tokens;
+  }
+};
+
+int test_parsing() {
+
+  {
+    ParserTest p("cond      %{READ_REQUEST_HDR_HOOK}");
+    CHECK_EQ(p.getTokens().size(), 2);
+    CHECK_EQ(p.getTokens()[0], "cond");
+    CHECK_EQ(p.getTokens()[1], "%{READ_REQUEST_HDR_HOOK}");
+  }
+
+  {
+    ParserTest p("cond %{CLIENT-HEADER:Host}    =a");
+    CHECK_EQ(p.getTokens().size(), 4);
+    CHECK_EQ(p.getTokens()[0], "cond");
+    CHECK_EQ(p.getTokens()[1], "%{CLIENT-HEADER:Host}");
+    CHECK_EQ(p.getTokens()[2], "=");
+    CHECK_EQ(p.getTokens()[3], "a");
+  }
+
+  {
+    ParserTest p(" # COMMENT!");
+    CHECK_EQ(p.getTokens().size(), 0);
+    CHECK_EQ(p.empty(), true);
+  }
+
+  {
+    ParserTest p("# COMMENT");
+    CHECK_EQ(p.getTokens().size(), 0);
+    CHECK_EQ(p.empty(), true);
+  }
+
+  {
+    ParserTest p("cond %{Client-HEADER:Foo} =b");
+    CHECK_EQ(p.getTokens().size(), 4);
+    CHECK_EQ(p.getTokens()[0], "cond");
+    CHECK_EQ(p.getTokens()[1], "%{Client-HEADER:Foo}");
+    CHECK_EQ(p.getTokens()[2], "=");
+    CHECK_EQ(p.getTokens()[3], "b");
+  }
+
+  {
+    ParserTest p("cond %{Client-HEADER:Blah}       =        x");
+    CHECK_EQ(p.getTokens().size(), 4);
+    CHECK_EQ(p.getTokens()[0], "cond");
+    CHECK_EQ(p.getTokens()[1], "%{Client-HEADER:Blah}");
+    CHECK_EQ(p.getTokens()[2], "=");
+    CHECK_EQ(p.getTokens()[3], "x");
+  }
+
+  {
+    ParserTest p("cond %{CLIENT-HEADER:non_existent_header} =  \"shouldnt_   
exist    _anyway\"          [AND]");
+    CHECK_EQ(p.getTokens().size(), 5);
+    CHECK_EQ(p.getTokens()[0], "cond");
+    CHECK_EQ(p.getTokens()[1], "%{CLIENT-HEADER:non_existent_header}");
+    CHECK_EQ(p.getTokens()[2], "=");
+    CHECK_EQ(p.getTokens()[3], "shouldnt_   exist    _anyway");
+    CHECK_EQ(p.getTokens()[4], "[AND]");
+  }
+
+  {
+    ParserTest p("cond %{CLIENT-HEADER:non_existent_header} =  \"shouldnt_   = 
   _anyway\"          [AND]");
+    CHECK_EQ(p.getTokens().size(), 5);
+    CHECK_EQ(p.getTokens()[0], "cond");
+    CHECK_EQ(p.getTokens()[1], "%{CLIENT-HEADER:non_existent_header}");
+    CHECK_EQ(p.getTokens()[2], "=");
+    CHECK_EQ(p.getTokens()[3], "shouldnt_   =    _anyway");
+    CHECK_EQ(p.getTokens()[4], "[AND]");
+  }
+
+  {
+    ParserTest p("cond %{CLIENT-HEADER:non_existent_header} =\"=\"          
[AND]");
+    CHECK_EQ(p.getTokens().size(), 5);
+    CHECK_EQ(p.getTokens()[0], "cond");
+    CHECK_EQ(p.getTokens()[1], "%{CLIENT-HEADER:non_existent_header}");
+    CHECK_EQ(p.getTokens()[2], "=");
+    CHECK_EQ(p.getTokens()[3], "=");
+    CHECK_EQ(p.getTokens()[4], "[AND]");
+  }
+
+  {
+    ParserTest p("cond %{CLIENT-HEADER:non_existent_header} =\"\"          
[AND]");
+    CHECK_EQ(p.getTokens().size(), 5);
+    CHECK_EQ(p.getTokens()[0], "cond");
+    CHECK_EQ(p.getTokens()[1], "%{CLIENT-HEADER:non_existent_header}");
+    CHECK_EQ(p.getTokens()[2], "=");
+    CHECK_EQ(p.getTokens()[3], "");
+    CHECK_EQ(p.getTokens()[4], "[AND]");
+  }
+
+  {
+    ParserTest p("add-header X-HeaderRewriteApplied true");
+    CHECK_EQ(p.getTokens().size(), 3);
+    CHECK_EQ(p.getTokens()[0], "add-header");
+    CHECK_EQ(p.getTokens()[1], "X-HeaderRewriteApplied");
+    CHECK_EQ(p.getTokens()[2], "true");
+  }
+
+  /*
+   * test some failure scenarios
+   */
+
+  { /* unterminated quote */
+    ParserTest p("cond %{CLIENT-HEADER:non_existent_header} =\" [AND]");
+    CHECK_EQ(p.getTokens().size(), 0);
+  }
+
+  { /* quote in a token */
+    ParserTest p("cond %{CLIENT-HEADER:non_existent_header} =a\"b [AND]");
+    CHECK_EQ(p.getTokens().size(), 0);
+  }
+
+  return 0;
+}
+
+int test_processing() {
+
+  /*
+   * These tests are designed to verify that the processing of the parsed 
input is correct.
+   */
+  {
+    ParserTest p("cond %{CLIENT-HEADER:non_existent_header} =\"=\"          
[AND]");
+    CHECK_EQ(p.getTokens().size(), 5);
+    CHECK_EQ(p.get_op(), "CLIENT-HEADER:non_existent_header");
+    CHECK_EQ(p.get_arg(), "==");
+    CHECK_EQ(p.is_cond(), true);
+  }
+
+  {
+    ParserTest p("cond %{CLIENT-HEADER:non_existent_header} =  \"shouldnt_   = 
   _anyway\"          [AND]");
+    CHECK_EQ(p.getTokens().size(), 5);
+    CHECK_EQ(p.get_op(), "CLIENT-HEADER:non_existent_header");
+    CHECK_EQ(p.get_arg(), "=shouldnt_   =    _anyway");
+    CHECK_EQ(p.is_cond(), true);
+  }
+
+  {
+    ParserTest p("add-header X-HeaderRewriteApplied true");
+    CHECK_EQ(p.getTokens().size(), 3);
+    CHECK_EQ(p.get_op(), "add-header");
+    CHECK_EQ(p.get_arg(), "X-HeaderRewriteApplied");
+    CHECK_EQ(p.get_value(), "true")
+    CHECK_EQ(p.is_cond(), false);
+  }
+
+  return 0;
+}
+
+int tests() {
+  if (test_parsing() ||
+      test_processing()) {
+    return 1;
+  }
+
+  return 0;
+}
+
+int main () {
+  return tests();
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8c148c9e/plugins/header_rewrite/parser.cc
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/parser.cc b/plugins/header_rewrite/parser.cc
index 549e4f7..e7e32c3 100644
--- a/plugins/header_rewrite/parser.cc
+++ b/plugins/header_rewrite/parser.cc
@@ -28,9 +28,92 @@
 
 #include "parser.h"
 
+Parser::Parser(const std::string &line) : _cond(false), _empty(false)
+{
+  TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Parser");
+  bool inquote = false;
+  bool extracting_token = false;
+  off_t cur_token_start = 0;
+  size_t cur_token_length = 0;
+  for (size_t i = 0; i < line.size(); ++i) {
+
+    if (!inquote &&
+        (std::isspace(line[i]) || (line[i] == '=' || line[i] == '>' || line[i] 
== '<'))) {
+      if (extracting_token) {
+        cur_token_length = i - cur_token_start;
+
+        if (cur_token_length) {
+          _tokens.push_back(line.substr(cur_token_start, cur_token_length));
+        }
+
+        extracting_token = false;
+      } else if (!std::isspace(line[i])) {
+        /* we got a standalone =, > or < */
+        _tokens.push_back(std::string(1, line[i]));
+      }
+      continue; /* always eat whitespace */
+    } else if (line[i] == '"') {
+      if (!inquote && !extracting_token) {
+        inquote = true;
+        extracting_token = true;
+        cur_token_start = i + 1; /* eat the leading quote */
+        continue;
+      } else if (inquote && extracting_token) {
+        cur_token_length = i - cur_token_start;
+        _tokens.push_back(line.substr(cur_token_start, cur_token_length));
+        inquote = false;
+        extracting_token = false;
+      } else {
+        /* malformed */
+        TSError("[%s] malformed line \"%s\" ignoring...", PLUGIN_NAME, 
line.c_str());
+        _tokens.clear();
+        _empty = true;
+        return;
+      }
+    } else if (!extracting_token) {
+      if (inquote)
+        continue; /* just keep eating until we hit the closing quote */
+
+      if (_tokens.empty() && line[i] == '#') {
+        // this is a comment line (it may have had leading whitespace before 
the #)
+        _empty = true;
+        break;
+      }
+
+      if (line[i] == '=' || line[i] == '>' || line[i] == '<') {
+        /* these are always a seperate token */
+        _tokens.push_back(std::string(1, line[i]));
+        continue;
+      }
+
+      extracting_token = true;
+      cur_token_start = i;
+    }
+  }
+
+  if (extracting_token) {
+    if (inquote) {
+      // unterminated quote, error case.
+      TSError("[%s] malformed line, unterminated quotation: \"%s\" 
ignoring...", PLUGIN_NAME, line.c_str());
+      _tokens.clear();
+      _empty = true;
+      return;
+    } else {
+      /* we hit the end of the line while parsing a token, let's add it */
+      _tokens.push_back(line.substr(cur_token_start));
+    }
+  }
+
+  if (_tokens.empty()) {
+    _empty = true;
+  } else {
+    preprocess(_tokens);
+  }
+}
+
 // This is the core "parser", parsing rule sets
 void
-Parser::preprocess(std::vector<std::string> &tokens)
+Parser::preprocess(std::vector<std::string> tokens)
 {
   // Special case for "conditional" values
   if (tokens[0].substr(0, 2) == "%{") {
@@ -46,9 +129,12 @@ Parser::preprocess(std::vector<std::string> &tokens)
       std::string s = tokens[0].substr(2, tokens[0].size() - 3);
 
       _op = s;
-      if (tokens.size() > 1)
+      if (tokens.size() > 2
+          && (tokens[1][0] == '=' || tokens[1][0] == '>' || tokens[1][0] == 
'<')) { // cond + (=/</>) + argument
+         _arg = tokens[1] + tokens[2];
+      } else if (tokens.size() > 1) {
         _arg = tokens[1];
-      else
+      } else
         _arg = "";
     } else {
       TSError("[%s] conditions must be embraced in %%{}", PLUGIN_NAME);
@@ -93,52 +179,3 @@ Parser::preprocess(std::vector<std::string> &tokens)
     }
   }
 }
-
-
-Parser::Parser(const std::string &line) : _cond(false), _empty(false)
-{
-  TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Parser");
-
-  if (line[0] == '#') {
-    _empty = true;
-  } else {
-    std::string tmp = line;
-    std::vector<std::string> tokens;
-    bool in_quotes = false;
-    int t = 0;
-
-    for (unsigned int i = 0; i < tmp.size(); i++) {
-      if (tmp[i] == '\\') {
-        tmp.erase(i, 1);
-        i++;
-      }
-
-      if (tmp[i] == '\"') {
-        tmp.erase(i, 1);
-
-        if (in_quotes) {
-          in_quotes = false;
-        } else {
-          in_quotes = true;
-        }
-      }
-
-      if ((tmp[i] == ' ' || i >= tmp.size() - 1) && !in_quotes) {
-        if (i == tmp.size() - 1) {
-          i++;
-        }
-        std::string s = tmp.substr(t, i - t);
-        t = i + 1;
-        if (s.size() > 0) {
-          tokens.push_back(s);
-        }
-      }
-    }
-
-    if (tokens.empty()) {
-      _empty = true;
-    } else {
-      preprocess(tokens);
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8c148c9e/plugins/header_rewrite/parser.h
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/parser.h b/plugins/header_rewrite/parser.h
index 35be7ec..687321d 100644
--- a/plugins/header_rewrite/parser.h
+++ b/plugins/header_rewrite/parser.h
@@ -81,7 +81,7 @@ public:
   }
 
 private:
-  void preprocess(std::vector<std::string> &tokens);
+  void preprocess(std::vector<std::string> tokens);
   DISALLOW_COPY_AND_ASSIGN(Parser);
 
   bool _cond;
@@ -90,6 +90,9 @@ private:
   std::string _op;
   std::string _arg;
   std::string _val;
+
+protected:
+  std::vector<std::string> _tokens;
 };
 
 

Reply via email to