From 6432759dbff6b079d752efeaa87c7aeb3b9e725d Mon Sep 17 00:00:00 2001 From: kataras Date: Sat, 29 Jul 2017 04:27:58 +0300 Subject: [PATCH] Give an easier and more permant solution for https://github.com/kataras/iris/pull/689 Former-commit-id: 8dc16d15f7bd14cb98b91b16d7d6b1bd756132bd --- HISTORY.md | 103 ++++++++++++++++++ README.md | 4 +- _examples/README.md | 3 +- .../http-listening/notify-on-shutdown/main.go | 51 +++++++++ context.go | 8 ++ core/host/supervisor.go | 22 ++++ doc.go | 95 +++++++++++++++- iris.go | 57 +++++++++- 8 files changed, 333 insertions(+), 10 deletions(-) create mode 100644 _examples/http-listening/notify-on-shutdown/main.go diff --git a/HISTORY.md b/HISTORY.md index cc42612f..3e31ac49 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -18,6 +18,109 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene **How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`. +# Sa, 29 July 2017 | v8.1.1 + +No breaking changes, just an addition to make your life easier. + +This feature has been implemented after @corebreaker 's request, posted at: https://github.com/kataras/iris/issues/688. He was also tried to fix that by a [PR](https://github.com/kataras/iris/pull/689), we thanks him but the problem with that PR was the duplication and the separation of concepts, however we thanks him for pushing for a solution. The current feature's implementation gives a permant solution to host supervisor access issues. + +Optional host configurators added to all common serve and listen functions. + +Below you'll find how to gain access to the host, **the second way is the new feature.** + +### Hosts + +Access to all hosts that serve your application can be provided by +the `Application#Hosts` field, after the `Run` method. + +But the most common scenario is that you may need access to the host before the `Run` method, +there are two ways of gain access to the host supervisor, read below. + +First way is to use the `app.NewHost` to create a new host +and use one of its `Serve` or `Listen` functions +to start the application via the `iris#Raw` Runner. +Note that this way needs an extra import of the `net/http` package. + +Example Code: + +```go +h := app.NewHost(&http.Server{Addr:":8080"}) +h.RegisterOnShutdown(func(){ + println("server was closed!") +}) + +app.Run(iris.Raw(h.ListenAndServe)) +``` + +Second, and probably easier way is to use the `host.Configurator`. + +Note that this method requires an extra import statement of +"github.com/kataras/iris/core/host" when using go < 1.9, +if you're targeting on go1.9 then you can use the `iris#Supervisor` +and omit the extra host import. + +All common `Runners` we saw earlier (`iris#Addr, iris#Listener, iris#Server, iris#TLS, iris#AutoTLS`) +accept a variadic argument of `host.Configurator`, there are just `func(*host.Supervisor)`. +Therefore the `Application` gives you the rights to modify the auto-created host supervisor through these. + + +Example Code: + +```go +package main + +import ( + stdContext "context" + "time" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/host" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

Hello, try to refresh the page after ~10 secs

