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 2020/03/26 20:46:48 UTC

[trafficserver] 03/03: Adjusts the format for header_rewrite string concatenation

This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 8.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 37c56b0b2fcd8b89b066baafdb8e498ea72bcc9e
Author: Randall Meyer <ra...@yahoo.com>
AuthorDate: Mon Nov 5 16:52:28 2018 -0800

    Adjusts the format for header_rewrite string concatenation
    
    Drop the "+" from the syntax. This syntax was never released and
    therefore doesn't have backwards compatibilty requirements.
    
    eg
    add-header X-Party "let's party like it's %{NOW:YEAR}!"
    
    (quotes optional)
    
    (cherry picked from commit 3a5763b1c0f649ec9fb2fe90c8c740b28ce0874e)
    
    Conflicts:
    	doc/admin-guide/plugins/header_rewrite.en.rst
---
 doc/admin-guide/plugins/header_rewrite.en.rst |  9 ++-
 plugins/header_rewrite/header_rewrite.cc      |  4 +-
 plugins/header_rewrite/header_rewrite_test.cc | 91 +++++++++++++++++++++++++--
 plugins/header_rewrite/parser.cc              | 71 +++++++++++++++++----
 plugins/header_rewrite/parser.h               | 16 ++++-
 plugins/header_rewrite/regex_helper.cc        | 12 ----
 plugins/header_rewrite/regex_helper.h         |  2 -
 plugins/header_rewrite/value.cc               | 30 ++++-----
 8 files changed, 186 insertions(+), 49 deletions(-)

diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 128f457..12f9b4a 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -806,9 +806,14 @@ QSA    Append the results of the rule to the query string.
 
 Values and Variable Expansion
 -----------------------------
-You can concatenate values using strings, condition values and variable expansions via the ``+`` operator. Variables (eg %<tag>) in the concatenation must be enclosed in double quotes ``"``::
 
-    add-header CustomHeader "Hello from " + %{IP:SERVER} + ":" + "%<INBOUND:LOCAL-PORT>"
+You can concatenate values using strings, condition values and variable expansions on the same line.
+
+    add-header CustomHeader "Hello from %{IP:SERVER}:%<INBOUND:LOCAL-PORT>"
+
+However, the above example is somewhat contrived to show the old tags, it should instead be written as
+
+    add-header CustomHeader "Hello from %{IP:SERVER}:%{INBOUND:LOCAL-PORT}"
 
 Concatenation is not supported in condition testing.
 
diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc
index af19305..60caecd 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -184,7 +184,7 @@ RulesConfig::parse_config(const std::string &fname, TSHttpHookID default_hook)
       continue;
     }
 
-    Parser p(line, true); // Tokenize and parse this line preserving quotes from input
+    Parser p(line); // Tokenize and parse this line
     if (p.empty()) {
       continue;
     }
@@ -470,6 +470,6 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
     rule = rule->next;
   }
 
-  TSDebug(PLUGIN_NAME_DBG, "Returing from TSRemapDoRemap with status: %d", rval);
+  TSDebug(PLUGIN_NAME_DBG, "Returning from TSRemapDoRemap with status: %d", rval);
   return rval;
 }
diff --git a/plugins/header_rewrite/header_rewrite_test.cc b/plugins/header_rewrite/header_rewrite_test.cc
index c81113a..6a4e181 100644
--- a/plugins/header_rewrite/header_rewrite_test.cc
+++ b/plugins/header_rewrite/header_rewrite_test.cc
@@ -33,6 +33,12 @@ const char PLUGIN_NAME_DBG[] = "TEST_dbg_header_rewrite";
 extern "C" void
 TSError(const char *fmt, ...)
 {
+  va_list args;
+
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  va_end(args);
+  fprintf(stderr, "\n");
 }
 
 class ParserTest : public Parser
@@ -40,7 +46,7 @@ class ParserTest : public Parser
 public:
   ParserTest(const std::string &line) : Parser(line), res(true) { std::cout << "Finished parser test: " << line << std::endl; }
   std::vector<std::string>
-  getTokens()
+  getTokens() const
   {
     return _tokens;
   }
