From 95d86d84b456fbf104aa8f567cd86013c4213c86 Mon Sep 17 00:00:00 2001 From: SALLEYRON Julien Date: Wed, 17 Oct 2018 14:22:03 +0200 Subject: [PATCH] Add keepTrailingSlash option --- configuration/configuration.go | 1 + docs/configuration/commons.md | 26 ++++++++++ integration/basic_test.go | 48 +++++++++++++++++++ integration/fixtures/keep_trailing_slash.toml | 23 +++++++++ server/server.go | 2 +- server/server_configuration.go | 2 +- 6 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 integration/fixtures/keep_trailing_slash.toml diff --git a/configuration/configuration.go b/configuration/configuration.go index 9d1a67865..026af61d1 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -87,6 +87,7 @@ type GlobalConfiguration struct { RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"` ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers" export:"true"` AllowMinWeightZero bool `description:"Allow weight to take 0 as minimum real value." export:"true"` // Deprecated + KeepTrailingSlash bool `description:"Do not remove trailing slash." export:"true"` // Deprecated Web *WebCompatibility `description:"(Deprecated) Enable Web backend with default settings" export:"true"` // Deprecated Docker *docker.Provider `description:"Enable Docker backend with default settings" export:"true"` File *file.Provider `description:"Enable File backend with default settings" export:"true"` diff --git a/docs/configuration/commons.md b/docs/configuration/commons.md index 082281892..4f59d698f 100644 --- a/docs/configuration/commons.md +++ b/docs/configuration/commons.md @@ -33,6 +33,13 @@ # # checkNewVersion = false +# Tells traefik whether it should keep the trailing slashes in the paths (e.g. /paths/) or redirect to the no trailing slash paths instead (/paths). +# +# Optional +# Default: false +# +# keepTrailingSlash = false + # Providers throttle duration. # # Optional @@ -103,6 +110,25 @@ If you encounter 'too many open files' errors, you can either increase this valu - `defaultEntryPoints`: Entrypoints to be used by frontends that do not specify any entrypoint. Each frontend can specify its own entrypoints. +- `keepTrailingSlash`: Tells Træfik whether it should keep the trailing slashes that might be present in the paths of incoming requests (true), or if it should redirect to the slashless version of the URL (default behavior: false) + +!!! note + Beware that the value of `keepTrailingSlash` can have a significant impact on the way your frontend rules are interpreted. + The table below tries to sum up several behaviors depending on requests/configurations. + The current default behavior is deprecated and kept for compatibility reasons. + As a consequence, we encourage you to set `keepTrailingSlash` to true. + + | Incoming request | keepTrailingSlash | Path:{value} | Behavior + |----------------------|-------------------|--------------|----------------------------| + | http://foo.com/path/ | false | Path:/path/ | Proceeds with the request | + | http://foo.com/path/ | false | Path:/path | 301 to http://foo.com/path | + | http://foo.com/path | false | Path:/path/ | Proceeds with the request | + | http://foo.com/path | false | Path:/path | Proceeds with the request | + | http://foo.com/path/ | true | Path:/path/ | Proceeds with the request | + | http://foo.com/path/ | true | Path:/path | 404 | + | http://foo.com/path | true | Path:/path/ | 404 | + | http://foo.com/path | true | Path:/path | Proceeds with the request | + ## Constraints diff --git a/integration/basic_test.go b/integration/basic_test.go index a576cea46..2263d7f11 100644 --- a/integration/basic_test.go +++ b/integration/basic_test.go @@ -396,3 +396,51 @@ func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) { c.Assert(err, checker.IsNil) } + +func (s *SimpleSuite) TestDontKeepTrailingSlash(c *check.C) { + file := s.adaptFile(c, "fixtures/keep_trailing_slash.toml", struct { + KeepTrailingSlash bool + }{false}) + defer os.Remove(file) + + cmd, output := s.traefikCmd(withConfigFile(file)) + defer output(c) + + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + oldCheckRedirect := http.DefaultClient.CheckRedirect + http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + + err = try.GetRequest("http://127.0.0.1:8000/test/foo/", 1*time.Second, try.StatusCodeIs(http.StatusMovedPermanently)) + c.Assert(err, checker.IsNil) + + http.DefaultClient.CheckRedirect = oldCheckRedirect +} + +func (s *SimpleSuite) TestKeepTrailingSlash(c *check.C) { + file := s.adaptFile(c, "fixtures/keep_trailing_slash.toml", struct { + KeepTrailingSlash bool + }{true}) + defer os.Remove(file) + + cmd, output := s.traefikCmd(withConfigFile(file)) + defer output(c) + + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + oldCheckRedirect := http.DefaultClient.CheckRedirect + http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + + err = try.GetRequest("http://127.0.0.1:8000/test/foo/", 1*time.Second, try.StatusCodeIs(http.StatusNotFound)) + c.Assert(err, checker.IsNil) + + http.DefaultClient.CheckRedirect = oldCheckRedirect +} diff --git a/integration/fixtures/keep_trailing_slash.toml b/integration/fixtures/keep_trailing_slash.toml new file mode 100644 index 000000000..9d9079814 --- /dev/null +++ b/integration/fixtures/keep_trailing_slash.toml @@ -0,0 +1,23 @@ +defaultEntryPoints = ["http"] + +keepTrailingSlash = {{ .KeepTrailingSlash }} +[entryPoints] + [entryPoints.http] + address = ":8000" + +logLevel = "DEBUG" + +[file] + +# rules +[backends] + [backends.backend1] + [backends.backend1.servers.server1] + url = "http://172.17.0.2:80" + weight = 1 + +[frontends] + [frontends.frontend1] + backend = "backend1" + [frontends.frontend1.routes.test_1] + rule = "Path:/test/foo" diff --git a/server/server.go b/server/server.go index 74c3beeb4..a4ab46b37 100644 --- a/server/server.go +++ b/server/server.go @@ -627,7 +627,7 @@ func buildProxyProtocolListener(entryPoint *configuration.EntryPoint, listener n func (s *Server) buildInternalRouter(entryPointName string) *mux.Router { internalMuxRouter := mux.NewRouter() - internalMuxRouter.StrictSlash(true) + internalMuxRouter.StrictSlash(!s.globalConfiguration.KeepTrailingSlash) internalMuxRouter.SkipClean(true) if entryPoint, ok := s.entryPoints[entryPointName]; ok && entryPoint.InternalRouter != nil { diff --git a/server/server_configuration.go b/server/server_configuration.go index 3d224e1c4..2646505f8 100644 --- a/server/server_configuration.go +++ b/server/server_configuration.go @@ -633,7 +633,7 @@ func buildDefaultCertificate(defaultCertificate *traefiktls.Certificate) (*tls.C func (s *Server) buildDefaultHTTPRouter() *mux.Router { rt := mux.NewRouter() rt.NotFoundHandler = s.wrapHTTPHandlerWithAccessLog(http.HandlerFunc(http.NotFound), "backend not found") - rt.StrictSlash(true) + rt.StrictSlash(!s.globalConfiguration.KeepTrailingSlash) rt.SkipClean(true) return rt }