") + }) + + app.Logger().Info("Wait 10 seconds and check your terminal again") + // simulate a shutdown action here... + go func() { + <-time.After(10 * time.Second) + timeout := 5 * time.Second + ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) + defer cancel() + // close all hosts, this will notify the callback we had register + // inside the `configureHost` func. + app.Shutdown(ctx) + }() + + // start the server as usual, the only difference is that + // we're adding a second (optional) function + // to configure the just-created host supervisor. + // + // http://localhost:8080 + // wait 10 seconds and check your terminal. + app.Run(iris.Addr(":8080", configureHost), iris.WithoutServerError(iris.ErrServerClosed)) + +} + +func configureHost(su *host.Supervisor) { + // here we have full access to the host that will be created + // inside the `Run` function. + // + // we register a shutdown "event" callback + su.RegisterOnShutdown(func() { + println("server is closed") + }) + // su.RegisterOnError + // su.RegisterOnServe +} +``` + +Read more about listening and gracefully shutdown by navigating to: https://github.com/kataras/iris/tree/master/_examples/#http-listening # We, 26 July 2017 | v8.1.0 diff --git a/README.md b/README.md index a153f83f..91539b2c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Iris is a fast, simple and efficient micro web framework for Go. It provides a b ### 📑 Table of contents * [Installation](#-installation) -* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#we-26-july-2017--v810) +* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#sa-29-july-2017--v811) * [Learn](#-learn) * [HTTP Listening](_examples/#http-listening) * [Configuration](_examples/#configuration) @@ -340,7 +340,7 @@ Thank You for your trust! ### 📌 Version -Current: **8.1.0** +Current: **8.1.1** Each new release is pushed to the master. It stays there until the next version. When a next version is released then the previous version goes to its own branch with `gopkg.in` as its import path (and its own vendor folder), in order to keep it working "for-ever". diff --git a/_examples/README.md b/_examples/README.md index fe6ad8ca..2dee2ade 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -31,7 +31,8 @@ It doesn't always contain the "best ways" but it does cover each important featu - Graceful Shutdown * [using the `RegisterOnInterrupt`](http-listening/graceful-shutdown/default-notifier/main.go) * [using a custom notifier](http-listening/graceful-shutdown/custom-notifier/main.go) - + * [notify on shutdown](http-listening/graceful-shutdown/notify-on-shutdown/main.go) + ### Configuration - [Functional](configuration/functional/main.go) diff --git a/_examples/http-listening/notify-on-shutdown/main.go b/_examples/http-listening/notify-on-shutdown/main.go new file mode 100644 index 00000000..8fe15607 --- /dev/null +++ b/_examples/http-listening/notify-on-shutdown/main.go @@ -0,0 +1,51 @@ +package main + +import ( + stdContext "context" + "time" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/host" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

Hello, try to refresh the page after ~10 secs

