Skip to content

Commit 872ffa1

Browse files
Add range request support to cache fill + global plugin support (#12391)
* Add range request support to cache fill * Update cache_fill.test.py * Add config option to make cache_fill only work for range requests * Update docs * Add --cache-range-req config option Update cache_fill.en.rst Update cache_fill.en.rst Update cache_fill.en.rst * Revert test back to original wont pass in jenkins, passes locally Fix test Update cache_fill.test.py Update cache_fill.test.py Revert test back to original
1 parent 7244504 commit 872ffa1

File tree

7 files changed

+396
-17
lines changed

7 files changed

+396
-17
lines changed

doc/admin-guide/plugins/cache_fill.en.rst

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,69 @@ The initial version of this plugin relays the initial request to the origin serv
2424
This plugin doesn't provide any improvement for smaller objects but could also degrade the performance as two outgoing requests for every cache update.
2525

2626

27-
Using the plugin
28-
----------------
27+
Configuration
28+
-------------
29+
This plugin functions as either a global or per remap plugin, and it takes an optional argument for
30+
specifying a config file with inclusion or exclusion criteria. The config file can be specified both
31+
via an absolute path or via a relative path to the install dir
2932

30-
This plugin functions as a per remap plugin.
33+
To activate the plugin in global mode, in :file:`plugin.config`, simply add::
3134

32-
To activate the plugin, in :file:`remap.config`, simply append the
35+
cache_fill.so --config <config-file>
36+
37+
To activate the plugin in per remap mode, in :file:`remap.config`, simply append the
3338
below to the specific remap line::
3439

3540
@plugin=cache_fill.so @pparam=<config-file>
3641

42+
include/exclude
43+
---------------
44+
The plugin supports a config file that can specify exclusion or inclusion of background fetch
45+
based on any arbitrary header or client-ip
46+
47+
The contents of the config-file could be as below::
48+
49+
include User-Agent ABCDEF
50+
exclude User-Agent *
51+
exclude Content-Type text
52+
exclude X-Foo-Bar text
53+
exclude Content-Length <1000
54+
exclude Client-IP 127.0.0.1
55+
include Client-IP 10.0.0.0/16
56+
57+
The ``include`` configuration directive is only used when there is a corresponding ``exclude`` to exempt.
58+
For example, a single line directive, ``include Host example.com`` would not make the plugin
59+
*only* act on example.com. To achieve classic allow (only) lists, one would need to have a broad
60+
exclude line, such as::
61+
62+
exclude Host *
63+
include Host example.com
64+
65+
range-request-only
66+
------------------
67+
When set to ``true``, this plugin will only trigger a background fetch if a range header is present.
68+
Range headers include ``Range``, ``If-Match``, ``If-Modified-Since``, ``If-None-Match``, ``If-Range``
69+
and ``If-Unmodified-Since``. By default, this is set to false.
70+
71+
This would look like::
72+
73+
@plugin=cache_fill.so @pparam=--range-request-only
74+
75+
cache-range-req
76+
---------------
77+
When set to ``false``. this plugin will not trigger a background fetch for range requests. By default,
78+
this is set to true.
79+
Note: you cannot set this to false and ``range-request-only`` to true.
80+
81+
This would look like::
82+
83+
@plugin=cache_fill.so @pparam=--cache-range-req=false
84+
3785
Functionality
3886
-------------
3987

4088
Plugin decides to trigger a background fetch of the original (Client) request if the request/response is cacheable and cache status is TS_CACHE_LOOKUP_MISS/TS_CACHE_LOOKUP_HIT_STALE.
89+
This will work for range requests by making a background fetch and removing the range header. To disable this feature, set ``--cache-range-req=false``
4190

4291
Future additions
4392
----------------

plugins/experimental/cache_fill/background_fetch.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,27 @@ namespace cache_fill_ns
4545
DbgCtl dbg_ctl{PLUGIN_NAME};
4646
}
4747

