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 9a7488f061 Adds the %{GROUP} condition to HRW, and else clause (#12009)
9a7488f061 is described below
commit 9a7488f061369dd2d563bb9b7517ec61d9e9b84e
Author: Leif Hedstrom <[email protected]>
AuthorDate: Wed Feb 5 09:24:34 2025 -0700
Adds the %{GROUP} condition to HRW, and else clause (#12009)
* Adds the %{GROUP} condition to HRW
This allows for grouping of conditions, like ()'s, and as such,
better control for logical AND and OR's.
* Adds an else clause to HRW conditions
---
doc/admin-guide/plugins/header_rewrite.en.rst | 65 +++++++++++++++-
plugins/header_rewrite/conditions.h | 66 ++++++++++++++++
plugins/header_rewrite/factory.cc | 4 +-
plugins/header_rewrite/header_rewrite.cc | 104 +++++++++++++++++++-------
plugins/header_rewrite/parser.cc | 5 ++
plugins/header_rewrite/parser.h | 7 ++
plugins/header_rewrite/ruleset.cc | 73 +++++++++---------
plugins/header_rewrite/ruleset.h | 82 +++++++++++++-------
8 files changed, 310 insertions(+), 96 deletions(-)
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst
b/doc/admin-guide/plugins/header_rewrite.en.rst
index 7fa890d869..903acca773 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -122,11 +122,24 @@ like the following::
cond %{STATUS} >399 [AND]
cond %{STATUS} <500
- set-status 404
+ set-status 404
Which converts any 4xx HTTP status code from the origin server to a 404. A
response from the origin with a status of 200 would be unaffected by this rule.
+An optional ``else`` clause may be specified, which will be executed if the
+conditions are not met. The ``else`` clause is specified by starting a new line
+with the word ``else``. The following example illustrates this::
+
+ cond %{STATUS} >399 [AND]
+ cond %{STATUS} <500
+ set-status 404
+ else
+ set-status 503
+
+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.
+
Conditions
----------
@@ -297,6 +310,56 @@ setting headers. For example::
set-header ATS-Geo-ASN %{GEO:ASN}
set-header ATS-Geo-ASN-NAME %{GEO:ASN-NAME}
+GROUP
+~~~~~
+;;
+ cond %{GROUP}
+ cond %{GROUP:END}
+
+This condition is a pseudo condition that is used to group conditions together.
+Using these groups, you can construct more complex expressions, that can mix
and
+match AND, OR and NOT operators. These groups are the equivalent of parenthesis
+in expressions.The following pseudo example illustrates this. Lets say you want
+to express::
+
+ (A and B) or (C and (D or E))
+
+Assuming A, B, C, D and E are all valid conditions, you would write this as::
+
+ cond %{GROUP} [OR]
+ cond A [AND]
+ cond B
+ cond %{GROUP:END}
+ cond %{GROUP]
+ cond C [AND]
+ cond %{GROUP}
+ cond D [OR]
+ cond E
+ cond %{GROUP:END}
+ cond %{GROUP:END}
+
+Here's a more realistic example, abeit constructed, showing how to use the
+groups to construct a complex expression with real header value comparisons::
+
+ cond %{SEND_REQUEST_HDR_HOOK} [AND]
+ cond %{GROUP} [OR]
+ cond %{CLIENT-HEADER:X-Bar} /foo/ [AND]
+ cond %{CLIENT-HEADER:User-Agent} /Chrome/
+ cond %{GROUP:END}
+ cond %{GROUP}
+ cond %{CLIENT-HEADER:X-Bar} /fie/ [AND]
+ cond %{CLIENT-HEADER:User-Agent} /MSIE/
+ cond %{GROUP:END}
+ set-header X-My-Header "This is a test"
+
+Note that the ``GROUP`` and ``GROUP:END`` conditions do not take any operands
per se,
+and you are still limited to operations after the last condition. Also, the
``GROUP:END``
+condition must match exactly with the last ``GROUP`` conditions, and they can
be
+nested in one or several levels.
+
+When closing a group with ``GROUP::END``, the modifiers are not used, in fact
that entire
+condition is discarded, being used only to close the group. You may still
decorate it
+with the same modifier as the opening ``GROUP`` condition, but it is not
necessary.
HEADER
~~~~~~
diff --git a/plugins/header_rewrite/conditions.h
b/plugins/header_rewrite/conditions.h
index 6568fbeba9..f49fcb3c04 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -659,3 +659,69 @@ protected:
private:
TSHttpCntlType _http_cntl_qual = TS_HTTP_CNTL_LOGGING_MODE;
};
+
+class ConditionGroup : public Condition
+{
+public:
+ ConditionGroup() { Dbg(dbg_ctl, "Calling CTOR for ConditionGroup"); }
+
+ ~ConditionGroup() override
+ {
+ Dbg(dbg_ctl, "Calling DTOR for ConditionGroup");
+ delete _cond;
+ }
+
+ void
+ set_qualifier(const std::string &q) override
+ {
+ Condition::set_qualifier(q);
+
+ if (!q.empty()) { // Anything goes here, but prefer END
+ _end = true;
+ }
+ }
+
+ // noncopyable
+ ConditionGroup(const ConditionGroup &) = delete;
+ void operator=(const ConditionGroup &) = delete;
+
+ bool
+ closes() const
+ {
+ return _end;
+ }
+
+ void
+ append_value(std::string & /* s ATS_UNUSED */, const Resources & /* res
ATS_UNUSED */) override
+ {
+ TSAssert(!"%{GROUP} should never be used as a condition value!");
+ TSError("[%s] %%{GROUP} should never be used as a condition value!",
PLUGIN_NAME);
+ }
+
+ void
+ add_condition(Condition *cond)
+ {
+ if (_cond) {
+ _cond->append(cond);
+ } else {
+ _cond = cond;
+ }
+ }
+
+ // This can't be protected, because we actually evaluate this condition
directly from the Ruleset
+ bool
+ eval(const Resources &res) override
+ {
+ Dbg(pi_dbg_ctl, "Evaluating GROUP()");
+
+ if (_cond) {
+ return _cond->do_eval(res);
+ } else {
+ return true;
+ }
+ }
+
+private:
+ Condition *_cond = nullptr; // First pre-condition (linked list)
+ bool _end = false;
+};
diff --git a/plugins/header_rewrite/factory.cc
b/plugins/header_rewrite/factory.cc
index 85136f0b04..7fe0290bdb 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -162,8 +162,10 @@ condition_factory(const std::string &cond)
c = new ConditionCache();
} else if (c_name == "NEXT-HOP") { // This condition adapts to the hook
c = new ConditionNextHop();
- } else if (c_name == "HTTP-CNTL") { // This condition adapts to the hook
+ } else if (c_name == "HTTP-CNTL") {
c = new ConditionHttpCntl();
+ } else if (c_name == "GROUP") {
+ c = new ConditionGroup();
} 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 ea6aec6852..7f651014f9 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -19,6 +19,7 @@
#include <fstream>
#include <mutex>
#include <string>
+#include <stack>
#include <stdexcept>
#include <getopt.h>
@@ -145,10 +146,12 @@ RulesConfig::add_rule(RuleSet *rule)
bool
RulesConfig::parse_config(const std::string &fname, TSHttpHookID default_hook,
char *from_url, char *to_url)
{
- std::unique_ptr<RuleSet> rule(nullptr);
- std::string filename;
- std::ifstream f;
- int lineno = 0;
+ std::unique_ptr<RuleSet> rule(nullptr);
+ std::string filename;
+ std::ifstream f;
+ int lineno = 0;
+ std::stack<ConditionGroup *> group_stack;
+ ConditionGroup *group = nullptr;
if (0 == fname.size()) {
TSError("[%s] no config filename provided", PLUGIN_NAME);
@@ -168,11 +171,12 @@ RulesConfig::parse_config(const std::string &fname,
TSHttpHookID default_hook, c
return false;
}
+ Dbg(dbg_ctl, "Parsing started on file: %s", filename.c_str());
while (!f.eof()) {
std::string line;
getline(f, line);
- ++lineno; // ToDo: we should probably use this for error messages ...
+ ++lineno;
Dbg(dbg_ctl, "Reading line: %d: %s", lineno, line.c_str());
while (std::isspace(line[0])) {
@@ -191,7 +195,8 @@ RulesConfig::parse_config(const std::string &fname,
TSHttpHookID default_hook, c
// Tokenize and parse this line
if (!p.parse_line(line)) {
- Dbg(dbg_ctl, "Error parsing line '%s'", line.c_str());
+ TSError("[%s] Error parsing file '%s', line '%s', lineno: %d",
PLUGIN_NAME, filename.c_str(), line.c_str(), lineno);
+ Dbg(dbg_ctl, "Error parsing line '%s', lineno: %d", line.c_str(),
lineno);
continue;
}
@@ -208,14 +213,20 @@ RulesConfig::parse_config(const std::string &fname,
TSHttpHookID default_hook, c
bool is_hook = p.cond_is_hook(hook); // This updates the hook if
explicitly set, if not leaves at default
if (nullptr == rule) {
+ if (!group_stack.empty()) {
+ TSError("[%s] mismatched %%{GROUP} conditions in file: %s, lineno:
%d", PLUGIN_NAME, fname.c_str(), lineno);
+ return false;
+ }
+
rule = std::make_unique<RuleSet>();
rule->set_hook(hook);
+ group = rule->get_group(); // This the implicit rule group to begin with
if (is_hook) {
// Check if the hooks are not available for the remap mode
if ((default_hook == TS_REMAP_PSEUDO_HOOK) &&
((TS_HTTP_READ_REQUEST_HDR_HOOK == hook) ||
(TS_HTTP_PRE_REMAP_HOOK == hook))) {
- TSError("[%s] you can not use cond %%{%s} in a remap rule",
PLUGIN_NAME, p.get_op().c_str());
+ TSError("[%s] you can not use cond %%{%s} in a remap rule, lineno:
%d", PLUGIN_NAME, p.get_op().c_str(), lineno);
return false;
}
continue;
@@ -233,20 +244,54 @@ RulesConfig::parse_config(const std::string &fname,
TSHttpHookID default_hook, c
// Long term, maybe we need to percolate all this up through
add_condition() / add_operator() rather than this big ugly try.
try {
if (p.is_cond()) {
- if (!rule->add_condition(p, filename.c_str(), lineno)) {
+ Condition *cond = rule->make_condition(p, filename.c_str(), lineno);
+
+ if (!cond) {
throw std::runtime_error("add_condition() failed");
+ } else {
+ ConditionGroup *ngrp = dynamic_cast<ConditionGroup *>(cond);
+
+ if (ngrp) {
+ if (ngrp->closes()) {
+ // Closing a group, pop the stack
+ if (group_stack.empty()) {
+ throw std::runtime_error("unmatched %{GROUP}");
+ } else {
+ delete cond; // We don't care about the closing group
condition, it's a no-op
+ ngrp = group;
+ group = group_stack.top();
+ group_stack.pop();
+ group->add_condition(ngrp); // Add the previous group to the
current group's conditions
+ }
+ } else {
+ // New group
+ group_stack.push(group);
+ group = ngrp;
+ }
+ } else {
+ group->add_condition(cond);
+ }
}
- } else {
+ } else if (p.is_else()) {
+ // Switch to the else portion of operators
+ rule->switch_branch();
+ } else { // Operator
if (!rule->add_operator(p, filename.c_str(), lineno)) {
throw std::runtime_error("add_operator() failed");
}
}
} catch (std::runtime_error &e) {
- TSError("[%s] header_rewrite configuration exception: %s in file: %s",
PLUGIN_NAME, e.what(), fname.c_str());
+ TSError("[%s] header_rewrite configuration exception: %s in file: %s,
lineno: %d", PLUGIN_NAME, e.what(), fname.c_str(),
+ lineno);
return false;
}
}
+ if (!group_stack.empty()) {
+ TSError("[%s] missing final %%{GROUP:END} condition in file: %s, lineno:
%d", PLUGIN_NAME, fname.c_str(), lineno);
+ return false;
+ }
+
// Add the last rule (possibly the only rule)
if (add_rule(rule.get())) {
rule.release();
@@ -301,25 +346,25 @@ cont_rewrite_headers(TSCont contp, TSEvent event, void
*edata)
}
bool reenable{true};
+
if (hook != TS_HTTP_LAST_HOOK) {
- const RuleSet *rule = conf->rule(hook);
- Resources res(txnp, contp);
+ RuleSet *rule = conf->rule(hook);
+ Resources res(txnp, contp);
// Get the resources necessary to process this event
res.gather(conf->resid(hook), hook);
// Evaluation of all rules. This code is sort of duplicate in DoRemap as
well.
while (rule) {
- if (rule->eval(res)) {
- OperModifiers rt = rule->exec(res);
+ const RuleSet::OperatorPair &ops = rule->eval(res);
+ const OperModifiers rt = rule->exec(ops, res);
- if (rt & OPER_NO_REENABLE) {
- reenable = false;
- }
+ if (rt & OPER_NO_REENABLE) {
+ reenable = false;
+ }
- if (rule->last() || (rt & OPER_LAST)) {
- break; // Conditional break, force a break with [L]
- }
+ if (rule->last() || (rt & OPER_LAST)) {
+ break; // Conditional break, force a break with [L]
}
rule = rule->next;
}
@@ -328,6 +373,7 @@ cont_rewrite_headers(TSCont contp, TSEvent event, void
*edata)
if (reenable) {
TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
}
+
return 0;
}
@@ -528,19 +574,19 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo
*rri)
res.gather(RSRC_CLIENT_REQUEST_HEADERS, TS_REMAP_PSEUDO_HOOK);
while (rule) {
- if (rule->eval(res)) {
- OperModifiers rt = rule->exec(res);
+ const RuleSet::OperatorPair &ops = rule->eval(res);
+ const OperModifiers rt = rule->exec(ops, res);
- ink_assert((rt & OPER_NO_REENABLE) == 0);
+ ink_assert((rt & OPER_NO_REENABLE) == 0);
- if (res.changed_url == true) {
- rval = TSREMAP_DID_REMAP;
- }
+ if (res.changed_url == true) {
+ rval = TSREMAP_DID_REMAP;
+ }
- if (rule->last() || (rt & OPER_LAST)) {
- break; // Conditional break, force a break with [L]
- }
+ if (rule->last() || (rt & OPER_LAST)) {
+ break; // Conditional break, force a break with [L]
}
+
rule = rule->next;
}
diff --git a/plugins/header_rewrite/parser.cc b/plugins/header_rewrite/parser.cc
index 5be73a22ba..921ec2f83a 100644
--- a/plugins/header_rewrite/parser.cc
+++ b/plugins/header_rewrite/parser.cc
@@ -173,6 +173,11 @@ Parser::preprocess(std::vector<std::string> tokens)
} else if (tokens[0] == "cond") {
_cond = true;
tokens.erase(tokens.begin());
+ } else if (tokens[0] == "else") {
+ _cond = false;
+ _else = true;
+
+ return true;
}
// Is it a condition or operator?
diff --git a/plugins/header_rewrite/parser.h b/plugins/header_rewrite/parser.h
index 25bfdd0a70..d621f07984 100644
--- a/plugins/header_rewrite/parser.h
+++ b/plugins/header_rewrite/parser.h
@@ -65,6 +65,12 @@ public:
return _cond;
}
+ bool
+ is_else() const
+ {
+ return _else;
+ }
+
const std::string &
get_op() const
{
@@ -110,6 +116,7 @@ private:
bool preprocess(std::vector<std::string> tokens);
bool _cond = false;
+ bool _else = false;
bool _empty = false;
char *_from_url = nullptr;
char *_to_url = nullptr;
diff --git a/plugins/header_rewrite/ruleset.cc
b/plugins/header_rewrite/ruleset.cc
index dca1139bbe..a5d5709932 100644
--- a/plugins/header_rewrite/ruleset.cc
+++ b/plugins/header_rewrite/ruleset.cc
@@ -40,66 +40,67 @@ RuleSet::append(RuleSet *rule)
tmp->next = rule;
}
-bool
-RuleSet::add_condition(Parser &p, const char *filename, int lineno)
+// This stays here, since the condition, albeit owned by a group, is tightly
couple to the ruleset.
+Condition *
+RuleSet::make_condition(Parser &p, const char *filename, int lineno)
{
Condition *c = condition_factory(p.get_op());
- if (nullptr != c) {
- Dbg(pi_dbg_ctl, " Adding condition: %%{%s} with arg: %s",
p.get_op().c_str(), p.get_arg().c_str());
- c->initialize(p);
- if (!c->set_hook(_hook)) {
- delete c;
- TSError("[%s] in %s:%d: can't use this condition in hook=%s: %%{%s} with
arg: %s", PLUGIN_NAME, filename, lineno,
- TSHttpHookNameLookup(_hook), p.get_op().c_str(),
p.get_arg().c_str());
- return false;
- }
- if (c->get_cond_op() == MATCH_ERROR) {
- delete c;
- TSError("[%s] in %s:%d: Invalid operator", PLUGIN_NAME, filename,
lineno);
- return false;
- }
- if (nullptr == _cond) {
- _cond = c;
- } else {
- _cond->append(c);
- }
+ if (nullptr == c) {
+ return nullptr; // Complete failure in the factory
+ }
- // Update some ruleset state based on this new condition
- _last |= c->last();
- _ids = static_cast<ResourceIDs>(_ids | _cond->get_resource_ids());
+ Dbg(pi_dbg_ctl, " Adding condition: %%{%s} with arg: %s",
p.get_op().c_str(), p.get_arg().c_str());
+ c->initialize(p);
+ if (!c->set_hook(_hook)) {
+ delete c;
+ TSError("[%s] in %s:%d: can't use this condition in hook=%s: %%{%s} with
arg: %s", PLUGIN_NAME, filename, lineno,
+ TSHttpHookNameLookup(_hook), p.get_op().c_str(),
p.get_arg().c_str());
+ return nullptr;
+ }
- return true;
+ if (c->get_cond_op() == MATCH_ERROR) {
+ delete c;
+ TSError("[%s] in %s:%d: Invalid operator", PLUGIN_NAME, filename, lineno);
+ return nullptr;
}
- return false;
+ // Update some ruleset state based on this new condition;
+ _last |= c->last();
+ _ids = static_cast<ResourceIDs>(_ids | c->get_resource_ids());
+
+ return c;
}
bool
RuleSet::add_operator(Parser &p, const char *filename, int lineno)
{
- Operator *o = operator_factory(p.get_op());
+ Operator *op = operator_factory(p.get_op());
- if (nullptr != o) {
+ if (nullptr != op) {
Dbg(pi_dbg_ctl, " Adding operator: %s(%s)=\"%s\"", p.get_op().c_str(),
p.get_arg().c_str(), p.get_value().c_str());
- o->initialize(p);
- if (!o->set_hook(_hook)) {
- delete o;
+ op->initialize(p);
+ if (!op->set_hook(_hook)) {
+ delete op;
Dbg(pi_dbg_ctl, "in %s:%d: can't use this operator in hook=%s: %s(%s)",
filename, lineno, TSHttpHookNameLookup(_hook),
p.get_op().c_str(), p.get_arg().c_str());
TSError("[%s] in %s:%d: can't use this operator in hook=%s: %s(%s)",
PLUGIN_NAME, filename, lineno,
TSHttpHookNameLookup(_hook), p.get_op().c_str(),
p.get_arg().c_str());
return false;
}
- if (nullptr == _oper) {
- _oper = o;
+
+ // Work on the appropriate operator list
+ OperatorPair &ops = _is_else ? _operators[1] : _operators[0];
+
+ if (nullptr == ops.oper) {
+ ops.oper = op;
} else {
- _oper->append(o);
+ ops.oper->append(op);
}
// Update some ruleset state based on this new operator
- _opermods = static_cast<OperModifiers>(_opermods |
_oper->get_oper_modifiers());
- _ids = static_cast<ResourceIDs>(_ids | _oper->get_resource_ids());
+ ops.oper_mods = static_cast<OperModifiers>(ops.oper_mods |
ops.oper->get_oper_modifiers());
+ _ids = static_cast<ResourceIDs>(_ids |
ops.oper->get_resource_ids());
return true;
}
diff --git a/plugins/header_rewrite/ruleset.h b/plugins/header_rewrite/ruleset.h
index d1b647e8c1..db19b406cf 100644
--- a/plugins/header_rewrite/ruleset.h
+++ b/plugins/header_rewrite/ruleset.h
@@ -29,6 +29,7 @@
#include "factory.h"
#include "resources.h"
#include "parser.h"
+#include "conditions.h"
///////////////////////////////////////////////////////////////////////////////
// Class holding one ruleset. A ruleset is one (or more) pre-conditions, and
@@ -37,14 +38,25 @@
class RuleSet
{
public:
+ // Holding the IF and ELSE operators and mods, in two separate linked lists.
+ struct OperatorPair {
+ OperatorPair() = default;
+
+ OperatorPair(const OperatorPair &) = delete;
+ OperatorPair &operator=(const OperatorPair &) = delete;
+
+ Operator *oper = nullptr;
+ OperModifiers oper_mods = OPER_NONE;
+ };
+
RuleSet() { Dbg(dbg_ctl, "RuleSet CTOR"); }
~RuleSet()
{
Dbg(dbg_ctl, "RulesSet DTOR");
+ delete _operators[0].oper; // These are pointers
+ delete _operators[1].oper;
delete next;
- delete _cond;
- delete _oper;
}
// noncopyable
@@ -53,20 +65,14 @@ public:
// No reason to inline these
void append(RuleSet *rule);
- bool add_condition(Parser &p, const char *filename, int lineno);
+ Condition *make_condition(Parser &p, const char *filename, int lineno);
bool add_operator(Parser &p, const char *filename, int lineno);
ResourceIDs get_all_resource_ids() const;
bool
has_operator() const
{
- return nullptr != _oper;
- }
-
- bool
- has_condition() const
- {
- return nullptr != _cond;
+ return (nullptr != _operators[0].oper) || (nullptr != _operators[1].oper);
}
void
@@ -75,6 +81,12 @@ public:
_hook = hook;
}
+ ConditionGroup *
+ get_group()
+ {
+ return &_group;
+ }
+
TSHttpHookID
get_hook() const
{
@@ -88,41 +100,53 @@ public:
}
bool
- eval(const Resources &res) const
+ last() const
{
- if (nullptr == _cond) {
- return true;
- } else {
- return _cond->do_eval(res);
- }
+ return _last;
}
- bool
- last() const
+ void
+ switch_branch()
{
- return _last;
+ _is_else = !_is_else;
}
OperModifiers
- exec(const Resources &res) const
+ exec(const OperatorPair &ops, const Resources &res) const
{
- auto no_reenable_count{_oper->do_exec(res)};
+ if (nullptr == ops.oper) {
+ return ops.oper_mods;
+ }
+
+ auto no_reenable_count{ops.oper->do_exec(res)};
+
ink_assert(no_reenable_count < 2);
if (no_reenable_count) {
- return static_cast<OperModifiers>(_opermods | OPER_NO_REENABLE);
+ return static_cast<OperModifiers>(ops.oper_mods | OPER_NO_REENABLE);
+ }
+
+ return ops.oper_mods;
+ }
+
+ const OperatorPair &
+ eval(const Resources &res)
+ {
+ if (_group.eval(res)) {
+ return _operators[0]; // IF conditions
+ } else {
+ return _operators[1]; // ELSE conditions
}
- return _opermods;
}
RuleSet *next = nullptr; // Linked list
private:
- Condition *_cond = nullptr; // First pre-condition
(linked list)
- Operator *_oper = nullptr; // First operator
(linked list)
- TSHttpHookID _hook = TS_HTTP_READ_RESPONSE_HDR_HOOK; // Which hook is this
rule for
+ ConditionGroup _group; // All conditions are now wrapped in a group
+ OperatorPair _operators[2]; // Holds both the IF and the ELSE set of
operators
// State values (updated when conds / operators are added)
- ResourceIDs _ids = RSRC_NONE;
- OperModifiers _opermods = OPER_NONE;
- bool _last = false;
+ TSHttpHookID _hook = TS_HTTP_READ_RESPONSE_HDR_HOOK; // Which hook is
this rule for
+ ResourceIDs _ids = RSRC_NONE;
+ bool _last = false;
+ bool _is_else = false; // Are we in the else clause of the new rule?
For parsing.
};