redirect: fix comparison when explicit port request and implicit redirect port

Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
This commit is contained in:
Tristan Colgate-McFarlane 2021-08-11 16:10:12 +01:00 committed by GitHub
parent ef9b79f85c
commit e73dd31619
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 38 deletions

View file

@ -4,13 +4,17 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"regexp" "regexp"
"strings"
"github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/ext"
"github.com/traefik/traefik/v2/pkg/tracing" "github.com/traefik/traefik/v2/pkg/tracing"
"github.com/vulcand/oxy/utils" "github.com/vulcand/oxy/utils"
) )
const (
schemeHTTP = "http"
schemeHTTPS = "https"
)
type redirect struct { type redirect struct {
next http.Handler next http.Handler
regex *regexp.Regexp regex *regexp.Regexp
@ -18,10 +22,11 @@ type redirect struct {
permanent bool permanent bool
errHandler utils.ErrorHandler errHandler utils.ErrorHandler
name string name string
rawURL func(*http.Request) string
} }
// New creates a Redirect middleware. // 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) re, err := regexp.Compile(regex)
if err != nil { if err != nil {
return nil, err return nil, err
@ -34,6 +39,7 @@ func newRedirect(next http.Handler, regex, replacement string, permanent bool, n
errHandler: utils.DefaultHandler, errHandler: utils.DefaultHandler,
next: next, next: next,
name: name, name: name,
rawURL: rawURL,
}, nil }, nil
} }
@ -42,7 +48,7 @@ func (r *redirect) GetTracingInformation() (string, ext.SpanKindEnum) {
} }
func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 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 the Regexp doesn't match, skip to the next handler.
if !r.regex.MatchString(oldURL) { 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) 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}, "")
}

View file

@ -3,6 +3,8 @@ package redirect
import ( import (
"context" "context"
"net/http" "net/http"
"regexp"
"strings"
"github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/log" "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.Debug("Creating middleware")
logger.Debugf("Setting up redirection from %s to %s", conf.Regex, conf.Replacement) 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}, "")
} }

View file

@ -3,7 +3,10 @@ package redirect
import ( import (
"context" "context"
"errors" "errors"
"net"
"net/http" "net/http"
"regexp"
"strings"
"github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/log"
@ -26,9 +29,47 @@ func NewRedirectScheme(ctx context.Context, next http.Handler, conf dynamic.Redi
} }
port := "" 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 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}, "")
} }

View file

@ -127,8 +127,18 @@ func TestRedirectSchemeHandler(t *testing.T) {
Port: "80", Port: "80",
}, },
url: "http://foo:80", url: "http://foo:80",
expectedURL: "http://foo", expectedURL: "http://foo:80",
expectedStatus: http.StatusFound, 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", desc: "HTTP to wss",
@ -248,6 +258,7 @@ func TestRedirectSchemeHandler(t *testing.T) {
if test.method != "" { if test.method != "" {
method = test.method method = test.method
} }
req := httptest.NewRequest(method, test.url, nil) req := httptest.NewRequest(method, test.url, nil)
for k, v := range test.headers { for k, v := range test.headers {