diff --git a/mux.go b/mux.go index 782a34b..214acb8 100644 --- a/mux.go +++ b/mux.go @@ -82,6 +82,9 @@ type routeConf struct { // will not redirect skipClean bool + // If set to a host skips the this host from r.Host() matcher. + skipHosts map[string]struct{} + // Manager for the variables from host and path. regexp routeRegexpGroup @@ -269,6 +272,21 @@ func (r *Router) UseEncodedPath() *Router { return r } +// SkipHosts tells the router to skip the these list of hosts from +// r.Host() rules. +// For eg: "r.Host("{subdomain}.domain.com") will match "test.subdomain.com" +// and you would want to skip the matcher for `test.subdomain.com`. +// +// Go regexes do not support PCRE style lookahead/lookbehind like +// features, this function is a way to provide this functionality. +func (r *Router) SkipHosts(hosts ...string) *Router { + r.skipHosts = map[string]struct{}{} + for _, host := range hosts { + r.skipHosts[host] = struct{}{} + } + return r +} + // ---------------------------------------------------------------------------- // Route factories // ---------------------------------------------------------------------------- diff --git a/mux_test.go b/mux_test.go index 2d8d2b3..58ea641 100644 --- a/mux_test.go +++ b/mux_test.go @@ -51,6 +51,16 @@ type routeTest struct { shouldRedirect bool // whether the request should result in a redirect } +func skipRoute(tpl string, skipHosts ...string) *Route { + skipRoute := &Route{} + skipRoute.skipHosts = map[string]struct{}{} + for _, host := range skipHosts { + skipRoute.skipHosts[host] = struct{}{} + } + skipRoute.Host(tpl) + return skipRoute +} + func TestHost(t *testing.T) { tests := []routeTest{ @@ -216,6 +226,16 @@ func TestHost(t *testing.T) { hostTemplate: `{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}`, shouldMatch: true, }, + { + title: "Host route match should skip specific hosts when specified", + route: skipRoute("{subdomain}.domain.com", "test.domain.com"), // skip matching 'test.domain.com' + request: newRequest("GET", "http://test.domain.com"), + vars: map[string]string{}, + host: "test.domain.com", + path: "", + hostTemplate: `{subdomain}.domain.com`, + shouldMatch: false, + }, } for _, test := range tests { t.Run(test.title, func(t *testing.T) { diff --git a/regexp.go b/regexp.go index 0144842..391e081 100644 --- a/regexp.go +++ b/regexp.go @@ -17,6 +17,7 @@ import ( type routeRegexpOptions struct { strictSlash bool useEncodedPath bool + skipHosts map[string]struct{} } type regexpType int @@ -180,6 +181,11 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { host = host[:i] } } + if len(r.options.skipHosts) > 0 { + if _, ok := r.options.skipHosts[host]; ok { + return false + } + } return r.regexp.MatchString(host) } diff --git a/route.go b/route.go index 750afe5..784a2b2 100644 --- a/route.go +++ b/route.go @@ -186,6 +186,7 @@ func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error { rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{ strictSlash: r.strictSlash, useEncodedPath: r.useEncodedPath, + skipHosts: r.skipHosts, }) if err != nil { return err