Skip to content

Commit bb08592

Browse files
committed
This fixes parsing where the [ ] section gets merged into values
For example: cond %{SEND_RESPONSE_HDR_HOOK} set-header X-First "First" set-header X-Last "Last" [L] would set the header X-Last: Last [L]. In addition, it also fixes a typo (I think?) in an unrelated test, however, it's unclear as to why it's not failing that test. CHECK_EQ(p.get_tokens()[1], "%{METHOD}c”);
1 parent d2386a5 commit bb08592

File tree

6 files changed

+75
-58
lines changed

6 files changed

+75
-58
lines changed

plugins/header_rewrite/header_rewrite.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,10 @@ RulesConfig::parse_config(const std::string &fname, TSHttpHookID default_hook)
184184
continue;
185185
}
186186

187-
Parser p(line); // Tokenize and parse this line
188-
if (p.empty()) {
187+
Parser p;
188+
189+
// Tokenize and parse this line
190+
if (!p.parse_line(line) || p.empty()) {
189191
continue;
190192
}
191193

plugins/header_rewrite/header_rewrite_test.cc

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ TSError(const char *fmt, ...)
4444
class ParserTest : public Parser
4545
{
4646
public:
47-
ParserTest(const std::string &line) : Parser(line), res(true) { std::cout << "Finished parser test: " << line << std::endl; }
47+
ParserTest(const std::string &line) : res(true)
48+
{
49+
Parser::parse_line(line);
50+
std::cout << "Finished parser test: " << line << std::endl;
51+
}
52+
4853
std::vector<std::string>
4954
getTokens() const
5055
{
@@ -353,12 +358,13 @@ test_parsing()
353358
}
354359

355360
{
356-
ParserTest p(R"(set-header Alt-Svc "quic=\":443\"; v=\"35\"")");
361+
ParserTest p(R"(set-header Alt-Svc "quic=\":443\"; v=\"35\"" [L])");
357362

358-
CHECK_EQ(p.getTokens().size(), 3UL);
363+
CHECK_EQ(p.getTokens().size(), 4UL);
359364
CHECK_EQ(p.getTokens()[0], "set-header");
360365
CHECK_EQ(p.getTokens()[1], "Alt-Svc");
361366
CHECK_EQ(p.getTokens()[2], R"(quic=":443"; v="35")");
367+
CHECK_EQ(p.get_value(), R"(quic=":443"; v="35")");
362368

363369
END_TEST();
364370
}
@@ -479,7 +485,7 @@ test_tokenizer()
479485
SimpleTokenizerTest p("A racoon's favorite tag is %{METHOD} in %{NOW:YEAR}!");
480486
CHECK_EQ(p.get_tokens().size(), 5UL);
481487
CHECK_EQ(p.get_tokens()[0], "A racoon's favorite tag is ");
482-
CHECK_EQ(p.get_tokens()[1], "%{METHOD}c");
488+
CHECK_EQ(p.get_tokens()[1], "%{METHOD}");
483489
CHECK_EQ(p.get_tokens()[2], " in ");
484490
CHECK_EQ(p.get_tokens()[3], "%{NOW:YEAR}");
485491
CHECK_EQ(p.get_tokens()[4], "!");

plugins/header_rewrite/parser.cc

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030

3131
enum ParserState { PARSER_DEFAULT, PARSER_IN_QUOTE, PARSER_IN_REGEX, PARSER_IN_EXPANSION };
3232

33-
Parser::Parser(const std::string &original_line) : _cond(false), _empty(false)
33+
bool
34+
Parser::parse_line(const std::string &original_line)
3435
{
3536
std::string line = original_line;
3637
ParserState state = PARSER_DEFAULT;
@@ -85,7 +86,7 @@ Parser::Parser(const std::string &original_line) : _cond(false), _empty(false)
8586
TSError("[%s] malformed line \"%s\", ignoring", PLUGIN_NAME, line.c_str());
8687
_tokens.clear();
8788
_empty = true;
88-
return;
89+
return false;
8990
}
9091
} else if (!extracting_token) {
9192
if (_tokens.empty() && line[i] == '#') {
@@ -114,21 +115,49 @@ Parser::Parser(const std::string &original_line) : _cond(false), _empty(false)
114115
TSError("[%s] malformed line, unterminated quotation: \"%s\", ignoring", PLUGIN_NAME, line.c_str());
115116
_tokens.clear();
116117
_empty = true;
117-
return;
118+
return false;
118119
}
119120
}
120121

121122
if (_tokens.empty()) {
122123
_empty = true;
123124
} else {
124-
preprocess(_tokens);
125+
return preprocess(_tokens);
125126
}
127+
128+
return true;
126129
}
127130