") + }) + + app.Logger().Info("Wait 10 seconds and check your terminal again") + // simulate a shutdown action here... + go func() { + <-time.After(10 * time.Second) + timeout := 5 * time.Second + ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) + defer cancel() + // close all hosts, this will notify the callback we had register + // inside the `configureHost` func. + app.Shutdown(ctx) + }() + + // start the server as usual, the only difference is that + // we're adding a second (optional) function + // to configure the just-created host supervisor. + // + // http://localhost:8080 + // wait 10 seconds and check your terminal. + app.Run(iris.Addr(":8080", configureHost), iris.WithoutServerError(iris.ErrServerClosed)) + +} + +func configureHost(su *host.Supervisor) { + // here we have full access to the host that will be created + // inside the `Run` function. + // + // we register a shutdown "event" callback + su.RegisterOnShutdown(func() { + println("server is closed") + }) + // su.RegisterOnError + // su.RegisterOnServe +} diff --git a/context.go b/context.go index 673dde23..81c3bb4f 100644 --- a/context.go +++ b/context.go @@ -4,8 +4,11 @@ package iris import ( "github.com/kataras/iris/context" + "github.com/kataras/iris/core/host" ) +// TODO: When go 1.9 will be released +// split this file in order to separate the concepts. type ( // Context is the midle-man server's "object" for the clients. // @@ -32,4 +35,9 @@ type ( Handler = context.Handler // A Map is a shortcut of the map[string]interface{}. Map = context.Map + + // Supervisor is a shortcut of the `host#Supervisor`. + // Used to add supervisor configurators on common Runners + // without the need of importing the `core/host` package. + Supervisor = host.Supervisor ) diff --git a/core/host/supervisor.go b/core/host/supervisor.go index 3def0a9f..688f2f44 100644 --- a/core/host/supervisor.go +++ b/core/host/supervisor.go @@ -13,6 +13,12 @@ import ( "golang.org/x/crypto/acme/autocert" ) +// Configurator provides an easy way to modify +// the Supervisor. +// +// Look the `Configure` func for more. +type Configurator func(su *Supervisor) + // Supervisor is the wrapper and the manager for a compatible server // and it's relative actions, called Tasks. // @@ -54,6 +60,22 @@ func New(srv *http.Server) *Supervisor { } } +// Configure accepts one or more `Configurator`. +// With this function you can use simple functions +// that are spread across your app to modify +// the supervisor, these Configurators can be +// used on any Supervisor instance. +// +// Look `Configurator` too. +// +// Returns itself. +func (su *Supervisor) Configure(configurators ...Configurator) *Supervisor { + for _, conf := range configurators { + conf(su) + } + return su +} + // DeferFlow defers the flow of the exeuction, // i.e: when server should return error and exit // from app, a DeferFlow call inside a Task diff --git a/doc.go b/doc.go index a3d898ca..af92e110 100644 --- a/doc.go +++ b/doc.go @@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub: Current Version -8.1.0 +8.1.1 Installation @@ -303,6 +303,99 @@ Example code: app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler) } + +Hosts + +Access to all hosts that serve your application can be provided by +the `Application#Hosts` field, after the `Run` method. + + +But the most common scenario is that you may need access to the host before the `Run` method, +there are two ways of gain access to the host supervisor, read below. + +First way is to use the `app.NewHost` to create a new host +and use one of its `Serve` or `Listen` functions +to start the application via the `iris#Raw` Runner. +Note that this way needs an extra import of the `net/http` package. + +Example Code: + + + h := app.NewHost(&http.Server{Addr:":8080"}) + h.RegisterOnShutdown(func(){ + println("server was closed!") + }) + + app.Run(iris.Raw(h.ListenAndServe)) + +Second, and probably easier way is to use the `host.Configurator`. + +Note that this method requires an extra import statement of +"github.com/kataras/iris/core/host" when using go < 1.9, +if you're targeting on go1.9 then you can use the `iris#Supervisor` +and omit the extra host import. + +All common `Runners` we saw earlier (`iris#Addr, iris#Listener, iris#Server, iris#TLS, iris#AutoTLS`) +accept a variadic argument of `host.Configurator`, there are just `func(*host.Supervisor)`. +Therefore the `Application` gives you the rights to modify the auto-created host supervisor through these. + + +Example Code: + + + package main + + import ( + stdContext "context" + "time" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/host" + ) + + func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

Hello, try to refresh the page after ~10 secs

