This is an automated email from the ASF dual-hosted git repository.
zwoop 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 59ecdebff9 Adds HRW state variables, 16 flags and 4 int8s (#12015)
59ecdebff9 is described below
commit 59ecdebff9c058c0a5e118d641ba8ec5bd1851df
Author: Leif Hedstrom <[email protected]>
AuthorDate: Fri Feb 28 15:15:03 2025 -0700
Adds HRW state variables, 16 flags and 4 int8s (#12015)
---
doc/admin-guide/plugins/header_rewrite.en.rst | 85 ++++++++++-
plugins/header_rewrite/conditions.cc | 160 ++++++++++++++++++---
plugins/header_rewrite/conditions.h | 131 +++++++++++++++--
plugins/header_rewrite/factory.cc | 13 ++
plugins/header_rewrite/header_rewrite.cc | 6 +-
plugins/header_rewrite/header_rewrite_test.cc | 3 +
plugins/header_rewrite/lulu.h | 5 +-
plugins/header_rewrite/operators.cc | 198 +++++++++++++++++++++++++-
plugins/header_rewrite/operators.h | 90 ++++++++++++
plugins/header_rewrite/parser.h | 7 -
plugins/header_rewrite/statement.cc | 22 +++
plugins/header_rewrite/statement.h | 35 ++++-
plugins/header_rewrite/value.h | 6 +
13 files changed, 707 insertions(+), 54 deletions(-)
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst
b/doc/admin-guide/plugins/header_rewrite.en.rst
index 903acca773..474df01ab3 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -140,6 +140,17 @@ with the word ``else``. The following example illustrates
this::
The ``else`` clause is not a condition, and does not take any flags, it is
of course optional, but when specified must be followed by at least one
operator.
+State variables
+---------------
+
+A set of state variables are also available for both conditions and operators.
+There are currently 16 flag states, 4 8-bit integers and one 16-bit integer
states.
+These states are all transactional, meaning they are usable and persistent
across
+all hooks.
+
+The flag states are numbers 0-15, the 8-bit integer states are numbered 0-3,
and the
+one 16-bit integer state is number 0.
+
Conditions
----------
@@ -457,9 +468,10 @@ As a special matcher, the inbound IP addresses can be
matched against a list of
cond %{INBOUND:REMOTE-ADDR} {192.168.201.0/24,10.0.0.0/8}
-Note that this will not work against the non-IP based conditions, such as the
protocol families,
-and the configuration parser will error out. The format here is very specific,
in particular no
-white spaces are allowed between the ranges.
+.. note::
+ This will not work against the non-IP based conditions, such as the
protocol families,
+ and the configuration parser will error out. The format here is very
specific, in particular no
+ white spaces are allowed between the ranges.
IP
~~
@@ -569,6 +581,36 @@ RANDOM
Generates a random integer from ``0`` up to (but not including) ``<n>``.
Mathematically, ``[0,n)`` or ``0 <= r < n``.
+STATE-FLAG
+~~~~~~~~~~
+::
+
+ cond %{STATE-FLAG:<n>}
+
+This condition allows you to check the state of a flag. The ``<n>`` is the
+number of the flag, from 0 to 15. This condition returns a ``true`` or
+``false`` value, depending on the state of the flag.
+
+STATE-INT8
+~~~~~~~~~~
+::
+
+ cond %{STATE-INT8:<n>}
+
+This condition allows you to check the state of an 8-bit unsigned integer.
+The ``<n>`` is the number of the integer, from 0 to 3. The current value of
+the state integer is returned, and all 4 integers are initialized to 0.
+
+STATE-INT16
+~~~~~~~~~~~
+::
+
+ cond %{STATE-INT16<:0>}
+
+This condition allows you to check the state of an 16-bit unsigned integer.
+There's only one such integer, and its value is returned from this condition.
+As such, the index, ``0``, is optional here.
+
STATUS
~~~~~~
::
@@ -920,6 +962,38 @@ location automatically. This operator supports `String
concatenations`_ for
``READ_RESPONSE_HDR_HOOK`` (the default when the plugin is global), the
``SEND_RESPONSE_HDR_HOOK``, or the ``REMAP_PSEUDO_HOOK``.
+set-state-flag
+~~~~~~~~~~~~~~
+::
+
+ set-state-flag <n> <value>
+
+This operator allows you to set the state of a flag. The ``<n>`` is the
+number of the flag, from 0 to 15. The ``<value>`` is either ``true`` or
``false``,
+turning the flag on or off.
+
+set-state-int8
+~~~~~~~~~~~~~~
+::
+
+ set-state-int8 <n> <value>
+
+This operator allows you to set the state of an 8-bit unsigned integer.
+The ``<n>`` is the number of the integer, from 0 to 3. The ``<value>`` is an
+unsigned 8-bit integer, 0-255. It can also be a condition, in which case the
+value of the condition is used.
+
+set-state-int16
+~~~~~~~~~~~~~~~
+::
+
+ set-state-int16 0 <value>
+
+This operator allows you to set the state of a 16-bit unsigned integer.
+The ``<value>`` is an unsigned 16-bit integer as well, 0-65535. It can also
+be a condition, in which case thevalue of the condition is used. The index,
+0, is always required eventhough there is only one 16-bit integer state
variable.
+
set-status
~~~~~~~~~~
::
@@ -1438,6 +1512,11 @@ could each be tagged with a consistent name to make
finding logs easier.::
(Then in :file:`logging.yaml`, log ``%<{@PropertyName}cqh>``)
+.. note::
+ With the new ``state-flag``, ``state-int8`` and ``state-int16`` operators,
you can
+ sometimes avoid setting internal ``@`` headers for passing information
between hooks.
+ These internal state variables are much more efficient than setting and
reading headers.
+
Remove Client Query Parameters
------------------------------------
diff --git a/plugins/header_rewrite/conditions.cc
b/plugins/header_rewrite/conditions.cc
index 747d260211..9422f5d3f9 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -137,10 +137,7 @@ ConditionRandom::eval(const Resources & /* res ATS_UNUSED
*/)
void
ConditionRandom::append_value(std::string &s, const Resources & /* res
ATS_UNUSED */)
{
- std::ostringstream oss;
-
- oss << rand_r(&_seed) % _max;
- s += oss.str();
+ s += std::to_string(rand_r(&_seed) % _max);
Dbg(pi_dbg_ctl, "Appending RANDOM(%d) to evaluation value -> %s", _max,
s.c_str());
}
@@ -746,10 +743,7 @@ ConditionNow::set_qualifier(const std::string &q)
void
ConditionNow::append_value(std::string &s, const Resources & /* res ATS_UNUSED
*/)
{
- std::ostringstream oss;
-
- oss << get_now_qualified(_now_qual);
- s += oss.str();
+ s += std::to_string(get_now_qualified(_now_qual));
Dbg(pi_dbg_ctl, "Appending NOW() to evaluation value -> %s", s.c_str());
}
@@ -822,14 +816,11 @@ ConditionGeo::set_qualifier(const std::string &q)
void
ConditionGeo::append_value(std::string &s, const Resources &res)
{
- std::ostringstream oss;
-
if (is_int_type()) {
- oss << get_geo_int(TSHttpTxnClientAddrGet(res.txnp));
+ s += std::to_string(get_geo_int(TSHttpTxnClientAddrGet(res.txnp)));
} else {
- oss << get_geo_string(TSHttpTxnClientAddrGet(res.txnp));
+ s += get_geo_string(TSHttpTxnClientAddrGet(res.txnp));
}
- s += oss.str();
Dbg(pi_dbg_ctl, "Appending GEO() to evaluation value -> %s", s.c_str());
}
@@ -899,10 +890,7 @@ ConditionId::append_value(std::string &s, const Resources
&res ATS_UNUSED)
{
switch (_id_qual) {
case ID_QUAL_REQUEST: {
- std::ostringstream oss;
-
- oss << TSHttpTxnIdGet(res.txnp);
- s += oss.str();
+ s += std::to_string(TSHttpTxnIdGet(res.txnp));
} break;
case ID_QUAL_PROCESS: {
TSUuid process = TSProcessUuidGet();
@@ -1428,16 +1416,146 @@ ConditionNextHop::eval(const Resources &res)
// ConditionHttpCntl: request header.
void
-ConditionHttpCntl::initialize(Parser &p)
+ConditionHttpCntl::set_qualifier(const std::string &q)
+{
+ Condition::set_qualifier(q);
+
+ Dbg(pi_dbg_ctl, "\tParsing %%{HTTP-CNTL:%s}", q.c_str());
+ _http_cntl_qual = parse_http_cntl_qualifier(q);
+}
+
+void
+ConditionHttpCntl::append_value(std::string &s, const Resources &res)
+{
+ s += TSHttpTxnCntlGet(res.txnp, _http_cntl_qual) ? "TRUE" : "FALSE";
+ Dbg(pi_dbg_ctl, "Evaluating HTTP-CNTL(%s)", _qualifier.c_str());
+}
+
+bool
+ConditionHttpCntl::eval(const Resources &res)
+{
+ Dbg(pi_dbg_ctl, "Evaluating HTTP-CNTL()");
+ return TSHttpTxnCntlGet(res.txnp, _http_cntl_qual);
+}
+
+// ConditionStateFlag
+void
+ConditionStateFlag::set_qualifier(const std::string &q)
+{
+ Condition::set_qualifier(q);
+
+ _flag_ix = strtol(q.c_str(), nullptr, 10);
+ if (_flag_ix < 0 || _flag_ix >= NUM_STATE_FLAGS) {
+ TSError("[%s] STATE-FLAG index out of range: %s", PLUGIN_NAME, q.c_str());
+ } else {
+ Dbg(pi_dbg_ctl, "\tParsing %%{STATE-FLAG:%s}", q.c_str());
+ _mask = 1ULL << _flag_ix;
+ }
+}
+
+void
+ConditionStateFlag::append_value(std::string &s, const Resources &res)
+{
+ s += eval(res) ? "TRUE" : "FALSE";
+ Dbg(pi_dbg_ctl, "Evaluating STATE-FLAG(%d)", _flag_ix);
+}
+
+bool
+ConditionStateFlag::eval(const Resources &res)
+{
+ auto data = reinterpret_cast<uint64_t>(TSUserArgGet(res.txnp, _txn_slot));
+
+ Dbg(pi_dbg_ctl, "Evaluating STATE-FLAG()");
+
+ return (data & _mask) == _mask;
+}
+
+// ConditionStateInt8
+void
+ConditionStateInt8::initialize(Parser &p)
{
Condition::initialize(p);
+ MatcherType *match = new MatcherType(_cond_op);
+
+ match->set(static_cast<uint8_t>(strtol(p.get_arg().c_str(), nullptr, 10)),
mods());
+ _matcher = match;
}
void
-ConditionHttpCntl::set_qualifier(const std::string &q)
+ConditionStateInt8::set_qualifier(const std::string &q)
{
Condition::set_qualifier(q);
- Dbg(pi_dbg_ctl, "\tParsing %%{HTTP-CNTL:%s}", q.c_str());
- _http_cntl_qual = parse_http_cntl_qualifier(q);
+ _byte_ix = strtol(q.c_str(), nullptr, 10);
+ if (_byte_ix < 0 || _byte_ix >= NUM_STATE_INT8S) {
+ TSError("[%s] STATE-INT8 index out of range: %s", PLUGIN_NAME, q.c_str());
+ } else {
+ Dbg(pi_dbg_ctl, "\tParsing %%{STATE-INT8:%s}", q.c_str());
+ }
+}
+
+void
+ConditionStateInt8::append_value(std::string &s, const Resources &res)
+{
+ uint8_t data = _get_data(res);
+
+ s += std::to_string(data);
+
+ Dbg(pi_dbg_ctl, "Appending STATE-INT8(%d) to evaluation value -> %s", data,
s.c_str());
+}
+
+bool
+ConditionStateInt8::eval(const Resources &res)
+{
+ uint8_t data = _get_data(res);
+
+ Dbg(pi_dbg_ctl, "Evaluating STATE-INT8()");
+
+ return static_cast<const MatcherType *>(_matcher)->test(data);
+}
+
+// ConditionStateInt16
+void
+ConditionStateInt16::initialize(Parser &p)
+{
+ Condition::initialize(p);
+ MatcherType *match = new MatcherType(_cond_op);
+
+ match->set(static_cast<uint16_t>(strtol(p.get_arg().c_str(), nullptr, 10)),
mods());
+ _matcher = match;
+}
+
+void
+ConditionStateInt16::set_qualifier(const std::string &q)
+{
+ Condition::set_qualifier(q);
+
+ if (!q.empty()) { // This qualifier is optional, but must be 0 if there
+ long ix = strtol(q.c_str(), nullptr, 10);
+
+ if (ix != 0) {
+ TSError("[%s] STATE-INT16 index out of range: %s", PLUGIN_NAME,
q.c_str());
+ } else {
+ Dbg(pi_dbg_ctl, "\tParsing %%{STATE-INT16:%s}", q.c_str());
+ }
+ }
+}
+
+void
+ConditionStateInt16::append_value(std::string &s, const Resources &res)
+{
+ uint16_t data = _get_data(res);
+
+ s += std::to_string(data);
+ Dbg(pi_dbg_ctl, "Appending STATE-INT16(%d) to evaluation value -> %s", data,
s.c_str());
+}
+
+bool
+ConditionStateInt16::eval(const Resources &res)
+{
+ uint16_t data = _get_data(res);
+
+ Dbg(pi_dbg_ctl, "Evaluating STATE-INT8()");
+
+ return static_cast<const MatcherType *>(_matcher)->test(data);
}
diff --git a/plugins/header_rewrite/conditions.h
b/plugins/header_rewrite/conditions.h
index f49fcb3c04..40ce009b89 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -638,23 +638,11 @@ public:
ConditionHttpCntl(const ConditionHttpCntl &) = delete;
void operator=(const ConditionHttpCntl &) = delete;
- void initialize(Parser &p) override;
void set_qualifier(const std::string &q) override;
-
- void
- append_value(std::string &s, const Resources &res) override
- {
- s += TSHttpTxnCntlGet(res.txnp, _http_cntl_qual) ? "TRUE" : "FALSE";
- Dbg(pi_dbg_ctl, "Evaluating HTTP-CNTL(%s)", _qualifier.c_str());
- }
+ void append_value(std::string &s, const Resources &res) override;
protected:
- bool
- eval(const Resources &res) override
- {
- Dbg(pi_dbg_ctl, "Evaluating HTTP-CNTL()");
- return TSHttpTxnCntlGet(res.txnp, _http_cntl_qual);
- }
+ bool eval(const Resources &res) override;
private:
TSHttpCntlType _http_cntl_qual = TS_HTTP_CNTL_LOGGING_MODE;
@@ -725,3 +713,118 @@ private:
Condition *_cond = nullptr; // First pre-condition (linked list)
bool _end = false;
};
+
+// State Flags
+class ConditionStateFlag : public Condition
+{
+public:
+ explicit ConditionStateFlag()
+ {
+ static_assert(sizeof(void *) == 8, "State Variables requires a 64-bit
system.");
+ Dbg(dbg_ctl, "Calling CTOR for ConditionStateFlag");
+ }
+
+ // noncopyable
+ ConditionStateFlag(const ConditionStateFlag &) = delete;
+ void operator=(const ConditionStateFlag &) = delete;
+
+ void set_qualifier(const std::string &q) override;
+ void append_value(std::string &s, const Resources &res) override;
+
+protected:
+ bool eval(const Resources &res) override;
+
+ bool
+ need_txn_slot() const override
+ {
+ return true;
+ }
+
+private:
+ int _flag_ix = -1;
+ uint64_t _mask = 0;
+};
+
+// INT8 state variables
+class ConditionStateInt8 : public Condition
+{
+ using MatcherType = Matchers<uint8_t>;
+
+public:
+ explicit ConditionStateInt8()
+ {
+ static_assert(sizeof(void *) == 8, "State Variables requires a 64-bit
system.");
+ Dbg(dbg_ctl, "Calling CTOR for ConditionStateInt8");
+ }
+
+ // noncopyable
+ ConditionStateInt8(const ConditionStateInt8 &) = delete;
+ void operator=(const ConditionStateInt8 &) = delete;
+
+ void initialize(Parser &p) override;
+ void set_qualifier(const std::string &q) override;
+ void append_value(std::string &s, const Resources &res) override;
+
+protected:
+ bool eval(const Resources &res) override;
+
+ bool
+ need_txn_slot() const override
+ {
+ return true;
+ }
+
+private:
+ // Little helper function to extract out the data from the TXN user pointer
+ uint8_t
+ _get_data(const Resources &res) const
+ {
+ TSAssert(_byte_ix >= 0 && _byte_ix < NUM_STATE_INT8S);
+ auto ptr = reinterpret_cast<uint64_t>(TSUserArgGet(res.txnp,
_txn_slot));
+ uint8_t data = (ptr & STATE_INT8_MASKS[_byte_ix]) >> (NUM_STATE_FLAGS +
_byte_ix * 8);
+
+ return data;
+ }
+
+ int _byte_ix = -1;
+};
+
+// INT16 state variables
+class ConditionStateInt16 : public Condition
+{
+ using MatcherType = Matchers<uint16_t>;
+
+public:
+ explicit ConditionStateInt16()
+ {
+ static_assert(sizeof(void *) == 8, "State Variables requires a 64-bit
system.");
+ Dbg(dbg_ctl, "Calling CTOR for ConditionStateInt16");
+ }
+
+ // noncopyable
+ ConditionStateInt16(const ConditionStateInt8 &) = delete;
+ void operator=(const ConditionStateInt8 &) = delete;
+
+ void initialize(Parser &p) override;
+ void set_qualifier(const std::string &q) override;
+ void append_value(std::string &s, const Resources &res) override;
+
+protected:
+ bool eval(const Resources &res) override;
+
+ bool
+ need_txn_slot() const override
+ {
+ return true;
+ }
+
+private:
+ // Little helper function to extract out the data from the TXN user pointer
+ uint16_t
+ _get_data(const Resources &res) const
+ {
+ auto ptr = reinterpret_cast<uint64_t>(TSUserArgGet(res.txnp, _txn_slot));
+
+ return ((ptr & STATE_INT16_MASK) >> 48);
+ }
+};
diff --git a/plugins/header_rewrite/factory.cc
b/plugins/header_rewrite/factory.cc
index 7fe0290bdb..4aa40a4b6d 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -79,6 +79,12 @@ operator_factory(const std::string &op)
o = new OperatorRunPlugin();
} else if (op == "set-body-from") {
o = new OperatorSetBodyFrom();
+ } else if (op == "set-state-flag") {
+ o = new OperatorSetStateFlag();
+ } else if (op == "set-state-int8") {
+ o = new OperatorSetStateInt8();
+ } else if (op == "set-state-int16") {
+ o = new OperatorSetStateInt16();
} else {
TSError("[%s] Unknown operator: %s", PLUGIN_NAME, op.c_str());
return nullptr;
@@ -166,6 +172,13 @@ condition_factory(const std::string &cond)
c = new ConditionHttpCntl();
} else if (c_name == "GROUP") {
c = new ConditionGroup();
+ } else if (c_name == "STATE-FLAG") {
+ c = new ConditionStateFlag();
+ } else if (c_name == "STATE-INT8") {
+ c = new ConditionStateInt8();
+ } else if (c_name == "STATE-INT16") {
+ c = new ConditionStateInt16();
+
} else {
TSError("[%s] Unknown condition %s", PLUGIN_NAME, c_name.c_str());
return nullptr;
diff --git a/plugins/header_rewrite/header_rewrite.cc
b/plugins/header_rewrite/header_rewrite.cc
index 7f651014f9..96a3fe31a4 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -37,10 +37,11 @@
#include "conditions_geo.h"
// Debugs
-const char PLUGIN_NAME[] = "header_rewrite";
-const char PLUGIN_NAME_DBG[] = "dbg_header_rewrite";
namespace header_rewrite_ns
{
+const char PLUGIN_NAME[] = "header_rewrite";
+const char PLUGIN_NAME_DBG[] = "dbg_header_rewrite";
+
DbgCtl dbg_ctl{PLUGIN_NAME_DBG};
DbgCtl pi_dbg_ctl{PLUGIN_NAME};
@@ -104,6 +105,7 @@ public:
{
return _resids[hook];
}
+
RuleSet *
rule(int hook) const
{
diff --git a/plugins/header_rewrite/header_rewrite_test.cc
b/plugins/header_rewrite/header_rewrite_test.cc
index 355ec164b3..8e92f7ae5f 100644
--- a/plugins/header_rewrite/header_rewrite_test.cc
+++ b/plugins/header_rewrite/header_rewrite_test.cc
@@ -27,8 +27,11 @@
#include "parser.h"
+namespace header_rewrite_ns
+{
const char PLUGIN_NAME[] = "TEST_header_rewrite";
const char PLUGIN_NAME_DBG[] = "TEST_dbg_header_rewrite";
+} // namespace header_rewrite_ns
void
TSError(const char *fmt, ...)
diff --git a/plugins/header_rewrite/lulu.h b/plugins/header_rewrite/lulu.h
index f12339346e..3900713b0d 100644
--- a/plugins/header_rewrite/lulu.h
+++ b/plugins/header_rewrite/lulu.h
@@ -35,13 +35,14 @@ std::string getIP(sockaddr const *s_sockaddr);
char *getIP(sockaddr const *s_sockaddr, char res[INET6_ADDRSTRLEN]);
uint16_t getPort(sockaddr const *s_sockaddr);
+namespace header_rewrite_ns
+{
extern const char PLUGIN_NAME[];
extern const char PLUGIN_NAME_DBG[];
-namespace header_rewrite_ns
-{
extern DbgCtl dbg_ctl;
extern DbgCtl pi_dbg_ctl;
extern PluginFactory plugin_factory;
} // namespace header_rewrite_ns
+
using namespace header_rewrite_ns;
diff --git a/plugins/header_rewrite/operators.cc
b/plugins/header_rewrite/operators.cc
index 190a23399e..28c1af6219 100644
--- a/plugins/header_rewrite/operators.cc
+++ b/plugins/header_rewrite/operators.cc
@@ -1157,7 +1157,7 @@ OperatorSetHttpCntl::initialize(Parser &p)
Operator::initialize(p);
_cntl_qual = parse_http_cntl_qualifier(p.get_arg());
- std::string flag = p.copy_value();
+ std::string flag = p.get_value(); // Make a copy of the value
std::transform(flag.begin(), flag.end(), flag.begin(), ::tolower);
@@ -1321,3 +1321,199 @@ OperatorSetBodyFrom::exec(const Resources &res) const
}
return false;
}
+
+void
+OperatorSetStateFlag::initialize(Parser &p)
+{
+ Operator::initialize(p);
+
+ _flag_ix = strtol(p.get_arg().c_str(), nullptr, 10);
+
+ if (_flag_ix < 0 || _flag_ix >= NUM_STATE_FLAGS) {
+ TSError("[%s] state flag with index %d is out of range", PLUGIN_NAME,
_flag_ix);
+ return;
+ }
+
+ std::string flag = p.get_value(); // Make a copy of the value
+
+ std::transform(flag.begin(), flag.end(), flag.begin(), ::tolower);
+
+ if (flag == "1" || flag == "true" || flag == "on" || flag == "enable") {
+ _mask = 1ULL << _flag_ix;
+ _flag = true;
+ } else {
+ _mask = ~(1ULL << _flag_ix);
+ _flag = false;
+ }
+}
+
+// This operator should be allowed everywhere
+void
+OperatorSetStateFlag::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
+OperatorSetStateFlag::exec(const Resources &res) const
+{
+ if (!res.txnp) {
+ TSError("[%s] OperatorSetStateFlag() failed. Transaction is null",
PLUGIN_NAME);
+ return false;
+ }
+
+ Dbg(pi_dbg_ctl, " Setting state flag %d to %d", _flag_ix, _flag);
+
+ auto data = reinterpret_cast<uint64_t>(TSUserArgGet(res.txnp, _txn_slot));
+
+ TSUserArgSet(res.txnp, _txn_slot, reinterpret_cast<void *>(_flag ? data |
_mask : data & _mask));
+
+ return true;
+}
+
+void
+OperatorSetStateInt8::initialize(Parser &p)
+{
+ Operator::initialize(p);
+
+ _byte_ix = strtol(p.get_arg().c_str(), nullptr, 10);
+
+ if (_byte_ix < 0 || _byte_ix >= NUM_STATE_INT8S) {
+ TSError("[%s] state int8 with index %d is out of range", PLUGIN_NAME,
_byte_ix);
+ return;
+ }
+
+ _value.set_value(p.get_value());
+ if (!_value.has_conds()) {
+ int v = _value.get_int_value();
+
+ if (v < 0 || v > 255) {
+ TSError("[%s] state int8 value %d is out of range", PLUGIN_NAME, v);
+ return;
+ }
+ }
+}
+
+// This operator should be allowed everywhere
+void
+OperatorSetStateInt8::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
+OperatorSetStateInt8::exec(const Resources &res) const
+{
+ if (!res.txnp) {
+ TSError("[%s] OperatorSetStateInt8() failed. Transaction is null",
PLUGIN_NAME);
+ return false;
+ }
+
+ auto ptr = reinterpret_cast<uint64_t>(TSUserArgGet(res.txnp, _txn_slot));
+ int val = 0;
+
+ if (_value.has_conds()) { // If there are conditions, we need to evaluate
them, which gives us a string
+ std::string v;
+
+ _value.append_value(v, res);
+ val = strtol(v.c_str(), nullptr, 10);
+ if (val < 0 || val > 255) {
+ TSWarning("[%s] state int8 value %d is out of range", PLUGIN_NAME, val);
+ return false;
+ }
+ } else {
+ // These values have already been checked at load time
+ val = _value.get_int_value();
+ }
+
+ Dbg(pi_dbg_ctl, " Setting state int8 %d to %d", _byte_ix, val);
+ ptr &= ~STATE_INT8_MASKS[_byte_ix]; // Clear any old value
+ ptr |= (static_cast<uint64_t>(val) << (NUM_STATE_FLAGS + _byte_ix * 8));
+ TSUserArgSet(res.txnp, _txn_slot, reinterpret_cast<void *>(ptr));
+
+ return true;
+}
+
+void
+OperatorSetStateInt16::initialize(Parser &p)
+{
+ Operator::initialize(p);
+
+ int ix = strtol(p.get_arg().c_str(), nullptr, 10);
+
+ if (ix != 0) {
+ TSError("[%s] state int16 with index %d is out of range", PLUGIN_NAME, ix);
+ return;
+ }
+
+ _value.set_value(p.get_value());
+ if (!_value.has_conds()) {
+ int v = _value.get_int_value();
+
+ if (v < 0 || v > 65535) {
+ TSError("[%s] state int16 value %d is out of range", PLUGIN_NAME, v);
+ return;
+ }
+ }
+}
+
+// This operator should be allowed everywhere
+void
+OperatorSetStateInt16::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
+OperatorSetStateInt16::exec(const Resources &res) const
+{
+ if (!res.txnp) {
+ TSError("[%s] OperatorSetStateInt16() failed. Transaction is null",
PLUGIN_NAME);
+ return false;
+ }
+
+ auto ptr = reinterpret_cast<uint64_t>(TSUserArgGet(res.txnp, _txn_slot));
+ int val = 0;
+
+ if (_value.has_conds()) { // If there are conditions, we need to evaluate
them, which gives us a string
+ std::string v;
+
+ _value.append_value(v, res);
+ val = strtol(v.c_str(), nullptr, 10);
+ if (val < 0 || val > 65535) {
+ TSWarning("[%s] state int8 value %d is out of range", PLUGIN_NAME, val);
+ return false;
+ }
+ } else {
+ // These values have already been checked at load time
+ val = _value.get_int_value();
+ }
+
+ Dbg(pi_dbg_ctl, " Setting state int16 to %d", val);
+ ptr &= ~STATE_INT16_MASK; // Clear any old value
+ ptr |= (static_cast<uint64_t>(val) << 48);
+ TSUserArgSet(res.txnp, _txn_slot, reinterpret_cast<void *>(ptr));
+
+ return true;
+}
diff --git a/plugins/header_rewrite/operators.h
b/plugins/header_rewrite/operators.h
index df4e189686..12d003c3cf 100644
--- a/plugins/header_rewrite/operators.h
+++ b/plugins/header_rewrite/operators.h
@@ -505,3 +505,93 @@ protected:
private:
Value _value;
};
+
+class OperatorSetStateFlag : public Operator
+{
+public:
+ OperatorSetStateFlag()
+ {
+ static_assert(sizeof(void *) == 8, "State Variables requires a 64-bit
system.");
+ Dbg(dbg_ctl, "Calling CTOR for OperatorSetStateFlag");
+ }
+
+ // noncopyable
+ OperatorSetStateFlag(const OperatorSetStateFlag &) = delete;
+ void operator=(const OperatorSetStateFlag &) = delete;
+
+ void initialize(Parser &p) override;
+
+protected:
+ void initialize_hooks() override;
+ bool exec(const Resources &res) const override;
+
+ bool
+ need_txn_slot() const override
+ {
+ return true;
+ }
+
+private:
+ int _flag_ix = -1;
+ int _flag = false;
+ uint64_t _mask = 0;
+};
+
+class OperatorSetStateInt8 : public Operator
+{
+public:
+ OperatorSetStateInt8()
+ {
+ static_assert(sizeof(void *) == 8, "State Variables requires a 64-bit
system.");
+ Dbg(dbg_ctl, "Calling CTOR for OperatorSetStateInt8");
+ }
+
+ // noncopyable
+ OperatorSetStateInt8(const OperatorSetStateInt8 &) = delete;
+ void operator=(const OperatorSetStateInt8 &) = delete;
+
+ void initialize(Parser &p) override;
+
+protected:
+ void initialize_hooks() override;
+ bool exec(const Resources &res) const override;
+
+ bool
+ need_txn_slot() const override
+ {
+ return true;
+ }
+
+private:
+ int _byte_ix = -1;
+ Value _value;
+};
+
+class OperatorSetStateInt16 : public Operator
+{
+public:
+ OperatorSetStateInt16()
+ {
+ static_assert(sizeof(void *) == 8, "State Variables requires a 64-bit
system.");
+ Dbg(dbg_ctl, "Calling CTOR for OperatorSetStateInt16");
+ }
+
+ // noncopyable
+ OperatorSetStateInt16(const OperatorSetStateInt16 &) = delete;
+ void operator=(const OperatorSetStateInt16 &) = delete;
+
+ void initialize(Parser &p) override;
+
+protected:
+ void initialize_hooks() override;
+ bool exec(const Resources &res) const override;
+
+ bool
+ need_txn_slot() const override
+ {
+ return true;
+ }
+
+private:
+ Value _value;
+};
diff --git a/plugins/header_rewrite/parser.h b/plugins/header_rewrite/parser.h
index d621f07984..17777d3999 100644
--- a/plugins/header_rewrite/parser.h
+++ b/plugins/header_rewrite/parser.h
@@ -89,13 +89,6 @@ public:
return _val;
}
- // Return a copy of the string, this implies RVO as well
- std::string
- copy_value() const
- {
- return _val;
- }
-
bool
mod_exist(const std::string &m) const
{
diff --git a/plugins/header_rewrite/statement.cc
b/plugins/header_rewrite/statement.cc
index 92e9fb67b5..f9d9fc59b9 100644
--- a/plugins/header_rewrite/statement.cc
+++ b/plugins/header_rewrite/statement.cc
@@ -73,6 +73,28 @@ Statement::initialize_hooks()
add_allowed_hook(TS_HTTP_TXN_CLOSE_HOOK);
}
+void
+Statement::acquire_txn_slot()
+{
+ // Don't do anything if we don't need it
+ if (!need_txn_slot() || _txn_slot >= 0) {
+ return;
+ }
+
+ // Only call the index reservation once per plugin load
+ static int txn_slot_index = []() -> int {
+ int index = -1;
+
+ if (TS_ERROR == TSUserArgIndexReserve(TS_USER_ARGS_TXN, PLUGIN_NAME, "HRW
txn variables", &index)) {
+ TSError("[%s] failed to reserve user arg index", PLUGIN_NAME);
+ return -1; // Fallback value
+ }
+ return index;
+ }();
+
+ _txn_slot = txn_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 9055558531..92954749af 100644
--- a/plugins/header_rewrite/statement.h
+++ b/plugins/header_rewrite/statement.h
@@ -32,6 +32,22 @@
#include "parser.h"
#include "lulu.h"
+namespace header_rewrite_ns
+{
+constexpr int NUM_STATE_FLAGS = 16;
+constexpr int NUM_STATE_INT8S = 4;
+
+constexpr uint64_t STATE_INT8_MASKS[NUM_STATE_INT8S] = {
+ // These would change if the number of flag bits changes
+ 0x0000000000FF0000ULL, // Bits 16-23
+ 0x00000000FF000000ULL, // Bits 24-31
+ 0x000000FF00000000ULL, // Bits 32-39
+ 0x0000FF0000000000ULL, // Bits 40-47
+};
+
+constexpr uint64_t STATE_INT16_MASK = 0xFFFF000000000000ULL; // Bits 48-63
+} // namespace header_rewrite_ns
+
// URL data (both client and server)
enum UrlQualifiers {
URL_QUAL_NONE,
@@ -138,6 +154,8 @@ public:
{
TSReleaseAssert(_initialized == false);
initialize_hooks();
+ acquire_txn_slot();
+
_initialized = true;
}
@@ -160,11 +178,20 @@ protected:
_rsrc = static_cast<ResourceIDs>(_rsrc | ids);
}
- Statement *_next = nullptr; // Linked list
+ virtual bool
+ need_txn_slot() const
+ {
+ return false;
+ }
+
+ Statement *_next = nullptr; // Linked list
+ int _txn_slot = -1;
private:
- ResourceIDs _rsrc = RSRC_NONE;
- bool _initialized = false;
- TSHttpHookID _hook = TS_HTTP_READ_RESPONSE_HDR_HOOK;
+ void acquire_txn_slot();
+
+ ResourceIDs _rsrc = RSRC_NONE;
+ TSHttpHookID _hook = TS_HTTP_READ_RESPONSE_HDR_HOOK;
std::vector<TSHttpHookID> _allowed_hooks;
+ bool _initialized = false;
};
diff --git a/plugins/header_rewrite/value.h b/plugins/header_rewrite/value.h
index df11e0c761..d27d3f8be3 100644
--- a/plugins/header_rewrite/value.h
+++ b/plugins/header_rewrite/value.h
@@ -91,6 +91,12 @@ public:
return _value.empty();
}
+ bool
+ has_conds() const
+ {
+ return !_cond_vals.empty();
+ }
+
private:
int _int_value = 0;
double _float_value = 0.0;