Redirection: permanent move option.

This commit is contained in:
Ludovic Fernandez 2018-01-31 19:10:04 +01:00 committed by Traefiker
parent c944d203fb
commit 58d6681824
83 changed files with 622 additions and 8611 deletions

View file

@ -31,6 +31,7 @@ import (
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/middlewares/accesslog"
mauth "github.com/containous/traefik/middlewares/auth"
"github.com/containous/traefik/middlewares/redirect"
"github.com/containous/traefik/middlewares/tracing"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/safe"
@ -51,10 +52,6 @@ import (
"golang.org/x/net/http2"
)
const (
defaultRedirectRegex = `^(?:https?:\/\/)?([\w\._-]+)(?::\d+)?(.*)$`
)
var (
httpServerLogger = stdlog.New(log.WriterLevel(logrus.DebugLevel), "", 0)
)
@ -1117,9 +1114,10 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
rewrite, err := s.buildRedirectHandler(entryPointName, frontend.Redirect)
if err != nil {
log.Errorf("Error creating Frontend Redirect: %v", err)
} else {
n.Use(s.wrapNegroniHandlerWithAccessLog(rewrite, fmt.Sprintf("frontend redirect for %s", frontendName)))
log.Debugf("Frontend %s redirect created", frontendName)
}
n.Use(s.wrapNegroniHandlerWithAccessLog(rewrite, fmt.Sprintf("frontend redirect for %s", frontendName)))
log.Debugf("Frontend %s redirect created", frontendName)
}
if len(frontend.BasicAuth) > 0 {
@ -1282,57 +1280,25 @@ func (s *Server) wireFrontendBackend(serverRoute *serverRoute, handler http.Hand
serverRoute.route.Handler(handler)
}
func (s *Server) buildRedirectHandler(srcEntryPointName string, redirect *types.Redirect) (*middlewares.Rewrite, error) {
func (s *Server) buildRedirectHandler(srcEntryPointName string, opt *types.Redirect) (negroni.Handler, error) {
// entry point redirect
if len(redirect.EntryPoint) > 0 {
return s.buildEntryPointRedirect(srcEntryPointName, redirect.EntryPoint)
if len(opt.EntryPoint) > 0 {
entryPoint := s.globalConfiguration.EntryPoints[opt.EntryPoint]
if entryPoint == nil {
return nil, fmt.Errorf("unknown target entrypoint %q", srcEntryPointName)
}
log.Debugf("Creating entry point redirect %s -> %s", srcEntryPointName, opt.EntryPoint)
return redirect.NewEntryPointHandler(entryPoint, opt.Permanent)
}
// regex redirect
rewrite, err := middlewares.NewRewrite(redirect.Regex, redirect.Replacement, true)
redirection, err := redirect.NewRegexHandler(opt.Regex, opt.Replacement, opt.Permanent)
if err != nil {
return nil, err
}
log.Debugf("Creating entryPoint redirect %s -> %s -> %s", srcEntryPointName, redirect.Regex, redirect.Replacement)
log.Debugf("Creating regex redirect %s -> %s -> %s", srcEntryPointName, opt.Regex, opt.Replacement)
return rewrite, nil
}
func (s *Server) buildEntryPointRedirect(srcEntryPointName string, redirectEntryPoint string) (*middlewares.Rewrite, error) {
regex, replacement, err := s.buildRedirect(redirectEntryPoint)
if err != nil {
return nil, err
}
rewrite, err := middlewares.NewRewrite(regex, replacement, true)
if err != nil {
// Impossible case because error is always nil
return nil, err
}
log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", srcEntryPointName, redirectEntryPoint, regex, replacement)
return rewrite, nil
}
func (s *Server) buildRedirect(entryPointName string) (string, string, error) {
entryPoint := s.globalConfiguration.EntryPoints[entryPointName]
if entryPoint == nil {
return "", "", fmt.Errorf("unknown target entrypoint %q", entryPointName)
}
exp := regexp.MustCompile(`(:\d+)`)
match := exp.FindStringSubmatch(entryPoint.Address)
if len(match) == 0 {
return "", "", fmt.Errorf("bad Address format %q", entryPoint.Address)
}
var protocol = "http"
if s.globalConfiguration.EntryPoints[entryPointName].TLS != nil {
protocol = "https"
}
replacement := protocol + "://$1" + match[0] + "$2"
return defaultRedirectRegex, replacement, nil
return redirection, nil
}
func (s *Server) buildDefaultHTTPRouter() *mux.Router {
@ -1440,7 +1406,7 @@ func registerMetricClients(metricsConfig *types.Metrics) metrics.Registry {
return metrics.NewVoidRegistry()
}
registries := []metrics.Registry{}
var registries []metrics.Registry
if metricsConfig.Prometheus != nil {
registries = append(registries, metrics.RegisterPrometheus(metricsConfig.Prometheus))
log.Debug("Configured Prometheus metrics")

View file

@ -923,7 +923,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
}
}
func TestBuildEntryPointRedirect(t *testing.T) {
func TestBuildRedirectHandler(t *testing.T) {
srv := Server{
globalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
@ -1016,133 +1016,6 @@ func TestBuildEntryPointRedirect(t *testing.T) {
}
}
func TestServerBuildEntryPointRedirect(t *testing.T) {
srv := Server{
globalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
"http": &configuration.EntryPoint{Address: ":80"},
"https": &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}},
},
},
}
testCases := []struct {
desc string
srcEntryPointName string
redirectEntryPoint string
url string
expectedURL string
errorExpected bool
}{
{
desc: "existing redirect entry point",
srcEntryPointName: "http",
redirectEntryPoint: "https",
url: "http://foo:80",
expectedURL: "https://foo:443",
},
{
desc: "non-existing redirect entry point",
srcEntryPointName: "http",
redirectEntryPoint: "foo",
url: "http://foo:80",
errorExpected: true,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
rewrite, err := srv.buildEntryPointRedirect(test.srcEntryPointName, test.redirectEntryPoint)
if test.errorExpected {
require.Error(t, err)
} else {
require.NoError(t, err)
recorder := httptest.NewRecorder()
r := testhelpers.MustNewRequest(http.MethodGet, test.url, nil)
rewrite.ServeHTTP(recorder, r, nil)
location, err := recorder.Result().Location()
require.NoError(t, err)
assert.Equal(t, test.expectedURL, location.String())
}
})
}
}
func TestServerBuildRedirect(t *testing.T) {
testCases := []struct {
desc string
globalConfiguration configuration.GlobalConfiguration
redirectEntryPointName string
expectedReplacement string
errorExpected bool
}{
{
desc: "Redirect endpoint http to https with HTTPS protocol",
redirectEntryPointName: "https",
globalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
"http": &configuration.EntryPoint{Address: ":80"},
"https": &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}},
},
},
expectedReplacement: "https://$1:443$2",
},
{
desc: "Redirect endpoint http to http02 with HTTP protocol",
redirectEntryPointName: "http02",
globalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
"http": &configuration.EntryPoint{Address: ":80"},
"http02": &configuration.EntryPoint{Address: ":88"},
},
},
expectedReplacement: "http://$1:88$2",
},
{
desc: "Redirect endpoint to non-existent entry point",
redirectEntryPointName: "foobar",
globalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
"http": &configuration.EntryPoint{Address: ":80"},
"http02": &configuration.EntryPoint{Address: ":88"},
},
},
errorExpected: true,
},
{
desc: "Redirect endpoint to an entry point with a malformed address",
redirectEntryPointName: "http02",
globalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
"http": &configuration.EntryPoint{Address: ":80"},
"http02": &configuration.EntryPoint{Address: "88"},
},
},
errorExpected: true,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
srv := Server{globalConfiguration: test.globalConfiguration}
_, replacement, err := srv.buildRedirect(test.redirectEntryPointName)
require.Equal(t, test.errorExpected, err != nil, "Expected an error but don't have error, or Expected no error but have an error: %v", err)
assert.Equal(t, test.expectedReplacement, replacement, "build redirect does not return the right replacement pattern")
})
}
}
func buildDynamicConfig(dynamicConfigBuilders ...func(*types.Configuration)) *types.Configuration {
config := &types.Configuration{
Frontends: make(map[string]*types.Frontend),