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

maskit 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 0983b5e783 header_rewrite: Add set-plugin-cntl (#12125)
0983b5e783 is described below

commit 0983b5e783a46dcea0e34405b1abc5e73f7f5934
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Tue May 13 09:50:54 2025 -0600

    header_rewrite: Add set-plugin-cntl (#12125)
    
    * header_rewrite: Add set-plugin-cntl
    
    * Fix compile errors on Dbg
---
 doc/admin-guide/plugins/header_rewrite.en.rst | 16 +++++++++
 plugins/header_rewrite/conditions.cc          | 17 ++++++---
 plugins/header_rewrite/conditions.h           |  8 ++++-
 plugins/header_rewrite/factory.cc             |  2 ++
 plugins/header_rewrite/operators.cc           | 51 +++++++++++++++++++++++++++
 plugins/header_rewrite/operators.h            | 30 ++++++++++++++++
 plugins/header_rewrite/statement.cc           | 22 ++++++++++++
 plugins/header_rewrite/statement.h            | 22 ++++++++++--
 8 files changed, 160 insertions(+), 8 deletions(-)

diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst 
b/doc/admin-guide/plugins/header_rewrite.en.rst
index 3a4469179c..f9eaf82a4c 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -1106,6 +1106,22 @@ TXN_DEBUG        Enable transaction debugging (default: 
``off``)
 SKIP_REMAP       Don't require a remap match for the transaction (default: 
``off``)
 ================ 
====================================================================
 
+set-plugin-cntl
+~~~~~~~~~~~~~~~
+::
+
+  set-plugin-cntl <controller> <value>
+
+This operator lets you control the fundamental behavior of this plugin for a 
particular transaction.
+The available controllers are:
+
+================== ===================== 
=============================================================================================
+Controller         Operators/Conditions  Description
+================== ===================== 
=============================================================================================
+TIMEZONE           ``NOW``               If ``GMT`` is passed, the operators 
and conditions use GMT regardles of the timezone setting
+                                         on your system. The default value is 
``LOCAL``.
+================== ===================== 
=============================================================================================
+
 Operator Flags
 --------------
 
diff --git a/plugins/header_rewrite/conditions.cc 
b/plugins/header_rewrite/conditions.cc
index de135608ff..0fbbb84de0 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -665,7 +665,7 @@ ConditionTransactCount::append_value(std::string &s, 
Resources const &res)
 // Time related functionality for statements. We return an int64_t here, to 
assure that
 // gettimeofday() / Epoch does not lose bits.
 int64_t
-ConditionNow::get_now_qualified(NowQualifiers qual) const
+ConditionNow::get_now_qualified(NowQualifiers qual, const Resources 
&resources) const
 {
   time_t now;
 
@@ -676,7 +676,14 @@ ConditionNow::get_now_qualified(NowQualifiers qual) const
   } else {
     struct tm res;
 
-    localtime_r(&now, &res);
+    PrivateSlotData private_data;
+    private_data.raw = reinterpret_cast<uint64_t>(TSUserArgGet(resources.txnp, 
_txn_private_slot));
+    if (private_data.timezone == 1) {
+      gmtime_r(&now, &res);
+    } else {
+      localtime_r(&now, &res);
+    }
+
     switch (qual) {
     case NOW_QUAL_YEAR:
       return static_cast<int64_t>(res.tm_year + 1900); // This makes more sense
@@ -747,16 +754,16 @@ ConditionNow::set_qualifier(const std::string &q)
 }
 
 void
-ConditionNow::append_value(std::string &s, const Resources & /* res ATS_UNUSED 
*/)
+ConditionNow::append_value(std::string &s, const Resources &res)
 {
-  s += std::to_string(get_now_qualified(_now_qual));
+  s += std::to_string(get_now_qualified(_now_qual, res));
   Dbg(pi_dbg_ctl, "Appending NOW() to evaluation value -> %s", s.c_str());
 }
 
 bool
 ConditionNow::eval(const Resources &res)
 {
-  int64_t now = get_now_qualified(_now_qual);
+  int64_t now = get_now_qualified(_now_qual, res);
 
   Dbg(pi_dbg_ctl, "Evaluating NOW()");
   return static_cast<const MatcherType *>(_matcher)->test(now, res);
diff --git a/plugins/header_rewrite/conditions.h 
b/plugins/header_rewrite/conditions.h
index 45aa153577..7317197bac 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -426,8 +426,14 @@ public:
 protected:
   bool eval(const Resources &res) override;
 
+  bool
+  need_txn_private_slot() const override
+  {
+    return true;
+  }
+
 private:
-  int64_t       get_now_qualified(NowQualifiers qual) const;
+  int64_t       get_now_qualified(NowQualifiers qual, const Resources &res) 
const;
   NowQualifiers _now_qual = NOW_QUAL_EPOCH;
 };
 
diff --git a/plugins/header_rewrite/factory.cc 
b/plugins/header_rewrite/factory.cc
index 2e28404377..f06029d24e 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -75,6 +75,8 @@ operator_factory(const std::string &op)
     o = new OperatorSetBody();
   } else if (op == "set-http-cntl") {
     o = new OperatorSetHttpCntl();
+  } else if (op == "set-plugin-cntl") {
+    o = new OperatorSetPluginCntl();
   } else if (op == "run-plugin") {
     o = new OperatorRunPlugin();
   } else if (op == "set-body-from") {
diff --git a/plugins/header_rewrite/operators.cc 
b/plugins/header_rewrite/operators.cc
index 5e35da1be9..c5e31664b3 100644
--- a/plugins/header_rewrite/operators.cc
+++ b/plugins/header_rewrite/operators.cc
@@ -1193,6 +1193,57 @@ OperatorSetHttpCntl::exec(const Resources &res) const
   return true;
 }
 
+void
+OperatorSetPluginCntl::initialize(Parser &p)
+{
+  Operator::initialize(p);
+  const std::string &name  = p.get_arg();
+  const std::string &value = p.get_value();
+
+  if (name == "TIMEZONE") {
+    _name = PluginCtrl::TIMEZONE;
+    if (value == "LOCAL") {
+      _value = TIMEZONE_LOCAL;
+    } else if (value == "GMT") {
+      _value = TIMEZONE_GMT;
+    } else {
+      TSError("[%s] Unknown value for TIMZEONE control: %s", PLUGIN_NAME, 
value.c_str());
+    }
+  }
+}
+
+// This operator should be allowed everywhere
+void
+OperatorSetPluginCntl::initialize_hooks()
+{
+  add_allowed_hook(TS_HTTP_READ_REQUEST_HDR_HOOK);
+  add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+  add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+  add_allowed_hook(TS_REMAP_PSEUDO_HOOK);
+  add_allowed_hook(TS_HTTP_PRE_REMAP_HOOK);
+  add_allowed_hook(TS_HTTP_SEND_REQUEST_HDR_HOOK);
+  add_allowed_hook(TS_HTTP_TXN_CLOSE_HOOK);
+  add_allowed_hook(TS_HTTP_TXN_START_HOOK);
+}
+
+bool
+OperatorSetPluginCntl::exec(const Resources &res) const
+{
+  PrivateSlotData private_data;
+  private_data.raw = reinterpret_cast<uint64_t>(TSUserArgGet(res.txnp, 
_txn_private_slot));
+
+  switch (_name) {
+  case PluginCtrl::TIMEZONE:
+    private_data.timezone = _value;
+    break;
+  }
+
+  Dbg(pi_dbg_ctl, "   Setting plugin control %d to %d", 
static_cast<int>(_name), _value);
+  TSUserArgSet(res.txnp, _txn_private_slot, reinterpret_cast<void 
*>(private_data.raw));
+
+  return true;
+}
+
 void
 OperatorRunPlugin::initialize(Parser &p)
 {
diff --git a/plugins/header_rewrite/operators.h 
b/plugins/header_rewrite/operators.h
index 12d003c3cf..2d273b19b9 100644
--- a/plugins/header_rewrite/operators.h
+++ b/plugins/header_rewrite/operators.h
@@ -453,6 +453,36 @@ private:
   TSHttpCntlType _cntl_qual;
 };
 
+class OperatorSetPluginCntl : public Operator
+{
+public:
+  OperatorSetPluginCntl() { Dbg(dbg_ctl, "Calling CTOR for 
OperatorSetPluginCntl"); }
+
+  // noncopyable
+  OperatorSetPluginCntl(const OperatorSetPluginCntl &) = delete;
+  void operator=(const OperatorSetPluginCntl &)        = delete;
+
+  void initialize(Parser &p) override;
+
+  enum class PluginCtrl {
+    TIMEZONE,
+  };
+
+protected:
+  void initialize_hooks() override;
+  bool exec(const Resources &res) const override;
+
+  bool
+  need_txn_private_slot() const override
+  {
+    return true;
+  }
+
+private:
+  PluginCtrl _name;
+  int        _value;
+};
+
 class RemapPluginInst; // Opaque to the HRW operator, but needed in the 
implementation.
 
 class OperatorRunPlugin : public Operator
diff --git a/plugins/header_rewrite/statement.cc 
b/plugins/header_rewrite/statement.cc
index f9d9fc59b9..979eb8f146 100644
--- a/plugins/header_rewrite/statement.cc
+++ b/plugins/header_rewrite/statement.cc
@@ -95,6 +95,28 @@ Statement::acquire_txn_slot()
   _txn_slot = txn_slot_index;
 }
 
+void
+Statement::acquire_txn_private_slot()
+{
+  // Don't do anything if we don't need it
+  if (!need_txn_private_slot() || _txn_private_slot >= 0) {
+    return;
+  }
+
+  // Only call the index reservation once per plugin load
+  static int txn_private_slot_index = []() -> int {
+    int index = -1;
+
+    if (TS_ERROR == TSUserArgIndexReserve(TS_USER_ARGS_TXN, PLUGIN_NAME, "HRW 
txn private variables", &index)) {
+      TSError("[%s] failed to reserve user arg index", PLUGIN_NAME);
+      return -1; // Fallback value
+    }
+    return index;
+  }();
+
+  _txn_private_slot = txn_private_slot_index;
+}
+
 // Parse NextHop qualifiers
 NextHopQualifiers
 Statement::parse_next_hop_qualifier(const std::string &q) const
diff --git a/plugins/header_rewrite/statement.h 
b/plugins/header_rewrite/statement.h
index 92954749af..bf1020451b 100644
--- a/plugins/header_rewrite/statement.h
+++ b/plugins/header_rewrite/statement.h
@@ -155,6 +155,7 @@ public:
     TSReleaseAssert(_initialized == false);
     initialize_hooks();
     acquire_txn_slot();
+    acquire_txn_private_slot();
 
     _initialized = true;
   }
@@ -184,14 +185,31 @@ protected:
     return false;
   }
 
-  Statement *_next     = nullptr; // Linked list
-  int        _txn_slot = -1;
+  virtual bool
+  need_txn_private_slot() const
+  {
+    return false;
+  }
+
+  Statement *_next             = nullptr; // Linked list
+  int        _txn_slot         = -1;
+  int        _txn_private_slot = -1;
 
 private:
   void acquire_txn_slot();
+  void acquire_txn_private_slot();
 
   ResourceIDs               _rsrc = RSRC_NONE;
   TSHttpHookID              _hook = TS_HTTP_READ_RESPONSE_HDR_HOOK;
   std::vector<TSHttpHookID> _allowed_hooks;
   bool                      _initialized = false;
 };
+
+union PrivateSlotData {
+  uint64_t raw;
+  struct {
+    uint64_t timezone : 1; // TIMEZONE_LOCAL, or TIMEZONE_GMT
+  };
+};
+
+enum { TIMEZONE_LOCAL, TIMEZONE_GMT };

Reply via email to