From e37e27036ee34792a0db6c7553d341e70d9b6f9e Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Sat, 12 Sep 2020 12:42:48 +0200 Subject: [PATCH] Implement graceful server shutdown. This gives the user a friendly message when the server shuts down. --- client.go | 4 ++++ group.go | 12 +++++++++++- sfu.go | 3 ++- webclient.go | 10 +++++++--- webserver.go | 19 +++++++++++++++++-- 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/client.go b/client.go index 32954cd..9873306 100644 --- a/client.go +++ b/client.go @@ -12,3 +12,7 @@ type client interface { pushConn(id string, conn upConnection, tracks []upTrack, label string) error pushClient(id, username string, add bool) error } + +type kickable interface { + kick(message string) error +} diff --git a/group.go b/group.go index a45bce9..a980ee7 100644 --- a/group.go +++ b/group.go @@ -64,7 +64,7 @@ type connectionFailedAction struct { type permissionsChangedAction struct{} -type kickAction struct{ +type kickAction struct { message string } @@ -288,6 +288,16 @@ func (g *group) Range(f func(c client) bool) { } } +func (g *group) shutdown(message string) { + g.Range(func(c client) bool { + cc, ok := c.(kickable) + if ok { + cc.kick(message) + } + return true + }) +} + const maxChatHistory = 20 func (g *group) clearChatHistory() { diff --git a/sfu.go b/sfu.go index 8f817d7..5368249 100644 --- a/sfu.go +++ b/sfu.go @@ -87,6 +87,7 @@ func main() { webserver() terminate := make(chan os.Signal, 1) - signal.Notify(terminate, syscall.SIGINT) + signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM) <-terminate + shutdown() } diff --git a/webclient.go b/webclient.go index 4a5beaa..e20e0db 100644 --- a/webclient.go +++ b/webclient.go @@ -943,6 +943,10 @@ func setPermissions(g *group, id string, perm string) error { return c.action(permissionsChangedAction{}) } +func (c *webClient) kick(message string) error { + return c.action(kickAction{message}) +} + func kickClient(g *group, id string, message string) error { g.mu.Lock() defer g.mu.Unlock() @@ -952,12 +956,12 @@ func kickClient(g *group, id string, message string) error { return userError("no such user") } - c, ok := client.(*webClient) + c, ok := client.(kickable) if !ok { - return userError("this is not a real user") + return userError("this client is not kickable") } - return c.action(kickAction{message}) + return c.kick(message) } func handleClientMessage(c *webClient, m clientMessage) error { diff --git a/webserver.go b/webserver.go index a512f93..007bd9d 100644 --- a/webserver.go +++ b/webserver.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "context" "encoding/json" "errors" "fmt" @@ -19,6 +20,8 @@ import ( "github.com/gorilla/websocket" ) +var server *http.Server + func webserver() { http.Handle("/", mungeHandler{http.FileServer(http.Dir(staticRoot))}) http.HandleFunc("/group/", groupHandler) @@ -39,17 +42,24 @@ func webserver() { http.HandleFunc("/stats", statsHandler) go func() { - server := &http.Server{ + server = &http.Server{ Addr: httpAddr, ReadHeaderTimeout: 60 * time.Second, IdleTimeout: 120 * time.Second, } + server.RegisterOnShutdown(func() { + groups.mu.Lock() + defer groups.mu.Unlock() + for _, g := range groups.groups { + go g.shutdown("server is shutting down") + } + }) var err error err = server.ListenAndServeTLS( filepath.Join(dataDir, "cert.pem"), filepath.Join(dataDir, "key.pem"), ) - if err != nil { + if err != nil && err != http.ErrServerClosed { log.Printf("ListenAndServeTLS: %v", err) } }() @@ -457,3 +467,8 @@ func serveGroupRecordings(w http.ResponseWriter, r *http.Request, f *os.File, gr fmt.Fprintf(w, "\n") fmt.Fprintf(w, "\n") } + +func shutdown() { + ctx, _ := context.WithTimeout(context.Background(), 2*time.Second) + server.Shutdown(ctx) +}