Code is more robust and should now never try to send a notification to a web client that can block, using a context with cancellation.

This commit is contained in:
Erik Brakkee 2024-08-10 17:27:50 +02:00
parent 55468c7135
commit 1983ee24de
2 changed files with 16 additions and 5 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"context"
"converge/pkg/server/converge"
"converge/pkg/support/websocketutil"
"fmt"
@ -134,13 +135,14 @@ func main() {
// for the web browser getting live status updates.
sessionService := websocketutil.WebSocketService{
Handler: func(w http.ResponseWriter, r *http.Request, conn net.Conn) {
websession := websessions.NewSession(conn)
ctx, cancel := context.WithCancel(context.Background())
websession := websessions.NewSession(conn, ctx)
defer websessions.SessionClosed(websession)
location, err := converge.GetUserLocation(r)
if err != nil {
panic(err)
}
websession.WriteNotifications(location)
websession.WriteNotifications(location, cancel)
},
Text: true,
}

View File

@ -20,6 +20,7 @@ type WebSessions struct {
type WebSession struct {
notifications chan *models.State
conn net.Conn
ctx context.Context
}
func NewWebSessions(notifications chan *models.State) *WebSessions {
@ -41,16 +42,22 @@ func (sessions *WebSessions) notifyWebSessions(notification *models.State) {
defer sessions.mutex.Unlock()
sessions.lastNotification = notification
for session, _ := range sessions.sessions {
session.notifications <- notification
select {
case <-session.ctx.Done():
// session is closed, will be removed at higher level when session is done.
case session.notifications <- notification:
// Sent notification
}
}
}
func (sessions *WebSessions) NewSession(wsConnection net.Conn) *WebSession {
func (sessions *WebSessions) NewSession(wsConnection net.Conn, ctx context.Context) *WebSession {
sessions.mutex.Lock()
defer sessions.mutex.Unlock()
session := &WebSession{
notifications: make(chan *models.State, 10),
conn: wsConnection,
ctx: ctx,
}
sessions.sessions[session] = true
sessions.logSessions()
@ -70,9 +77,11 @@ func GetUserLocation(r *http.Request) (*time.Location, error) {
return time.LoadLocation(tzName)
}
func (session *WebSession) WriteNotifications(location *time.Location) {
func (session *WebSession) WriteNotifications(location *time.Location, cancel context.CancelFunc) {
timer := time.NewTicker(10 * time.Second)
defer timer.Stop()
// if for some reason we cannot send notifications to the web client then the context is canceled.
defer cancel()
for {
select {
case notification, ok := <-session.notifications: