From 75fbc3e9048ad63164196729147372465dfb0246 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Sun, 30 Aug 2020 21:13:41 -0700 Subject: [PATCH] allow skipping specified hosts from Host matcher introduce SkipHosts(hosts ...string) at the router level which can be used to allow certain hosts to be ignored by the Host() matcher, this is some times necessary for an application which registers handlers for one domain to be at a completely different style of operation One example is S3 APIs when servicing them as path style v/s bucket as part of the DNS style. --- mux.go | 18 ++++++++++++++++++ mux_test.go | 20 ++++++++++++++++++++ regexp.go | 6 ++++++ route.go | 1 + 4 files changed, 45 insertions(+) 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