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
|
2020-08-21 03:19:13 +02:00
|
|
|
// matched Applications through the "provided cases".
|
2020-08-17 20:53:17 +02:00
|
|
|
//
|
2020-08-21 03:19:13 +02:00
|
|
|
// The cases are filtered in order of their registration.
|
2020-08-17 20:53:17 +02:00
|
|
|
//
|
2020-08-21 03:19:13 +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
|
|
|
//
|
2020-08-21 03:19:13 +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
|
|
|
//
|
2020-08-21 03:19:13 +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.
|
2020-08-19 04:32:21 +02:00
|
|
|
//
|
|
|
|
// 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
|
2020-08-19 04:32:21 +02:00
|
|
|
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-19 04:32:21 +02:00
|
|
|
}
|
2020-08-18 03:17:53 +02:00
|
|
|
|
2020-08-19 04:32:21 +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
|
|
|
|
2020-08-19 04:32:21 +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) {
|
2020-08-19 04:32:21 +02:00
|
|
|
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() {
|
2020-08-19 04:32:21 +02:00
|
|
|
// 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
|
|
|
|
}
|