trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bc...@apache.org
Subject [trafficserver] branch master updated: header_rewrite: Adds basic string concatenation
Date Tue, 09 Oct 2018 20:55:35 GMT
This is an automated email from the ASF dual-hosted git repository.

bcall pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new ed1ded2  header_rewrite: Adds basic string concatenation
ed1ded2 is described below

commit ed1ded2623674bf6957a0faeb68a8ca10cb0725f
Author: Randall Meyer <randallmeyer@yahoo.com>
AuthorDate: Tue Sep 11 10:56:56 2018 -0700

    header_rewrite: Adds basic string concatenation
    
    eg:
    
    add-header X-Party "let's party like it's " + %{NOW:YEAR} + "!"
---
 doc/admin-guide/plugins/header_rewrite.en.rst | 18 ++++----
 plugins/header_rewrite/Examples/Geo           |  2 +-
 plugins/header_rewrite/Makefile.inc           |  1 +
 plugins/header_rewrite/condition.h            |  2 +-
 plugins/header_rewrite/conditions.cc          | 46 +++++++++++++++++++
 plugins/header_rewrite/conditions.h           | 34 ++++++++++++++
 plugins/header_rewrite/factory.cc             |  2 +-
 plugins/header_rewrite/header_rewrite.cc      |  2 +-
 plugins/header_rewrite/header_rewrite_test.cc |  1 +
 plugins/header_rewrite/parser.cc              | 29 +++++++++---
 plugins/header_rewrite/parser.h               |  3 +-
 plugins/header_rewrite/value.cc               | 65 +++++++++++++++++++++++++++
 plugins/header_rewrite/value.h                | 34 ++++----------
 13 files changed, 192 insertions(+), 47 deletions(-)

diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 25f6c52..128f457 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -804,11 +804,15 @@ L      Last rule, do not continue.
 QSA    Append the results of the rule to the query string.
 ====== ========================================================================
 
-Variable Expansion
-------------------
+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>"
+
+Concatenation is not supported in condition testing.
 
-Only limited variable expansion is supported in `add-header`_ and `set-header`_ . Supported
-substitutions are currently:
+Supported substitutions are currently:
 
 ======================= ==================================================================================
 Variable                Description
@@ -833,12 +837,6 @@ Variable                Description
 %<INBOUND:STACK>        The full protocol stack of the inbound connection separated
by ','.
 ======================= ==================================================================================
 
-Variables to be expanded must be enclosed in double quotes ``"``, as in this example using
the arbitrary header
-``Custom-Client-IP``::
-
-    set-header Custom-Client-IP "%<chi>"
-
-
 Header Values
 -------------
 
diff --git a/plugins/header_rewrite/Examples/Geo b/plugins/header_rewrite/Examples/Geo
index 3ea3a60..d4261e1 100644
--- a/plugins/header_rewrite/Examples/Geo
+++ b/plugins/header_rewrite/Examples/Geo
@@ -16,7 +16,7 @@
 # limitations under the License.
 
 cond %{SEND_RESPONSE_HDR_HOOK} [AND]
-cond %${GEO:COUNTRY} =US
+cond %{GEO:COUNTRY} =US
     set-header ATS-Geo-Country %{GEO:COUNTRY}
     set-header ATS-Geo-Country-ISO %{GEO:COUNTRY-ISO}
     set-header ATS-Geo-ASN %{GEO:ASN}
diff --git a/plugins/header_rewrite/Makefile.inc b/plugins/header_rewrite/Makefile.inc
index 436e3da..c4b81d6 100644
--- a/plugins/header_rewrite/Makefile.inc
+++ b/plugins/header_rewrite/Makefile.inc
@@ -43,6 +43,7 @@ header_rewrite_header_rewrite_la_SOURCES = \
 	header_rewrite/ruleset.h \
 	header_rewrite/statement.cc \
 	header_rewrite/statement.h \
+	header_rewrite/value.cc \
 	header_rewrite/value.h
 
 header_rewrite_parser_la_SOURCES = \
diff --git a/plugins/header_rewrite/condition.h b/plugins/header_rewrite/condition.h
index ebafab4..4543e03 100644
--- a/plugins/header_rewrite/condition.h
+++ b/plugins/header_rewrite/condition.h
@@ -112,7 +112,7 @@ public:
     return _qualifier;
   }
 
-  // Virtual methods, has to be implemented by each conditional;
+  // Virtual methods, has to be implemented by each conditional
   void initialize(Parser &p) override;
   virtual void append_value(std::string &s, const Resources &res) = 0;
 
diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc
index 1208d13..e1755a9 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -30,6 +30,7 @@
 #include "ts/ts.h"
 
 #include "conditions.h"
+#include "expander.h"
 #include "lulu.h"
 
 // ConditionStatus
@@ -1373,3 +1374,48 @@ ConditionInbound::append_value(std::string &s, const Resources
&res, NetworkSess
     s += zret;
   }
 }
