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

masaori 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 27478a7ffe Add config file support for cache_fill (#12347)
27478a7ffe is described below

commit 27478a7ffea60dfc1d7ad27bf1cb4fd2413f0943
Author: Jasmine Emanouel <40879549+jasmine-nahr...@users.noreply.github.com>
AuthorDate: Thu Jul 17 11:14:28 2025 +1000

    Add config file support for cache_fill (#12347)
---
 plugins/experimental/cache_fill/CMakeLists.txt |   2 +-
 plugins/experimental/cache_fill/cache_fill.cc  |  80 +++++++---
 plugins/experimental/cache_fill/configs.cc     | 213 +++++++++++++++++++++++++
 plugins/experimental/cache_fill/configs.h      |  89 +++++++++++
 plugins/experimental/cache_fill/rules.cc       | 145 +++++++++++++++++
 plugins/experimental/cache_fill/rules.h        |  78 +++++++++
 6 files changed, 583 insertions(+), 24 deletions(-)

diff --git a/plugins/experimental/cache_fill/CMakeLists.txt 
b/plugins/experimental/cache_fill/CMakeLists.txt
index 5a780a7f60..61f1da8764 100644
--- a/plugins/experimental/cache_fill/CMakeLists.txt
+++ b/plugins/experimental/cache_fill/CMakeLists.txt
@@ -15,6 +15,6 @@
 #
 #######################
 
-add_atsplugin(cache_fill background_fetch.cc cache_fill.cc)
+add_atsplugin(cache_fill background_fetch.cc cache_fill.cc configs.cc rules.cc)
 
 verify_remap_plugin(cache_fill)
diff --git a/plugins/experimental/cache_fill/cache_fill.cc 
b/plugins/experimental/cache_fill/cache_fill.cc
index c1df5d1451..ad3d90c3bf 100644
--- a/plugins/experimental/cache_fill/cache_fill.cc
+++ b/plugins/experimental/cache_fill/cache_fill.cc
@@ -39,6 +39,7 @@
 #include "ts/remap.h"
 #include "ts/remap_version.h"
 #include "background_fetch.h"
+#include "configs.h"
 
 static const char *
 getCacheLookupResultName(TSCacheLookupResult result)
@@ -108,23 +109,29 @@ cont_check_cacheable(TSHttpTxn txnp)
 // if a background fetch is allowed for this request
 //
 static int
-cont_handle_cache(TSCont /* contp ATS_UNUSED */, TSEvent event, void *edata)
+cont_handle_cache(TSCont contp, TSEvent event, void *edata)
 {
-  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
-  if (TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE == event) {
-    bool const requested = cont_check_cacheable(txnp);
-    if (requested) // Made a background fetch request, do not cache the 
response
-    {
-      Dbg(dbg_ctl, "setting no store");
-      TSHttpTxnCntlSet(txnp, TS_HTTP_CNTL_SERVER_NO_STORE, true);
-      TSHttpTxnCacheLookupStatusSet(txnp, TS_CACHE_LOOKUP_MISS);
-    }
+  TSHttpTxn      txnp   = static_cast<TSHttpTxn>(edata);
+  BgFetchConfig *config = static_cast<BgFetchConfig *>(TSContDataGet(contp));
+
+  if (nullptr == config) {
+    // something seriously wrong..
+    TSError("[%s] Can't get configurations", PLUGIN_NAME);
+  } else if (config->bgFetchAllowed(txnp)) {
+    if (TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE == event) {
+      bool const requested = cont_check_cacheable(txnp);
+      if (requested) // Made a background fetch request, do not cache the 
response
+      {
+        Dbg(dbg_ctl, "setting no store");
+        TSHttpTxnCntlSet(txnp, TS_HTTP_CNTL_SERVER_NO_STORE, true);
+        TSHttpTxnCacheLookupStatusSet(txnp, TS_CACHE_LOOKUP_MISS);
+      }
 
-  } else {
-    TSError("[%s] Unknown event for this plugin %d", PLUGIN_NAME, event);
-    Dbg(dbg_ctl, "unknown event for this plugin %d", event);
+    } else {
+      TSError("[%s] Unknown event for this plugin %d", PLUGIN_NAME, event);
+      Dbg(dbg_ctl, "unknown event for this plugin %d", event);
+    }
   }
-
   // Reenable and continue with the state machine.
   TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
   return 0;
@@ -147,19 +154,46 @@ TSRemapInit(TSRemapInterface *api_info, char *errbuf, int 
errbuf_size)
 // We don't have any specific "instances" here, at least not yet.
 //
 TSReturnCode
-TSRemapNewInstance(int /* argc ATS_UNUSED */, char ** /* argv ATS_UNUSED */, 
void **ih, char * /* errbuf ATS_UNUSED */,
-                   int /* errbuf_size ATS_UNUSED */)
+TSRemapNewInstance(int argc, char **argv, void **ih, char * /* errbuf 
ATS_UNUSED */, int /* errbuf_size ATS_UNUSED */)
 {
-  TSCont cont = TSContCreate(cont_handle_cache, nullptr);
-  *ih         = cont;
-  return TS_SUCCESS;
+  TSCont         cont    = TSContCreate(cont_handle_cache, nullptr);
+  BgFetchConfig *config  = new BgFetchConfig(cont);
+  bool           success = true;
+
+  // The first two arguments are the "from" and "to" URL string. We need to
+  // skip them, but we also require that there be an option to masquerade as
+  // argv[0], so we increment the argument indexes by 1 rather than by 2.
+  argc--;
+  argv++;
+
+  // This is for backwards compatibility, ugly! ToDo: Remove for ATS v9.0.0 
IMO.
+  if (argc > 1 && *argv[1] != '-') {
+    Dbg(dbg_ctl, "config file %s", argv[1]);
+    if (!config->readConfig(argv[1])) {
+      success = false;
+    }
+  } else {
+    if (!config->parseOptions(argc, const_cast<const char **>(argv))) {
+      success = false;
+    }
+  }
+
+  if (success) {
+    *ih = config;
+
+    return TS_SUCCESS;
+  }
+
+  // Something went wrong with the configuration setup.
+  delete config;
+  return TS_ERROR;
 }
 
 void
 TSRemapDeleteInstance(void *ih)
 {
-  TSCont cont = static_cast<TSCont>(ih);
-  TSContDestroy(cont);
+  BgFetchConfig *config = static_cast<BgFetchConfig *>(ih);
+  delete config;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -171,8 +205,8 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo 
* /* rri */)
   if (nullptr == ih) {
     return TSREMAP_NO_REMAP;
   }
-  TSCont const cont = static_cast<TSCont>(ih);
-  TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, cont);
+  BgFetchConfig *config = static_cast<BgFetchConfig *>(ih);
+  TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, 
config->getCont());
   Dbg(dbg_ctl, "TSRemapDoRemap() added hook");
 
   return TSREMAP_NO_REMAP;
diff --git a/plugins/experimental/cache_fill/configs.cc 
b/plugins/experimental/cache_fill/configs.cc
new file mode 100644
index 0000000000..9eef083b8d
--- /dev/null
+++ b/plugins/experimental/cache_fill/configs.cc
@@ -0,0 +1,213 @@
+/** @file
+
+    Plugin to perform background fetches of certain content that would
+    otherwise not be cached. For example, Range: requests / responses.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#include <getopt.h>
+#include <cstdio>
+#include <memory.h>
+
+#include "configs.h"
+#include "background_fetch.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...
+bool
+BgFetchConfig::parseOptions(int argc, const char *argv[])
+{
+  static const struct option longopt[] = {
+    {const_cast<char *>("log"),       required_argument, nullptr, 'l' },
+    {const_cast<char *>("config"),    required_argument, nullptr, 'c' },
+    {const_cast<char *>("allow-304"), no_argument,       nullptr, 'a' },
+    {nullptr,                         no_argument,       nullptr, '\0'}
+  };
+
+  while (true) {
+    int opt = getopt_long(argc, const_cast<char *const *>(argv), "lc", 
longopt, nullptr);
+
+    if (opt == -1) {
+      break;
+    }
+
+    switch (opt) {
+    case 'l':
+      Dbg(dbg_ctl, "option: log file specified: %s", optarg);
+      _log_file = optarg;
+      break;
+    case 'c':
+      Dbg(dbg_ctl, "option: config file '%s'", optarg);
+      if (!readConfig(optarg)) {
+        // Error messages are written in the parser
+        return false;
+      }
+      break;
+    case 'a':
+      Dbg(dbg_ctl, "option: --allow-304 set");
+      _allow_304 = true;
+      break;
+    default:
+      TSError("[%s] invalid plugin option: %c", PLUGIN_NAME, opt);
+      return false;
+      break;
+    }
+  }
+
+  return true;
+}
+
+// Read a config file, populate the linked list (chain the BgFetchRule's)
+bool
+BgFetchConfig::readConfig(const char *config_file)
+{
+  if (nullptr == config_file) {
+    TSError("[%s] invalid config file", PLUGIN_NAME);
+    return false;
+  }
+
+  swoc::file::path path(config_file);
+
+  Dbg(dbg_ctl, "trying to open config file in this path: %s", config_file);
+
+  if (!path.is_absolute()) {
+    path = swoc::file::path(TSConfigDirGet()) / path;
+  }
+  Dbg(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(dbg_ctl, "%s", ts::bw_dbg.c_str());
+    return false;
+  }
+
+  swoc::TextView text{content};
+  while (text) {
+    auto line = text.take_prefix_at('\n').ltrim_if(&isspace);
+
+    if (line.empty() || line.front() == '#') {
+      continue;
+    }
+
+    auto cfg_type = line.take_prefix_if(&isspace);
+    if (cfg_type.empty()) {
+      continue;
+    }
+
+    Dbg(dbg_ctl, "setting background_fetch exclusion criterion based on 
string: %.*s", int(cfg_type.size()), cfg_type.data());
+
+    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;
+    }
+
+    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(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 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(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(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());
+      }
+    }
+  }
+
+  Dbg(dbg_ctl, "Done parsing config");
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Check the configuration (either per remap, or global), and decide if
+// this request is allowed to trigger a background fetch.
+//
+bool
+BgFetchConfig::bgFetchAllowed(TSHttpTxn txnp) const
+{
+  Dbg(dbg_ctl, "Testing: request is internal?");
+  if (TSHttpTxnIsInternal(txnp)) {
+    return false;
+  }
+
+  bool allow_bg_fetch = true;
+
+  // We could do this recursively, but following the linked list is probably 
more efficient.
+  for (auto const &r : _rules) {
+    if (r.check_field_configured(txnp)) {
+      Dbg(dbg_ctl, "found %s rule match", r._exclude ? "exclude" : "include");
+      allow_bg_fetch = !r._exclude;
+      break;
+    }
+  }
+
+  return allow_bg_fetch;
+}
diff --git a/plugins/experimental/cache_fill/configs.h 
b/plugins/experimental/cache_fill/configs.h
new file mode 100644
index 0000000000..7d3304cea8
--- /dev/null
+++ b/plugins/experimental/cache_fill/configs.h
@@ -0,0 +1,89 @@
+/** @file
+
+    Plugin to perform background fetches of certain content that would
+    otherwise not be cached. For example, Range: requests / responses.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#pragma once
+
+#include <cstdlib>
+#include <atomic>
+#include <string>
+#include <list>
+#include <memory>
+#include <sys/socket.h>
+
+#include "rules.h"
+
+///////////////////////////////////////////////////////////////////////////
+// This holds one complete background fetch rule
+//
+class BgFetchConfig
+{
+public:
+  using list_type = std::list<BgFetchRule>;
+
+  explicit BgFetchConfig(TSCont cont) : _cont(cont) { TSContDataSet(cont, 
static_cast<void *>(this)); }
+
+  ~BgFetchConfig()
+  {
+    if (_cont) {
+      TSContDestroy(_cont);
+    }
+  }
+
+  bool parseOptions(int argc, const char *argv[]);
+
+  list_type const &
+  getRules() const
+  {
+    return _rules;
+  }
+
+  TSCont
+  getCont() const
+  {
+    return _cont;
+  }
+
+  const std::string &
+  logFile() const
+  {
+    return _log_file;
+  }
+
+  bool
+  allow304() const
+  {
+    return _allow_304;
+  }
+
+  // This parses and populates the BgFetchRule linked list (_rules).
+  bool readConfig(const char *file_name);
+
+  bool bgFetchAllowed(TSHttpTxn txnp) const;
+
+private:
+  TSCont      _cont = nullptr;
+  list_type   _rules;
+  bool        _allow_304 = false;
+  std::string _log_file;
+};
diff --git a/plugins/experimental/cache_fill/rules.cc 
b/plugins/experimental/cache_fill/rules.cc
new file mode 100644
index 0000000000..9fc5fea31b
--- /dev/null
+++ b/plugins/experimental/cache_fill/rules.cc
@@ -0,0 +1,145 @@
+/** @file
+
+    Plugin to perform background fetches of certain content that would
+    otherwise not be cached. For example, Range: requests / responses.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#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 "background_fetch.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_value(TSHttpTxn txnp, swoc::IPRange const &range)
+{
+  const sockaddr *client_ip = TSHttpTxnClientAddrGet(txnp);
+  if (!client_ip) {
+    return false;
+  }
+
+  if (range.empty()) { // this means "match any address".
+    return true;
+  }
+
+  swoc::IPEndpoint client_addr{client_ip};
+
+  swoc::bwprint(ts::bw_dbg, "cfg_ip {::c}, client_ip {}", range, client_addr);
+  Dbg(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_value(TSHttpTxn txnp, BgFetchRule::size_cmp_type const &cmp)
+{
+  TSMBuffer hdr_bufp;
+  TSMLoc    hdr_loc;
+
+  if (TS_SUCCESS != TSHttpTxnServerRespGet(txnp, &hdr_bufp, &hdr_loc)) {
+    TSError("[%s] Failed to get resp headers", PLUGIN_NAME);
+    return false;
+  }
+
+  TSMLoc loc = TSMimeHdrFieldFind(hdr_bufp, hdr_loc, 
TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH);
+  if (TS_NULL_MLOC == loc) {
+    Dbg(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;
+  }
+
+  return false;
+}
+
+static bool
+check_value(TSHttpTxn txnp, BgFetchRule::field_cmp_type const &cmp)
+{
+  TSMBuffer hdr_bufp;
+  TSMLoc    hdr_loc;
+
+  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(dbg_ctl, "no field %s in request header", cmp._name.c_str());
+    return false;
+  }
+
+  if (cmp._name.size() == 1 && cmp._name.front() == '*') {
+    Dbg(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(dbg_ctl, "invalid field");
+  } else {
+    Dbg(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;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// 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/experimental/cache_fill/rules.h 
b/plugins/experimental/cache_fill/rules.h
new file mode 100644
index 0000000000..bbfc7a36ec
--- /dev/null
+++ b/plugins/experimental/cache_fill/rules.h
@@ -0,0 +1,78 @@
+/** @file
+
+    Plugin to perform background fetches of certain content that would
+    otherwise not be cached. For example, Range: requests / responses.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#pragma once
+
+#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"
+
+///////////////////////////////////////////////////////////////////////////
+// This is a linked list of rule entries. This gets stored and parsed with the
+// BgFetchConfig object.
+//
+class BgFetchRule
+{
+  using self_type = BgFetchRule;
+
+public:
+  /// 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.
+  };
+
+  /// 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) {}
+
+  BgFetchRule(bool exc, swoc::TextView name, swoc::TextView value)
+    : _exclude(exc), _value(field_cmp_type{std::string(name), 
std::string(value)})
+  {
+  }
+
+  BgFetchRule(self_type &&that) = default;
+
+  // Main evaluation entry point.
+  bool bgFetchAllowed(TSHttpTxn txnp) const;
+  bool check_field_configured(TSHttpTxn txnp) const;
+
+  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.
+};

Reply via email to