diff --git a/HISTORY.md b/HISTORY.md index 39a44971..e653e25b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -359,6 +359,10 @@ Response: Other Improvements: +- New [apps](https://github.com/kataras/iris/tree/master/apps) subpackage. [Example of usage](https://github.com/kataras/iris/tree/master/_examples/routing/subdomains/redirect/multi-instances). + +![apps image example](https://user-images.githubusercontent.com/22900943/90459288-8a54f400-e109-11ea-8dea-20631975c9fc.png) + - Fix `AutoTLS` when used with `iris.TLSNoRedirect` [*](https://github.com/kataras/iris/issues/1577). The `AutoTLS` runner can be customized through the new `iris.AutoTLSNoRedirect` instead, read its go documentation. Example of having both TLS and non-TLS versions of the same application without conflicts with letsencrypt `./well-known` path: ![](https://iris-go.com/images/github/autotls-1.png) diff --git a/_examples/apidoc/yaag/go.mod b/_examples/apidoc/yaag/go.mod index e1c27c2f..e0c9281a 100644 --- a/_examples/apidoc/yaag/go.mod +++ b/_examples/apidoc/yaag/go.mod @@ -4,5 +4,5 @@ go 1.15 require ( github.com/betacraft/yaag v1.0.1-0.20200719063524-47d781406108 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd + github.com/kataras/iris/v12 vv12.1.9-0.20200817185317-589c8c6242ce ) diff --git a/_examples/database/mongodb/go.mod b/_examples/database/mongodb/go.mod index 511a8000..6e3205a6 100644 --- a/_examples/database/mongodb/go.mod +++ b/_examples/database/mongodb/go.mod @@ -4,6 +4,6 @@ go 1.15 require ( github.com/joho/godotenv v1.3.0 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd + github.com/kataras/iris/v12 vv12.1.9-0.20200817185317-589c8c6242ce go.mongodb.org/mongo-driver v1.3.4 ) diff --git a/_examples/database/mysql/go.mod b/_examples/database/mysql/go.mod index 3e7d3e76..d25896ab 100644 --- a/_examples/database/mysql/go.mod +++ b/_examples/database/mysql/go.mod @@ -4,6 +4,6 @@ go 1.15 require ( github.com/go-sql-driver/mysql v1.5.0 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd + github.com/kataras/iris/v12 vv12.1.9-0.20200817185317-589c8c6242ce github.com/mailgun/groupcache/v2 v2.1.0 ) diff --git a/_examples/kafka-api/go.mod b/_examples/kafka-api/go.mod index 101fed8b..b6519424 100644 --- a/_examples/kafka-api/go.mod +++ b/_examples/kafka-api/go.mod @@ -4,5 +4,5 @@ go 1.15 require ( github.com/Shopify/sarama v1.26.4 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd + github.com/kataras/iris/v12 vv12.1.9-0.20200817185317-589c8c6242ce ) diff --git a/_examples/logging/rollbar/go.mod b/_examples/logging/rollbar/go.mod index 6a3e5ca1..a5e37ffb 100644 --- a/_examples/logging/rollbar/go.mod +++ b/_examples/logging/rollbar/go.mod @@ -3,6 +3,6 @@ module github.com/kataras/iris/examples/logging/rollbar go 1.15 require ( - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd + github.com/kataras/iris/v12 vv12.1.9-0.20200817185317-589c8c6242ce github.com/rollbar/rollbar-go v1.2.0 ) diff --git a/_examples/mvc/overview/go.mod b/_examples/mvc/overview/go.mod index cb0cb9cd..8d8ffd70 100644 --- a/_examples/mvc/overview/go.mod +++ b/_examples/mvc/overview/go.mod @@ -2,4 +2,4 @@ module app go 1.15 -require github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd +require github.com/kataras/iris/v12 vv12.1.9-0.20200817185317-589c8c6242ce diff --git a/_examples/routing/subdomains/redirect/hosts b/_examples/routing/subdomains/redirect/hosts index c4c06478..b4cd51fe 100644 --- a/_examples/routing/subdomains/redirect/hosts +++ b/_examples/routing/subdomains/redirect/hosts @@ -1,4 +1,5 @@ 127.0.0.1 mydomain.com 127.0.0.1 www.mydomain.com +127.0.0.1 otherdomain.com # Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts \ No newline at end of file diff --git a/_examples/routing/subdomains/redirect/main_test.go b/_examples/routing/subdomains/redirect/main_test.go deleted file mode 100644 index 9e4410cf..00000000 --- a/_examples/routing/subdomains/redirect/main_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestSubdomainRedirectWWW(t *testing.T) { - app := newApp() - root := strings.TrimSuffix(addr, ":80") - - e := httptest.New(t, app) - - tests := []struct { - path string - response string - }{ - {"/", fmt.Sprintf("This is the www.%s endpoint.", root)}, - {"/users", fmt.Sprintf("This is the www.%s/users endpoint.", root)}, - {"/users/login", fmt.Sprintf("This is the www.%s/users/login endpoint.", root)}, - } - - for _, test := range tests { - e.GET(test.path).Expect().Status(httptest.StatusOK).Body().Equal(test.response) - e.GET(test.path).WithURL("www.mydomain.com").Expect().Status(httptest.StatusOK).Body().Equal(test.response) - } -} diff --git a/_examples/routing/subdomains/redirect/multi-instances/go.mod b/_examples/routing/subdomains/redirect/multi-instances/go.mod new file mode 100644 index 00000000..b3957171 --- /dev/null +++ b/_examples/routing/subdomains/redirect/multi-instances/go.mod @@ -0,0 +1,7 @@ +module github.com/kataras/iris/_examples/routing/subdomains/redirect/multi-instances + +go 1.15 + +require github.com/kataras/iris/v12 v12.1.9-0.20200817185317-589c8c6242ce + +replace github.com/kataras/iris/v12 => ../../../../../ \ No newline at end of file diff --git a/_examples/routing/subdomains/redirect/multi-instances/main.go b/_examples/routing/subdomains/redirect/multi-instances/main.go index 2c0bffa8..d3e59279 100644 --- a/_examples/routing/subdomains/redirect/multi-instances/main.go +++ b/_examples/routing/subdomains/redirect/multi-instances/main.go @@ -1,82 +1,70 @@ package main import ( - "net/http" + _ "github.com/kataras/iris/_examples/routing/subdomains/redirect/multi-instances/other" + _ "github.com/kataras/iris/_examples/routing/subdomains/redirect/multi-instances/root" "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/apps" ) +// In this example, you wanna use three different applications exposed as one. +// The first one is the "other" package, the second is the "root", +// the third is the switcher application which will expose the above. +// Unlike the previous example, on this one we will NOT redirect, +// the Hosts switcher will just pass the request to the matched Application to handle. +// This is NOT an alternative of your favorite load balancer. +// Read the comments carefully, if you need more information +// you can head over to the "apps" package's godocs and tests. func main() { - app := iris.New() - - hosts := map[string]*iris.Application{ - "mydomain.com": createRoot("www.mydomain.com"), // redirects to www. - "www.mydomain.com": createWWW(), - "test.mydomain.com": createTest(), - } - for _, r := range hosts { - r.Build() - } - - app.Downgrade(func(w http.ResponseWriter, r *http.Request) { - host := r.Host - if host == "" { - host = r.URL.Host - } - - if router, ok := hosts[host]; ok { - router.ServeHTTP(w, r) - return - } - - http.NotFound(w, r) + // The `apps.Hosts` switch provider: + // The pattern. A regexp for matching the host part of incoming requests. + // The target. An iris.Application instance (created by iris.New()) + // OR + // You can use the Application's name (app.SetName("myapp")). + // Example: + // package rootdomain + // func init() { + // app := iris.New().SetName("root app") + // ... + // } + // On the main package add an empty import statement: ` _ import "rootdomain"` + // And set the "root app" as the key to reference that application (of the same program). + // Thats the target we wanna use now ^ (see ../hosts file). + // OR + // An external host or a local running in the same machine but different port or host behind proxy. + switcher := apps.Switch(apps.Hosts{ + {"^(www.)?mydomain.com$", "root app"}, + {"^otherdomain.com$", "other app"}, }) + // The registration order matters, so we can register a fallback server (when host no match) + // using "*". However, you have alternatives by using the Switch Iris Application value + // (let's call it "switcher"): + // 1. Handle the not founds, e.g. switcher.OnErrorCode(404, ...) + // 2. Use the switcher.WrapRouter, e.g. to log the flow of a request of all hosts exposed. + // 3. Just register routes to match, e.g. switcher.Get("/", ...) + switcher.Get("/", fallback) + // OR + // Change the response code to 502 + // instead of 404 and write a message: + // switcher.OnErrorCode(iris.StatusNotFound, fallback) - app.Listen(":80") + // The switcher is a common Iris Application, so you have access to the Iris features. + // And it should be listening to a host:port in order to match and serve its apps. + // + // http://mydomain.com (OK) + // http://www.mydomain.com (OK) + // http://mydomain.com/dsa (404) + // http://no.mydomain.com (502 Bad Host) + // + // http://otherdomain.com (OK) + // http://www.otherdomain.com (502 Bad Host) + // http://otherdomain.com/dsa (404 JSON) + // ... + switcher.Listen(":80") } -func createRoot(redirectTo string) *iris.Application { - app := iris.New() - app.Downgrade(func(w http.ResponseWriter, r *http.Request) { - fullScheme := "http://" - if r.TLS != nil { - fullScheme = "https://" - } - - http.Redirect(w, r, fullScheme+redirectTo+r.URL.RequestURI(), iris.StatusMovedPermanently) - }) - - return app -} - -func createWWW() *iris.Application { - app := iris.New() - app.Get("/", index) - - users := app.Party("/users") - users.Get("/", usersIndex) - users.Get("/login", getLogin) - - return app -} - -func createTest() *iris.Application { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.WriteString("Test Index") - }) - - return app -} - -func index(ctx iris.Context) { - ctx.Writef("This is the www.mydomain.com endpoint.") -} - -func usersIndex(ctx iris.Context) { - ctx.Writef("This is the www.mydomain.com/users endpoint.") -} - -func getLogin(ctx iris.Context) { - ctx.Writef("This is the www.mydomain.com/users/login endpoint.") +func fallback(ctx iris.Context) { + ctx.StatusCode(iris.StatusBadGateway) + ctx.Writef("Bad Host %s", ctx.Host()) } diff --git a/_examples/routing/subdomains/redirect/multi-instances/other/server.go b/_examples/routing/subdomains/redirect/multi-instances/other/server.go new file mode 100644 index 00000000..7452a406 --- /dev/null +++ b/_examples/routing/subdomains/redirect/multi-instances/other/server.go @@ -0,0 +1,30 @@ +package other + +import ( + "time" + + "github.com/kataras/iris/v12" +) + +func init() { + app := iris.New() + app.SetName("other app") + + app.OnAnyErrorCode(handleErrors) + app.Get("/", index) +} + +func index(ctx iris.Context) { + ctx.HTML("Other Index (App Name: %s | Host: %s)", + ctx.Application().String(), ctx.Host()) +} + +func handleErrors(ctx iris.Context) { + errCode := ctx.GetStatusCode() + ctx.JSON(iris.Map{ + "Server": ctx.Application().String(), + "Code": errCode, + "Message": iris.StatusText(errCode), + "Timestamp": time.Now().Unix(), + }) +} diff --git a/_examples/routing/subdomains/redirect/multi-instances/root/server.go b/_examples/routing/subdomains/redirect/multi-instances/root/server.go new file mode 100644 index 00000000..79aa9792 --- /dev/null +++ b/_examples/routing/subdomains/redirect/multi-instances/root/server.go @@ -0,0 +1,15 @@ +package root + +import "github.com/kataras/iris/v12" + +func init() { + app := iris.New() + app.SetName("root app") + + app.Get("/", index) +} + +func index(ctx iris.Context) { + ctx.HTML("Main Root Index (App Name: %s | Host: %s)", + ctx.Application().String(), ctx.Host()) +} diff --git a/_examples/sessions/database/redis/go.mod b/_examples/sessions/database/redis/go.mod index 549a5828..90822d02 100644 --- a/_examples/sessions/database/redis/go.mod +++ b/_examples/sessions/database/redis/go.mod @@ -2,5 +2,5 @@ module app go 1.15 -require github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd +require github.com/kataras/iris/v12 vv12.1.9-0.20200817185317-589c8c6242ce diff --git a/_examples/websocket/socketio/go.mod b/_examples/websocket/socketio/go.mod index 61b7561a..673a246e 100644 --- a/_examples/websocket/socketio/go.mod +++ b/_examples/websocket/socketio/go.mod @@ -4,5 +4,5 @@ go 1.15 require ( github.com/googollee/go-socket.io v1.4.3-0.20191109153049-7451e2f8c2e0 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd + github.com/kataras/iris/v12 vv12.1.9-0.20200817185317-589c8c6242ce ) diff --git a/apps/switch.go b/apps/switch.go index 5d7aa233..73aa8cfc 100644 --- a/apps/switch.go +++ b/apps/switch.go @@ -1,6 +1,8 @@ package apps import ( + "strings" + "github.com/kataras/iris/v12" ) @@ -33,11 +35,18 @@ func Switch(providers ...SwitchProvider) *iris.Application { panic("iris: switch: empty providers") } + var friendlyAddrs []string var cases []SwitchCase for _, p := range providers { for _, c := range p.GetSwitchCases() { cases = append(cases, c) } + + if fp, ok := p.(FriendlyNameProvider); ok { + if friendlyName := fp.GetFriendlyName(); friendlyName != "" { + friendlyAddrs = append(friendlyAddrs, friendlyName) + } + } } if len(cases) == 0 { @@ -61,7 +70,7 @@ func Switch(providers ...SwitchProvider) *iris.Application { app.UseRouter(func(ctx iris.Context) { for _, c := range cases { if c.Filter(ctx) { - c.App.ServeHTTP(ctx.ResponseWriter().Naive(), ctx.Request()) + c.App.ServeHTTP(ctx.ResponseWriter(), ctx.Request()) // if c.App.Downgraded() { // c.App.ServeHTTP(ctx.ResponseWriter(), ctx.Request()) @@ -81,6 +90,12 @@ func Switch(providers ...SwitchProvider) *iris.Application { ctx.Next() }) + // Configure the switcher's supervisor. + app.ConfigureHost(func(su *iris.Supervisor) { + if len(friendlyAddrs) > 0 { + su.FriendlyAddr = strings.Join(friendlyAddrs, ", ") + } + }) return app } @@ -88,8 +103,8 @@ type ( // SwitchCase contains the filter // and the matched Application instance. SwitchCase struct { - Filter iris.Filter - App *iris.Application + Filter iris.Filter // Filter runs against the Switcher. + App *iris.Application // App is the main target application responsible to handle the request. } // A SwitchProvider should return the switch cases. @@ -100,6 +115,12 @@ type ( GetSwitchCases() []SwitchCase } + // FriendlyNameProvider can be optionally implemented by providers + // to customize the Switcher's Supervisor.FriendlyAddr field (Startup log). + FriendlyNameProvider interface { + GetFriendlyName() string + } + // Join returns a new slice which joins different type of switch cases. Join []SwitchProvider ) diff --git a/apps/switch_hosts.go b/apps/switch_hosts.go index f143d4f7..36bfd858 100644 --- a/apps/switch_hosts.go +++ b/apps/switch_hosts.go @@ -5,6 +5,7 @@ import ( "net/http" "net/url" "regexp" + "strings" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/context" @@ -53,6 +54,18 @@ func (hosts Hosts) GetSwitchCases() []SwitchCase { return cases } +// GetFriendlyName implements the FriendlyNameProvider. +func (hosts Hosts) GetFriendlyName() string { + var patterns []string + for _, host := range hosts { + if strings.TrimSpace(host.Pattern) != "" { + patterns = append(patterns, host.Pattern) + } + } + + return strings.Join(patterns, ", ") +} + func hostApp(host Host) *iris.Application { if host.Target == nil { return nil diff --git a/core/host/supervisor.go b/core/host/supervisor.go index 546f35d2..1551cdb5 100644 --- a/core/host/supervisor.go +++ b/core/host/supervisor.go @@ -30,8 +30,9 @@ type Configurator func(su *Supervisor) // // Interfaces are separated to return relative functionality to them. type Supervisor struct { - Server *http.Server - friendlyAddr string // e.g mydomain.com instead of :443 when AutoTLS is used, see `WriteStartupLogOnServe` task. + Server *http.Server + // FriendlyAddr can be set to customize the "Now Listening on: {FriendlyAddr}". + FriendlyAddr string // e.g mydomain.com instead of :443 when AutoTLS is used, see `WriteStartupLogOnServe` task. disableHTTP1ToHTTP2Redirection bool closedManually uint32 // future use, accessed atomically (non-zero means we've called the Shutdown) closedByInterruptHandler uint32 // non-zero means that the end-developer interrupted it by-purpose. @@ -350,7 +351,7 @@ func (su *Supervisor) ListenAndServeAutoTLS(domain string, email string, cacheDi if strings.TrimSpace(domain) != "" { domains := strings.Split(domain, " ") - su.friendlyAddr = domains[0] + su.FriendlyAddr = strings.Join(domains, ", ") hostPolicy = autocert.HostWhitelist(domains...) } diff --git a/core/host/task.go b/core/host/task.go index 3fd10652..7a4f030d 100644 --- a/core/host/task.go +++ b/core/host/task.go @@ -23,7 +23,7 @@ import ( func WriteStartupLogOnServe(w io.Writer) func(TaskHost) { return func(h TaskHost) { guessScheme := netutil.ResolveScheme(h.Supervisor.manuallyTLS || h.Supervisor.Fallback != nil) - addr := h.Supervisor.friendlyAddr + addr := h.Supervisor.FriendlyAddr if addr == "" { addr = h.Supervisor.Server.Addr }