@@ -58,17 +64,38 @@ public:
   bool res;
 };
 
+class SimpleTokenizerTest : public SimpleTokenizer
+{
+public:
+  SimpleTokenizerTest(const std::string &line) : SimpleTokenizer(line), res(true)
+  {
+    std::cout << "Finished tokenizer test: " << line << std::endl;
+  }
+
+  template <typename T, typename U>
+  void
+  do_parser_check(T x, U y, int line = 0)
+  {
+    if (x != y) {
+      std::cerr << "CHECK FAILED on line " << line << ": |" << x << "| != |" << y << "|" << std::endl;
+      res = false;
+    }
+  }
+
+  bool res;
+};
+
 #define CHECK_EQ(x, y)                     \
   do {                                     \
     p.do_parser_check((x), (y), __LINE__); \
-  } while (false);
+  } while (false)
 
 #define END_TEST(s) \
   do {              \
     if (!p.res) {   \
       ++errors;     \
     }               \
-  } while (false);
+  } while (false)
 
 int
 test_parsing()
@@ -336,6 +363,17 @@ test_parsing()
     END_TEST();
   }
 
+  {
+    ParserTest p(R"(set-header Alt-Svc "quic=\":443\"; v=\"35\"")");
+
+    CHECK_EQ(p.getTokens().size(), 3UL);
+    CHECK_EQ(p.getTokens()[0], "set-header");
+    CHECK_EQ(p.getTokens()[1], "Alt-Svc");
+    CHECK_EQ(p.getTokens()[2], R"(quic=":443"; v="35")");
+
+    END_TEST();
+  }
+
   /*
    * test some failure scenarios
    */
@@ -426,9 +464,54 @@ test_processing()
 }
 
 int
