diff --git a/config/load.go b/config/load.go index d75188471..54d992922 100644 --- a/config/load.go +++ b/config/load.go @@ -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" { + if cfg.Proxy.Matcher != "prefix" && cfg.Proxy.Matcher != "glob" && cfg.Proxy.Matcher != "gobwas/glob" { return nil, fmt.Errorf("invalid proxy.matcher: %s", cfg.Proxy.Matcher) } diff --git a/route/matcher.go b/route/matcher.go index 710be013c..30a438654 100644 --- a/route/matcher.go +++ b/route/matcher.go @@ -4,6 +4,8 @@ import ( "log" "path" "strings" + + "github.com/gobwas/glob" ) // matcher determines whether a host/path matches a route @@ -12,8 +14,9 @@ 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, + "prefix": prefixMatcher, + "glob": globMatcher, + "gobwas/glob": gobwasGlobMatcher, } // prefixMatcher matches path to the routes' path. @@ -30,3 +33,10 @@ func globMatcher(uri string, r *Route) bool { } 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) +} diff --git a/route/matcher_test.go b/route/matcher_test.go index 592d7d610..622f69f9f 100644 --- a/route/matcher_test.go +++ b/route/matcher_test.go @@ -54,3 +54,36 @@ 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) + } + }) + } +} \ No newline at end of file