1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-07-03 00:58:13 +02:00

finish redirect middlewares

This commit is contained in:
Lee Brown
2019-07-12 11:41:41 -08:00
parent df86310a8b
commit 0efe444c05
7 changed files with 337 additions and 7 deletions

View File

@ -0,0 +1,198 @@
package mid
import (
"context"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"net/http"
)
type (
// Skipper defines a function to skip middleware. Returning true skips processing
// the middleware.
Skipper func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) bool
// RedirectConfig defines the config for Redirect middleware.
RedirectConfig struct {
// Skipper defines a function to skip middleware.
Skipper
// Status code to be used when redirecting the request.
// Optional. Default value http.StatusMovedPermanently.
Code int
}
// DomainNameRedirectConfig defines the details needed to apply redirects based on domain names.
DomainNameRedirectConfig struct {
RedirectConfig
DomainName string
HTTPSEnabled bool
}
// redirectLogic represents a function that given a scheme, host and uri
// can both: 1) determine if redirect is needed (will set ok accordingly) and
// 2) return the appropriate redirect url.
redirectLogic func(scheme, host, uri string) (ok bool, url string)
)
const www = "www."
// DefaultRedirectConfig is the default Redirect middleware config.
var DefaultRedirectConfig = RedirectConfig{
Skipper: DefaultSkipper,
Code: http.StatusMovedPermanently,
}
// DefaultSkipper returns false which processes the middleware.
func DefaultSkipper(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) bool {
return false
}
// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config.
// See `HTTPSRedirect()`.
func DomainNameRedirect(config DomainNameRedirectConfig) web.Middleware {
return redirect(config.RedirectConfig, func(scheme, host, uri string) (ok bool, url string) {
// Redirects http requests to https.
if config.HTTPSEnabled {
if ok = scheme != "https"; ok {
url = "https://" + host + uri
scheme = "https"
}
}
// Redirects all domain name alternatives to the primary hostname.
if host != config.DomainName {
host = config.DomainName
}
url = scheme + "://" + host + uri
return
})
}
// HTTPSRedirect redirects http requests to https.
// For example, http://geeksinthewoods.com will be redirect to https://geeksinthewoods.com.
func HTTPSRedirect() web.Middleware {
return HTTPSRedirectWithConfig(DefaultRedirectConfig)
}
// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config.
// See `HTTPSRedirect()`.
func HTTPSRedirectWithConfig(config RedirectConfig)web.Middleware {
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
if ok = scheme != "https"; ok {
url = "https://" + host + uri
}
return
})
}
// HTTPSWWWRedirect redirects http requests to https www.
// For example, http://geeksinthewoods.com will be redirect to https://www.geeksinthewoods.com.
func HTTPSWWWRedirect() web.Middleware {
return HTTPSWWWRedirectWithConfig(DefaultRedirectConfig)
}
// HTTPSWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
// See `HTTPSWWWRedirect()`.
func HTTPSWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
if ok = scheme != "https" && host[:3] != www; ok {
url = "https://www." + host + uri
}
return
})
}
// HTTPSNonWWWRedirect redirects http requests to https non www.
// For example, http://www.geeksinthewoods.com will be redirect to https://geeksinthewoods.com.
func HTTPSNonWWWRedirect() web.Middleware {
return HTTPSNonWWWRedirectWithConfig(DefaultRedirectConfig)
}
// HTTPSNonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
// See `HTTPSNonWWWRedirect()`.
func HTTPSNonWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
if ok = scheme != "https"; ok {
if host[:3] == www {
host = host[4:]
}
url = "https://" + host + uri
}
return
})
}
// WWWRedirect redirects non www requests to www.
// For example, http://geeksinthewoods.com will be redirect to http://www.geeksinthewoods.com.
func WWWRedirect() web.Middleware {
return WWWRedirectWithConfig(DefaultRedirectConfig)
}
// WWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
// See `WWWRedirect()`.
func WWWRedirectWithConfig(config RedirectConfig) web.Middleware {
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
if ok = host[:3] != www; ok {
url = scheme + "://www." + host + uri
}
return
})
}
// NonWWWRedirect redirects www requests to non www.
// For example, http://www.geeksinthewoods.com will be redirect to http://geeksinthewoods.com.
func NonWWWRedirect() web.Middleware {
return NonWWWRedirectWithConfig(DefaultRedirectConfig)
}
// NonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
// See `NonWWWRedirect()`.
func NonWWWRedirectWithConfig(config RedirectConfig) web.Middleware {
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
if ok = host[:3] == www; ok {
url = scheme + "://" + host[4:] + uri
}
return
})
}
func redirect(config RedirectConfig, cb redirectLogic) web.Middleware {
if config.Skipper == nil {
config.Skipper = DefaultSkipper
}
if config.Code == 0 {
config.Code = DefaultRedirectConfig.Code
}
// This is the actual middleware function to be executed.
f := func(after web.Handler) web.Handler {
h := func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
span, ctx := tracer.StartSpanFromContext(ctx, "internal.mid.redirect")
defer span.Finish()
if config.Skipper(ctx, w, r, params) {
return after(ctx, w, r, params)
}
scheme := web.RequestScheme(r)
if ok, url := cb(scheme, r.Host, r.RequestURI); ok {
http.Redirect(w, r, url, config.Code)
return nil
}
return after(ctx, w, r, params)
}
return h
}
return f
}

View File

@ -2,6 +2,7 @@ package web
import (
"encoding/json"
"net"
"net/http"
"reflect"
"strings"
@ -16,6 +17,22 @@ import (
en_translations "gopkg.in/go-playground/validator.v9/translations/en"
)
// Headers
const (
HeaderUpgrade = "Upgrade"
HeaderXForwardedFor = "X-Forwarded-For"
HeaderXForwardedProto = "X-Forwarded-Proto"
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
HeaderXForwardedSsl = "X-Forwarded-Ssl"
HeaderXUrlScheme = "X-Url-Scheme"
HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
HeaderXRealIP = "X-Real-IP"
HeaderXRequestID = "X-Request-ID"
HeaderXRequestedWith = "X-Requested-With"
HeaderServer = "Server"
HeaderOrigin = "Origin"
)
// validate holds the settings and caches for validating request struct values.
var validate = validator.New()
@ -154,3 +171,44 @@ func RequestIsJson(r *http.Request) bool {
return false
}
func RequestIsTLS(r *http.Request) bool {
return r.TLS != nil
}
func RequestIsWebSocket(r *http.Request) bool {
upgrade := r.Header.Get(HeaderUpgrade)
return strings.ToLower(upgrade) == "websocket"
}
func RequestScheme(r *http.Request) string {
// Can't use `r.Request.URL.Scheme`
// See: https://groups.google.com/forum/#!topic/golang-nuts/pMUkBlQBDF0
if RequestIsTLS(r) {
return "https"
}
if scheme := r.Header.Get(HeaderXForwardedProto); scheme != "" {
return scheme
}
if scheme := r.Header.Get(HeaderXForwardedProtocol); scheme != "" {
return scheme
}
if ssl := r.Header.Get(HeaderXForwardedSsl); ssl == "on" {
return "https"
}
if scheme := r.Header.Get(HeaderXUrlScheme); scheme != "" {
return scheme
}
return "http"
}
func RequestRealIP(r *http.Request) string {
if ip := r.Header.Get(HeaderXForwardedFor); ip != "" {
return strings.Split(ip, ", ")[0]
}
if ip := r.Header.Get(HeaderXRealIP); ip != "" {
return ip
}
ra, _, _ := net.SplitHostPort(r.RemoteAddr)
return ra
}