iris/apps/switch.go

159 lines
4.1 KiB
Go
Raw Normal View History

2020-08-17 20:53:17 +02:00
package apps
import (
2020-08-18 03:17:53 +02:00
"strings"
2020-08-17 20:53:17 +02:00
"github.com/kataras/iris/v12"
)
// Switch returns a new Application
// with the sole purpose of routing the
// matched Applications through the "provided cases".
2020-08-17 20:53:17 +02:00
//
// The cases are filtered in order of their registration.
2020-08-17 20:53:17 +02:00
//
// Example Code:
// switcher := Switch(Hosts{
// "mydomain.com": app,
// "test.mydomain.com": testSubdomainApp,
// "otherdomain.com": "appName",
// })
// switcher.Listen(":80")
2020-08-17 20:53:17 +02:00
//
// Note that this is NOT an alternative for a load balancer.
// The filters are executed by registration order and a matched Application
// handles the request, that's all it does.
2020-08-17 20:53:17 +02:00
//
// The returned Switch Iris Application can register routes that will run
// when neither of the registered Applications is responsible
// to handle the incoming request against the provided filters.
// The returned Switch Iris Application can also register custom error code handlers,
// e.g. to inject the 404 on not responsible Application was found.
2020-08-17 20:53:17 +02:00
// It can also be wrapped with its `WrapRouter` method,
// which is really useful for logging and statistics.
//
// Wrap with the `Join` slice to pass
// more than one provider at the same time.
func Switch(provider SwitchProvider, options ...SwitchOption) *iris.Application {
cases := provider.GetSwitchCases()
if len(cases) == 0 {
panic("iris: switch: empty cases")
2020-08-17 20:53:17 +02:00
}
2020-08-18 03:17:53 +02:00
var friendlyAddrs []string
if fp, ok := provider.(FriendlyNameProvider); ok {
if friendlyName := fp.GetFriendlyName(); friendlyName != "" {
friendlyAddrs = append(friendlyAddrs, friendlyName)
2020-08-17 20:53:17 +02:00
}
}
2020-08-18 03:17:53 +02:00
opts := DefaultSwitchOptions()
for _, opt := range options {
if opt == nil {
continue
2020-08-18 03:17:53 +02:00
}
2020-08-17 20:53:17 +02:00
opt.Apply(&opts)
2020-08-17 20:53:17 +02:00
}
app := iris.New()
// Try to build the cases apps on app.Build/Listen/Run so
// end-developers don't worry about it.
app.OnBuild = func() error {
for _, c := range cases {
if err := c.App.Build(); err != nil {
return err
}
}
return nil
}
// If we have a request to support
// middlewares in that switcher app then
// we can use app.Get("{p:path}"...) instead.
app.UseRouter(func(ctx iris.Context) {
for _, c := range cases {
if c.Filter(ctx) {
w := ctx.ResponseWriter()
r := ctx.Request()
for _, reqMod := range opts.RequestModifiers {
reqMod(r)
}
c.App.ServeHTTP(w, r)
2020-08-17 20:53:17 +02:00
// if c.App.Downgraded() {
// c.App.ServeHTTP(w, r)
2020-08-17 20:53:17 +02:00
// } else {
// Note(@kataras): don't ever try something like that;
// the context pool is the switcher's one.
// ctx.SetApplication(c.App)
// c.App.ServeHTTPC(ctx)
// ctx.SetApplication(app)
// }
return
}
}
// let the "switch app" handle it or fire a custom 404 error page,
// next is the switch app's router.
ctx.Next()
})
2020-08-18 03:17:53 +02:00
// Configure the switcher's supervisor.
app.ConfigureHost(func(su *iris.Supervisor) {
if len(friendlyAddrs) > 0 {
su.FriendlyAddr = strings.Join(friendlyAddrs, ", ")
}
})
2020-08-17 20:53:17 +02:00
return app
}
type (
// SwitchCase contains the filter
// and the matched Application instance.
SwitchCase struct {
2020-08-18 03:17:53 +02:00
Filter iris.Filter // Filter runs against the Switcher.
App *iris.Application // App is the main target application responsible to handle the request.
2020-08-17 20:53:17 +02:00
}
// A SwitchProvider should return the switch cases.
// It's an interface instead of a direct slice because
// we want to make available different type of structures
// without wrapping.
SwitchProvider interface {
GetSwitchCases() []SwitchCase
}
2020-08-18 03:17:53 +02:00
// FriendlyNameProvider can be optionally implemented by providers
// to customize the Switcher's Supervisor.FriendlyAddr field (Startup log).
FriendlyNameProvider interface {
GetFriendlyName() string
}
2020-08-17 20:53:17 +02:00
// Join returns a new slice which joins different type of switch cases.
Join []SwitchProvider
)
var _ SwitchProvider = SwitchCase{}
// GetSwitchCases completes the SwitchProvider, it returns itself.
func (sc SwitchCase) GetSwitchCases() []SwitchCase {
return []SwitchCase{sc}
}
var _ SwitchProvider = Join{}
// GetSwitchCases completes the switch provider.
func (j Join) GetSwitchCases() (cases []SwitchCase) {
for _, p := range j {
if p == nil {
continue
}
cases = append(cases, p.GetSwitchCases()...)
}
return
}