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/routes.go | 64 +++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 19 deletions(-) (limited to 'modules/caddyhttp/routes.go') diff --git a/modules/caddyhttp/routes.go b/modules/caddyhttp/routes.go index 07e0566..59f287e 100644 --- a/modules/caddyhttp/routes.go +++ b/modules/caddyhttp/routes.go @@ -12,17 +12,42 @@ import ( // middlewares, and a responder for handling HTTP // requests. type ServerRoute struct { - Group string `json:"group,omitempty"` - Matchers map[string]json.RawMessage `json:"match,omitempty"` - Apply []json.RawMessage `json:"apply,omitempty"` - Respond json.RawMessage `json:"respond,omitempty"` + Group string `json:"group,omitempty"` + MatcherSets []map[string]json.RawMessage `json:"match,omitempty"` + Apply []json.RawMessage `json:"apply,omitempty"` + Respond json.RawMessage `json:"respond,omitempty"` Terminal bool `json:"terminal,omitempty"` // decoded values - matchers []RequestMatcher - middleware []MiddlewareHandler - responder Handler + matcherSets []MatcherSet + middleware []MiddlewareHandler + responder Handler +} + +func (sr ServerRoute) anyMatcherSetMatches(r *http.Request) bool { + for _, ms := range sr.matcherSets { + if ms.Match(r) { + return true + } + } + return false +} + +// MatcherSet is a set of matchers which +// must all match in order for the request +// to be matched successfully. +type MatcherSet []RequestMatcher + +// Match returns true if the request matches all +// matchers in mset. +func (mset MatcherSet) Match(r *http.Request) bool { + for _, m := range mset { + if !m.Match(r) { + return false + } + } + return true } // RouteList is a list of server routes that can @@ -33,14 +58,18 @@ type RouteList []ServerRoute func (routes RouteList) Provision(ctx caddy2.Context) error { for i, route := range routes { // matchers - for modName, rawMsg := range route.Matchers { - val, err := ctx.LoadModule("http.matchers."+modName, rawMsg) - if err != nil { - return fmt.Errorf("loading matcher module '%s': %v", modName, err) + for _, matcherSet := range route.MatcherSets { + var matchers MatcherSet + for modName, rawMsg := range matcherSet { + val, err := ctx.LoadModule("http.matchers."+modName, rawMsg) + if err != nil { + return fmt.Errorf("loading matcher module '%s': %v", modName, err) + } + matchers = append(matchers, val.(RequestMatcher)) } - routes[i].matchers = append(routes[i].matchers, val.(RequestMatcher)) + routes[i].matcherSets = append(routes[i].matcherSets, matchers) } - routes[i].Matchers = nil // allow GC to deallocate - TODO: Does this help? + routes[i].MatcherSets = nil // allow GC to deallocate - TODO: Does this help? // middleware for j, rawMsg := range route.Apply { @@ -78,13 +107,10 @@ func (routes RouteList) BuildCompositeRoute(rw http.ResponseWriter, req *http.Re var responder Handler groups := make(map[string]struct{}) -routeLoop: for _, route := range routes { - // see if route matches - for _, m := range route.matchers { - if !m.Match(req) { - continue routeLoop - } + // route must match at least one of the matcher sets + if !route.anyMatcherSetMatches(req) { + continue } // if route is part of a group, ensure only -- cgit v1.2.3