From e73dd31619fa3dfac95525c024cecd1e492a367a Mon Sep 17 00:00:00 2001 From: Tristan Colgate-McFarlane Date: Wed, 11 Aug 2021 16:10:12 +0100 Subject: [PATCH] redirect: fix comparison when explicit port request and implicit redirect port Co-authored-by: Julien Salleyron Co-authored-by: Ludovic Fernandez --- pkg/middlewares/redirect/redirect.go | 42 ++++------------- pkg/middlewares/redirect/redirect_regex.go | 34 +++++++++++++- pkg/middlewares/redirect/redirect_scheme.go | 45 ++++++++++++++++++- .../redirect/redirect_scheme_test.go | 15 ++++++- 4 files changed, 98 insertions(+), 38 deletions(-) diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go index e58422315..d2783d1bc 100644 --- a/pkg/middlewares/redirect/redirect.go +++ b/pkg/middlewares/redirect/redirect.go @@ -4,13 +4,17 @@ import ( "net/http" "net/url" "regexp" - "strings" "github.com/opentracing/opentracing-go/ext" "github.com/traefik/traefik/v2/pkg/tracing" "github.com/vulcand/oxy/utils" ) +const ( + schemeHTTP = "http" + schemeHTTPS = "https" +) + type redirect struct { next http.Handler regex *regexp.Regexp @@ -18,10 +22,11 @@ type redirect struct { permanent bool errHandler utils.ErrorHandler name string + rawURL func(*http.Request) string } // New creates a Redirect middleware. -func newRedirect(next http.Handler, regex, replacement string, permanent bool, name string) (http.Handler, error) { +func newRedirect(next http.Handler, regex, replacement string, permanent bool, rawURL func(*http.Request) string, name string) (http.Handler, error) { re, err := regexp.Compile(regex) if err != nil { return nil, err @@ -34,6 +39,7 @@ func newRedirect(next http.Handler, regex, replacement string, permanent bool, n errHandler: utils.DefaultHandler, next: next, name: name, + rawURL: rawURL, }, nil } @@ -42,7 +48,7 @@ func (r *redirect) GetTracingInformation() (string, ext.SpanKindEnum) { } func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - oldURL := rawURL(req) + oldURL := r.rawURL(req) // If the Regexp doesn't match, skip to the next handler. if !r.regex.MatchString(oldURL) { @@ -98,33 +104,3 @@ func (m *moveHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { http.Error(rw, err.Error(), http.StatusInternalServerError) } } - -func rawURL(req *http.Request) string { - scheme := "http" - host := req.Host - port := "" - uri := req.RequestURI - - schemeRegex := `^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$` - re, _ := regexp.Compile(schemeRegex) - if re.Match([]byte(req.RequestURI)) { - match := re.FindStringSubmatch(req.RequestURI) - scheme = match[1] - - if len(match[2]) > 0 { - host = match[2] - } - - if len(match[3]) > 0 { - port = match[3] - } - - uri = match[4] - } - - if req.TLS != nil { - scheme = "https" - } - - return strings.Join([]string{scheme, "://", host, port, uri}, "") -} diff --git a/pkg/middlewares/redirect/redirect_regex.go b/pkg/middlewares/redirect/redirect_regex.go index c4a6918c9..f26b5e3d4 100644 --- a/pkg/middlewares/redirect/redirect_regex.go +++ b/pkg/middlewares/redirect/redirect_regex.go @@ -3,6 +3,8 @@ package redirect import ( "context" "net/http" + "regexp" + "strings" "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/log" @@ -19,5 +21,35 @@ func NewRedirectRegex(ctx context.Context, next http.Handler, conf dynamic.Redir logger.Debug("Creating middleware") logger.Debugf("Setting up redirection from %s to %s", conf.Regex, conf.Replacement) - return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, name) + return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, rawURL, name) +} + +func rawURL(req *http.Request) string { + scheme := schemeHTTP + host := req.Host + port := "" + uri := req.RequestURI + + schemeRegex := `^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$` + re, _ := regexp.Compile(schemeRegex) + if re.Match([]byte(req.RequestURI)) { + match := re.FindStringSubmatch(req.RequestURI) + scheme = match[1] + + if len(match[2]) > 0 { + host = match[2] + } + + if len(match[3]) > 0 { + port = match[3] + } + + uri = match[4] + } + + if req.TLS != nil { + scheme = schemeHTTPS + } + + return strings.Join([]string{scheme, "://", host, port, uri}, "") } diff --git a/pkg/middlewares/redirect/redirect_scheme.go b/pkg/middlewares/redirect/redirect_scheme.go index e83b20136..36bcb25fd 100644 --- a/pkg/middlewares/redirect/redirect_scheme.go +++ b/pkg/middlewares/redirect/redirect_scheme.go @@ -3,7 +3,10 @@ package redirect import ( "context" "errors" + "net" "net/http" + "regexp" + "strings" "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/log" @@ -26,9 +29,47 @@ func NewRedirectScheme(ctx context.Context, next http.Handler, conf dynamic.Redi } port := "" - if len(conf.Port) > 0 && !(conf.Scheme == "http" && conf.Port == "80" || conf.Scheme == "https" && conf.Port == "443") { + if len(conf.Port) > 0 && !(conf.Scheme == schemeHTTP && conf.Port == "80" || conf.Scheme == schemeHTTPS && conf.Port == "443") { port = ":" + conf.Port } - return newRedirect(next, schemeRedirectRegex, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, name) + return newRedirect(next, schemeRedirectRegex, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, rawURLScheme, name) +} + +func rawURLScheme(req *http.Request) string { + scheme := schemeHTTP + host, port, err := net.SplitHostPort(req.Host) + if err != nil { + host = req.Host + } else { + port = ":" + port + } + uri := req.RequestURI + + schemeRegex := `^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$` + re, _ := regexp.Compile(schemeRegex) + if re.Match([]byte(req.RequestURI)) { + match := re.FindStringSubmatch(req.RequestURI) + scheme = match[1] + + if len(match[2]) > 0 { + host = match[2] + } + + if len(match[3]) > 0 { + port = match[3] + } + + uri = match[4] + } + + if req.TLS != nil { + scheme = schemeHTTPS + } + + if scheme == schemeHTTP && port == ":80" || scheme == schemeHTTPS && port == ":443" || port == "" { + port = "" + } + + return strings.Join([]string{scheme, "://", host, port, uri}, "") } diff --git a/pkg/middlewares/redirect/redirect_scheme_test.go b/pkg/middlewares/redirect/redirect_scheme_test.go index 4b38ec8e9..710a681ee 100644 --- a/pkg/middlewares/redirect/redirect_scheme_test.go +++ b/pkg/middlewares/redirect/redirect_scheme_test.go @@ -127,8 +127,18 @@ func TestRedirectSchemeHandler(t *testing.T) { Port: "80", }, url: "http://foo:80", - expectedURL: "http://foo", - expectedStatus: http.StatusFound, + expectedURL: "http://foo:80", + expectedStatus: http.StatusOK, + }, + { + desc: "to HTTPS 443", + config: dynamic.RedirectScheme{ + Scheme: "https", + Port: "443", + }, + url: "https://foo:443", + expectedURL: "https://foo:443", + expectedStatus: http.StatusOK, }, { desc: "HTTP to wss", @@ -248,6 +258,7 @@ func TestRedirectSchemeHandler(t *testing.T) { if test.method != "" { method = test.method } + req := httptest.NewRequest(method, test.url, nil) for k, v := range test.headers {