diff --git a/server/rules.go b/rules/rules.go similarity index 86% rename from server/rules.go rename to rules/rules.go index be7f2b033..301d15ccb 100644 --- a/server/rules.go +++ b/rules/rules.go @@ -1,4 +1,4 @@ -package server +package rules import ( "errors" @@ -16,12 +16,12 @@ import ( // Rules holds rule parsing and configuration type Rules struct { - route *serverRoute + Route *types.ServerRoute err error } func (r *Rules) host(hosts ...string) *mux.Route { - return r.route.route.MatcherFunc(func(req *http.Request, route *mux.RouteMatch) bool { + return r.Route.Route.MatcherFunc(func(req *http.Request, route *mux.RouteMatch) bool { reqHost, _, err := net.SplitHostPort(req.Host) if err != nil { reqHost = req.Host @@ -36,27 +36,27 @@ func (r *Rules) host(hosts ...string) *mux.Route { } func (r *Rules) hostRegexp(hosts ...string) *mux.Route { - router := r.route.route.Subrouter() + router := r.Route.Route.Subrouter() for _, host := range hosts { router.Host(types.CanonicalDomain(host)) } - return r.route.route + return r.Route.Route } func (r *Rules) path(paths ...string) *mux.Route { - router := r.route.route.Subrouter() + router := r.Route.Route.Subrouter() for _, path := range paths { router.Path(strings.TrimSpace(path)) } - return r.route.route + return r.Route.Route } func (r *Rules) pathPrefix(paths ...string) *mux.Route { - router := r.route.route.Subrouter() + router := r.Route.Route.Subrouter() for _, path := range paths { buildPath(path, router) } - return r.route.route + return r.Route.Route } func buildPath(path string, router *mux.Router) { @@ -88,75 +88,75 @@ func (a bySize) Less(i, j int) bool { return len(a[i]) > len(a[j]) } func (r *Rules) pathStrip(paths ...string) *mux.Route { sort.Sort(bySize(paths)) - r.route.stripPrefixes = paths - router := r.route.route.Subrouter() + r.Route.StripPrefixes = paths + router := r.Route.Route.Subrouter() for _, path := range paths { router.Path(strings.TrimSpace(path)) } - return r.route.route + return r.Route.Route } func (r *Rules) pathStripRegex(paths ...string) *mux.Route { sort.Sort(bySize(paths)) - r.route.stripPrefixesRegex = paths - router := r.route.route.Subrouter() + r.Route.StripPrefixesRegex = paths + router := r.Route.Route.Subrouter() for _, path := range paths { router.Path(strings.TrimSpace(path)) } - return r.route.route + return r.Route.Route } func (r *Rules) replacePath(paths ...string) *mux.Route { for _, path := range paths { - r.route.replacePath = path + r.Route.ReplacePath = path } - return r.route.route + return r.Route.Route } func (r *Rules) replacePathRegex(paths ...string) *mux.Route { for _, path := range paths { - r.route.replacePathRegex = path + r.Route.ReplacePathRegex = path } - return r.route.route + return r.Route.Route } func (r *Rules) addPrefix(paths ...string) *mux.Route { for _, path := range paths { - r.route.addPrefix = path + r.Route.AddPrefix = path } - return r.route.route + return r.Route.Route } func (r *Rules) pathPrefixStrip(paths ...string) *mux.Route { sort.Sort(bySize(paths)) - r.route.stripPrefixes = paths - router := r.route.route.Subrouter() + r.Route.StripPrefixes = paths + router := r.Route.Route.Subrouter() for _, path := range paths { buildPath(path, router) } - return r.route.route + return r.Route.Route } func (r *Rules) pathPrefixStripRegex(paths ...string) *mux.Route { sort.Sort(bySize(paths)) - r.route.stripPrefixesRegex = paths - router := r.route.route.Subrouter() + r.Route.StripPrefixesRegex = paths + router := r.Route.Route.Subrouter() for _, path := range paths { router.PathPrefix(strings.TrimSpace(path)) } - return r.route.route + return r.Route.Route } func (r *Rules) methods(methods ...string) *mux.Route { - return r.route.route.Methods(methods...) + return r.Route.Route.Methods(methods...) } func (r *Rules) headers(headers ...string) *mux.Route { - return r.route.route.Headers(headers...) + return r.Route.Route.Headers(headers...) } func (r *Rules) headersRegexp(headers ...string) *mux.Route { - return r.route.route.HeadersRegexp(headers...) + return r.Route.Route.HeadersRegexp(headers...) } func (r *Rules) query(query ...string) *mux.Route { @@ -165,7 +165,7 @@ func (r *Rules) query(query ...string) *mux.Route { queries = append(queries, strings.Split(elem, "=")...) } - return r.route.route.Queries(queries...) + return r.Route.Route.Queries(queries...) } func (r *Rules) parseRules(expression string, onRule func(functionName string, function interface{}, arguments []string) error) error { diff --git a/server/rules_test.go b/rules/rules_test.go similarity index 92% rename from server/rules_test.go rename to rules/rules_test.go index 7d3712289..fe40f8e26 100644 --- a/server/rules_test.go +++ b/rules/rules_test.go @@ -1,4 +1,4 @@ -package server +package rules import ( "net/http" @@ -7,6 +7,7 @@ import ( "github.com/containous/mux" "github.com/containous/traefik/testhelpers" + "github.com/containous/traefik/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -14,8 +15,8 @@ import ( func TestParseOneRule(t *testing.T) { router := mux.NewRouter() route := router.NewRoute() - serverRoute := &serverRoute{route: route} - rules := &Rules{route: serverRoute} + serverRoute := &types.ServerRoute{Route: route} + rules := &Rules{Route: serverRoute} expression := "Host:foo.bar" routeResult, err := rules.Parse(expression) @@ -30,8 +31,8 @@ func TestParseOneRule(t *testing.T) { func TestParseTwoRules(t *testing.T) { router := mux.NewRouter() route := router.NewRoute() - serverRoute := &serverRoute{route: route} - rules := &Rules{route: serverRoute} + serverRoute := &types.ServerRoute{Route: route} + rules := &Rules{Route: serverRoute} expression := "Host: Foo.Bar ; Path:/FOObar" routeResult, err := rules.Parse(expression) @@ -90,7 +91,7 @@ func TestParseDomains(t *testing.T) { func TestPriorites(t *testing.T) { router := mux.NewRouter() router.StrictSlash(true) - rules := &Rules{route: &serverRoute{route: router.NewRoute()}} + rules := &Rules{Route: &types.ServerRoute{Route: router.NewRoute()}} expression01 := "PathPrefix:/foo" routeFoo, err := rules.Parse(expression01) @@ -105,7 +106,7 @@ func TestPriorites(t *testing.T) { routeMatch = router.Match(&http.Request{URL: &url.URL{Path: "/fo"}}, &mux.RouteMatch{}) assert.False(t, routeMatch, "Error matching route") - multipleRules := &Rules{route: &serverRoute{route: router.NewRoute()}} + multipleRules := &Rules{Route: &types.ServerRoute{Route: router.NewRoute()}} expression02 := "PathPrefix:/foobar" routeFoobar, err := multipleRules.Parse(expression02) @@ -172,8 +173,8 @@ func TestHostRegexp(t *testing.T) { t.Parallel() rls := &Rules{ - route: &serverRoute{ - route: &mux.Route{}, + Route: &types.ServerRoute{ + Route: &mux.Route{}, }, } @@ -239,8 +240,8 @@ func TestPathPrefix(t *testing.T) { t.Parallel() rls := &Rules{ - route: &serverRoute{ - route: &mux.Route{}, + Route: &types.ServerRoute{ + Route: &mux.Route{}, }, } diff --git a/server/server.go b/server/server.go index e61f7b1d4..0790be28f 100644 --- a/server/server.go +++ b/server/server.go @@ -34,6 +34,7 @@ import ( "github.com/containous/traefik/middlewares/redirect" "github.com/containous/traefik/middlewares/tracing" "github.com/containous/traefik/provider" + "github.com/containous/traefik/rules" "github.com/containous/traefik/safe" "github.com/containous/traefik/server/cookie" traefikTls "github.com/containous/traefik/tls" @@ -84,15 +85,6 @@ type serverEntryPoint struct { certs safe.Safe } -type serverRoute struct { - route *mux.Route - stripPrefixes []string - stripPrefixesRegex []string - addPrefix string - replacePath string - replacePathRegex string -} - // NewServer returns an initialized Server. func NewServer(globalConfiguration configuration.GlobalConfiguration, provider provider.Provider) *Server { server := new(Server) @@ -524,7 +516,7 @@ func (s *Server) postLoadConfiguration() { if acmeEnabled { for _, route := range frontend.Routes { - rules := Rules{} + rules := rules.Rules{} domains, err := rules.ParseDomains(route.Rule) if err != nil { log.Errorf("Error parsing domains: %v", err) @@ -899,7 +891,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura for _, entryPointName := range frontend.EntryPoints { log.Debugf("Wiring frontend %s to entryPoint %s", frontendName, entryPointName) - newServerRoute := &serverRoute{route: serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)} + newServerRoute := &types.ServerRoute{Route: serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)} for routeName, route := range frontend.Routes { err := getRoute(newServerRoute, &route) if err != nil { @@ -1177,11 +1169,11 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura log.Debugf("Reusing backend %s", frontend.Backend) } if frontend.Priority > 0 { - newServerRoute.route.Priority(frontend.Priority) + newServerRoute.Route.Priority(frontend.Priority) } s.wireFrontendBackend(newServerRoute, backends[entryPointName+providerName+frontend.Backend]) - err := newServerRoute.route.GetError() + err := newServerRoute.Route.GetError() if err != nil { log.Errorf("Error building route: %s", err) } @@ -1236,48 +1228,48 @@ func configureIPWhitelistMiddleware(whitelistSourceRanges []string) (negroni.Han return nil, nil } -func (s *Server) wireFrontendBackend(serverRoute *serverRoute, handler http.Handler) { +func (s *Server) wireFrontendBackend(serverRoute *types.ServerRoute, handler http.Handler) { // path replace - This needs to always be the very last on the handler chain (first in the order in this function) // -- Replacing Path should happen at the very end of the Modifier chain, after all the Matcher+Modifiers ran - if len(serverRoute.replacePath) > 0 { + if len(serverRoute.ReplacePath) > 0 { handler = &middlewares.ReplacePath{ - Path: serverRoute.replacePath, + Path: serverRoute.ReplacePath, Handler: handler, } } - if len(serverRoute.replacePathRegex) > 0 { - sp := strings.Split(serverRoute.replacePathRegex, " ") + if len(serverRoute.ReplacePathRegex) > 0 { + sp := strings.Split(serverRoute.ReplacePathRegex, " ") if len(sp) == 2 { handler = middlewares.NewReplacePathRegexHandler(sp[0], sp[1], handler) } else { - log.Warnf("Invalid syntax for ReplacePathRegex: %s. Separate the regular expression and the replacement by a space.", serverRoute.replacePathRegex) + log.Warnf("Invalid syntax for ReplacePathRegex: %s. Separate the regular expression and the replacement by a space.", serverRoute.ReplacePathRegex) } } // add prefix - This needs to always be right before ReplacePath on the chain (second in order in this function) // -- Adding Path Prefix should happen after all *Strip Matcher+Modifiers ran, but before Replace (in case it's configured) - if len(serverRoute.addPrefix) > 0 { + if len(serverRoute.AddPrefix) > 0 { handler = &middlewares.AddPrefix{ - Prefix: serverRoute.addPrefix, + Prefix: serverRoute.AddPrefix, Handler: handler, } } // strip prefix - if len(serverRoute.stripPrefixes) > 0 { + if len(serverRoute.StripPrefixes) > 0 { handler = &middlewares.StripPrefix{ - Prefixes: serverRoute.stripPrefixes, + Prefixes: serverRoute.StripPrefixes, Handler: handler, } } // strip prefix with regex - if len(serverRoute.stripPrefixesRegex) > 0 { - handler = middlewares.NewStripPrefixRegex(handler, serverRoute.stripPrefixesRegex) + if len(serverRoute.StripPrefixesRegex) > 0 { + handler = middlewares.NewStripPrefixRegex(handler, serverRoute.StripPrefixesRegex) } - serverRoute.route.Handler(handler) + serverRoute.Route.Handler(handler) } func (s *Server) buildRedirectHandler(srcEntryPointName string, opt *types.Redirect) (negroni.Handler, error) { @@ -1335,14 +1327,14 @@ func parseHealthCheckOptions(lb healthcheck.LoadBalancer, backend string, hc *ty } } -func getRoute(serverRoute *serverRoute, route *types.Route) error { - rules := Rules{route: serverRoute} +func getRoute(serverRoute *types.ServerRoute, route *types.Route) error { + rules := rules.Rules{Route: serverRoute} newRoute, err := rules.Parse(route.Rule) if err != nil { return err } - newRoute.Priority(serverRoute.route.GetPriority() + len(route.Rule)) - serverRoute.route = newRoute + newRoute.Priority(serverRoute.Route.GetPriority() + len(route.Rule)) + serverRoute.Route = newRoute return nil } diff --git a/server/server_test.go b/server/server_test.go index 6449ad9a6..b142533a2 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -15,6 +15,7 @@ import ( "github.com/containous/traefik/healthcheck" "github.com/containous/traefik/metrics" "github.com/containous/traefik/middlewares" + "github.com/containous/traefik/rules" "github.com/containous/traefik/testhelpers" "github.com/containous/traefik/tls" "github.com/containous/traefik/types" @@ -393,8 +394,8 @@ func TestServerMultipleFrontendRules(t *testing.T) { router := mux.NewRouter() route := router.NewRoute() - serverRoute := &serverRoute{route: route} - rules := &Rules{route: serverRoute} + serverRoute := &types.ServerRoute{Route: route} + rules := &rules.Rules{Route: serverRoute} expression := test.expression routeResult, err := rules.Parse(expression) @@ -417,7 +418,7 @@ func TestServerMultipleFrontendRules(t *testing.T) { t.Fatalf("got URL %s, expected %s", r.URL.String(), test.expectedURL) } })) - serverRoute.route.GetHandler().ServeHTTP(nil, request) + serverRoute.Route.GetHandler().ServeHTTP(nil, request) }) } } diff --git a/types/types.go b/types/types.go index 7a7c90034..385ad629d 100644 --- a/types/types.go +++ b/types/types.go @@ -13,6 +13,7 @@ import ( "github.com/abronan/valkeyrie/store" "github.com/containous/flaeg" + "github.com/containous/mux" "github.com/containous/traefik/log" traefikTls "github.com/containous/traefik/tls" "github.com/ryanuber/go-glob" @@ -78,6 +79,16 @@ type Route struct { Rule string `json:"rule,omitempty"` } +// ServerRoute holds ServerRoute configuration. +type ServerRoute struct { + Route *mux.Route + StripPrefixes []string + StripPrefixesRegex []string + AddPrefix string + ReplacePath string + ReplacePathRegex string +} + //ErrorPage holds custom error page configuration type ErrorPage struct { Status []string `json:"status,omitempty"`