From 2fb75e754e04962a4708a9e1b2cb7acf5881712e Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Mon, 2 Oct 2023 07:46:08 -0700 Subject: echo: don't prefix www. to IPs --- echo/echo_default.go | 2 +- echo/middleware/https_redirect.go | 75 ++++++++++++++++++++++++++++++++++++++ echo/middleware/redirect.go | 75 -------------------------------------- echo/middleware/www_redirect.go | 77 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 76 deletions(-) create mode 100644 echo/middleware/https_redirect.go delete mode 100644 echo/middleware/redirect.go create mode 100644 echo/middleware/www_redirect.go diff --git a/echo/echo_default.go b/echo/echo_default.go index 067e52f..916cbd0 100644 --- a/echo/echo_default.go +++ b/echo/echo_default.go @@ -306,7 +306,7 @@ func (w *EchoWrapper) configureRedirects(c *EchoConfig, bindings *AddressPortCon })) if c.RedirectToWWW { - w.Use(middleware.WWWRedirectWithConfig(middleware.RedirectConfig{ + w.Use(glmw.WWWRedirectWithConfig(glmw.WWWRedirectConfig{ Skipper: metricsSkipper, })) } diff --git a/echo/middleware/https_redirect.go b/echo/middleware/https_redirect.go new file mode 100644 index 0000000..311f1c9 --- /dev/null +++ b/echo/middleware/https_redirect.go @@ -0,0 +1,75 @@ +package middleware + +/* +HTTP to HTTPS Redirect Middleware + +This is a duplicate of existing functionality in Echo because the Echo default +middleware doesn't support redirecting to a different HTTPS port which is +needed in dev environments and some prod environments where the server runs on +an off-port. +*/ + +import ( + "fmt" + "net" + "net/http" + + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" +) + +type HTTPSRedirectConfig struct { + Skipper middleware.Skipper + Port int + Code int +} + +var DefaultHTTPSRedirectConfig = HTTPSRedirectConfig{ + Skipper: middleware.DefaultSkipper, + Port: 443, + Code: http.StatusMovedPermanently, +} + +func HTTPSRedirect() echo.MiddlewareFunc { + return HTTPSRedirectWithConfig(DefaultHTTPSRedirectConfig) +} + +func HTTPSRedirectWithConfig(config HTTPSRedirectConfig) echo.MiddlewareFunc { + if config.Skipper == nil { + config.Skipper = DefaultHTTPSRedirectConfig.Skipper + } + if config.Code == 0 { + config.Code = DefaultHTTPSRedirectConfig.Code + } + if config.Port == 0 { + config.Port = DefaultHTTPSRedirectConfig.Port + } + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if config.Skipper(c) || c.Scheme() == "https" { + return next(c) + } + + var err error + req := c.Request() + + host := req.URL.Host + if host == "" { + host, _, err = net.SplitHostPort(req.Host) + if err != nil { + return echo.ErrBadRequest + } + } + + // Browers assume 443 if it's an https request, otherwise the port + // needs to be specified in the URL + redir := fmt.Sprintf("https://%s%s", host, req.RequestURI) + if config.Port != 443 { + redir = fmt.Sprintf("https://%s:%d%s", host, config.Port, req.RequestURI) + } + + return c.Redirect(config.Code, redir) + } + } +} diff --git a/echo/middleware/redirect.go b/echo/middleware/redirect.go deleted file mode 100644 index 134bfee..0000000 --- a/echo/middleware/redirect.go +++ /dev/null @@ -1,75 +0,0 @@ -package middleware - -/* -HTTP to HTTPS Redirect Middleware - -This is a duplicate of existing functionality in Echo because the Echo default -middleware doesn't support redirecting to a different HTTPS port which is -needed in dev environments and some prod environments where the server runs on -an off-port. -*/ - -import ( - "fmt" - "net" - "net/http" - - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" -) - -type HTTPSRedirectConfig struct { - Skipper middleware.Skipper - Port int - Code int -} - -var DefaultHTTPSRedirectConfig = HTTPSRedirectConfig{ - Skipper: middleware.DefaultSkipper, - Port: 443, - Code: http.StatusMovedPermanently, -} - -func HTTPSRedirect() echo.MiddlewareFunc { - return HTTPSRedirectWithConfig(DefaultHTTPSRedirectConfig) -} - -func HTTPSRedirectWithConfig(config HTTPSRedirectConfig) echo.MiddlewareFunc { - if config.Skipper == nil { - config.Skipper = DefaultHTTPSRedirectConfig.Skipper - } - if config.Code == 0 { - config.Code = DefaultHTTPSRedirectConfig.Code - } - if config.Port == 0 { - config.Port = DefaultHTTPSRedirectConfig.Port - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) || c.Scheme() == "https" { - return next(c) - } - - var err error - req := c.Request() - - host := req.URL.Host - if host == "" { - host, _, err = net.SplitHostPort(req.Host) - if err != nil { - return echo.ErrBadRequest - } - } - - // Browers assume 443 if it's an https request, otherwise the port - // needs to be specified in the URL - redir := fmt.Sprintf("https://%s%s", host, req.RequestURI) - if config.Port != 443 { - redir = fmt.Sprintf("https://%s:%d%s", host, config.Port, req.RequestURI) - } - - return c.Redirect(http.StatusMovedPermanently, redir) - } - } -} diff --git a/echo/middleware/www_redirect.go b/echo/middleware/www_redirect.go new file mode 100644 index 0000000..905a5cf --- /dev/null +++ b/echo/middleware/www_redirect.go @@ -0,0 +1,77 @@ +package middleware + +import ( + "fmt" + "net" + "net/http" + "strings" + + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" +) + +/* +WWW Redirect Middleware + +This is a duplicate of existing functionality in Echo. This exists +because we don't want to prefix www to a request that is an IP address +only, only requests that use hostnames. +*/ + +type WWWRedirectConfig struct { + Skipper middleware.Skipper + Code int +} + +var DefaultWWWRedirectConfig = WWWRedirectConfig{ + Skipper: middleware.DefaultSkipper, + Code: http.StatusMovedPermanently, +} + +func WWWRedirect() echo.MiddlewareFunc { + return WWWRedirectWithConfig(DefaultWWWRedirectConfig) +} + +func WWWRedirectWithConfig(config WWWRedirectConfig) echo.MiddlewareFunc { + if config.Skipper == nil { + config.Skipper = DefaultWWWRedirectConfig.Skipper + } + if config.Code == 0 { + config.Code = DefaultWWWRedirectConfig.Code + } + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + req := c.Request() + + if config.Skipper(c) || strings.HasPrefix(req.Host, "www.") { + return next(c) + } + + host, _, err := net.SplitHostPort(req.Host) + if err != nil { + return echo.ErrBadRequest + } + + // Never prefix an IP with www + // + // We have to parse the hostname for something that looks like an IP + // because the golang http server removes the header from the header + // map and there's no good way to unambiguously determine if this was + // a raw IP request or if a hostname was submitted. + // + // TLS connections could use the req.TLS.ServerName property which + // should have the correct hostname but there's no such thing for HTTP. + // Instead we just try to parse anything that makes it to this point + // because it should be a small one-time cost for a few requests. + if ip := net.ParseIP(host); ip != nil { + return next(c) + } + + return c.Redirect( + config.Code, + fmt.Sprintf("https://www.%s%s", req.Host, req.RequestURI), + ) + } + } +} -- cgit v1.2.3