48+
///////////////////////////////////////////////////////////////////////////
49+
// Remove a header (fully) from an TSMLoc / TSMBuffer. Return the number
50+
// of fields (header values) we removed.
51+
int
52+
remove_header(TSMBuffer bufp, TSMLoc hdr_loc, const char *header, int len)
53+
{
54+
TSMLoc field = TSMimeHdrFieldFind(bufp, hdr_loc, header, len);
55+
int cnt = 0;
56+
57+
while (field) {
58+
TSMLoc tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field);
59+
60+
++cnt;
61+
TSMimeHdrFieldDestroy(bufp, hdr_loc, field);
62+
TSHandleMLocRelease(bufp, hdr_loc, field);
63+
field = tmp;
64+
}
65+
66+
return cnt;
67+
}
68+
4869
///////////////////////////////////////////////////////////////////////////
4970
// Set a header to a specific value. This will avoid going to through a
5071
// remove / add sequence in case of an existing header.
@@ -176,6 +197,12 @@ BgFetchData::initialize(TSMBuffer request, TSMLoc req_hdr, TSHttpTxn txnp)
176197
if (set_header(mbuf, hdr_loc, TS_MIME_FIELD_HOST, TS_MIME_LEN_HOST, hostp, len)) {
177198
Dbg(dbg_ctl, "Set header Host: %.*s", len, hostp);
178199
}
200+
// Next, remove the Range headers and IMS (conditional) headers from the request
201+
for (auto const &header : FILTER_HEADERS) {
202+
if (remove_header(mbuf, hdr_loc, header.data(), header.size()) > 0) {
203+
Dbg(dbg_ctl, "Removed the %s header from request", header.data());
204+
}
205+
}
179206
ret = true;
180207
}
181208
}

plugins/experimental/cache_fill/background_fetch.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@
4343
using OutstandingRequests = std::unordered_map<std::string, bool>;
4444
const char PLUGIN_NAME[] = "cache_fill";
4545

46+
// This is the list of all headers that must be removed when we make the actual background
47+
// fetch request for range requests.
48+
static const std::array<const std::string_view, 6> FILTER_HEADERS{
49+
{{TS_MIME_FIELD_RANGE, static_cast<size_t>(TS_MIME_LEN_RANGE)},
50+
{TS_MIME_FIELD_IF_MATCH, static_cast<size_t>(TS_MIME_LEN_IF_MATCH)},
51+
{TS_MIME_FIELD_IF_MODIFIED_SINCE, static_cast<size_t>(TS_MIME_LEN_IF_MODIFIED_SINCE)},
52+
{TS_MIME_FIELD_IF_NONE_MATCH, static_cast<size_t>(TS_MIME_LEN_IF_NONE_MATCH)},
53+
{TS_MIME_FIELD_IF_RANGE, static_cast<size_t>(TS_MIME_LEN_IF_RANGE)},
54+
{TS_MIME_FIELD_IF_UNMODIFIED_SINCE, static_cast<size_t>(TS_MIME_LEN_IF_UNMODIFIED_SINCE)}}
55+
};
56+
4657
namespace cache_fill_ns
4758
{
4859
extern DbgCtl dbg_ctl;

plugins/experimental/cache_fill/cache_fill.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
#include "background_fetch.h"
4242
#include "configs.h"
4343

44+
// Global config, if we don't have a remap specific config.
45+
static BgFetchConfig *gConfig = nullptr;
46+
4447
static const char *
4548
getCacheLookupResultName(TSCacheLookupResult result)
4649
{
@@ -137,6 +140,34 @@ cont_handle_cache(TSCont contp, TSEvent event, void *edata)
137140
return 0;
138141
}
139142

143+
///////////////////////////////////////////////////////////////////////////
144+
// Setup global hooks
145+
void
146+
TSPluginInit(int argc, const char *argv[])
147+
{
148+
TSPluginRegistrationInfo info;
149+
150+
info.plugin_name = (char *)PLUGIN_NAME;
151+
info.vendor_name = (char *)"Apache Software Foundation";
152+
info.support_email = (char *)"[email protected]";
153+
154+
if (TS_SUCCESS != TSPluginRegister(&info)) {
155+
TSError("[%s] Plugin registration failed", PLUGIN_NAME);
156+
}
157+
158+
TSCont cont = TSContCreate(cont_handle_cache, nullptr);
159+
160+
gConfig = new BgFetchConfig(cont);
161+
162+
if (gConfig->parseOptions(argc, argv)) {
163+
Dbg(dbg_ctl, "cache fill plugin is successfully initialized globally");
164+
TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, cont);
165+
} else {
166+
// ToDo: Hmmm, no way to fail a global plugin here?
167+
Dbg(dbg_ctl, "Failed to initialize as global plugin");
168+
}
169+
}
170+
140171
///////////////////////////////////////////////////////////////////////////
141172
// Setup Remap mode
142173
///////////////////////////////////////////////////////////////////////////////

