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:
parent
ef9b79f85c
commit
e73dd31619
4 changed files with 98 additions and 38 deletions
|
@ -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}, "")
|
|
||||||
}
|
|
||||||
|
|
|
@ -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}, "")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}, "")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue