Skip to content

Commit

Permalink
Use one glob matcher & cache compiled pattern in route table
Browse files Browse the repository at this point in the history
  • Loading branch information
sharbov committed Mar 1, 2018
1 parent 6e2b05b commit 6cd642b
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 65 deletions.
2 changes: 1 addition & 1 deletion config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func load(cmdline, environ, envprefix []string, props *properties.Properties) (c
return nil, fmt.Errorf("invalid proxy.strategy: %s", cfg.Proxy.Strategy)
}

if cfg.Proxy.Matcher != "prefix" && cfg.Proxy.Matcher != "glob" && cfg.Proxy.Matcher != "gobwas/glob" {
if cfg.Proxy.Matcher != "prefix" && cfg.Proxy.Matcher != "glob" {
return nil, fmt.Errorf("invalid proxy.matcher: %s", cfg.Proxy.Matcher)
}

Expand Down
25 changes: 4 additions & 21 deletions route/matcher.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package route

import (
"log"
"path"
"strings"

"github.com/gobwas/glob"
)

// matcher determines whether a host/path matches a route
Expand All @@ -14,29 +10,16 @@ type matcher func(uri string, r *Route) bool
// Matcher contains the available matcher functions.
// Update config/load.go#load after updating.
var Matcher = map[string]matcher{
"prefix": prefixMatcher,
"glob": globMatcher,
"gobwas/glob": gobwasGlobMatcher,
"prefix": prefixMatcher,
"glob": globMatcher,
}

// prefixMatcher matches path to the routes' path.
func prefixMatcher(uri string, r *Route) bool {
return strings.HasPrefix(uri, r.Path)
}

// globMatcher matches path to the routes' path using globbing.
// globMatcher matches path to the routes' path using gobwas/glob.
func globMatcher(uri string, r *Route) bool {
var hasMatch, err = path.Match(r.Path, uri)
if err != nil {
log.Printf("[ERROR] Glob matching error %s for path %s route %s", err, uri, r.Path)
return false
}
return hasMatch
}


// gobwasGlobMatcher matches path to the routes' path using gobwas/glob.
func gobwasGlobMatcher(uri string, r *Route) bool {
var g = glob.MustCompile(r.Path)
return g.Match(uri)
return r.Matcher.Match(uri)
}
60 changes: 18 additions & 42 deletions route/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package route

import (
"testing"

"github.com/gobwas/glob"
)

func TestPrefixMatcher(t *testing.T) {
Expand Down Expand Up @@ -32,18 +34,21 @@ func TestGlobMatcher(t *testing.T) {
route *Route
}{
// happy flows
{uri: "/foo", matches: true, route: &Route{Path: "/foo"}},
{uri: "/fool", matches: true, route: &Route{Path: "/foo?"}},
{uri: "/fool", matches: true, route: &Route{Path: "/foo*"}},
{uri: "/fools", matches: true, route: &Route{Path: "/foo*"}},
{uri: "/fools", matches: true, route: &Route{Path: "/foo*"}},
{uri: "/foo/x/bar", matches: true, route: &Route{Path: "/foo/*/bar"}},
{uri: "/foo", matches: true, route: getRoute("/foo")},
{uri: "/fool", matches: true, route: getRoute("/foo?")},
{uri: "/fool", matches: true, route: getRoute("/foo*")},
{uri: "/fools", matches: true, route: getRoute("/foo*")},
{uri: "/fools", matches: true, route: getRoute("/foo*")},
{uri: "/foo/x/bar", matches: true, route: getRoute("/foo/*/bar")},
{uri: "/foo/x/y/z/w/bar", matches: true, route: getRoute("/foo/**")},
{uri: "/foo/x/y/z/w/bar", matches: true, route: getRoute("/foo/**/bar")},

// error flows
{uri: "/fo", matches: false, route: &Route{Path: "/foo"}},
{uri: "/fools", matches: false, route: &Route{Path: "/foo"}},
{uri: "/fo", matches: false, route: &Route{Path: "/foo*"}},
{uri: "/fools", matches: false, route: &Route{Path: "/foo.*"}},
{uri: "/fo", matches: false, route: getRoute("/foo")},
{uri: "/fools", matches: false, route: getRoute("/foo")},
{uri: "/fo", matches: false, route: getRoute("/foo*")},
{uri: "/fools", matches: false, route: getRoute("/foo.*")},
{uri: "/foo/x/y/z/w/baz", matches: false, route: getRoute("/foo/**/bar")},
}

for _, tt := range tests {
Expand All @@ -55,35 +60,6 @@ func TestGlobMatcher(t *testing.T) {
}
}

func TestGobwasGlobMatcher(t *testing.T) {
tests := []struct {
uri string
matches bool
route *Route
}{
// happy flows
{uri: "/foo", matches: true, route: &Route{Path: "/foo"}},
{uri: "/fool", matches: true, route: &Route{Path: "/foo?"}},
{uri: "/fool", matches: true, route: &Route{Path: "/foo*"}},
{uri: "/fools", matches: true, route: &Route{Path: "/foo*"}},
{uri: "/fools", matches: true, route: &Route{Path: "/foo*"}},
{uri: "/foo/x/bar", matches: true, route: &Route{Path: "/foo/*/bar"}},
{uri: "/foo/x/y/z/w/bar", matches: true, route: &Route{Path: "/foo/**"}},
{uri: "/foo/x/y/z/w/bar", matches: true, route: &Route{Path: "/foo/**/bar"}},

// error flows
{uri: "/fo", matches: false, route: &Route{Path: "/foo"}},
{uri: "/fools", matches: false, route: &Route{Path: "/foo"}},
{uri: "/fo", matches: false, route: &Route{Path: "/foo*"}},
{uri: "/fools", matches: false, route: &Route{Path: "/foo.*"}},
{uri: "/foo/x/y/z/w/baz", matches: false, route: &Route{Path: "/foo/**/bar"}},
}

for _, tt := range tests {
t.Run(tt.uri, func(t *testing.T) {
if got, want := gobwasGlobMatcher(tt.uri, tt.route), tt.matches; got != want {
t.Fatalf("got %v want %v", got, want)
}
})
}
}
func getRoute(path string) *Route {
return &Route{Path: path, Matcher: glob.MustCompile(path)}
}
4 changes: 4 additions & 0 deletions route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strconv"
"strings"

"github.com/gobwas/glob"
"github.com/fabiolb/fabio/metrics"
)

Expand Down Expand Up @@ -36,6 +37,9 @@ type Route struct {
// total contains the total number of requests for this route.
// Used by the RRPicker
total uint64

// Matcher represents compiled pattern.
Matcher glob.Glob
}

func (r *Route) addTarget(service string, targetURL *url.URL, fixedWeight float64, tags []string, opts map[string]string) {
Expand Down
3 changes: 2 additions & 1 deletion route/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/fabiolb/fabio/metrics"
"github.com/ryanuber/go-glob"
glob2 "github.com/gobwas/glob"
)

var errInvalidPrefix = errors.New("route: prefix must not be empty")
Expand Down Expand Up @@ -153,7 +154,7 @@ func (t Table) addRoute(d *RouteDef) error {
switch {
// add new host
case t[host] == nil:
r := &Route{Host: host, Path: path}
r := &Route{Host: host, Path: path, Matcher: glob2.MustCompile(path)}
r.addTarget(d.Service, targetURL, d.Weight, d.Tags, d.Opts)
t[host] = Routes{r}

Expand Down

0 comments on commit 6cd642b

Please sign in to comment.