128-
// This is the core "parser", parsing rule sets
129-
void
131+
// This is the main "parser", a helper function to the above tokenizer. NOTE: this modifies (possibly) the tokens list,
132+
// therefore, we pass in a copy of the parsers tokens here, such that the original token list is retained (useful for tests etc.).
133+
bool
130134
Parser::preprocess(std::vector<std::string> tokens)
131135
{
136+
// The last token might be the "flags" section, lets consume it if it is
137+
if (tokens.size() > 0) {
138+
std::string m = tokens[tokens.size() - 1];
139+
140+
if (!m.empty() && (m[0] == '[')) {
141+
if (m[m.size() - 1] == ']') {
142+
m = m.substr(1, m.size() - 2);
143+
if (m.find_first_of(',') != std::string::npos) {
144+
std::istringstream iss(m);
145+
std::string t;
146+
while (getline(iss, t, ',')) {
147+
_mods.push_back(t);
148+
}
149+
} else {
150+
_mods.push_back(m);
151+
}
152+
tokens.pop_back(); // consume it, so we don't concatenate it into the value
153+
} else {
154+
// Syntax error
155+
TSError("[%s] mods have to be enclosed in []", PLUGIN_NAME);
156+
return false;
157+
}
158+
}
159+
}
160+
132161
// Special case for "conditional" values
133162
if (tokens[0].substr(0, 2) == "%{") {
134163
_cond = true;
@@ -155,7 +184,7 @@ Parser::preprocess(std::vector<std::string> tokens)
155184
}
156185
} else {
157186
TSError("[%s] conditions must be embraced in %%{}", PLUGIN_NAME);
158-
return;
187+
return false;
159188
}
160189
} else {
161190
// Operator has no qualifiers, but could take an optional second argument
@@ -179,29 +208,7 @@ Parser::preprocess(std::vector<std::string> tokens)
179208
}
180209
}
181210

182-
// The last token might be the "flags" section
183-
if (tokens.size() > 0) {
184-
std::string m = tokens[tokens.size() - 1];
185-
186-
if (!m.empty() && (m[0] == '[')) {
187-
if (m[m.size() - 1] == ']') {
188-
m = m.substr(1, m.size() - 2);
189-
if (m.find_first_of(',') != std::string::npos) {
190-
std::istringstream iss(m);
191-
std::string t;
192-
while (getline(iss, t, ',')) {
193-
_mods.push_back(t);
194-
}
195-
} else {
196-
_mods.push_back(m);
197-
}
198-
} else {
199-
// Syntax error
200-
TSError("[%s] mods have to be enclosed in []", PLUGIN_NAME);
201-
return;
202-
}
203-
}
204-
}
211+
return true;
205212
}
206213

207214
// Check if the operator is a condition, a hook, and if so, which hook. If the cond is not a hook
@@ -241,12 +248,6 @@ Parser::cond_is_hook(TSHttpHookID &hook) const
241248
return false;
242249
}
243250

244-
const std::vector<std::string> &
245-
Parser::get_tokens() const
246-
{
247-
return _tokens;
248-
}
249-
250251
SimpleTokenizer::SimpleTokenizer(const std::string &original_line)
251252
{
252253
std::string line = original_line;
@@ -294,9 +295,3 @@ SimpleTokenizer::SimpleTokenizer(const std::string &original_line)
294295
_tokens.push_back(line.substr(cur_token_start));
295296
}
296297
}
297-
298-
const std::vector<std::string> &
299-
SimpleTokenizer::get_tokens() const
300-
{
301-
return _tokens;
302-
}

plugins/header_rewrite/parser.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
class Parser
3434
{
3535
public:
36-
explicit Parser(const std::string &line);
36+
Parser(){};
3737

3838
// noncopyable
3939
Parser(const Parser &) = delete;
@@ -76,13 +76,20 @@ class Parser
7676
}
7777

7878
bool cond_is_hook(TSHttpHookID &hook) const;
79-
const std::vector<std::string> &get_tokens() const;
79+
80+
const std::vector<std::string> &
81+
get_tokens() const
82+
{
83+
return _tokens;
84+
}
85+
86+
bool parse_line(const std::string &original_line);
8087

8188
private:
82-
void preprocess(std::vector<std::string> tokens);
89+
bool preprocess(std::vector<std::string> tokens);
8390

84-
bool _cond;
85-
bool _empty;
91+
bool _cond = false;
92+
bool _empty = false;
8693
std::vector<std::string> _mods;
8794
std::string _op;
8895
std::string _arg;
@@ -101,7 +108,11 @@ class SimpleTokenizer
101108
SimpleTokenizer(const SimpleTokenizer &) = delete;
102109
void operator=(const SimpleTokenizer &) = delete;
103110

104-
const std::vector<std::string> &get_tokens() const;
111+
const std::vector<std::string> &
112+
get_tokens() const
113+
{
114+
return _tokens;
115+
}
105116

106117
protected:
107118
std::vector<std::string> _tokens;

plugins/header_rewrite/ruleset.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ RuleSet::add_operator(Parser &p, const char *filename, int lineno)
7676
Operator *o = operator_factory(p.get_op());
7777

7878
if (nullptr != o) {
79-
// TODO: This should be extended to show both the "argument" and the "value" (if both are used)
80-
TSDebug(PLUGIN_NAME, " Adding operator: %s(%s)", p.get_op().c_str(), p.get_arg().c_str());
79+
TSDebug(PLUGIN_NAME, " Adding operator: %s(%s)=\"%s\"", p.get_op().c_str(), p.get_arg().c_str(), p.get_value().c_str());
8180
o->initialize(p);
8281
if (!o->set_hook(_hook)) {
8382
delete o;

plugins/header_rewrite/value.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,13 @@ Value::set_value(const std::string &val)
5454
std::string cond_token = token.substr(2, token.size() - 3);
5555

5656
if ((tcond_val = condition_factory(cond_token))) {
57-
Parser parser(_value);
57+
Parser parser;
5858

59-
tcond_val->initialize(parser);
59+
if (parser.parse_line(_value)) {
60+
tcond_val->initialize(parser);
61+
} else {
62+
// TODO: should we produce error here?
63+
}
6064
}
6165
} else {
6266
tcond_val = new ConditionStringLiteral(token);

0 commit comments

Comments
 (0)