Skip to content

Commit ecac03c

Browse files
authored
caddyhttp: Enhance vars matcher (#4433)
* caddyhttp: Enhance vars matcher Enable "or" logic for multiple values. Fall back to checking placeholders if not a var name. * Fix tests (thanks @mohammed90 !)
1 parent c04d24c commit ecac03c

File tree

2 files changed

+55
-25
lines changed

2 files changed

+55
-25
lines changed

caddytest/integration/caddyfile_adapt/matcher_syntax.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@
101101
"match": [
102102
{
103103
"vars": {
104-
"{http.request.uri}": "/vars-matcher"
104+
"{http.request.uri}": [
105+
"/vars-matcher"
106+
]
105107
}
106108
}
107109
],

modules/caddyhttp/vars.go

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,15 @@ func (t VarsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next H
5959
}
6060

6161
// VarsMatcher is an HTTP request matcher which can match
62-
// requests based on variables in the context.
63-
type VarsMatcher map[string]string
62+
// requests based on variables in the context. The key is
63+
// the name of the variable, and the values are possible
64+
// values the variable can be in order to match (OR'ed).
65+
//
66+
// As a special case, this matcher can also match on
67+
// placeholders generally. If the key is not an HTTP chain
68+
// variable, it will be checked to see if it is a
69+
// placeholder name, and if so, will compare its value.
70+
type VarsMatcher map[string][]string
6471

6572
// CaddyModule returns the Caddy module information.
6673
func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
@@ -73,44 +80,65 @@ func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
7380
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
7481
func (m *VarsMatcher) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
7582
if *m == nil {
76-
*m = make(map[string]string)
83+
*m = make(map[string][]string)
7784
}
7885
for d.Next() {
79-
var field, val string
80-
if !d.Args(&field, &val) {
81-
return d.Errf("malformed vars matcher: expected both field and value")
86+
var field string
87+
if !d.Args(&field) {
88+
return d.Errf("malformed vars matcher: expected field name")
89+
}
90+
vals := d.RemainingArgs()
91+
if len(vals) == 0 {
92+
return d.Errf("malformed vars matcher: expected at least one value to match against")
8293
}
83-
(*m)[field] = val
94+
(*m)[field] = vals
8495
if d.NextBlock(0) {
8596
return d.Err("malformed vars matcher: blocks are not supported")
8697
}
8798
}
8899
return nil
89100
}
90101

91-
// Match matches a request based on variables in the context.
102+
// Match matches a request based on variables in the context,
103+
// or placeholders if the key is not a variable.
92104
func (m VarsMatcher) Match(r *http.Request) bool {
105+
if len(m) == 0 {
106+
return true
107+
}
108+
93109
vars := r.Context().Value(VarsCtxKey).(map[string]interface{})
94110
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
95-
for k, v := range m {
96-
keyExpanded := repl.ReplaceAll(k, "")
97-
valExpanded := repl.ReplaceAll(v, "")
98-
var varStr string
99-
switch vv := vars[keyExpanded].(type) {
100-
case string:
101-
varStr = vv
102-
case fmt.Stringer:
103-
varStr = vv.String()
104-
case error:
105-
varStr = vv.Error()
106-
default:
107-
varStr = fmt.Sprintf("%v", vv)
111+
112+
for key, vals := range m {
113+
// look up the comparison value we will check against with this key
114+
matcherVarNameExpanded := repl.ReplaceAll(key, "")
115+
varValue, ok := vars[matcherVarNameExpanded]
116+
if !ok {
117+
// as a special case, if it's not an HTTP variable,
118+
// see if it's a placeholder name
119+
varValue, _ = repl.Get(matcherVarNameExpanded)
108120
}
109-
if varStr != valExpanded {
110-
return false
121+
122+
// see if any of the values given in the matcher match the actual value
123+
for _, v := range vals {
124+
matcherValExpanded := repl.ReplaceAll(v, "")
125+
var varStr string
126+
switch vv := varValue.(type) {
127+
case string:
128+
varStr = vv
129+
case fmt.Stringer:
130+
varStr = vv.String()
131+
case error:
132+
varStr = vv.Error()
133+
default:
134+
varStr = fmt.Sprintf("%v", vv)
135+
}
136+
if varStr == matcherValExpanded {
137+
return true
138+
}
111139
}
112140
}
113-
return true
141+
return false
114142
}
115143

116144
// MatchVarsRE matches the value of the context variables by a given regular expression.

0 commit comments

Comments
 (0)