@marcel91200 / caddy Mirror · read-only / commits / ecac03cd
Fork 0

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.