From 231cffa49b738c244dde424fa9d999eeec31773d Mon Sep 17 00:00:00 2001 From: Adam S Levy Date: Tue, 17 Mar 2020 19:00:05 -0800 Subject: [PATCH 1/2] Mux.Handler: prioritize most specific match Path matches still take priority, but ties are broken with the number of additional matching criteria. --- handler.go | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/handler.go b/handler.go index 119b78b..b0b6ded 100644 --- a/handler.go +++ b/handler.go @@ -39,12 +39,16 @@ func (h HandlerFunc) Handle(ctx *Context, res *http.Response, err error) { // it will be called. Otherwise, if there is a Handler registered for any error, // this Handler will be called. // -// For Response Handlers, a match with a path criteria has higher priority than other -// matches, and the longer path match will get called. +// For Response Handlers, a match with a path criteria has higher priority than +// other matches, and the longer path match takes priority. // -// If multiple Response handlers with the same path length (or no path criteria) -// match a response, the actual handler called is undefined, but one and only one -// will be called. +// If multiple Response Handlers match with the same path length (or no path +// criteria), the Handler with the greatest number of matching criteria is +// called. +// +// If there are multiple Response handlers with the same path length (or no +// path criteria) and the same number of matching criteria, the actual handler +// called is undefined, but one and only one will be called. // // In any case, if no Handler matches, the DefaultHandler is called, and it // defaults to a no-op. @@ -84,11 +88,16 @@ func (mux *Mux) Handle(ctx *Context, res *http.Response, err error) { } else { // Find a matching response handler var h Handler + var criteria = -1 var n = -1 for r := range mux.res { - if ok, cnt := r.match(res); ok { + if ok, cnt, crit := r.match(res); ok { if cnt > n { - h, n = r.h, cnt + h, crit, n = r.h, crit, cnt + continue + } + if cnt == n && crit > criteria { + h, criteria = r.h, crit } } } @@ -138,44 +147,51 @@ type ResponseMatcher struct { // match indicates if the response Handler matches the provided response, and if so, // and if a path criteria is specified, it also indicates the length of the path match. -func (r *ResponseMatcher) match(res *http.Response) (bool, int) { +func (r *ResponseMatcher) match(res *http.Response) (bool, int, int) { + var criteria int if r.method != "" { if r.method != res.Request.Method { - return false, 0 + return false, 0, 0 } + criteria++ } if r.contentType != "" { if r.contentType != getContentType(res.Header.Get("Content-Type")) { - return false, 0 + return false, 0, 0 } + criteria++ } if r.minStatus != 0 || r.maxStatus != 0 { if res.StatusCode < r.minStatus || res.StatusCode > r.maxStatus { - return false, 0 + return false, 0, 0 } + criteria++ } if r.scheme != "" { if res.Request.URL.Scheme != r.scheme { - return false, 0 + return false, 0, 0 } + criteria++ } if r.host != "" { if res.Request.URL.Host != r.host { - return false, 0 + return false, 0, 0 } + criteria++ } if r.predicate != nil { if !r.predicate(res) { - return false, 0 + return false, 0, 0 } + criteria++ } if r.path != "" { - if strings.HasPrefix(res.Request.URL.Path, r.path) { - return true, len(r.path) + if !strings.HasPrefix(res.Request.URL.Path, r.path) { + return false, 0, 0 } - return false, 0 + criteria++ } - return true, 0 + return true, len(r.path), criteria } // Returns the content type stripped of any additional parameters (following the ;). From 9f979fdf691327cad751fa0aa1b5976304b6d8c4 Mon Sep 17 00:00:00 2001 From: Adam S Levy Date: Thu, 19 Mar 2020 18:00:58 -0800 Subject: [PATCH 2/2] Fix bug in Mux priority match selection Co-Authored-By: Martin Angers --- handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler.go b/handler.go index b0b6ded..d559234 100644 --- a/handler.go +++ b/handler.go @@ -93,7 +93,7 @@ func (mux *Mux) Handle(ctx *Context, res *http.Response, err error) { for r := range mux.res { if ok, cnt, crit := r.match(res); ok { if cnt > n { - h, crit, n = r.h, crit, cnt + h, criteria, n = r.h, crit, cnt continue } if cnt == n && crit > criteria {