This is an automated email from the ASF dual-hosted git repository.
eze 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 c70884108f TSFile - remove from background_fetch plugin (#10980)
c70884108f is described below
commit c70884108fa71063f34b0f8427dc402fb14a9b0a
Author: Evan Zelkowitz <[email protected]>
AuthorDate: Wed Jan 10 11:04:32 2024 -0700
TSFile - remove from background_fetch plugin (#10980)
* TSFile - remove from background_fetch plugin.
* Update docs.
* Moved back to .h, used in other places besides configs.cc
* Update cmake and includes to reference the moved tscpputil to tsutil
---------
Co-authored-by: Alan M. Carroll <[email protected]>
---
doc/admin-guide/plugins/background_fetch.en.rst | 2 +
plugins/background_fetch/CMakeLists.txt | 1 +
plugins/background_fetch/configs.cc | 150 +++++++++++----------
plugins/background_fetch/configs.h | 16 ++-
plugins/background_fetch/rules.cc | 171 ++++++++++++------------
plugins/background_fetch/rules.h | 47 ++++---
6 files changed, 204 insertions(+), 183 deletions(-)
diff --git a/doc/admin-guide/plugins/background_fetch.en.rst
b/doc/admin-guide/plugins/background_fetch.en.rst
index 659976599f..1cd9addd9c 100644
--- a/doc/admin-guide/plugins/background_fetch.en.rst
+++ b/doc/admin-guide/plugins/background_fetch.en.rst
@@ -73,6 +73,8 @@ The contents of the config-file could be as below::
exclude Content-Type text
exclude X-Foo-Bar text
exclude Content-Length <1000
+ exclude Client-IP 127.0.0.1
+ include Client-IP 10.0.0.0/16
.. important::
diff --git a/plugins/background_fetch/CMakeLists.txt
b/plugins/background_fetch/CMakeLists.txt
index 94c86f4e46..0638a1071f 100644
--- a/plugins/background_fetch/CMakeLists.txt
+++ b/plugins/background_fetch/CMakeLists.txt
@@ -16,3 +16,4 @@
#######################
add_atsplugin(background_fetch background_fetch.cc configs.cc headers.cc
rules.cc)
+target_link_libraries(background_fetch PRIVATE libswoc::libswoc ts::tsutil)
diff --git a/plugins/background_fetch/configs.cc
b/plugins/background_fetch/configs.cc
index a7b86dc23f..2e8a6d9fa4 100644
--- a/plugins/background_fetch/configs.cc
+++ b/plugins/background_fetch/configs.cc
@@ -28,6 +28,12 @@
#include "configs.h"
+#include <swoc/TextView.h>
+#include <swoc/swoc_file.h>
+#include <tsutil/ts_bw_format.h>
+
+using namespace swoc::literals;
+
// Parse the command line options. This got a little wonky, since we decided
to have different
// syntax for remap vs global plugin initialization, and the global BG fetch
state :-/. Clean up
// later...
@@ -78,98 +84,102 @@ BgFetchConfig::parseOptions(int argc, const char *argv[])
bool
BgFetchConfig::readConfig(const char *config_file)
{
- char file_path[4096];
- TSFile file;
-
if (nullptr == config_file) {
TSError("[%s] invalid config file", PLUGIN_NAME);
return false;
}
+ swoc::file::path path(config_file);
+
Dbg(Bg_dbg_ctl, "trying to open config file in this path: %s", config_file);
- if (*config_file == '/') {
- snprintf(file_path, sizeof(file_path), "%s", config_file);
- } else {
- snprintf(file_path, sizeof(file_path), "%s/%s", TSConfigDirGet(),
config_file);
+ if (!path.is_absolute()) {
+ path = swoc::file::path(TSConfigDirGet()) / path;
}
- Dbg(Bg_dbg_ctl, "chosen config file is at: %s", file_path);
-
- file = TSfopen(file_path, "r");
- if (nullptr == file) {
- TSError("[%s] invalid config file: %s", PLUGIN_NAME, file_path);
- Dbg(Bg_dbg_ctl, "invalid config file: %s", file_path);
+ Dbg(Bg_dbg_ctl, "chosen config file is at: %s", path.c_str());
+
+ std::error_code ec;
+ auto content = swoc::file::load(path, ec);
+ if (ec) {
+ swoc::bwprint(ts::bw_dbg, "[{}] invalid config file: {} {}", PLUGIN_NAME,
path, ec);
+ TSError("%s", ts::bw_dbg.c_str());
+ Dbg(Bg_dbg_ctl, "%s", ts::bw_dbg.c_str());
return false;
}
- BgFetchRule *cur = nullptr;
- char buffer[8192];
-
- memset(buffer, 0, sizeof(buffer));
- while (TSfgets(file, buffer, sizeof(buffer) - 1) != nullptr) {
- char *eol = nullptr;
+ swoc::TextView text{content};
+ while (text) {
+ auto line = text.take_prefix_at('\n').ltrim_if(&isspace);
- // make sure line was not bigger than buffer
- if (nullptr == (eol = strchr(buffer, '\n')) && nullptr == (eol =
strstr(buffer, "\r\n"))) {
- TSError("[%s] exclusion line too long, did not get a good line in cfg,
skipping, line: %s", PLUGIN_NAME, buffer);
- memset(buffer, 0, sizeof(buffer));
+ if (line.empty() || line.front() == '#') {
continue;
}
- // make sure line has something useful on it
- if (eol - buffer < 2 || buffer[0] == '#') {
- memset(buffer, 0, sizeof(buffer));
+
+ auto cfg_type = line.take_prefix_if(&isspace);
+ if (cfg_type.empty()) {
continue;
}
- char *savePtr = nullptr;
- char *cfg = strtok_r(buffer, "\n\r\n", &savePtr);
-
- if (nullptr != cfg) {
- Dbg(Bg_dbg_ctl, "setting background_fetch exclusion criterion based on
string: %s", cfg);
- char *cfg_type = strtok_r(buffer, " ", &savePtr);
- char *cfg_name = nullptr;
- char *cfg_value = nullptr;
-
- if (cfg_type) {
- bool exclude = false;
- if (!strcmp(cfg_type, "exclude")) {
- exclude = true;
- } else if (strcmp(cfg_type, "include")) {
- TSError("[%s] invalid specifier %s, skipping config line",
PLUGIN_NAME, cfg_type);
- memset(buffer, 0, sizeof(buffer));
- continue;
- }
- cfg_name = strtok_r(nullptr, " ", &savePtr);
- if (cfg_name) {
- cfg_value = strtok_r(nullptr, " ", &savePtr);
- if (cfg_value) {
- if (!strcmp(cfg_name, "Content-Length")) {
- if ((cfg_value[0] != '<') && (cfg_value[0] != '>')) {
- TSError("[%s] invalid content-len condition %s, skipping
config value", PLUGIN_NAME, cfg_value);
- memset(buffer, 0, sizeof(buffer));
- continue;
- }
- }
- BgFetchRule *r = new BgFetchRule(exclude, cfg_name, cfg_value);
+ Dbg(Bg_dbg_ctl, "setting background_fetch exclusion criterion based on
string: %.*s", int(cfg_type.size()), cfg_type.data());
- if (nullptr == cur) {
- _rules = r;
- } else {
- cur->chain(r);
- }
- cur = r;
+ bool exclude = false;
+ if (0 == strcasecmp(cfg_type, "exclude")) {
+ exclude = true;
+ } else if (0 != strcasecmp(cfg_type, "include")) {
+ swoc::bwprint(ts::bw_dbg, "[{}] invalid specifier {}, skipping config
line", PLUGIN_NAME, cfg_type);
+ TSError("%s", ts::bw_dbg.c_str());
+ continue;
+ }
- Dbg(Bg_dbg_ctl, "adding background_fetch exclusion rule %d for %s:
%s", exclude, cfg_name, cfg_value);
+ if (auto cfg_name = line.take_prefix_if(&isspace); !cfg_name.empty()) {
+ if (auto cfg_value = line.take_prefix_if(&isspace); !cfg_value.empty()) {
+ if ("Client-IP"_tv == cfg_name) {
+ swoc::IPRange r;
+ // '*' is special - match any address. Signalled by empty range.
+ if (cfg_value.size() != 1 || cfg_value.front() == '*') {
+ if (!r.load(cfg_value)) { // assume if it loads, it's not empty.
+ TSError("[%s] invalid IP address range %.*s, skipping config
value", PLUGIN_NAME, int(cfg_value.size()),
+ cfg_value.data());
+ continue;
+ }
+ }
+ _rules.emplace_back(exclude, r);
+ swoc::bwprint(ts::bw_dbg, "adding background_fetch address range
rule {} for {}: {}", exclude, cfg_name, cfg_value);
+ Dbg(Bg_dbg_ctl, "%s", ts::bw_dbg.c_str());
+ } else if ("Content-Length"_tv == cfg_name) {
+ BgFetchRule::size_cmp_type::OP op;
+ if (cfg_value[0] == '<') {
+ op = BgFetchRule::size_cmp_type::LESS_THAN_OR_EQUAL;
+ } else if (cfg_value[0] == '>') {
+ op = BgFetchRule::size_cmp_type::LESS_THAN_OR_EQUAL;
} else {
- TSError("[%s] invalid value %s, skipping config line",
PLUGIN_NAME, cfg_name);
+ TSError("[%s] invalid Content-Length condition %.*s, skipping
config value", PLUGIN_NAME, int(cfg_value.size()),
+ cfg_value.data());
+ continue;
+ }
+ ++cfg_value; // Drop leading character.
+ swoc::TextView parsed;
+ auto n = swoc::svtou(cfg_value, &parsed);
+ if (parsed.size() != cfg_value.size()) {
+ TSError("[%s] invalid Content-Length size value %.*s, skipping
config value", PLUGIN_NAME, int(cfg_value.size()),
+ cfg_value.data());
+ continue;
}
+ _rules.emplace_back(exclude, op, size_t(n));
+
+ swoc::bwprint(ts::bw_dbg, "adding background_fetch content length
rule {} for {}: {}", exclude, cfg_name, cfg_value);
+ Dbg(Bg_dbg_ctl, "%s", ts::bw_dbg.c_str());
+ } else {
+ _rules.emplace_back(exclude, cfg_name, cfg_value);
+ swoc::bwprint(ts::bw_dbg, "adding background_fetch field compare
rule {} for {}: {}", exclude, cfg_name, cfg_value);
+ Dbg(Bg_dbg_ctl, "%s", ts::bw_dbg.c_str());
}
+ } else {
+ TSError("[%s] invalid value %.*s, skipping config line", PLUGIN_NAME,
int(cfg_name.size()), cfg_name.data());
}
- memset(buffer, 0, sizeof(buffer));
}
}
- TSfclose(file);
Dbg(Bg_dbg_ctl, "Done parsing config");
return true;
@@ -190,10 +200,10 @@ BgFetchConfig::bgFetchAllowed(TSHttpTxn txnp) const
bool allow_bg_fetch = true;
// We could do this recursively, but following the linked list is probably
more efficient.
- for (const BgFetchRule *r = _rules; nullptr != r; r = r->_next) {
- if (r->check_field_configured(txnp)) {
- Dbg(Bg_dbg_ctl, "found field match %s, exclude %d", r->_field,
static_cast<int>(r->_exclude));
- allow_bg_fetch = !r->_exclude;
+ for (auto const &r : _rules) {
+ if (r.check_field_configured(txnp)) {
+ Dbg(Bg_dbg_ctl, "found %s rule match", r._exclude ? "exclude" :
"include");
+ allow_bg_fetch = !r._exclude;
break;
}
}
diff --git a/plugins/background_fetch/configs.h
b/plugins/background_fetch/configs.h
index 1bf2ab7396..cdd3b04e67 100644
--- a/plugins/background_fetch/configs.h
+++ b/plugins/background_fetch/configs.h
@@ -27,13 +27,16 @@
#include <cstdlib>
#include <atomic>
#include <string>
+#include <list>
+#include <memory>
+#include <sys/socket.h>
#include "rules.h"
// Constants
const char PLUGIN_NAME[] = "background_fetch";
-extern DbgCtl Bg_dbg_ctl;
+static DbgCtl Bg_dbg_ctl{PLUGIN_NAME};
///////////////////////////////////////////////////////////////////////////
// This holds one complete background fetch rule
@@ -41,11 +44,12 @@ extern DbgCtl Bg_dbg_ctl;
class BgFetchConfig
{
public:
+ using list_type = std::list<BgFetchRule>;
+
explicit BgFetchConfig(TSCont cont) : _cont(cont) { TSContDataSet(cont,
static_cast<void *>(this)); }
~BgFetchConfig()
{
- delete _rules;
if (_cont) {
TSContDestroy(_cont);
}
@@ -53,7 +57,7 @@ public:
bool parseOptions(int argc, const char *argv[]);
- BgFetchRule *
+ list_type const &
getRules() const
{
return _rules;
@@ -83,8 +87,8 @@ public:
bool bgFetchAllowed(TSHttpTxn txnp) const;
private:
- TSCont _cont = nullptr;
- BgFetchRule *_rules = nullptr;
- bool _allow_304 = false;
+ TSCont _cont = nullptr;
+ list_type _rules;
+ bool _allow_304 = false;
std::string _log_file;
};
diff --git a/plugins/background_fetch/rules.cc
b/plugins/background_fetch/rules.cc
index b8f339d6d2..cad2f2282d 100644
--- a/plugins/background_fetch/rules.cc
+++ b/plugins/background_fetch/rules.cc
@@ -25,127 +25,120 @@
#include <cstdlib>
#include <string_view>
#include <cstring>
+#include <sys/socket.h>
+
+#include <swoc/IPEndpoint.h>
+#include <swoc/swoc_meta.h>
#include "configs.h"
#include "rules.h"
+#include "tsutil/ts_bw_format.h"
+#include "tsutil/ts_ip.h"
+
///////////////////////////////////////////////////////////////////////////
// These are little helper functions for the main rules evaluator.
//
static bool
-check_client_ip_configured(TSHttpTxn txnp, const char *cfg_ip)
+check_value(TSHttpTxn txnp, swoc::IPRange const &range)
{
const sockaddr *client_ip = TSHttpTxnClientAddrGet(txnp);
- char ip_buf[INET6_ADDRSTRLEN];
-
- if (AF_INET == client_ip->sa_family) {
- inet_ntop(AF_INET, &(reinterpret_cast<const sockaddr_in
*>(client_ip)->sin_addr), ip_buf, INET_ADDRSTRLEN);
- } else if (AF_INET6 == client_ip->sa_family) {
- inet_ntop(AF_INET6, &(reinterpret_cast<const sockaddr_in6
*>(client_ip)->sin6_addr), ip_buf, INET6_ADDRSTRLEN);
- } else {
- TSError("[%s] Unknown family %d", PLUGIN_NAME, client_ip->sa_family);
+ if (!client_ip) {
return false;
}
- Dbg(Bg_dbg_ctl, "cfg_ip %s, client_ip %s", cfg_ip, ip_buf);
-
- if ((strlen(cfg_ip) == strlen(ip_buf)) && !strcmp(cfg_ip, ip_buf)) {
- Dbg(Bg_dbg_ctl, "bg fetch for ip %s, configured ip %s", ip_buf, cfg_ip);
+ if (range.empty()) { // this means "match any address".
return true;
}
- return false;
+ swoc::IPEndpoint client_addr{client_ip};
+
+ swoc::bwprint(ts::bw_dbg, "cfg_ip {::c}, client_ip {}", range, client_addr);
+ Dbg(Bg_dbg_ctl, "%s", ts::bw_dbg.c_str());
+
+ if (client_addr.family() == range.family()) {
+ return (range.is_ip4() &&
range.ip4().contains(swoc::IP4Addr(client_addr.ip4()))) ||
+ (range.is_ip6() &&
range.ip6().contains(swoc::IP6Addr(client_addr.ip6())));
+ }
+
+ return false; // Different family, no match.
}
static bool
-check_content_length(const uint32_t len, const char *cfg_val)
+check_value(TSHttpTxn txnp, BgFetchRule::size_cmp_type const &cmp)
{
- uint32_t cfg_cont_len = atoi(&cfg_val[1]);
+ TSMBuffer hdr_bufp;
+ TSMLoc hdr_loc;
- if (cfg_val[0] == '<') {
- return (len <= cfg_cont_len);
- } else if (cfg_val[0] == '>') {
- return (len >= cfg_cont_len);
- } else {
- TSError("[%s] Invalid content length condition %c", PLUGIN_NAME,
cfg_val[0]);
+ if (TS_SUCCESS != TSHttpTxnServerRespGet(txnp, &hdr_bufp, &hdr_loc)) {
+ TSError("[%s] Failed to get resp headers", PLUGIN_NAME);
return false;
}
-}
-///////////////////////////////////////////////////////////////////////////
-// Check if a header excludes us from running the background fetch
-//
-bool
-BgFetchRule::check_field_configured(TSHttpTxn txnp) const
-{
- // check for client-ip first
- if (!strcmp(_field, "Client-IP")) {
- if (!strcmp(_value, "*")) {
- Dbg(Bg_dbg_ctl, "Found client_ip wild card");
- return true;
- }
- if (check_client_ip_configured(txnp, _value)) {
- Dbg(Bg_dbg_ctl, "Found client_ip match");
- return true;
- }
+ TSMLoc loc = TSMimeHdrFieldFind(hdr_bufp, hdr_loc,
TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH);
+ if (TS_NULL_MLOC == loc) {
+ Dbg(Bg_dbg_ctl, "No content-length field in resp");
+ return false; // Field not found.
+ }
+
+ auto content_len = TSMimeHdrFieldValueUintGet(hdr_bufp, hdr_loc, loc, 0 /*
index */);
+ TSHandleMLocRelease(hdr_bufp, hdr_loc, loc);
+
+ if (cmp._op == BgFetchRule::size_cmp_type::OP::GREATER_THAN_OR_EQUAL) {
+ return content_len >= cmp._size;
+ } else if (cmp._op == BgFetchRule::size_cmp_type::OP::LESS_THAN_OR_EQUAL) {
+ return content_len <= cmp._size;
}
- bool hdr_found = false;
+ return false;
+}
+
+static bool
+check_value(TSHttpTxn txnp, BgFetchRule::field_cmp_type const &cmp)
+{
TSMBuffer hdr_bufp;
TSMLoc hdr_loc;
- // Check response headers. ToDo: This doesn't check e.g. Content-Type :-/.
- if (!strcmp(_field, "Content-Length")) {
- if (TS_SUCCESS == TSHttpTxnServerRespGet(txnp, &hdr_bufp, &hdr_loc)) {
- TSMLoc loc = TSMimeHdrFieldFind(hdr_bufp, hdr_loc, _field, -1);
-
- if (TS_NULL_MLOC != loc) {
- unsigned int content_len = TSMimeHdrFieldValueUintGet(hdr_bufp,
hdr_loc, loc, 0 /* index */);
-
- if (check_content_length(content_len, _value)) {
- Dbg(Bg_dbg_ctl, "Found content-length match");
- hdr_found = true;
- }
- TSHandleMLocRelease(hdr_bufp, hdr_loc, loc);
- } else {
- Dbg(Bg_dbg_ctl, "No content-length field in resp");
- }
- TSHandleMLocRelease(hdr_bufp, TS_NULL_MLOC, hdr_loc);
- } else {
- TSError("[%s] Failed to get resp headers", PLUGIN_NAME);
- }
- return hdr_found;
+ if (TS_SUCCESS != TSHttpTxnClientReqGet(txnp, &hdr_bufp, &hdr_loc)) {
+ TSError("[%s] Failed to get resp headers", PLUGIN_NAME);
+ return false;
+ }
+
+ TSMLoc loc = TSMimeHdrFieldFind(hdr_bufp, hdr_loc, cmp._name.data(),
cmp._name.size());
+
+ if (TS_NULL_MLOC == loc) {
+ Dbg(Bg_dbg_ctl, "no field %s in request header", cmp._name.c_str());
+ return false;
}
- // Check request headers
- if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &hdr_bufp, &hdr_loc)) {
- TSMLoc loc = TSMimeHdrFieldFind(hdr_bufp, hdr_loc, _field, -1);
-
- if (TS_NULL_MLOC != loc) {
- if (!strcmp(_value, "*")) {
- Dbg(Bg_dbg_ctl, "Found %s wild card", _field);
- hdr_found = true;
- } else {
- int val_len = 0;
- const char *val_str = TSMimeHdrFieldValueStringGet(hdr_bufp, hdr_loc,
loc, 0, &val_len);
-
- if (!val_str || val_len <= 0) {
- Dbg(Bg_dbg_ctl, "invalid field");
- } else {
- Dbg(Bg_dbg_ctl, "comparing with %s", _value);
- if (std::string_view::npos != std::string_view(val_str,
val_len).find(_value)) {
- hdr_found = true;
- }
- }
- }
- TSHandleMLocRelease(hdr_bufp, hdr_loc, loc);
- } else {
- Dbg(Bg_dbg_ctl, "no field %s in request header", _field);
- }
- TSHandleMLocRelease(hdr_bufp, TS_NULL_MLOC, hdr_loc);
+ if (cmp._name.size() == 1 && cmp._name.front() == '*') {
+ Dbg(Bg_dbg_ctl, "Found %s wild card", cmp._name.c_str());
+ return true;
+ }
+
+ int val_len = 0;
+ char const *val_str = TSMimeHdrFieldValueStringGet(hdr_bufp, hdr_loc, loc,
0, &val_len);
+ bool zret = false;
+
+ if (!val_str || val_len <= 0) {
+ Dbg(Bg_dbg_ctl, "invalid field");
} else {
- TSError("[%s] Failed to get resp headers", PLUGIN_NAME);
+ Dbg(Bg_dbg_ctl, "comparing with %s", cmp._value.c_str());
+ zret = std::string_view::npos != std::string_view(val_str,
val_len).find(cmp._value);
}
+ TSHandleMLocRelease(hdr_bufp, hdr_loc, loc);
+ return zret;
+}
- return hdr_found;
+///////////////////////////////////////////////////////////////////////////
+// Check if a header excludes us from running the background fetch
+//
+bool
+BgFetchRule::check_field_configured(TSHttpTxn txnp) const
+{
+ return std::visit(swoc::meta::vary{[=](std::monostate) { return false; },
+ [=](swoc::IPRange const &range) { return
check_value(txnp, range); },
+ [=](size_cmp_type const &cmp) { return
check_value(txnp, cmp); },
+ [=](field_cmp_type const &cmp) { return
check_value(txnp, cmp); }},
+ _value);
}
diff --git a/plugins/background_fetch/rules.h b/plugins/background_fetch/rules.h
index d0652c87a1..bbfc7a36ec 100644
--- a/plugins/background_fetch/rules.h
+++ b/plugins/background_fetch/rules.h
@@ -27,6 +27,11 @@
#include <cstdlib>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <variant>
+#include <sys/socket.h>
+
+#include <swoc/TextView.h>
+#include <swoc/IPRange.h>
#include "ts/ts.h"
@@ -36,32 +41,38 @@
//
class BgFetchRule
{
+ using self_type = BgFetchRule;
+
public:
- BgFetchRule(bool exc, const char *field, const char *value)
- : _exclude(exc), _field(TSstrdup(field)), _value(TSstrdup(value)),
_next(nullptr)
- {
- }
+ /// Content length / size comparison.
+ struct size_cmp_type {
+ enum OP { LESS_THAN_OR_EQUAL, GREATER_THAN_OR_EQUAL } _op; ///< Comparison
to use.
+ size_t _size; ///< Size for
comparison.
+ };
- ~BgFetchRule()
- {
- delete _field;
- delete _value;
- delete _next;
- }
+ /// Field value comparison.
+ struct field_cmp_type {
+ std::string _name; ///< Field name.
+ std::string _value; ///< Value to compare. A single '*' means match
anything - check for field presence.
+ };
+
+ BgFetchRule(bool exc, size_cmp_type::OP op, size_t n) : _exclude(exc),
_value(size_cmp_type{op, n}) {}
+ BgFetchRule(bool exc, swoc::IPRange const &range) : _exclude(exc),
_value(range) {}
- // For chaining the linked list
- void
- chain(BgFetchRule *n)
+ BgFetchRule(bool exc, swoc::TextView name, swoc::TextView value)
+ : _exclude(exc), _value(field_cmp_type{std::string(name),
std::string(value)})
{
- _next = n;
}
+ BgFetchRule(self_type &&that) = default;
+
// Main evaluation entry point.
bool bgFetchAllowed(TSHttpTxn txnp) const;
bool check_field_configured(TSHttpTxn txnp) const;
- bool _exclude;
- const char *_field;
- const char *_value;
- BgFetchRule *_next; // For the linked list
+ bool _exclude; ///< Exclusion @c true or inclusion @c false.
+
+ /// Value type for the rule, which also indicates the type of check.
+ using value_type = std::variant<std::monostate, size_cmp_type,
field_cmp_type, swoc::IPRange>;
+ value_type _value; ///< Value instance for checking.
};