Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 00:15 UTC
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 !)
Matt Holt committed
Dec 13, 2021 at 13:59 UTC
ecac03cdcb6cceae743aac16faca7f32e5da1607
2 files changed
+55
-25
caddytest/integration/caddyfile_adapt/matcher_syntax.txt
+3
-1
@@ -101,7 +101,9 @@
101
"match": [
102
{
103
"vars": {
104
- "{http.request.uri}": "/vars-matcher"
104
+ "{http.request.uri}": [
105
+ "/vars-matcher"
106
+ ]
107
}
108
}
109
],
modules/caddyhttp/vars.go
+52
-24
@@ -59,8 +59,15 @@ func (t VarsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next H
59
}
60
61
// 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
71
72
// CaddyModule returns the Caddy module information.
73
func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
@@ -73,14 +80,18 @@ func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
80
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
81
func (m *VarsMatcher) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
82
if *m == nil {
76
- *m = make(map[string]string)
83
+ *m = make(map[string][]string)
84
}
85
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")
93
}
83
- (*m)[field] = val
94
+ (*m)[field] = vals
95
if d.NextBlock(0) {
96
return d.Err("malformed vars matcher: blocks are not supported")
97
}
@@ -88,29 +99,46 @@ func (m *VarsMatcher) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
99
return nil
100
}
101
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.
104
func (m VarsMatcher) Match(r *http.Request) bool {
105
+ if len(m) == 0 {
106
+ return true
107
+ }
108
+
109
vars := r.Context().Value(VarsCtxKey).(map[string]interface{})
110
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)
120
}
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
+ }
139
}
140
}
113
- return true
141
+ return false
142
}
143
144
// MatchVarsRE matches the value of the context variables by a given regular expression.