Skip to content

Commit 8fdf694

Browse files
authored
slice plugin: add --include-regex, --exclude-regex parameters (#6701)
1 parent 583e5ed commit 8fdf694

File tree

7 files changed

+346
-33
lines changed

7 files changed

+346
-33
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,17 @@ The slice plugin supports the following options::
8888
Disable writing block stitch errors to the error log.
8989
-d for short
9090

91+
--exclude-regex=<regex> (optional)
92+
If provided, only slice what matches.
93+
If not provided will always slice
94+
Cannot be used with --include-regex
95+
-e for short
96+
97+
--include-regex=<regex> (optional)
98+
If provided, only slice what matches.
99+
If not provided will always slice
100+
Cannot be used with --exclude-regex
101+
-i for short
91102

92103
Examples::
93104

@@ -122,6 +133,16 @@ After modifying :file:`remap.config`, restart or reload traffic server
122133
(sudo traffic_ctl config reload) or (sudo traffic_ctl server restart)
123134
to activate the new configuration values.
124135

136+
Don't slice txt files::
137+
138+
slice.so --exclude-regex=\\.txt
139+
slice.so -e \\.txt
140+
141+
Slice only mp4 files::
142+
143+
slice.so --include-regex=\\.mp4
144+
slice.so -i \\.mp4
145+
125146
Debug Options
126147
-------------
127148

plugins/experimental/slice/Config.cc

Lines changed: 98 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@
2626

2727
#include "ts/experimental.h"
2828

29+
Config::~Config()
30+
{
31+
if (nullptr != m_regex_extra) {
32+
#ifndef PCRE_STUDY_JIT_COMPILE
33+
pcre_free(m_regex_extra);
34+
#else
35+
pcre_free_study(m_regex_extra);
36+
#endif
37+
}
38+
if (nullptr != m_regex) {
39+
pcre_free(m_regex);
40+
}
41+
}
42+
2943
int64_t
3044
Config::bytesFrom(char const *const valstr)
3145
{
@@ -94,18 +108,20 @@ Config::fromArgs(int const argc, char const *const argv[])
94108
// standard parsing
95109
constexpr struct option longopts[] = {
96110
{const_cast<char *>("blockbytes"), required_argument, nullptr, 'b'},
97-
{const_cast<char *>("blockbytes-test"), required_argument, nullptr, 't'},
98-
{const_cast<char *>("remap-host"), required_argument, nullptr, 'r'},
99-
{const_cast<char *>("pace-errorlog"), required_argument, nullptr, 'p'},
100111
{const_cast<char *>("disable-errorlog"), no_argument, nullptr, 'd'},
112+
{const_cast<char *>("exclude-regex"), required_argument, nullptr, 'e'},
113+
{const_cast<char *>("include-regex"), required_argument, nullptr, 'i'},
101114
{const_cast<char *>("throttle"), no_argument, nullptr, 'o'},
115+
{const_cast<char *>("pace-errorlog"), required_argument, nullptr, 'p'},
116+
{const_cast<char *>("remap-host"), required_argument, nullptr, 'r'},
117+
{const_cast<char *>("blockbytes-test"), required_argument, nullptr, 't'},
102118
{nullptr, 0, nullptr, 0},
103119
};
104120

105121
// getopt assumes args start at '1' so this hack is needed
106122
char *const *argvp = (const_cast<char *const *>(argv) - 1);
107123
for (;;) {
108-
int const opt = getopt_long(argc + 1, argvp, "b:t:r:p:do", longopts, nullptr);
124+
int const opt = getopt_long(argc + 1, argvp, "b:de:i:op:r:t:", longopts, nullptr);
109125
if (-1 == opt) {
110126
break;
111127
}
@@ -122,6 +138,61 @@ Config::fromArgs(int const argc, char const *const argv[])
122138
ERROR_LOG("Invalid blockbytes: %s", optarg);
123139
}
124140
} break;
141+
case 'd': {
142+
m_paceerrsecs = -1;
143+
} break;
144+
case 'e': {
145+
if (None != m_regex_type) {
146+
ERROR_LOG("Regex already specified!");
147+
break;
148+
}
149+
150+
const char *errptr;
151+
int erroffset;
152+
m_regexstr = optarg;
153+
m_regex = pcre_compile(m_regexstr.c_str(), 0, &errptr, &erroffset, NULL);
154+
if (nullptr == m_regex) {
155+
ERROR_LOG("Invalid regex: '%s'", m_regexstr.c_str());
156+
} else {
157+
m_regex_type = Exclude;
158+
m_regex_extra = pcre_study(m_regex, 0, &errptr);
159+
DEBUG_LOG("Using regex for url exclude: '%s'", m_regexstr.c_str());
160+
}
161+
} break;
162+
case 'i': {
163+
if (None != m_regex_type) {
164+
ERROR_LOG("Regex already specified!");
165+
break;
166+
}
167+
168+
const char *errptr;
169+
int erroffset;
170+
m_regexstr = optarg;
171+
m_regex = pcre_compile(m_regexstr.c_str(), 0, &errptr, &erroffset, NULL);
172+
if (nullptr == m_regex) {
173+
ERROR_LOG("Invalid regex: '%s'", m_regexstr.c_str());
174+
} else {
175+
m_regex_type = Include;
176+
m_regex_extra = pcre_study(m_regex, 0, &errptr);
177+
DEBUG_LOG("Using regex for url include: '%s'", m_regexstr.c_str());
178+
}
179+
} break;
180+
case 'o': {
181+
m_throttle = true;
182+
DEBUG_LOG("Enabling internal block throttling");
183+
} break;
184+
case 'p': {
185+
int const secsread = atoi(optarg);
186+
if (0 < secsread) {
187+
m_paceerrsecs = std::min(secsread, 60);
188+
} else {
189+
ERROR_LOG("Ignoring pace-errlog argument");
190+
}
191+
} break;
192+
case 'r': {
193+
m_remaphost = optarg;
194+
DEBUG_LOG("Using loopback remap host override: %s", m_remaphost.c_str());
195+
} break;
125196
case 't': {
126197
if (0 == blockbytes) {
127198
int64_t const bytesread = bytesFrom(optarg);
@@ -135,25 +206,6 @@ Config::fromArgs(int const argc, char const *const argv[])
135206
DEBUG_LOG("Skipping blockbytes-test in favor of blockbytes");
136207
}
137208
} break;
138-
case 'r':
139-
m_remaphost = optarg;
140-
DEBUG_LOG("Using loopback remap host override: %s", m_remaphost.c_str());
141-
break;
142-
case 'p': {
143-
int const secsread = atoi(optarg);
144-
if (0 < secsread) {
145-
m_paceerrsecs = std::min(secsread, 60);
146-
} else {
147-
ERROR_LOG("Ignoring pace-errlog argument");
148-
}
149-
} break;
150-
case 'd':
151-
m_paceerrsecs = -1;
152-
break;
153-
case 'o':
154-
m_throttle = true;
155-
DEBUG_LOG("Enabling internal block throttling");
156-
break;
157209
default:
158210
break;
159211
}
@@ -204,3 +256,26 @@ Config::canLogError()
204256

