From 284fb3a98cae2e6e6ca79327988230a3a916996a Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Wed, 22 May 2019 13:13:39 -0600 Subject: Allow multiple matcher sets in routes (OR'ed together) Also export MatchRegexp in case other matcher modules find it useful. Add comments to the exported matchers. --- modules/caddyhttp/matchers.go | 72 +++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 17 deletions(-) (limited to 'modules/caddyhttp/matchers.go') diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index eb57156..33300da 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -17,16 +17,35 @@ import ( ) type ( - MatchHost []string - MatchPath []string - MatchPathRE struct{ matchRegexp } - MatchMethod []string - MatchQuery url.Values - MatchHeader http.Header - MatchHeaderRE map[string]*matchRegexp - MatchProtocol string + // MatchHost matches requests by the Host value. + MatchHost []string + + // MatchPath matches requests by the URI's path. + MatchPath []string + + // MatchPathRE matches requests by a regular expression on the URI's path. + MatchPathRE struct{ MatchRegexp } + + // MatchMethod matches requests by the method. + MatchMethod []string + + // MatchQuery matches requests by URI's query string. + MatchQuery url.Values + + // MatchHeader matches requests by header fields. + MatchHeader http.Header + + // MatchHeaderRE matches requests by a regular expression on header fields. + MatchHeaderRE map[string]*MatchRegexp + + // MatchProtocol matches requests by protocol. + MatchProtocol string + + // MatchStarlarkExpr matches requests by evaluating a Starlark expression. MatchStarlarkExpr string - MatchTable string // TODO: finish implementing + + // MatchTable matches requests by values in the table. + MatchTable string // TODO: finish implementing ) func init() { @@ -68,6 +87,7 @@ func init() { }) } +// Match returns true if r matches m. func (m MatchHost) Match(r *http.Request) bool { outer: for _, host := range m { @@ -93,6 +113,7 @@ outer: return false } +// Match returns true if r matches m. func (m MatchPath) Match(r *http.Request) bool { for _, matchPath := range m { compare := r.URL.Path @@ -111,11 +132,13 @@ func (m MatchPath) Match(r *http.Request) bool { return false } +// Match returns true if r matches m. func (m MatchPathRE) Match(r *http.Request) bool { repl := r.Context().Value(caddy2.ReplacerCtxKey).(caddy2.Replacer) - return m.match(r.URL.Path, repl, "path_regexp") + return m.MatchRegexp.Match(r.URL.Path, repl, "path_regexp") } +// Match returns true if r matches m. func (m MatchMethod) Match(r *http.Request) bool { for _, method := range m { if r.Method == method { @@ -125,6 +148,7 @@ func (m MatchMethod) Match(r *http.Request) bool { return false } +// Match returns true if r matches m. func (m MatchQuery) Match(r *http.Request) bool { for param, vals := range m { paramVal := r.URL.Query().Get(param) @@ -137,6 +161,7 @@ func (m MatchQuery) Match(r *http.Request) bool { return false } +// Match returns true if r matches m. func (m MatchHeader) Match(r *http.Request) bool { for field, allowedFieldVals := range m { var match bool @@ -157,10 +182,11 @@ func (m MatchHeader) Match(r *http.Request) bool { return true } +// Match returns true if r matches m. func (m MatchHeaderRE) Match(r *http.Request) bool { for field, rm := range m { repl := r.Context().Value(caddy2.ReplacerCtxKey).(caddy2.Replacer) - match := rm.match(r.Header.Get(field), repl, "header_regexp") + match := rm.Match(r.Header.Get(field), repl, "header_regexp") if !match { return false } @@ -168,6 +194,7 @@ func (m MatchHeaderRE) Match(r *http.Request) bool { return true } +// Provision compiles m's regular expressions. func (m MatchHeaderRE) Provision() error { for _, rm := range m { err := rm.Provision() @@ -178,6 +205,7 @@ func (m MatchHeaderRE) Provision() error { return nil } +// Validate validates m's regular expressions. func (m MatchHeaderRE) Validate() error { for _, rm := range m { err := rm.Validate() @@ -188,6 +216,7 @@ func (m MatchHeaderRE) Validate() error { return nil } +// Match returns true if r matches m. func (m MatchProtocol) Match(r *http.Request) bool { switch string(m) { case "grpc": @@ -200,6 +229,7 @@ func (m MatchProtocol) Match(r *http.Request) bool { return false } +// Match returns true if r matches m. func (m MatchStarlarkExpr) Match(r *http.Request) bool { input := string(m) thread := new(starlark.Thread) @@ -213,15 +243,16 @@ func (m MatchStarlarkExpr) Match(r *http.Request) bool { return val.String() == "True" } -// matchRegexp is just the fields common among -// matchers that can use regular expressions. -type matchRegexp struct { +// MatchRegexp is an embeddable type for matching +// using regular expressions. +type MatchRegexp struct { Name string `json:"name"` Pattern string `json:"pattern"` compiled *regexp.Regexp } -func (mre *matchRegexp) Provision() error { +// Provision compiles the regular expression. +func (mre *MatchRegexp) Provision() error { re, err := regexp.Compile(mre.Pattern) if err != nil { return fmt.Errorf("compiling matcher regexp %s: %v", mre.Pattern, err) @@ -230,14 +261,21 @@ func (mre *matchRegexp) Provision() error { return nil } -func (mre *matchRegexp) Validate() error { +// Validate ensures mre is set up correctly. +func (mre *MatchRegexp) Validate() error { if mre.Name != "" && !wordRE.MatchString(mre.Name) { return fmt.Errorf("invalid regexp name (must contain only word characters): %s", mre.Name) } return nil } -func (mre *matchRegexp) match(input string, repl caddy2.Replacer, scope string) bool { +// Match returns true if input matches the compiled regular +// expression in mre. It sets values on the replacer repl +// associated with capture groups, using the given scope +// (namespace). Capture groups stored to repl will take on +// the name "http.matchers..." where +// is the name or number of the capture group. +func (mre *MatchRegexp) Match(input string, repl caddy2.Replacer, scope string) bool { matches := mre.compiled.FindStringSubmatch(input) if matches == nil { return false -- cgit v1.2.3