mirror of
https://github.com/kataras/iris.git
synced 2025-12-31 16:57:04 +00:00
new apps/switch (beta)
This commit is contained in:
120
apps/switch_hosts.go
Normal file
120
apps/switch_hosts.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
type (
|
||||
// Host holds the pattern for the SwitchCase filter
|
||||
// and the Target host or application.
|
||||
Host struct {
|
||||
// Pattern is the incoming host matcher regexp or a literal.
|
||||
Pattern string
|
||||
// Target is the target Host that incoming requests will be redirected on pattern match
|
||||
// or an Application's Name that will handle the incoming request matched the Pattern.
|
||||
Target interface{} // It was a string in my initial design but let's do that interface{}, we may support more types here in the future, until generics are in, keep it interface{}.
|
||||
}
|
||||
// Hosts is a switch provider.
|
||||
// It can be used as input argument to the `Switch` function
|
||||
// to map host to existing Iris Application instances, e.g.
|
||||
// { "www.mydomain.com": "mydomainApp" } .
|
||||
// It can accept regexp as a host too, e.g.
|
||||
// { "^my.*$": "mydomainApp" } .
|
||||
Hosts []Host
|
||||
|
||||
// Good by we need order and map can't provide it for us
|
||||
// (e.g. "fallback" regexp }
|
||||
// Hosts map[string]*iris.Application
|
||||
)
|
||||
|
||||
var _ SwitchProvider = Hosts{}
|
||||
|
||||
// GetSwitchCases completes the SwitchProvider.
|
||||
// It returns a slice of SwitchCase which
|
||||
// if passed on `Switch` function, they act
|
||||
// as a router between matched domains and subdomains
|
||||
// between existing Iris Applications.
|
||||
func (hosts Hosts) GetSwitchCases() []SwitchCase {
|
||||
cases := make([]SwitchCase, 0, len(hosts))
|
||||
|
||||
for _, host := range hosts {
|
||||
cases = append(cases, SwitchCase{
|
||||
Filter: hostFilter(host.Pattern),
|
||||
App: hostApp(host),
|
||||
})
|
||||
}
|
||||
|
||||
return cases
|
||||
}
|
||||
|
||||
func hostApp(host Host) *iris.Application {
|
||||
if host.Target == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch target := host.Target.(type) {
|
||||
case context.Application:
|
||||
return target.(*iris.Application)
|
||||
case string:
|
||||
// Check if the given target is an application name, if so
|
||||
// we must not redirect (loop) we must serve the request
|
||||
// using that app.
|
||||
if targetApp, ok := context.GetApplication(target); ok {
|
||||
// It's always iris.Application so we are totally safe here.
|
||||
return targetApp.(*iris.Application)
|
||||
}
|
||||
// If it's a real host, warn the user of invalid input.
|
||||
u, err := url.Parse(target)
|
||||
if err == nil && u.IsAbs() {
|
||||
// remember, we redirect hosts, not full URLs here.
|
||||
panic(fmt.Sprintf(`iris: switch: hosts: invalid target host: "%s"`, target))
|
||||
}
|
||||
|
||||
if regex := regexp.MustCompile(host.Pattern); regex.MatchString(target) {
|
||||
panic(fmt.Sprintf(`iris: switch: hosts: loop detected between expression: "%s" and target host: "%s"`, host.Pattern, host.Target))
|
||||
}
|
||||
|
||||
return newHostRedirectApp(target, HostsRedirectCode)
|
||||
default:
|
||||
panic(fmt.Sprintf("iris: switch: hosts: invalid target type: %T", target))
|
||||
}
|
||||
}
|
||||
|
||||
func hostFilter(expr string) iris.Filter {
|
||||
regex := regexp.MustCompile(expr)
|
||||
return func(ctx iris.Context) bool {
|
||||
return regex.MatchString(ctx.Host())
|
||||
}
|
||||
}
|
||||
|
||||
// HostsRedirectCode is the default status code is used
|
||||
// to redirect a matching host to a url.
|
||||
var HostsRedirectCode = iris.StatusMovedPermanently
|
||||
|
||||
func newHostRedirectApp(targetHost string, code int) *iris.Application {
|
||||
app := iris.New()
|
||||
app.Downgrade(func(w http.ResponseWriter, r *http.Request) {
|
||||
if targetHost == context.GetHost(r) {
|
||||
// Note(@kataras):
|
||||
// this should never happen as the HostsRedirect
|
||||
// carefully checks if the expression already matched the "redirectTo"
|
||||
// to avoid the redirect loops at all.
|
||||
// iris: switch: hosts redirect: loop detected between expression: "^my.*$" and target host: "mydomain.com"
|
||||
http.Error(w, http.StatusText(iris.StatusLoopDetected), iris.StatusLoopDetected)
|
||||
return
|
||||
}
|
||||
|
||||
r.Host = targetHost
|
||||
r.URL.Host = targetHost
|
||||
|
||||
// r.URL.User = nil
|
||||
http.Redirect(w, r, r.URL.String(), code)
|
||||
})
|
||||
return app
|
||||
}
|
||||
Reference in New Issue
Block a user