+test_tokenizer()
+{
+  int errors = 0;
+
+  {
+    SimpleTokenizerTest p("a simple test");
+    CHECK_EQ(p.get_tokens().size(), 1UL);
+    CHECK_EQ(p.get_tokens()[0], "a simple test");
+  }
+
+  {
+    SimpleTokenizerTest p(R"(quic=":443"; v="35")");
+    CHECK_EQ(p.get_tokens().size(), 1UL);
+    CHECK_EQ(p.get_tokens()[0], R"(quic=":443"; v="35")");
+  }
+
+  {
+    SimpleTokenizerTest p(R"(let's party like it's  %{NOW:YEAR})");
+    CHECK_EQ(p.get_tokens().size(), 2UL);
+    CHECK_EQ(p.get_tokens()[0], "let's party like it's  ");
+    CHECK_EQ(p.get_tokens()[1], "%{NOW:YEAR}");
+  }
+  {
+    SimpleTokenizerTest p("A racoon's favorite tag is %<cqhm> in %{NOW:YEAR}!");
+    CHECK_EQ(p.get_tokens().size(), 5UL);
+    CHECK_EQ(p.get_tokens()[0], "A racoon's favorite tag is ");
+    CHECK_EQ(p.get_tokens()[1], "%<cqhm>");
+    CHECK_EQ(p.get_tokens()[2], " in ");
+    CHECK_EQ(p.get_tokens()[3], "%{NOW:YEAR}");
+    CHECK_EQ(p.get_tokens()[4], "!");
+  }
+
+  {
+    SimpleTokenizerTest p(R"(Hello from %{IP:SERVER}:%{INBOUND:LOCAL-PORT})");
+    CHECK_EQ(p.get_tokens().size(), 4UL);
+    CHECK_EQ(p.get_tokens()[0], "Hello from ");
+    CHECK_EQ(p.get_tokens()[1], "%{IP:SERVER}");
+    CHECK_EQ(p.get_tokens()[2], ":");
+    CHECK_EQ(p.get_tokens()[3], "%{INBOUND:LOCAL-PORT}");
+  }
+
+  return errors;
+}
+
+int
 main()
 {
-  if (test_parsing() || test_processing()) {
+  if (test_parsing() || test_processing() || test_tokenizer()) {
     return 1;
   }
 
diff --git a/plugins/header_rewrite/parser.cc b/plugins/header_rewrite/parser.cc
index fc821d3..4fc1bf4 100644
--- a/plugins/header_rewrite/parser.cc
+++ b/plugins/header_rewrite/parser.cc
@@ -28,9 +28,9 @@
 
 #include "parser.h"
 
-enum ParserState { PARSER_DEFAULT, PARSER_IN_QUOTE, PARSER_IN_REGEX };
+enum ParserState { PARSER_DEFAULT, PARSER_IN_QUOTE, PARSER_IN_REGEX, PARSER_IN_EXPANSION };
 
-Parser::Parser(const std::string &original_line, bool preserve_quotes) : _cond(false), _empty(false)
+Parser::Parser(const std::string &original_line) : _cond(false), _empty(false)
 {
   std::string line        = original_line;
   ParserState state       = PARSER_DEFAULT;
@@ -39,8 +39,7 @@ Parser::Parser(const std::string &original_line, bool preserve_quotes) : _cond(f
   size_t cur_token_length = 0;
 
   for (size_t i = 0; i < line.size(); ++i) {
-    if ((state == PARSER_DEFAULT) &&
-        (std::isspace(line[i]) || ((line[i] == '=') || (line[i] == '>') || (line[i] == '<') || (line[i] == '+')))) {
+    if ((state == PARSER_DEFAULT) && (std::isspace(line[i]) || ((line[i] == '=')))) {
       if (extracting_token) {
         cur_token_length = i - cur_token_start;
         if (cur_token_length > 0) {
@@ -78,11 +77,7 @@ Parser::Parser(const std::string &original_line, bool preserve_quotes) : _cond(f
         cur_token_start  = i + 1; // Eat the leading quote
       } else if ((state == PARSER_IN_QUOTE) && extracting_token) {
         cur_token_length = i - cur_token_start;
-        if (preserve_quotes) {
-          _tokens.push_back(line.substr(cur_token_start - 1, i - cur_token_start + 2));
-        } else {
-          _tokens.push_back(line.substr(cur_token_start, cur_token_length));
-        }
+        _tokens.push_back(line.substr(cur_token_start, cur_token_length));
         state            = PARSER_DEFAULT;
         extracting_token = false;
       } else {
@@ -99,7 +94,7 @@ Parser::Parser(const std::string &original_line, bool preserve_quotes) : _cond(f
         break;
       }
 
-      if ((line[i] == '=') || (line[i] == '>') || (line[i] == '<') || (line[i] == '+')) {
+      if ((line[i] == '=') || (line[i] == '+')) {
         // These are always a seperate token
         _tokens.push_back(std::string(1, line[i]));
         continue;
@@ -202,7 +197,7 @@ Parser::preprocess(std::vector<std::string> tokens)
         }
       } else {
         // Syntax error
-        TSError("[%s] mods have to be embraced in []", PLUGIN_NAME);
+        TSError("[%s] mods have to be enclosed in []", PLUGIN_NAME);
         return;
       }
     }
@@ -251,3 +246,57 @@ Parser::get_tokens() const
 {
   return _tokens;
 }
+
+SimpleTokenizer::SimpleTokenizer(const std::string &original_line)
+{
+  std::string line        = original_line;
+  ParserState state       = PARSER_DEFAULT;
+  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) {
+    extracting_token = true;
+    switch (state) {
+    case PARSER_DEFAULT:
+      if ((line[i] == '{') || (line[i] == '<')) {
+        if (line[i - 1] == '%') {
+          // pickup what we currently have
+          cur_token_length = i - cur_token_start - 1;
+          if (cur_token_length > 0) {
+            _tokens.push_back(line.substr(cur_token_start, cur_token_length));
+          }
+
+          cur_token_start  = i - 1;
+          state            = PARSER_IN_EXPANSION;
+          extracting_token = false;
+        }
+      }
+      break;
+    case PARSER_IN_EXPANSION:
+      if ((line[i] == '}') || (line[i] == '>')) {
+        cur_token_length = i - cur_token_start + 1;
+        if (cur_token_length > 0) {
+          _tokens.push_back(line.substr(cur_token_start, cur_token_length));
+        }
+        cur_token_start  = i + 1;
+        state            = PARSER_DEFAULT;
+        extracting_token = false;
+      }
+      break;
+    default:
+      break;
+    }
+  }
+
+  // take what was left behind
+  if (extracting_token) {
+    _tokens.push_back(line.substr(cur_token_start));
+  }
+}
+
+const std::vector<std::string> &
+SimpleTokenizer::get_tokens() const
+{
+  return _tokens;
+}
diff --git a/plugins/header_rewrite/parser.h b/plugins/header_rewrite/parser.h
index ce167fd..8ea4c30 100644
--- a/plugins/header_rewrite/parser.h
+++ b/plugins/header_rewrite/parser.h
@@ -33,7 +33,7 @@
 class Parser
 {
 public:
-  explicit Parser(const std::string &line, bool preserve_quotes = false);
+  explicit Parser(const std::string &line);
 
   bool
   empty() const
@@ -88,3 +88,17 @@ private:
 protected:
   std::vector<std::string> _tokens;
 };
+
+class SimpleTokenizer
+{
+public:
+  explicit SimpleTokenizer(const std::string &line);
+
+  const std::vector<std::string> &get_tokens() const;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(SimpleTokenizer);
+
+protected:
+  std::vector<std::string> _tokens;
+};
diff --git a/plugins/header_rewrite/regex_helper.cc b/plugins/header_rewrite/regex_helper.cc
index c07d117..8b30361 100644
--- a/plugins/header_rewrite/regex_helper.cc
+++ b/plugins/header_rewrite/regex_helper.cc
@@ -40,18 +40,6 @@ regexHelper::setRegexMatch(const std::string &s)
   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
 {
diff --git a/plugins/header_rewrite/regex_helper.h b/plugins/header_rewrite/regex_helper.h
index 0b88f11..ed00ac7 100644
--- a/plugins/header_rewrite/regex_helper.h
+++ b/plugins/header_rewrite/regex_helper.h
@@ -40,8 +40,6 @@ public:
   }
 
   bool setRegexMatch(const std::string &s);
-  const std::string &getRegexString() const;
-  int getRegexCcount() const;
   int regexMatch(const char *, int, int ovector[]) const;
 
 private:
diff --git a/plugins/header_rewrite/value.cc b/plugins/header_rewrite/value.cc
index ef15e43..bd664c6 100644
--- a/plugins/header_rewrite/value.cc
+++ b/plugins/header_rewrite/value.cc
@@ -43,26 +43,26 @@ Value::set_value(const std::string &val)
 {
   _value = val;
 
-  if (_value.find("%{") != std::string::npos || _value.find("%<") != std::string::npos || _value.find("\"") != std::string::npos) {
-    Parser parser(_value);
-    auto tokens = parser.get_tokens();
+  if (_value.find("%{") != std::string::npos || _value.find("%<") != std::string::npos) {
+    SimpleTokenizer tokenizer(_value);
+
+    auto tokens = tokenizer.get_tokens();
     for (auto it = tokens.begin(); it != tokens.end(); it++) {
-      Parser tparser(*it);
+      std::string token = *it;
 
       Condition *tcond_val = nullptr;
-      if ((*it).substr(0, 2) == "%<") {
+      if (token.substr(0, 2) == "%<") {
         tcond_val = new ConditionExpandableString(*it);
-      } else if ((*it) == "+") {
-        // Skip concat token
-        continue;
-      } else {
-        tcond_val = condition_factory(tparser.get_op());
+      } else if (token.substr(0, 2) == "%{") {
+        std::string cond_token = token.substr(2, token.size() - 3);
+        tcond_val              = condition_factory(cond_token);
+      }
 
-        if (tcond_val) {
-          tcond_val->initialize(tparser);
-        } else {
-          tcond_val = new ConditionStringLiteral(*it);
-        }
+      if (tcond_val) {
+        Parser parser(_value);
+        tcond_val->initialize(parser);
+      } else {
+        tcond_val = new ConditionStringLiteral(token);
       }
       _cond_vals.push_back(tcond_val);
     }