From 9bdd6caa0bcced5caf30872548700277f2db1877 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sat, 8 Feb 2020 22:26:31 +0300 Subject: v2: Implement RegExp Vars Matcher (#2997) * implement regexp var matcher * use subtests pattern for tests * be more consistent with naming: MatchVarRE -> MatchVarsRE, var_regexp -> vars_regexp --- modules/caddyhttp/vars.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'modules/caddyhttp/vars.go') diff --git a/modules/caddyhttp/vars.go b/modules/caddyhttp/vars.go index 1208d9c..9561d46 100644 --- a/modules/caddyhttp/vars.go +++ b/modules/caddyhttp/vars.go @@ -25,6 +25,7 @@ import ( func init() { caddy.RegisterModule(VarsMiddleware{}) caddy.RegisterModule(VarsMatcher{}) + caddy.RegisterModule(MatchVarsRE{}) } // VarsMiddleware is an HTTP middleware which sets variables @@ -88,6 +89,74 @@ func (m VarsMatcher) Match(r *http.Request) bool { return true } +// MatchVarsRE matches the value of the context variables by a given regular expression. +// +// Upon a match, it adds placeholders to the request: `{http.regexp.name.capture_group}` +// where `name` is the regular expression's name, and `capture_group` is either +// the named or positional capture group from the expression itself. If no name +// is given, then the placeholder omits the name: `{http.regexp.capture_group}` +// (potentially leading to collisions). +type MatchVarsRE map[string]*MatchRegexp + +// CaddyModule returns the Caddy module information. +func (MatchVarsRE) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + ID: "http.matchers.vars_regexp", + New: func() caddy.Module { return new(MatchVarsRE) }, + } +} + +// Provision compiles m's regular expressions. +func (m MatchVarsRE) Provision(ctx caddy.Context) error { + for _, rm := range m { + err := rm.Provision(ctx) + if err != nil { + return err + } + } + return nil +} + +// Match returns true if r matches m. +func (m MatchVarsRE) Match(r *http.Request) bool { + vars := r.Context().Value(VarsCtxKey).(map[string]interface{}) + repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) + for k, rm := range m { + var varStr string + switch vv := vars[k].(type) { + case string: + varStr = vv + case fmt.Stringer: + varStr = vv.String() + case error: + varStr = vv.Error() + default: + varStr = fmt.Sprintf("%v", vv) + } + valExpanded := repl.ReplaceAll(varStr, "") + if match := rm.Match(valExpanded, repl); match { + return match + } + + replacedVal := repl.ReplaceAll(k, "") + if match := rm.Match(replacedVal, repl); match { + return match + } + } + return false +} + +// Validate validates m's regular expressions. +func (m MatchVarsRE) Validate() error { + for _, rm := range m { + err := rm.Validate() + if err != nil { + return err + } + } + return nil +} + // GetVar gets a value out of the context's variable table by key. // If the key does not exist, the return value will be nil. func GetVar(ctx context.Context, key string) interface{} { -- cgit v1.2.3