") + }) + + app.Logger().Info("Wait 10 seconds and check your terminal again") + // simulate a shutdown action here... + go func() { + <-time.After(10 * time.Second) + timeout := 5 * time.Second + ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) + defer cancel() + // close all hosts, this will notify the callback we had register + // inside the `configureHost` func. + app.Shutdown(ctx) + }() + + // start the server as usual, the only difference is that + // we're adding a second (optional) function + // to configure the just-created host supervisor. + // + // http://localhost:8080 + // wait 10 seconds and check your terminal. + app.Run(iris.Addr(":8080", configureHost), iris.WithoutServerError(iris.ErrServerClosed)) + + } + + func configureHost(su *host.Supervisor) { + // here we have full access to the host that will be created + // inside the `Run` function. + // + // we register a shutdown "event" callback + su.RegisterOnShutdown(func() { + println("server is closed") + }) + // su.RegisterOnError + // su.RegisterOnServe + } + + Read more about listening and gracefully shutdown by navigating to: https://github.com/kataras/iris/tree/master/_examples/#http-listening diff --git a/iris.go b/iris.go index 588a60f8..04154c65 100644 --- a/iris.go +++ b/iris.go @@ -33,7 +33,7 @@ import ( const ( // Version is the current version number of the Iris Web Framework. - Version = "8.1.0" + Version = "8.1.1" ) // HTTP status codes as registered with IANA. @@ -397,11 +397,20 @@ type Runner func(*Application) error // Listener can be used as an argument for the `Run` method. // It can start a server with a custom net.Listener via server's `Serve`. // +// Second argument is optional, it accepts one or more +// `func(*host.Configurator)` that are being executed +// on that specific host that this function will create to start the server. +// Via host configurators you can configure the back-end host supervisor, +// i.e to add events for shutdown, serve or error. +// An example of this use case can be found at: +// https://github.com/kataras/iris/blob/master/_examples/http-listening/notify-on-shutdown/main.go +// // See `Run` for more. -func Listener(l net.Listener) Runner { +func Listener(l net.Listener, hostConfigs ...host.Configurator) Runner { return func(app *Application) error { app.config.vhost = netutil.ResolveVHost(l.Addr().String()) return app.NewHost(new(http.Server)). + Configure(hostConfigs...). Serve(l) } } @@ -409,10 +418,19 @@ func Listener(l net.Listener) Runner { // Server can be used as an argument for the `Run` method. // It can start a server with a *http.Server. // +// Second argument is optional, it accepts one or more +// `func(*host.Configurator)` that are being executed +// on that specific host that this function will create to start the server. +// Via host configurators you can configure the back-end host supervisor, +// i.e to add events for shutdown, serve or error. +// An example of this use case can be found at: +// https://github.com/kataras/iris/blob/master/_examples/http-listening/notify-on-shutdown/main.go +// // See `Run` for more. -func Server(srv *http.Server) Runner { +func Server(srv *http.Server, hostConfigs ...host.Configurator) Runner { return func(app *Application) error { return app.NewHost(srv). + Configure(hostConfigs...). ListenAndServe() } } @@ -423,10 +441,19 @@ func Server(srv *http.Server) Runner { // // Addr should have the form of [host]:port, i.e localhost:8080 or :8080. // +// Second argument is optional, it accepts one or more +// `func(*host.Configurator)` that are being executed +// on that specific host that this function will create to start the server. +// Via host configurators you can configure the back-end host supervisor, +// i.e to add events for shutdown, serve or error. +// An example of this use case can be found at: +// https://github.com/kataras/iris/blob/master/_examples/http-listening/notify-on-shutdown/main.go +// // See `Run` for more. -func Addr(addr string) Runner { +func Addr(addr string, hostConfigs ...host.Configurator) Runner { return func(app *Application) error { return app.NewHost(&http.Server{Addr: addr}). + Configure(hostConfigs...). ListenAndServe() } } @@ -439,10 +466,19 @@ func Addr(addr string) Runner { // Addr should have the form of [host]:port, i.e localhost:443 or :443. // CertFile & KeyFile should be filenames with their extensions. // +// Second argument is optional, it accepts one or more +// `func(*host.Configurator)` that are being executed +// on that specific host that this function will create to start the server. +// Via host configurators you can configure the back-end host supervisor, +// i.e to add events for shutdown, serve or error. +// An example of this use case can be found at: +// https://github.com/kataras/iris/blob/master/_examples/http-listening/notify-on-shutdown/main.go +// // See `Run` for more. -func TLS(addr string, certFile, keyFile string) Runner { +func TLS(addr string, certFile, keyFile string, hostConfigs ...host.Configurator) Runner { return func(app *Application) error { return app.NewHost(&http.Server{Addr: addr}). + Configure(hostConfigs...). ListenAndServeTLS(certFile, keyFile) } } @@ -454,10 +490,19 @@ func TLS(addr string, certFile, keyFile string) Runner { // // Addr should have the form of [host]:port, i.e mydomain.com:443. // +// Second argument is optional, it accepts one or more +// `func(*host.Configurator)` that are being executed +// on that specific host that this function will create to start the server. +// Via host configurators you can configure the back-end host supervisor, +// i.e to add events for shutdown, serve or error. +// An example of this use case can be found at: +// https://github.com/kataras/iris/blob/master/_examples/http-listening/notify-on-shutdown/main.go +// // See `Run` for more. -func AutoTLS(addr string) Runner { +func AutoTLS(addr string, hostConfigs ...host.Configurator) Runner { return func(app *Application) error { return app.NewHost(&http.Server{Addr: addr}). + Configure(hostConfigs...). ListenAndServeAutoTLS() } }