diff --git a/HISTORY.md b/HISTORY.md index 23d82a97..8a60b3e7 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -190,6 +190,8 @@ Other Improvements: ![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png) +- Server will not return neither log the `ErrServerClosed` if `app.Shutdown` was called manually via interrupt signal(CTRL/CMD+C), note that if the server closed by any other reason the error will be fired as previously (unless `iris.WithoutServerError(iris.ErrServerClosed)`). + - Finally, Log level's and Route debug information colorization is respected across outputs. Previously if the application used more than one output destination (e.g. a file through `app.Logger().AddOutput`) the color support was automatically disabled from all, including the terminal one, this problem is fixed now. Developers can now see colors in their terminals while log files are kept with clear text. - New `iris.WithLowercaseRouting` option which forces all routes' paths to be lowercase and converts request paths to their lowercase for matching. diff --git a/_examples/apidoc/yaag/go.mod b/_examples/apidoc/yaag/go.mod index 67fa43ad..508c717c 100644 --- a/_examples/apidoc/yaag/go.mod +++ b/_examples/apidoc/yaag/go.mod @@ -4,5 +4,5 @@ go 1.13 require ( github.com/betacraft/yaag v1.0.1-0.20191027021412-565f65e36090 - github.com/kataras/iris/v12 v12.1.5 + github.com/kataras/iris/v12 v12.1.8 ) diff --git a/_examples/hello-world/main.go b/_examples/hello-world/main.go index 3b86e0d7..2b0ec41d 100644 --- a/_examples/hello-world/main.go +++ b/_examples/hello-world/main.go @@ -38,5 +38,5 @@ func main() { // http://localhost:8080 // http://localhost:8080/ping // http://localhost:8080/hello - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/hero/overview/main.go b/_examples/hero/overview/main.go index 35ac4054..264ed5b1 100644 --- a/_examples/hero/overview/main.go +++ b/_examples/hero/overview/main.go @@ -50,8 +50,6 @@ func main() { app.Run( // Start the web server at localhost:8080 iris.Addr("localhost:8080"), - // skip err server closed when CTRL/CMD+C pressed: - iris.WithoutServerError(iris.ErrServerClosed), // enables faster json serialization and more: iris.WithOptimizations, ) diff --git a/_examples/hero/sessions/main.go b/_examples/hero/sessions/main.go index 6ee76a16..4a9da3c1 100644 --- a/_examples/hero/sessions/main.go +++ b/_examples/hero/sessions/main.go @@ -32,8 +32,5 @@ func main() { // Path: http://localhost:8080 app.Get("/", indexHandler) - app.Run( - iris.Addr(":8080"), - iris.WithoutServerError(iris.ErrServerClosed), - ) + app.Listen(":8080") } diff --git a/_examples/http-listening/README.md b/_examples/http-listening/README.md index ffedd47f..0a0a43dd 100644 --- a/_examples/http-listening/README.md +++ b/_examples/http-listening/README.md @@ -235,7 +235,7 @@ func main() { app := iris.New() iris.RegisterOnInterrupt(func() { - timeout := 5 * time.Second + timeout := 10 * time.Second ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() // close all hosts diff --git a/_examples/http-listening/graceful-shutdown/custom-notifier/main.go b/_examples/http-listening/graceful-shutdown/custom-notifier/main.go index abde2644..3984aefb 100644 --- a/_examples/http-listening/graceful-shutdown/custom-notifier/main.go +++ b/_examples/http-listening/graceful-shutdown/custom-notifier/main.go @@ -33,7 +33,7 @@ func main() { case <-ch: println("shutdown...") - timeout := 5 * time.Second + timeout := 10 * time.Second ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) defer cancel() app.Shutdown(ctx) diff --git a/_examples/http-listening/graceful-shutdown/default-notifier/main.go b/_examples/http-listening/graceful-shutdown/default-notifier/main.go index c692af57..06acf4ab 100644 --- a/_examples/http-listening/graceful-shutdown/default-notifier/main.go +++ b/_examples/http-listening/graceful-shutdown/default-notifier/main.go @@ -18,7 +18,7 @@ func main() { app := iris.New() iris.RegisterOnInterrupt(func() { - timeout := 5 * time.Second + timeout := 10 * time.Second ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) defer cancel() // close all hosts diff --git a/_examples/http-listening/listen-addr/omit-server-errors/main.go b/_examples/http-listening/listen-addr/omit-server-errors/main.go index 339a5b57..d2a62a6e 100644 --- a/_examples/http-listening/listen-addr/omit-server-errors/main.go +++ b/_examples/http-listening/listen-addr/omit-server-errors/main.go @@ -17,7 +17,8 @@ func main() { } // same as: // err := app.Listen(":8080") - // if err != nil && (err != iris.ErrServerClosed || err.Error() != iris.ErrServerClosed.Error()) { + // import "errors" + // if errors.Is(err, iris.ErrServerClosed) { // [...] // } } diff --git a/_examples/http-listening/listen-addr/omit-server-errors/main_test.go b/_examples/http-listening/listen-addr/omit-server-errors/main_test.go index e26aaf90..89bc6bf9 100644 --- a/_examples/http-listening/listen-addr/omit-server-errors/main_test.go +++ b/_examples/http-listening/listen-addr/omit-server-errors/main_test.go @@ -60,9 +60,8 @@ func TestListenAddrWithoutServerErr(t *testing.T) { app.Shutdown(ctx) }() - // we disable the ErrServerClosed, so the error should be nil when server is closed by `app.Shutdown`. - - // so in this case the iris/http.ErrServerClosed should be NOT logged and NOT return. + // we disable the ErrServerClosed, so the error should be nil when server is closed by `app.Shutdown` + // or by an external issue. err := app.Listen(":9827", iris.WithoutServerError(iris.ErrServerClosed)) if err != nil { t.Fatalf("expecting err to be nil but got: %v", err) diff --git a/_examples/http-listening/notify-on-shutdown/main.go b/_examples/http-listening/notify-on-shutdown/main.go index a7564b02..b35a4818 100644 --- a/_examples/http-listening/notify-on-shutdown/main.go +++ b/_examples/http-listening/notify-on-shutdown/main.go @@ -11,14 +11,14 @@ func main() { app := iris.New() app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Hello, try to refresh the page after ~10 secs

") + ctx.HTML("

Hello, try to refresh the page after ~5 secs

") }) - app.Logger().Info("Wait 10 seconds and check your terminal again") + app.Logger().Info("Wait 5 seconds and check your terminal again") // simulate a shutdown action here... go func() { - <-time.After(10 * time.Second) - timeout := 5 * time.Second + <-time.After(5 * time.Second) + timeout := 10 * time.Second ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() // close all hosts, this will notify the callback we had register @@ -36,21 +36,24 @@ func main() { // wait 10 seconds and check your terminal. app.Run(iris.Addr(":8080", configureHost), iris.WithoutServerError(iris.ErrServerClosed)) - /* - Or for simple cases you can just use the: - iris.RegisterOnInterrupt for global catch of the CTRL/CMD+C and OS events. - Look at the "graceful-shutdown" example for more. + time.Sleep(500 * time.Millisecond) // give time to the separate go routine(`onServerShutdown`) to finish. + + /* See + iris.RegisterOnInterrupt(callback) for global catch of the CTRL/CMD+C and OS events. + Look at the "graceful-shutdown" example for more. */ } +func onServerShutdown() { + println("server is closed") +} + func configureHost(su *iris.Supervisor) { // here we have full access to the host that will be created // inside the `app.Run` function or `NewHost`. // // we're registering a shutdown "event" callback here: - su.RegisterOnShutdown(func() { - println("server is closed") - }) + su.RegisterOnShutdown(onServerShutdown) // su.RegisterOnError // su.RegisterOnServe } diff --git a/_examples/http_request/extract-referer/main.go b/_examples/http_request/extract-referer/main.go index 33615d05..7703c19c 100644 --- a/_examples/http_request/extract-referer/main.go +++ b/_examples/http_request/extract-referer/main.go @@ -24,5 +24,5 @@ func main() { // http://localhost:8080?referer=https://twitter.com/Xinterio/status/1023566830974251008 // http://localhost:8080?referer=https://www.google.com/search?q=Top+6+golang+web+frameworks&oq=Top+6+golang+web+frameworks - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/http_request/read-custom-per-type/main.go b/_examples/http_request/read-custom-per-type/main.go index 9a425339..e03d172a 100644 --- a/_examples/http_request/read-custom-per-type/main.go +++ b/_examples/http_request/read-custom-per-type/main.go @@ -19,7 +19,7 @@ func main() { // // The response should be: // Received: main.config{Addr:"localhost:8080", ServerName:"Iris"} - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) + app.Listen(":8080", iris.WithOptimizations) } func newApp() *iris.Application { diff --git a/_examples/http_request/read-custom-via-unmarshaler/main.go b/_examples/http_request/read-custom-via-unmarshaler/main.go index c71f7c69..13e22bcc 100644 --- a/_examples/http_request/read-custom-via-unmarshaler/main.go +++ b/_examples/http_request/read-custom-via-unmarshaler/main.go @@ -19,7 +19,7 @@ func main() { // // The response should be: // Received: main.config{Addr:"localhost:8080", ServerName:"Iris"} - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) + app.Listen(":8080", iris.WithOptimizations) } func newApp() *iris.Application { diff --git a/_examples/http_request/read-json/main.go b/_examples/http_request/read-json/main.go index 1d06d968..692ec87e 100644 --- a/_examples/http_request/read-json/main.go +++ b/_examples/http_request/read-json/main.go @@ -60,5 +60,5 @@ func main() { // // The response should be: // Received: main.Company{Name:"iris-Go", City:"New York", Other:"Something here"} - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) + app.Listen(":8080", iris.WithOptimizations) } diff --git a/_examples/http_request/read-xml/main.go b/_examples/http_request/read-xml/main.go index e7234987..def65876 100644 --- a/_examples/http_request/read-xml/main.go +++ b/_examples/http_request/read-xml/main.go @@ -20,7 +20,7 @@ func main() { // // The response should be: // Received: main.person{XMLName:xml.Name{Space:"", Local:"person"}, Name:"Winston Churchill", Age:90, Description:"Description of this person, the body of this inner element."} - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) + app.Listen(":8080", iris.WithOptimizations) } func newApp() *iris.Application { diff --git a/_examples/http_request/request-logger/main.go b/_examples/http_request/request-logger/main.go index 73d31503..1e89d833 100644 --- a/_examples/http_request/request-logger/main.go +++ b/_examples/http_request/request-logger/main.go @@ -61,5 +61,5 @@ func main() { // http://localhost:8080/2 // http://lcoalhost:8080/notfoundhere // see the output on the console. - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/http_request/request-logger/request-logger-file-json/main.go b/_examples/http_request/request-logger/request-logger-file-json/main.go index b905a3d1..5a5d7627 100644 --- a/_examples/http_request/request-logger/request-logger-file-json/main.go +++ b/_examples/http_request/request-logger/request-logger-file-json/main.go @@ -82,7 +82,7 @@ func main() { // http://localhost:8080/1 // http://localhost:8080/2 // http://lcoalhost:8080/notfoundhere - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } var excludeExtensions = [...]string{ diff --git a/_examples/http_request/request-logger/request-logger-file/main.go b/_examples/http_request/request-logger/request-logger-file/main.go index a6a6ec9b..ec41ec27 100644 --- a/_examples/http_request/request-logger/request-logger-file/main.go +++ b/_examples/http_request/request-logger/request-logger-file/main.go @@ -35,7 +35,7 @@ func main() { // http://localhost:8080/1 // http://localhost:8080/2 // http://lcoalhost:8080/notfoundhere - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } // get a filename based on the date, file logs works that way the most times diff --git a/_examples/http_responsewriter/quicktemplate/main.go b/_examples/http_responsewriter/quicktemplate/main.go index 607f35d2..cc11e82e 100644 --- a/_examples/http_responsewriter/quicktemplate/main.go +++ b/_examples/http_responsewriter/quicktemplate/main.go @@ -18,5 +18,5 @@ func main() { app := newApp() // http://localhost:8080 // http://localhost:8080/yourname - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/http_responsewriter/sse-third-party/main.go b/_examples/http_responsewriter/sse-third-party/main.go index d0e55941..ed87b9f0 100644 --- a/_examples/http_responsewriter/sse-third-party/main.go +++ b/_examples/http_responsewriter/sse-third-party/main.go @@ -44,7 +44,7 @@ func main() { }) }() // ... - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } /* For a golang SSE client you can look at: https://github.com/r3labs/sse#example-client */ diff --git a/_examples/http_responsewriter/sse/main.go b/_examples/http_responsewriter/sse/main.go index 1abd48c2..40b263ef 100644 --- a/_examples/http_responsewriter/sse/main.go +++ b/_examples/http_responsewriter/sse/main.go @@ -187,5 +187,5 @@ func main() { // http://localhost:8080 // http://localhost:8080/events - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/http_responsewriter/write-rest/main.go b/_examples/http_responsewriter/write-rest/main.go index b861130b..2fe99793 100644 --- a/_examples/http_responsewriter/write-rest/main.go +++ b/_examples/http_responsewriter/write-rest/main.go @@ -113,5 +113,5 @@ func main() { // // `iris.WithoutServerError` is an optional configurator, // if passed to the `Run` then it will not print its passed error as an actual server error. - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations) + app.Listen(":8080", iris.WithOptimizations) } diff --git a/_examples/miscellaneous/file-logger/main.go b/_examples/miscellaneous/file-logger/main.go index 2141b70e..ad7cc1c3 100644 --- a/_examples/miscellaneous/file-logger/main.go +++ b/_examples/miscellaneous/file-logger/main.go @@ -42,7 +42,7 @@ func main() { // Navigate to http://localhost:8080/ping // and open the ./logs{TODAY}.txt file. - if err := app.Listen(":8080", iris.WithoutBanner, iris.WithoutServerError(iris.ErrServerClosed)); err != nil { + if err := app.Listen(":8080", iris.WithoutBanner); err != nil { app.Logger().Warn("Shutdown with error: " + err.Error()) } } diff --git a/_examples/mvc/grpc-compatible/http-client/main.go b/_examples/mvc/grpc-compatible/http-client/main.go index d4560c09..f198d398 100644 --- a/_examples/mvc/grpc-compatible/http-client/main.go +++ b/_examples/mvc/grpc-compatible/http-client/main.go @@ -35,7 +35,7 @@ func main() { log.Fatal(err) } - resp, err := client.Post("https://localhost/hello", "application/json", buf) + resp, err := client.Post("https://localhost/helloworld.Greeter/SayHello", "application/json", buf) if err != nil { log.Fatal(err) } diff --git a/_examples/mvc/grpc-compatible/main.go b/_examples/mvc/grpc-compatible/main.go index 6d790e31..0d00e0fb 100644 --- a/_examples/mvc/grpc-compatible/main.go +++ b/_examples/mvc/grpc-compatible/main.go @@ -23,7 +23,7 @@ func main() { app.Logger().SetLevel("debug") // The Iris server should ran under TLS (it's a gRPC requirement). - // POST: https://localhost:443/helloworld.greeter/sayhello + // POST: https://localhost:443/helloworld.Greeter/SayHello // with request data: {"name": "John"} // and expected output: {"message": "Hello John"} app.Run(iris.TLS(":443", "server.crt", "server.key")) @@ -32,7 +32,6 @@ func main() { func newApp() *iris.Application { app := iris.New() // app.Configure(iris.WithLowercaseRouting) // OPTIONAL. - app.Logger().SetLevel("debug") app.Get("/", func(ctx iris.Context) { ctx.HTML("

Index Page

") @@ -55,7 +54,9 @@ func newApp() *iris.Application { return app } -type myController struct{} +type myController struct { + // Ctx iris.Context +} // SayHello implements helloworld.GreeterServer. func (c *myController) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { diff --git a/_examples/mvc/login/main.go b/_examples/mvc/login/main.go index af807b51..c7f9df80 100644 --- a/_examples/mvc/login/main.go +++ b/_examples/mvc/login/main.go @@ -76,12 +76,8 @@ func main() { // http://localhost:8080/user/me // http://localhost:8080/user/logout // basic auth: "admin", "password", see "./middleware/basicauth.go" source file. - app.Run( - // Starts the web server at localhost:8080 - iris.Addr("localhost:8080"), - // Ignores err server closed log when CTRL/CMD+C pressed. - iris.WithoutServerError(iris.ErrServerClosed), - // Enables faster json serialization and more. - iris.WithOptimizations, - ) + + // Starts the web server at localhost:8080 + // Enables faster json serialization and more. + app.Listen(":8080", iris.WithOptimizations) } diff --git a/_examples/mvc/overview/main.go b/_examples/mvc/overview/main.go index 21363454..38748134 100644 --- a/_examples/mvc/overview/main.go +++ b/_examples/mvc/overview/main.go @@ -30,14 +30,7 @@ func main() { // http://localhost:8080/hello/iris // http://localhost:8080/movies // http://localhost:8080/movies/1 - app.Run( - // Start the web server at localhost:8080 - iris.Addr("localhost:8080"), - // skip err server closed when CTRL/CMD+C pressed: - iris.WithoutServerError(iris.ErrServerClosed), - // enables faster json serialization and more: - iris.WithOptimizations, - ) + app.Listen(":8080", iris.WithOptimizations) } // note the mvc.Application, it's not iris.Application. diff --git a/_examples/orm/gorm/main.go b/_examples/orm/gorm/main.go index 5cef4619..534e56db 100644 --- a/_examples/orm/gorm/main.go +++ b/_examples/orm/gorm/main.go @@ -165,7 +165,8 @@ func main() { "data": user.Serializer(), }) }) - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + + app.Listen(":8080") } type patchParam struct { diff --git a/_examples/orm/xorm/main.go b/_examples/orm/xorm/main.go index 32b02608..2419977a 100644 --- a/_examples/orm/xorm/main.go +++ b/_examples/orm/xorm/main.go @@ -70,5 +70,5 @@ func main() { // http://localhost:8080/insert // http://localhost:8080/get - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/routing/custom-high-level-router/main.go b/_examples/routing/custom-high-level-router/main.go index 66a24b32..764daab4 100644 --- a/_examples/routing/custom-high-level-router/main.go +++ b/_examples/routing/custom-high-level-router/main.go @@ -98,5 +98,5 @@ func main() { myCustomRouter := new(customRouter) app.BuildRouter(app.ContextPool, myCustomRouter, app.APIBuilder, true) - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/routing/dynamic-path/root-wildcard/main.go b/_examples/routing/dynamic-path/root-wildcard/main.go index 06f53927..2ccb8489 100644 --- a/_examples/routing/dynamic-path/root-wildcard/main.go +++ b/_examples/routing/dynamic-path/root-wildcard/main.go @@ -38,7 +38,7 @@ func main() { // this will handle only GET "/other2/static" app.Get("/other2/static2", staticPathOther2) - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } func h(ctx iris.Context) { diff --git a/_examples/sessions/database/badger/main.go b/_examples/sessions/database/badger/main.go index fe084941..afdcfcfe 100644 --- a/_examples/sessions/database/badger/main.go +++ b/_examples/sessions/database/badger/main.go @@ -104,5 +104,5 @@ func main() { } }) - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/sessions/database/boltdb/main.go b/_examples/sessions/database/boltdb/main.go index 6bfbc27b..36ac4b45 100644 --- a/_examples/sessions/database/boltdb/main.go +++ b/_examples/sessions/database/boltdb/main.go @@ -105,5 +105,5 @@ func main() { } }) - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/tutorial/api-for-apache-kafka/src/main.go b/_examples/tutorial/api-for-apache-kafka/src/main.go index bc8f609e..df63cfbd 100644 --- a/_examples/tutorial/api-for-apache-kafka/src/main.go +++ b/_examples/tutorial/api-for-apache-kafka/src/main.go @@ -77,7 +77,7 @@ func main() { // POST, GET: http://localhost:8080/api/v1/topics // POST : http://localhost:8080/apiv1/topics/{topic}/produce?key=my-key // GET : http://localhost:8080/apiv1/topics/{topic}/consume?partition=0&offset=0 (these url query parameters are optional) - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } // simple use-case, you can use templates and views obviously, see the "_examples/views" examples. diff --git a/_examples/websocket/basic/server.go b/_examples/websocket/basic/server.go index 8bf95d8a..1d0a94e0 100644 --- a/_examples/websocket/basic/server.go +++ b/_examples/websocket/basic/server.go @@ -116,5 +116,5 @@ func main() { // serves the npm browser websocket client usage example. app.HandleDir("/browserify", "./browserify") - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) + app.Listen(":8080") } diff --git a/_examples/websocket/socketio/go.mod b/_examples/websocket/socketio/go.mod index acb37741..57ce9686 100644 --- a/_examples/websocket/socketio/go.mod +++ b/_examples/websocket/socketio/go.mod @@ -3,6 +3,6 @@ module github.com/kataras/iris/_examples/websocket/socketio go 1.13 require ( - github.com/googollee/go-socket.io v1.4.3-0.20191109153049-7451e2f8c2e0 // indirect - github.com/kataras/iris/v12 v12.1.5 + github.com/googollee/go-socket.io v1.4.3-0.20191109153049-7451e2f8c2e0 + github.com/kataras/iris/v12 v12.1.8 ) diff --git a/_examples/websocket/socketio/main.go b/_examples/websocket/socketio/main.go index 622b30fc..c4e0250d 100644 --- a/_examples/websocket/socketio/main.go +++ b/_examples/websocket/socketio/main.go @@ -47,10 +47,8 @@ func main() { app.HandleMany("GET POST", "/socket.io/{any:path}", iris.FromStd(server)) app.HandleDir("/", "./asset") - app.Listen(":8000", - iris.WithoutPathCorrection, - iris.WithoutServerError(iris.ErrServerClosed), - ) + + app.Listen(":8000", iris.WithoutPathCorrection) } /* diff --git a/configuration.go b/configuration.go index e1249e6b..a0248296 100644 --- a/configuration.go +++ b/configuration.go @@ -185,11 +185,8 @@ var WithGlobalConfiguration = func(app *Application) { app.Configure(WithConfiguration(YAML(globalConfigurationKeyword))) } -// variables for configurators don't need any receivers, functions -// for them that need (helps code editors to recognise as variables without parenthesis completion). - // WithoutServerError will cause to ignore the matched "errors" -// from the main application's `Run` function. +// from the main application's `Run/Listen` function. // // Usage: // err := app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) diff --git a/context/application.go b/context/application.go index f0b935f5..5f760c08 100644 --- a/context/application.go +++ b/context/application.go @@ -1,6 +1,7 @@ package context import ( + stdContext "context" "io" "net/http" @@ -41,6 +42,10 @@ type Application interface { // It is ready to use after Build state. ServeHTTP(w http.ResponseWriter, r *http.Request) + // Shutdown gracefully terminates all the application's server hosts and any tunnels. + // Returns an error on the first failure, otherwise nil. + Shutdown(ctx stdContext.Context) error + // GetRouteReadOnly returns the registered "read-only" route based on its name, otherwise nil. // One note: "routeName" should be case-sensitive. Used by the context to get the current route. // It returns an interface instead to reduce wrong usage and to keep the decoupled design between diff --git a/core/host/supervisor.go b/core/host/supervisor.go index 97790dd8..a90fa2ff 100644 --- a/core/host/supervisor.go +++ b/core/host/supervisor.go @@ -27,11 +27,12 @@ type Configurator func(su *Supervisor) // // Interfaces are separated to return relative functionality to them. type Supervisor struct { - Server *http.Server - closedManually int32 // future use, accessed atomically (non-zero means we've called the Shutdown) - manuallyTLS bool // we need that in order to determinate what to output on the console before the server begin. - shouldWait int32 // non-zero means that the host should wait for unblocking - unblockChan chan struct{} + Server *http.Server + 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. + manuallyTLS bool // we need that in order to determinate what to output on the console before the server begin. + shouldWait int32 // non-zero means that the host should wait for unblocking + unblockChan chan struct{} mu sync.Mutex @@ -39,14 +40,11 @@ type Supervisor struct { // IgnoreErrors should contains the errors that should be ignored // on both serve functions return statements and error handlers. // - // i.e: http.ErrServerClosed.Error(). - // // Note that this will match the string value instead of the equality of the type's variables. // // Defaults to empty. IgnoredErrors []string onErr []func(error) - onShutdown []func() } // New returns a new host supervisor @@ -143,6 +141,10 @@ func (su *Supervisor) validateErr(err error) error { return nil } + if errors.Is(err, http.ErrServerClosed) && atomic.LoadUint32(&su.closedByInterruptHandler) > 0 { + return nil + } + su.mu.Lock() defer su.mu.Unlock() @@ -151,6 +153,7 @@ func (su *Supervisor) validateErr(err error) error { return nil } } + return err } @@ -189,6 +192,8 @@ func (su *Supervisor) supervise(blockFunc func() error) error { host := createTaskHost(su) su.notifyServe(host) + atomic.StoreUint32(&su.closedByInterruptHandler, 0) + atomic.StoreUint32(&su.closedManually, 0) err := blockFunc() su.notifyErr(err) @@ -319,7 +324,7 @@ func (su *Supervisor) ListenAndServeAutoTLS(domain string, email string, cacheDi // supervisor in order to close the "secondary redirect server" as well. su.RegisterOnShutdown(func() { // give it some time to close itself... - timeout := 5 * time.Second + timeout := 10 * time.Second ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() srv2.Shutdown(ctx) @@ -346,21 +351,10 @@ func (su *Supervisor) ListenAndServeAutoTLS(domain string, email string, cacheDi // undergone NPN/ALPN protocol upgrade or that have been hijacked. // This function should start protocol-specific graceful shutdown, // but should not wait for shutdown to complete. +// +// Callbacks will run as separate go routines. func (su *Supervisor) RegisterOnShutdown(cb func()) { - // when go1.9: replace the following lines with su.Server.RegisterOnShutdown(f) - su.mu.Lock() - su.onShutdown = append(su.onShutdown, cb) - su.mu.Unlock() -} - -func (su *Supervisor) notifyShutdown() { - // when go1.9: remove the lines below - su.mu.Lock() - for _, f := range su.onShutdown { - go f() - } - su.mu.Unlock() - // end + su.Server.RegisterOnShutdown(cb) } // Shutdown gracefully shuts down the server without interrupting any @@ -375,7 +369,11 @@ func (su *Supervisor) notifyShutdown() { // separately notify such long-lived connections of shutdown and wait // for them to close, if desired. func (su *Supervisor) Shutdown(ctx context.Context) error { - atomic.AddInt32(&su.closedManually, 1) // future-use - su.notifyShutdown() + atomic.StoreUint32(&su.closedManually, 1) // future-use return su.Server.Shutdown(ctx) } + +func (su *Supervisor) shutdownOnInterrupt(ctx context.Context) { + atomic.StoreUint32(&su.closedByInterruptHandler, 1) + su.Shutdown(ctx) +} diff --git a/core/host/supervisor_task_example_test.go b/core/host/supervisor_task_example_test.go index 18b8a30f..d0db8edd 100644 --- a/core/host/supervisor_task_example_test.go +++ b/core/host/supervisor_task_example_test.go @@ -87,7 +87,7 @@ func ExampleSupervisor_RegisterOnServe() { logger := log.New(os.Stdout, "Supervisor: ", 0) mytask := myTestTask{ - restartEvery: 6 * time.Second, + restartEvery: 3 * time.Second, maxRestarts: 2, logger: logger, } diff --git a/core/host/task.go b/core/host/task.go index f454ede3..83942446 100644 --- a/core/host/task.go +++ b/core/host/task.go @@ -6,6 +6,7 @@ package host // supervisor. import ( "context" + "errors" "fmt" "io" "net/http" @@ -27,8 +28,9 @@ func WriteStartupLogOnServe(w io.Writer) func(TaskHost) { if runtime.GOOS == "darwin" { interruptkey = "CMD" } - _, _ = w.Write([]byte(fmt.Sprintf("Now listening on: %s\nApplication started. Press %s+C to shut down.\n", - listeningURI, interruptkey))) + + _, _ = fmt.Fprintf(w, "Now listening on: %s\nApplication started. Press %s+C to shut down.\n", + listeningURI, interruptkey) } } @@ -38,7 +40,7 @@ func ShutdownOnInterrupt(su *Supervisor, shutdownTimeout time.Duration) func() { return func() { ctx, cancel := context.WithTimeout(context.TODO(), shutdownTimeout) defer cancel() - su.Shutdown(ctx) + su.shutdownOnInterrupt(ctx) su.RestoreFlow() } } @@ -58,9 +60,9 @@ func (h TaskHost) Serve() error { return err } - // if http.serverclosed ignroe the error, it will have this error + // if http.serverclosed ignore the error, it will have this error // from the previous close - if err := h.Supervisor.Server.Serve(l); err != http.ErrServerClosed { + if err := h.Supervisor.Server.Serve(l); !errors.Is(err, http.ErrServerClosed) { return err } return nil diff --git a/iris.go b/iris.go index 0a6cc223..ece3f40a 100644 --- a/iris.go +++ b/iris.go @@ -668,8 +668,8 @@ func (app *Application) NewHost(srv *http.Server) *host.Supervisor { } if !app.config.DisableInterruptHandler { - // when CTRL+C/CMD+C pressed. - shutdownTimeout := 5 * time.Second + // when CTRL/CMD+C pressed. + shutdownTimeout := 10 * time.Second host.RegisterOnInterrupt(host.ShutdownOnInterrupt(su, shutdownTimeout)) // app.logger.Debugf("Host: register server shutdown on interrupt(CTRL+C/CMD+C)") }