205257
return true;
206258
}
259+
260+
bool
261+
Config::matchesRegex(char const *const url, int const urllen) const
262+
{
263+
bool matches = true;
264+
265+
switch (m_regex_type) {
266+
case Exclude: {
267+
if (0 <= pcre_exec(m_regex, m_regex_extra, url, urllen, 0, 0, NULL, 0)) {
268+
matches = false;
269+
}
270+
} break;
271+
case Include: {
272+
if (pcre_exec(m_regex, m_regex_extra, url, urllen, 0, 0, NULL, 0) < 0) {
273+
matches = false;
274+
}
275+
} break;
276+
default:
277+
break;
278+
}
279+
280+
return matches;
281+
}

plugins/experimental/slice/Config.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020

2121
#include "slice.h"
2222

23+
#ifdef HAVE_PCRE_PCRE_H
24+
#include <pcre/pcre.h>
25+
#else
26+
#include <pcre.h>
27+
#endif
28+
2329
#include <mutex>
2430

2531
// Data Structures and Classes
@@ -30,18 +36,36 @@ struct Config {
3036

3137
int64_t m_blockbytes{blockbytesdefault};
3238
std::string m_remaphost; // remap host to use for loopback slice GET
33-
bool m_throttle{false}; // internal block throttling
34-
int m_paceerrsecs{0}; // -1 disable logging, 0 no pacing, max 60s
39+
std::string m_regexstr; // regex string for things to slice (default all)
40+
enum RegexType { None, Include, Exclude };
41+
RegexType m_regex_type{None};
42+
pcre *m_regex{nullptr};
43+
pcre_extra *m_regex_extra{nullptr};
44+
bool m_throttle{false}; // internal block throttling
45+
int m_paceerrsecs{0}; // -1 disable logging, 0 no pacing, max 60s
3546

3647
// Convert optarg to bytes
3748
static int64_t bytesFrom(char const *const valstr);
3849

50+
// clean up pcre if applicable
51+
~Config();
52+
3953
// Parse from args, ast one wins
4054
bool fromArgs(int const argc, char const *const argv[]);
4155

4256
// Check if the error should can be logged, if sucessful may update m_nexttime
4357
bool canLogError();
4458

59+
// Check if regex supplied
60+
bool
61+
hasRegex() const
62+
{
63+
return None != m_regex_type;
64+
}
65+
66+
// If no null reg, true, otherwise check against regex
67+
bool matchesRegex(char const *const url, int const urllen) const;
68+
4569
private:
4670
TSHRTime m_nextlogtime{0}; // next time to log in ns
4771
std::mutex m_mutex;

plugins/experimental/slice/HttpHeader.cc

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,20 @@ HttpHeader::setStatus(TSHttpStatus const newstatus)
5454
}
5555

