Skip to content

Commit

Permalink
Adds HRW state variables, 16 flags and 4 int8s
Browse files Browse the repository at this point in the history
  • Loading branch information
zwoop committed Feb 5, 2025
1 parent 9a7488f commit 5e64327
Show file tree
Hide file tree
Showing 13 changed files with 705 additions and 54 deletions.
83 changes: 80 additions & 3 deletions doc/admin-guide/plugins/header_rewrite.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ 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, and 4 8-bit integer states. These states are
all transactional, meaning they are usable and persistent across all hooks.

The flag states are numbers 0-15, and the integer states are numbers 0-3.

Conditions
----------

Expand Down Expand Up @@ -457,9 +466,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
~~
Expand Down Expand Up @@ -569,6 +579,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 8-bit unsigned integer.
There's only one 16-bit integer, and its value is returned. As such, the
index, ``0``, is optional here.

STATUS
~~~~~~
::
Expand Down Expand Up @@ -920,6 +960,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-int8 0 <value>

This operator allows you to set the state of a 16-bit unsigned integer.
The ``<value>`` is an unsigned 16-bit integer, 0-65535. It can also be a condition,
in which case thevalue of the condition is used. The index, 0, is always required even
though there is only one 16-bit integer state variable.

set-status
~~~~~~~~~~
::
Expand Down Expand Up @@ -1438,6 +1510,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
------------------------------------

Expand Down
160 changes: 139 additions & 21 deletions plugins/header_rewrite/conditions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down Expand Up @@ -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());
}

Expand Down Expand Up @@ -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());
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
Loading

0 comments on commit 5e64327

Please sign in to comment.