plugins/experimental/cache_fill/configs.cc

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,19 @@ bool
4242
BgFetchConfig::parseOptions(int argc, const char *argv[])
4343
{
4444
static const struct option longopt[] = {
45-
{const_cast<char *>("log"), required_argument, nullptr, 'l' },
46-
{const_cast<char *>("config"), required_argument, nullptr, 'c' },
47-
{const_cast<char *>("allow-304"), no_argument, nullptr, 'a' },
48-
{nullptr, no_argument, nullptr, '\0'}
45+
{const_cast<char *>("log"), required_argument, nullptr, 'l' },
46+
{const_cast<char *>("config"), required_argument, nullptr, 'c' },
47+
{const_cast<char *>("range-req-only"), optional_argument, nullptr, 'r' },
48+
{const_cast<char *>("cache-range-req"), optional_argument, nullptr, 'a' },
49+
{nullptr, no_argument, nullptr, '\0'},
4950
};
5051

5152
while (true) {
52-
int opt = getopt_long(argc, const_cast<char *const *>(argv), "lc", longopt, nullptr);
53+
int opt = getopt_long(argc, const_cast<char *const *>(argv), "", longopt, nullptr);
5354

5455
if (opt == -1) {
5556
break;
5657
}
57-
5858
switch (opt) {
5959
case 'l':
6060
Dbg(dbg_ctl, "option: log file specified: %s", optarg);
@@ -67,9 +67,13 @@ BgFetchConfig::parseOptions(int argc, const char *argv[])
6767
return false;
6868
}
6969
break;
70+
case 'r':
71+
Dbg(dbg_ctl, "option: --range-req-only set");
72+
_range_req_only = isTrue(optarg);
73+
break;
7074
case 'a':
71-
Dbg(dbg_ctl, "option: --allow-304 set");
72-
_allow_304 = true;
75+
Dbg(dbg_ctl, "option: --cache-range-req set");
76+
_cache_range_req = isTrue(optarg);
7377
break;
7478
default:
7579
TSError("[%s] invalid plugin option: %c", PLUGIN_NAME, opt);
@@ -78,6 +82,11 @@ BgFetchConfig::parseOptions(int argc, const char *argv[])
7882
}
7983
}
8084

85+
if (_range_req_only && !_cache_range_req) {
86+
TSError("[%s] Cannot define _range_req_only=true and _cache_range_req=false", PLUGIN_NAME);
87+
return false;
88+
}
89+
8190
return true;
8291
}
8392

@@ -198,8 +207,29 @@ BgFetchConfig::bgFetchAllowed(TSHttpTxn txnp) const
198207
return false;
199208
}
200209

201-
bool allow_bg_fetch = true;
210+
if (_range_req_only || !_cache_range_req) {
211+
TSMBuffer bufp;
212+
TSMLoc hdr_loc;
213+
if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) {
214+
bool hasRangeHdrs = false;
215+
for (auto const &header : FILTER_HEADERS) {
216+
if (TSMimeHdrFieldFind(bufp, hdr_loc, header.data(), header.size() == TS_SUCCESS)) {
217+
hasRangeHdrs = true;
218+
break;
219+
}
220+
}
221+
if (!hasRangeHdrs && _range_req_only) {
222+
Dbg(dbg_ctl, "_range_req_only=true; This transaction is not a range request");
223+
return false;
224+
}
225+
if (hasRangeHdrs && !_cache_range_req) {
226+
Dbg(dbg_ctl, "_cache_range_req=false; This transaction is a range request");
227+
return false;
228+
}
229+
}
230+
}
202231

232+
bool allow_bg_fetch = true;
203233
// We could do this recursively, but following the linked list is probably more efficient.
204234
for (auto const &r : _rules) {
205235
if (r.check_field_configured(txnp)) {

plugins/experimental/cache_fill/configs.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ class BgFetchConfig
7070
return _log_file;
7171
}
7272

73-
bool
74-
allow304() const
73+
static bool
74+
isTrue(const char *arg)
7575
{
76-
return _allow_304;
76+
return (nullptr == arg || 0 == strncasecmp("true", arg, 4) || 0 == strncasecmp("1", arg, 1) || 0 == strncasecmp("yes", arg, 3));
7777
}
7878

7979
// This parses and populates the BgFetchRule linked list (_rules).
@@ -84,6 +84,7 @@ class BgFetchConfig
8484
private:
8585
TSCont _cont = nullptr;
8686
list_type _rules;
87-
bool _allow_304 = false;
87+
bool _range_req_only = false;
88+
bool _cache_range_req = true;
8889
std::string _log_file;
8990
};

0 commit comments

Comments
 (0)