5656
char *
57-
HttpHeader ::urlString(int *const urllen) const
57+
HttpHeader::urlString(int *const urllen) const
5858
{
5959
char *urlstr = nullptr;
6060
TSAssert(nullptr != urllen);
6161

6262
TSMLoc locurl = nullptr;
6363
TSReturnCode const rcode = TSHttpHdrUrlGet(m_buffer, m_lochdr, &locurl);
64-
if (TS_SUCCESS == rcode && nullptr != locurl) {
65-
urlstr = TSUrlStringGet(m_buffer, locurl, urllen);
64+
if (nullptr != locurl) {
65+
if (TS_SUCCESS == rcode) {
66+
urlstr = TSUrlStringGet(m_buffer, locurl, urllen);
67+
} else {
68+
*urllen = 0;
69+
}
6670
TSHandleMLocRelease(m_buffer, m_lochdr, locurl);
67-
} else {
68-
*urllen = 0;
6971
}
7072

7173
return urlstr;

plugins/experimental/slice/Makefile.inc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,22 @@ experimental_slice_test_content_range_SOURCES = \
4949
experimental/slice/unit-tests/test_content_range.cc \
5050
experimental/slice/ContentRange.cc
5151

52+
experimental_slice_test_content_range_LDADD = @LIBPCRE@
53+
5254
check_PROGRAMS += experimental/slice/test_range
5355

5456
experimental_slice_test_range_CPPFLAGS = $(AM_CPPFLAGS) -I$(abs_top_srcdir)/tests/include -DUNITTEST
5557
experimental_slice_test_range_SOURCES = \
5658
experimental/slice/unit-tests/test_range.cc \
5759
experimental/slice/Range.cc
5860

61+
experimental_slice_test_range_LDADD = @LIBPCRE@
62+
5963
check_PROGRAMS += experimental/slice/test_config
6064

6165
experimental_slice_test_config_CPPFLAGS = $(AM_CPPFLAGS) -I$(abs_top_srcdir)/tests/include -DUNITTEST
6266
experimental_slice_test_config_SOURCES = \
6367
experimental/slice/unit-tests/test_config.cc \
6468
experimental/slice/Config.cc
69+
70+
experimental_slice_test_config_LDADD = @LIBPCRE@

plugins/experimental/slice/slice.cc

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,26 @@ read_request(TSHttpTxn txnp, Config *const config)
5959
// check if any previous plugin has monkeyed with the transaction status
6060
TSHttpStatus const txnstat = TSHttpTxnStatusGet(txnp);
6161
if (0 != (int)txnstat) {
62-
DEBUG_LOG("slice: txn status change detected (%d), skipping plugin\n", (int)txnstat);
62+
DEBUG_LOG("txn status change detected (%d), skipping plugin\n", (int)txnstat);
6363
return false;
6464
}
6565

66+
if (config->hasRegex()) {
67+
int urllen = 0;
68+
char *const urlstr = TSHttpTxnEffectiveUrlStringGet(txnp, &urllen);
69+
if (nullptr != urlstr) {
70+
bool const shouldslice = config->matchesRegex(urlstr, urllen);
71+
if (!shouldslice) {
72+
DEBUG_LOG("request failed regex, not slicing: '%.*s'", urllen, urlstr);
73+
TSfree(urlstr);
74+
return false;
75+
}
76+
77+
DEBUG_LOG("request passed regex, slicing: '%.*s'", urllen, urlstr);
78+
TSfree(urlstr);
79+
}
80+
}
81+
6682
// turn off any and all transaction caching (shouldn't matter)
6783
TSHttpTxnServerRespNoStoreSet(txnp, 1);
6884
TSHttpTxnRespCacheableSet(txnp, 0);
@@ -99,8 +115,8 @@ read_request(TSHttpTxn txnp, Config *const config)
99115
// is the plugin configured to use a remap host?
100116
std::string const &newhost = config->m_remaphost;
101117
if (newhost.empty()) {
102-
TSMBuffer urlbuf;
103-
TSMLoc urlloc;
118+
TSMBuffer urlbuf = nullptr;
119+
TSMLoc urlloc = nullptr;
104120
TSReturnCode rcode = TSHttpTxnPristineUrlGet(txnp, &urlbuf, &urlloc);
105121

106122
if (TS_SUCCESS == rcode) {

0 commit comments

Comments
 (0)