diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 680ae776..6f8fbba2 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,4 +1,4 @@
-- Version : **6.0.8**
+- Version : **6.0.9**
- Operating System:
diff --git a/HISTORY.md b/HISTORY.md
index ab18f2b4..431f0edc 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -2,6 +2,29 @@
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
+## 6.0.8 -> 6.0.9
+
+- Add `PostInterrupt` plugin, useful for customization of the **os.Interrupt** singal, before that Iris closed the server automatically.
+
+```go
+iris.Plugins.PostInterrupt(func(s *Framework){
+ // when os.Interrupt signal is fired the body of this function will be fired,
+ // you're responsible for closing the server with s.Close()
+
+ // if that event is not registered then the framework
+ // will close the server for you.
+
+
+
+ /* Do any custom cleanup and finally call the s.Close()
+ remember you have the iris.Plugins.PreClose(func(s *Framework)) event too
+ so you can split your logic in two logically places.
+ */
+
+})
+
+```
+
## 6.0.7 -> 6.0.8
- Add `iris.UseTemplateFunc(functionName string, function interface{})`. You could always set custom template funcs by using each of [template engine's](https://github.com/kataras/go-template) configuration but this function will help newcomers to start creating their custom template funcs.
diff --git a/README.md b/README.md
index 73b6e6ae..349e8567 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@
-
+
@@ -129,7 +129,7 @@ $ go run hellojson.go
```
> TIP #1> $ iris run main.go to enable hot-reload on .go source code changes.
-> TIP #2> iris.Config.IsDevelopment = true to monitor the changes you make in the templates.
+> TIP #2> iris.Config.IsDevelopment = true to monitor the changes you make in the templates.
Open your browser or any other http client at http://localhost:6000/api/user/42.
@@ -947,7 +947,7 @@ I recommend testing your API using this new library, [httpexpect](https://github
Versioning
------------
-Current: **v6.0.8**
+Current: **v6.0.9**
Older: **[v5/fasthttp](https://github.com/kataras/iris/tree/5.0.0)**
diff --git a/iris.go b/iris.go
index a5c0857c..b578f7b0 100644
--- a/iris.go
+++ b/iris.go
@@ -66,6 +66,7 @@ import (
"strconv"
"strings"
"sync"
+ "sync/atomic"
"time"
"github.com/kataras/go-errors"
@@ -80,7 +81,7 @@ const (
// IsLongTermSupport flag is true when the below version number is a long-term-support version
IsLongTermSupport = false
// Version is the current version number of the Iris web framework
- Version = "6.0.8"
+ Version = "6.0.9"
banner = ` _____ _
|_ _| (_)
@@ -478,21 +479,51 @@ func (s *Framework) Serve(ln net.Listener) error {
}
// maybe a 'race' here but user should not call .Serve more than one time especially in more than one go routines...
s.ln = ln
-
+ // build the handler and all other components
s.Build()
+ // fire all PreListen plugins
s.Plugins.DoPreListen(s)
- // This didn't helped me ,here, but maybe can help you:
- // https://www.oreilly.com/learning/run-strikingly-fast-parallel-file-searches-in-go-with-sync-errgroup?utm_source=golangweekly&utm_medium=email
- // new experimental package: errgroup
-
+ // catch any panics to the user defined logger.
defer func() {
if err := recover(); err != nil {
s.Logger.Panic(err)
}
}()
- // start the server in goroutine, .Available will block instead
- go func() { s.Must(s.srv.Serve(ln)) }()
+
+ // prepare for 'after serve' actions
+ var stop uint32
+ go func() {
+ // wait for the server's Serve func (309 mill is a lot or not, I found that this number is the perfect for most of the environments.)
+ time.Sleep(309 * time.Millisecond)
+ if atomic.LoadUint32(&stop) > 0 {
+ return
+ }
+ // print the banner
+ // fire the PostListen plugins
+ // wait for system channel interrupt
+ // fire the PostInterrupt plugins or close and exit.
+ s.postServe()
+ }()
+
+ serverStartUpErr := s.srv.Serve(ln)
+ if serverStartUpErr != nil {
+ // if an error then it would be nice to stop the banner and all next plugin events.
+ atomic.AddUint32(&stop, 1)
+ }
+ // finally return the error or block here, remember,
+ // you can always use the iris.Available to 'see' if and when the server is up (virtually),
+ // until go1.8 these are our best options.
+ return serverStartUpErr
+}
+
+// runs only when server starts without errors
+// what it does?
+// 0. print the banner
+// 1. fire the PostListen plugins
+// 2. wait for system channel interrupt
+// 3. fire the PostInterrupt plugins or close and exit.
+func (s *Framework) postServe() {
if !s.Config.DisableBanner {
bannerMessage := fmt.Sprintf("%s: Running at %s", time.Now().Format(s.Config.TimeFormat), s.Config.VHost)
@@ -506,17 +537,22 @@ func (s *Framework) Serve(ln net.Listener) error {
s.Plugins.DoPostListen(s)
go func() { s.Available <- true }()
+
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt)
<-ch
- if err := s.Close(); err != nil {
- if s.Config.IsDevelopment {
- s.Logger.Printf("Error while closing the server: %s\n", err)
+ // catch custom plugin event for interrupt
+ s.Plugins.DoPostInterrupt(s)
+ if !s.Plugins.PostInterruptFired() {
+ // if no PostInterrupt events fired, then I assume that the user doesn't cares
+ // so close the server automatically.
+ if err := s.Close(); err != nil {
+ if s.Config.IsDevelopment {
+ s.Logger.Printf("Error while closing the server: %s\n", err)
+ }
}
- return err
+ os.Exit(1)
}
- os.Exit(1)
- return nil
}
// Listen starts the standalone http server
diff --git a/plugin.go b/plugin.go
index 9c9c3a8a..dfb8640e 100644
--- a/plugin.go
+++ b/plugin.go
@@ -89,6 +89,20 @@ type (
}
// PostListenFunc implements the simple function listener for the PostListen(*Framework)
PostListenFunc func(*Framework)
+
+ // pluginPostInterrupt implements the PostInterrupt(*Framework) method
+ pluginPostInterrupt interface {
+ // PostInterrupt it's being called only one time, when os.Interrupt system event catched
+ // graceful shutdown can be done here
+ //
+ // Read more here: https://github.com/kataras/iris/blob/master/HISTORY.md#608---609
+ PostInterrupt(*Framework)
+ }
+ // PostInterruptFunc implements the simple function listener for the PostInterrupt(*Framework)
+ //
+ // Read more here: https://github.com/kataras/iris/blob/master/HISTORY.md#608---609
+ PostInterruptFunc func(*Framework)
+
// pluginPreClose implements the PreClose(*Framework) method
pluginPreClose interface {
// PreClose it's being called only one time, BEFORE the Iris .Close method
@@ -139,6 +153,9 @@ type (
PostListen(PostListenFunc)
DoPostListen(*Framework)
PostListenFired() bool
+ PostInterrupt(PostInterruptFunc)
+ DoPostInterrupt(*Framework)
+ PostInterruptFired() bool
PreClose(PreCloseFunc)
DoPreClose(*Framework)
PreCloseFired() bool
@@ -520,6 +537,29 @@ func (p *pluginContainer) PostListenFired() bool {
return p.Fired("postlisten") > 0
}
+// PostInterrupt adds a PostInterrupt plugin-function to the plugin flow container
+//
+// Read more here: https://github.com/kataras/iris/blob/master/HISTORY.md#608---609
+func (p *pluginContainer) PostInterrupt(fn PostInterruptFunc) {
+ p.Add(fn)
+}
+
+// DoPostInterrupt raise all plugins which has the PostInterrupt method
+func (p *pluginContainer) DoPostInterrupt(station *Framework) {
+ for i := range p.activatedPlugins {
+ // check if this method exists on our plugin obj, these are optionaly and call it
+ if pluginObj, ok := p.activatedPlugins[i].(pluginPostInterrupt); ok {
+ pluginObj.PostInterrupt(station)
+ p.fire("postinterrupt")
+ }
+ }
+}
+
+// PostInterruptFired returns true if PostInterrupt event/ plugin type is fired at least one time
+func (p *pluginContainer) PostInterruptFired() bool {
+ return p.Fired("postinterrupt") > 0
+}
+
// PreClose adds a PreClose plugin-function to the plugin flow container
func (p *pluginContainer) PreClose(fn PreCloseFunc) {
p.Add(fn)