package mid import ( "context" "net/http" "geeks-accelerator/oss/saas-starter-kit/internal/platform/web" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) 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 { scheme = "https" } } // Redirects all domain name alternatives to the primary hostname. if host != config.DomainName { host = config.DomainName ok = true } 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 }