+
+ConditionStringLiteral::ConditionStringLiteral(const std::string &v)
+{
+  TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionStringLiteral");
+  _literal = v;
+}
+
+void
+ConditionStringLiteral::append_value(std::string &s, const Resources &res)
+{
+  s += _literal;
+  TSDebug(PLUGIN_NAME, "Appending '%s' to evaluation value", _literal.c_str());
+}
+
+bool
+ConditionStringLiteral::eval(const Resources &res)
+{
+  TSDebug(PLUGIN_NAME, "Evaluating StringLiteral");
+
+  return static_cast<const MatcherType *>(_matcher)->test(_literal);
+}
+
+ConditionExpandableString::ConditionExpandableString(const std::string &v)
+{
+  TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionExpandableString");
+  _value = v;
+}
+
+bool
+ConditionExpandableString::eval(const Resources &res)
+{
+  std::string s;
+
+  append_value(s, res);
+
+  return static_cast<const MatcherType *>(_matcher)->test(s);
+}
+
+void
+ConditionExpandableString::append_value(std::string &s, const Resources &res)
+{
+  VariableExpander ve(_value);
+  s += ve.expand(res);
+  TSDebug(PLUGIN_NAME, "Appending to evaluation value -> %s", s.c_str());
+}
diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h
index c0c8e85..a9bb92f 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -541,3 +541,37 @@ protected:
 private:
   NetworkSessionQualifiers _net_qual = NET_QUAL_STACK;
 };
+
+class ConditionStringLiteral : public Condition
+{
+  typedef Matchers<std::string> MatcherType;
+
+public:
+  ConditionStringLiteral(const std::string &v);
+
+  void append_value(std::string &s, const Resources & /* res ATS_UNUSED */) override;
+
+protected:
+  bool eval(const Resources & /* res ATS_UNUSED */) override;
+
+private:
+  std::string _literal;
+  DISALLOW_COPY_AND_ASSIGN(ConditionStringLiteral);
+};
+
+class ConditionExpandableString : public Condition
+{
+  typedef Matchers<std::string> MatcherType;
+
+public:
+  ConditionExpandableString(const std::string &v);
+
+  void append_value(std::string &s, const Resources &res) override;
+
+protected:
+  bool eval(const Resources &res) override;
+
+private:
+  std::string _value;
+  DISALLOW_COPY_AND_ASSIGN(ConditionExpandableString);
+};
diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc
index 857ca5e..41e29fe 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -144,7 +144,7 @@ condition_factory(const std::string &cond)
   } else if (c_name == "INBOUND") {
     c = new ConditionInbound();
   } else {
-    TSError("[%s] Unknown condition: %s", PLUGIN_NAME, c_name.c_str());
+    TSDebug(PLUGIN_NAME, "Unknown condition: %s", c_name.c_str());
     return nullptr;
   }
 
diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc
index fb25b41..6f5a041 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -179,7 +179,7 @@ RulesConfig::parse_config(const std::string &fname, TSHttpHookID default_hook)
       continue;
     }
 
-    Parser p(line); // Tokenize and parse this line
+    Parser p(line, true); // Tokenize and parse this line preserving quotes from input
     if (p.empty()) {
       continue;
     }
diff --git a/plugins/header_rewrite/header_rewrite_test.cc b/plugins/header_rewrite/header_rewrite_test.cc
index fa00fba..c81113a 100644
--- a/plugins/header_rewrite/header_rewrite_test.cc
+++ b/plugins/header_rewrite/header_rewrite_test.cc
@@ -424,6 +424,7 @@ test_processing()
 
   return errors;
 }
+
 int
 main()
 {
diff --git a/plugins/header_rewrite/parser.cc b/plugins/header_rewrite/parser.cc
index 77da446..fc821d3 100644
--- a/plugins/header_rewrite/parser.cc
+++ b/plugins/header_rewrite/parser.cc
@@ -30,7 +30,7 @@
 
 enum ParserState { PARSER_DEFAULT, PARSER_IN_QUOTE, PARSER_IN_REGEX };
 
-Parser::Parser(const std::string &original_line) : _cond(false), _empty(false)
+Parser::Parser(const std::string &original_line, bool preserve_quotes) : _cond(false),
_empty(false)
 {
   std::string line        = original_line;
   ParserState state       = PARSER_DEFAULT;
@@ -39,7 +39,8 @@ Parser::Parser(const std::string &original_line) : _cond(false), _empty(false)
   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] == '<')))) {
+    if ((state == PARSER_DEFAULT) &&
+        (std::isspace(line[i]) || ((line[i] == '=') || (line[i] == '>') || (line[i] ==
'<') || (line[i] == '+')))) {
       if (extracting_token) {
         cur_token_length = i - cur_token_start;
         if (cur_token_length > 0) {
@@ -77,7 +78,11 @@ Parser::Parser(const std::string &original_line) : _cond(false), _empty(false)
         cur_token_start  = i + 1; // Eat the leading quote
       } else if ((state == PARSER_IN_QUOTE) && extracting_token) {
         cur_token_length = i - cur_token_start;
-        _tokens.push_back(line.substr(cur_token_start, cur_token_length));
+        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));
+        }
         state            = PARSER_DEFAULT;
         extracting_token = false;
       } else {
@@ -94,7 +99,7 @@ Parser::Parser(const std::string &original_line) : _cond(false), _empty(false)
         break;
       }
 
