mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03:00
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 !)
This commit is contained in:
parent
c04d24cafa
commit
ecac03cdcb
2 changed files with 55 additions and 25 deletions
|
@ -101,7 +101,9 @@
|
||||||
"match": [
|
"match": [
|
||||||
{
|
{
|
||||||
"vars": {
|
"vars": {
|
||||||
"{http.request.uri}": "/vars-matcher"
|
"{http.request.uri}": [
|
||||||
|
"/vars-matcher"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -59,8 +59,15 @@ func (t VarsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next H
|
||||||
}
|
}
|
||||||
|
|
||||||
// VarsMatcher is an HTTP request matcher which can match
|
// VarsMatcher is an HTTP request matcher which can match
|
||||||
// requests based on variables in the context.
|
// requests based on variables in the context. The key is
|
||||||
type VarsMatcher map[string]string
|
// the name of the variable, and the values are possible
|
||||||
|
// values the variable can be in order to match (OR'ed).
|
||||||
|
//
|
||||||
|
// As a special case, this matcher can also match on
|
||||||
|
// placeholders generally. If the key is not an HTTP chain
|
||||||
|
// variable, it will be checked to see if it is a
|
||||||
|
// placeholder name, and if so, will compare its value.
|
||||||
|
type VarsMatcher map[string][]string
|
||||||
|
|
||||||
// CaddyModule returns the Caddy module information.
|
// CaddyModule returns the Caddy module information.
|
||||||
func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
|
func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
|
||||||
|
@ -73,14 +80,18 @@ func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
|
||||||
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
|
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
|
||||||
func (m *VarsMatcher) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (m *VarsMatcher) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if *m == nil {
|
if *m == nil {
|
||||||
*m = make(map[string]string)
|
*m = make(map[string][]string)
|
||||||
}
|
}
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
var field, val string
|
var field string
|
||||||
if !d.Args(&field, &val) {
|
if !d.Args(&field) {
|
||||||
return d.Errf("malformed vars matcher: expected both field and value")
|
return d.Errf("malformed vars matcher: expected field name")
|
||||||
}
|
}
|
||||||
(*m)[field] = val
|
vals := d.RemainingArgs()
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return d.Errf("malformed vars matcher: expected at least one value to match against")
|
||||||
|
}
|
||||||
|
(*m)[field] = vals
|
||||||
if d.NextBlock(0) {
|
if d.NextBlock(0) {
|
||||||
return d.Err("malformed vars matcher: blocks are not supported")
|
return d.Err("malformed vars matcher: blocks are not supported")
|
||||||
}
|
}
|
||||||
|
@ -88,29 +99,46 @@ func (m *VarsMatcher) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match matches a request based on variables in the context.
|
// Match matches a request based on variables in the context,
|
||||||
|
// or placeholders if the key is not a variable.
|
||||||
func (m VarsMatcher) Match(r *http.Request) bool {
|
func (m VarsMatcher) Match(r *http.Request) bool {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
vars := r.Context().Value(VarsCtxKey).(map[string]interface{})
|
vars := r.Context().Value(VarsCtxKey).(map[string]interface{})
|
||||||
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||||
for k, v := range m {
|
|
||||||
keyExpanded := repl.ReplaceAll(k, "")
|
for key, vals := range m {
|
||||||
valExpanded := repl.ReplaceAll(v, "")
|
// look up the comparison value we will check against with this key
|
||||||
var varStr string
|
matcherVarNameExpanded := repl.ReplaceAll(key, "")
|
||||||
switch vv := vars[keyExpanded].(type) {
|
varValue, ok := vars[matcherVarNameExpanded]
|
||||||
case string:
|
if !ok {
|
||||||
varStr = vv
|
// as a special case, if it's not an HTTP variable,
|
||||||
case fmt.Stringer:
|
// see if it's a placeholder name
|
||||||
varStr = vv.String()
|
varValue, _ = repl.Get(matcherVarNameExpanded)
|
||||||
case error:
|
|
||||||
varStr = vv.Error()
|
|
||||||
default:
|
|
||||||
varStr = fmt.Sprintf("%v", vv)
|
|
||||||
}
|
}
|
||||||
if varStr != valExpanded {
|
|
||||||
return false
|
// see if any of the values given in the matcher match the actual value
|
||||||
|
for _, v := range vals {
|
||||||
|
matcherValExpanded := repl.ReplaceAll(v, "")
|
||||||
|
var varStr string
|
||||||
|
switch vv := varValue.(type) {
|
||||||
|
case string:
|
||||||
|
varStr = vv
|
||||||
|
case fmt.Stringer:
|
||||||
|
varStr = vv.String()
|
||||||
|
case error:
|
||||||
|
varStr = vv.Error()
|
||||||
|
default:
|
||||||
|
varStr = fmt.Sprintf("%v", vv)
|
||||||
|
}
|
||||||
|
if varStr == matcherValExpanded {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchVarsRE matches the value of the context variables by a given regular expression.
|
// MatchVarsRE matches the value of the context variables by a given regular expression.
|
||||||
|
|
Loading…
Reference in a new issue