-      if ((line[i] == '=') || (line[i] == '>') || (line[i] == '<')) {
+      if ((line[i] == '=') || (line[i] == '>') || (line[i] == '<') || (line[i] == '+'))
{
         // These are always a seperate token
         _tokens.push_back(std::string(1, line[i]));
         continue;
@@ -158,12 +163,18 @@ Parser::preprocess(std::vector<std::string> tokens)
       return;
     }
   } else {
-    // Operator has no qualifiers, but could take an optional second argumetn
+    // Operator has no qualifiers, but could take an optional second argument
     _op = tokens[0];
     if (tokens.size() > 1) {
       _arg = tokens[1];
+
       if (tokens.size() > 2) {
-        _val = tokens[2];
+        for (auto it = tokens.begin() + 2; it != tokens.end(); it++) {
+          _val = _val + *it;
+          if (std::next(it) != tokens.end()) {
+            _val = _val + " ";
+          }
+        }
       } else {
         _val = "";
       }
@@ -234,3 +245,9 @@ Parser::cond_is_hook(TSHttpHookID &hook) const
 
   return false;
 }
+
+const std::vector<std::string> &
+Parser::get_tokens() const
+{
+  return _tokens;
+}
diff --git a/plugins/header_rewrite/parser.h b/plugins/header_rewrite/parser.h
index 23e27b7..ce167fd 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);
+  explicit Parser(const std::string &line, bool preserve_quotes = false);
 
   bool
   empty() const
@@ -72,6 +72,7 @@ public:
   }
 
   bool cond_is_hook(TSHttpHookID &hook) const;
+  const std::vector<std::string> &get_tokens() const;
 
 private:
   void preprocess(std::vector<std::string> tokens);
diff --git a/plugins/header_rewrite/value.cc b/plugins/header_rewrite/value.cc
new file mode 100644
index 0000000..6dadde0
--- /dev/null
+++ b/plugins/header_rewrite/value.cc
@@ -0,0 +1,65 @@
+/* @file
+
+  Implementation for creating all values.
+
+  @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 <string>
+
+#include "value.h"
+
+#include "condition.h"
+#include "factory.h"
+#include "parser.h"
+#include "conditions.h"
+
+void
+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();
+    for (auto it = tokens.begin(); it != tokens.end(); it++) {
+      Parser tparser(*it);
+
+      Condition *tcond_val = nullptr;
+      if ((*it).substr(0, 2) == "%<") {
+        tcond_val = new ConditionExpandableString(*it);
+      } else if ((*it) == "+") {
+        // Skip concat token
+        continue;
+      } else {
+        tcond_val = condition_factory(tparser.get_op());
+
+        if (tcond_val) {
+          tcond_val->initialize(tparser);
+        } else {
+          tcond_val = new ConditionStringLiteral(*it);
+        }
+      }
+      _cond_vals.push_back(tcond_val);
+    }
+  } else {
+    _int_value   = strtol(_value.c_str(), nullptr, 10);
+    _float_value = strtod(_value.c_str(), nullptr);
+  }
+}
diff --git a/plugins/header_rewrite/value.h b/plugins/header_rewrite/value.h
index 11706bc..19facf4 100644
--- a/plugins/header_rewrite/value.h
+++ b/plugins/header_rewrite/value.h
@@ -22,14 +22,13 @@
 #pragma once
 
 #include <string>
+#include <vector>
 
 #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).
@@ -40,37 +39,20 @@
 class Value : Statement
 {
 public:
-  Value() : _need_expander(false), _value(""), _int_value(0), _float_value(0.0), _cond_val(nullptr)
+  Value() : _need_expander(false), _value(""), _int_value(0), _float_value(0.0)
   {
     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);
-      }
-    } else if (_value.find("%<") != std::string::npos) { // It has a Variable to expand
-      _need_expander = true;                             // And this is clearly not an integer
or float ...
-      // TODO: This is still not optimal, we should pre-parse the _value string here,
-      // and perhaps populate a per-Value VariableExpander that holds state.
-    } else {
-      _int_value   = strtol(_value.c_str(), nullptr, 10);
-      _float_value = strtod(_value.c_str(), nullptr);
-    }
-  }
+  void set_value(const std::string &val);
 
   void
   append_value(std::string &s, const Resources &res) const
   {
-    if (_cond_val) {
-      _cond_val->append_value(s, res);
+    if (!_cond_vals.empty()) {
+      for (auto it = _cond_vals.begin(); it != _cond_vals.end(); it++) {
+        (*it)->append_value(s, res);
+      }
     } else {
       s += _value;
     }
@@ -119,5 +101,5 @@ private:
   std::string _value;
   int _int_value;
   double _float_value;
-  Condition *_cond_val;
+  std::vector<Condition *> _cond_vals;
 };


Mime
View raw message