Refactor websocket session sync (#44)

* refactor websocket peer and comment functions.

* update comments.

* add DestroyWebSocketPeer.
This commit is contained in:
Miroslav Šedivý 2023-04-23 11:23:20 +02:00 committed by GitHub
parent 43f8fe339f
commit 0ea1c2870f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 61 deletions

View File

@ -142,7 +142,7 @@ func (manager *SessionManagerCtx) Delete(id string) error {
manager.sessionsMu.Unlock() manager.sessionsMu.Unlock()
if session.State().IsConnected { if session.State().IsConnected {
session.GetWebSocketPeer().Destroy("session deleted") session.DestroyWebSocketPeer("session deleted")
} }
if session.State().IsWatching { if session.State().IsWatching {

View File

@ -51,7 +51,7 @@ func (session *SessionCtx) profileChanged() {
} }
if (!session.profile.CanConnect || !session.profile.CanLogin) && session.state.IsConnected { if (!session.profile.CanConnect || !session.profile.CanLogin) && session.state.IsConnected {
session.GetWebSocketPeer().Destroy("profile changed") session.DestroyWebSocketPeer("profile changed")
} }
// update webrtc paused state // update webrtc paused state
@ -82,30 +82,49 @@ func (session *SessionCtx) SetCursor(cursor types.Cursor) {
// websocket // websocket
// --- // ---
func (session *SessionCtx) SetWebSocketPeer(websocketPeer types.WebSocketPeer) { //
// Connect WebSocket peer sets current peer and emits connected event. It also destroys the
// previous peer, if there was one. If the peer is already set, it will be ignored.
//
func (session *SessionCtx) ConnectWebSocketPeer(websocketPeer types.WebSocketPeer) {
session.websocketMu.Lock() session.websocketMu.Lock()
isCurrentPeer := websocketPeer == session.websocketPeer
session.websocketPeer, websocketPeer = websocketPeer, session.websocketPeer session.websocketPeer, websocketPeer = websocketPeer, session.websocketPeer
session.websocketMu.Unlock() session.websocketMu.Unlock()
if websocketPeer != nil && websocketPeer != session.websocketPeer { // ignore if already set
if isCurrentPeer {
return
}
session.logger.Info().Msg("set websocket connected")
session.state.IsConnected = true
session.manager.emmiter.Emit("connected", session)
// if there is a previous peer, destroy it
if websocketPeer != nil {
websocketPeer.Destroy("connection replaced") websocketPeer.Destroy("connection replaced")
} }
} }
func (session *SessionCtx) SetWebSocketConnected(websocketPeer types.WebSocketPeer, connected bool, delayed bool) { //
// Disconnect WebSocket peer sets current peer to nil and emits disconnected event. It also
// allows for a delayed disconnect. That means, the peer will not be disconnected immediately,
// but after a delay. If the peer is connected again before the delay, the disconnect will be
// cancelled.
//
// If the peer is not the current peer or the peer is nil, it will be ignored.
//
func (session *SessionCtx) DisconnectWebSocketPeer(websocketPeer types.WebSocketPeer, delayed bool) {
session.websocketMu.Lock() session.websocketMu.Lock()
isCurrentPeer := websocketPeer == session.websocketPeer isCurrentPeer := websocketPeer == session.websocketPeer && websocketPeer != nil
session.websocketMu.Unlock() session.websocketMu.Unlock()
// ignore if not current peer
if !isCurrentPeer { if !isCurrentPeer {
return return
} }
session.logger.Info().
Bool("connected", connected).
Bool("delayed", delayed).
Msg("set websocket connected")
// //
// ws delayed // ws delayed
// //
@ -114,7 +133,7 @@ func (session *SessionCtx) SetWebSocketConnected(websocketPeer types.WebSocketPe
if delayed { if delayed {
wsDelayedTimer = time.AfterFunc(WS_DELAYED_DURATION, func() { wsDelayedTimer = time.AfterFunc(WS_DELAYED_DURATION, func() {
session.SetWebSocketConnected(websocketPeer, connected, false) session.DisconnectWebSocketPeer(websocketPeer, false)
}) })
} }
@ -126,6 +145,7 @@ func (session *SessionCtx) SetWebSocketConnected(websocketPeer types.WebSocketPe
session.wsDelayedMu.Unlock() session.wsDelayedMu.Unlock()
if delayed { if delayed {
session.logger.Info().Msg("delayed websocket disconnected")
return return
} }
@ -133,13 +153,8 @@ func (session *SessionCtx) SetWebSocketConnected(websocketPeer types.WebSocketPe
// not delayed // not delayed
// //
session.state.IsConnected = connected session.logger.Info().Msg("set websocket disconnected")
session.state.IsConnected = false
if connected {
session.manager.emmiter.Emit("connected", session)
return
}
session.manager.emmiter.Emit("disconnected", session) session.manager.emmiter.Emit("disconnected", session)
session.websocketMu.Lock() session.websocketMu.Lock()
@ -149,15 +164,34 @@ func (session *SessionCtx) SetWebSocketConnected(websocketPeer types.WebSocketPe
session.websocketMu.Unlock() session.websocketMu.Unlock()
} }
func (session *SessionCtx) GetWebSocketPeer() types.WebSocketPeer { //
// Destroy WebSocket peer disconnects the peer and destroys it. It ensures that the peer is
// disconnected immediately even though normal flow would be to disconnect it delayed.
//
func (session *SessionCtx) DestroyWebSocketPeer(reason string) {
session.websocketMu.Lock() session.websocketMu.Lock()
defer session.websocketMu.Unlock() peer := session.websocketPeer
session.websocketMu.Unlock()
return session.websocketPeer if peer == nil {
return
} }
// disconnect peer first, so that it is not used anymore
session.DisconnectWebSocketPeer(peer, false)
// destroy it afterwards
peer.Destroy(reason)
}
//
// Send event to websocket peer.
//
func (session *SessionCtx) Send(event string, payload any) { func (session *SessionCtx) Send(event string, payload any) {
peer := session.GetWebSocketPeer() session.websocketMu.Lock()
peer := session.websocketPeer
session.websocketMu.Unlock()
if peer != nil { if peer != nil {
peer.Send(event, payload) peer.Send(event, payload)
} }
@ -167,6 +201,9 @@ func (session *SessionCtx) Send(event string, payload any) {
// webrtc // webrtc
// --- // ---
//
// Set webrtc peer and destroy the old one, if there is old one.
//
func (session *SessionCtx) SetWebRTCPeer(webrtcPeer types.WebRTCPeer) { func (session *SessionCtx) SetWebRTCPeer(webrtcPeer types.WebRTCPeer) {
session.webrtcMu.Lock() session.webrtcMu.Lock()
session.webrtcPeer, webrtcPeer = webrtcPeer, session.webrtcPeer session.webrtcPeer, webrtcPeer = webrtcPeer, session.webrtcPeer
@ -177,6 +214,14 @@ func (session *SessionCtx) SetWebRTCPeer(webrtcPeer types.WebRTCPeer) {
} }
} }
//
// Set if current webrtc peer is connected or not. Since there might be lefover calls from
// webrtc peer, that are not used anymore, we need to check if the webrtc peer is still the
// same as the one we are setting the connected state for.
//
// If webrtc peer is disconnected, we don't expect it to be reconnected, so we set it to nil
// and send a signal close to the client. New connection is expected to use a new webrtc peer.
//
func (session *SessionCtx) SetWebRTCConnected(webrtcPeer types.WebRTCPeer, connected bool) { func (session *SessionCtx) SetWebRTCConnected(webrtcPeer types.WebRTCPeer, connected bool) {
session.webrtcMu.Lock() session.webrtcMu.Lock()
isCurrentPeer := webrtcPeer == session.webrtcPeer isCurrentPeer := webrtcPeer == session.webrtcPeer
@ -209,6 +254,9 @@ func (session *SessionCtx) SetWebRTCConnected(webrtcPeer types.WebRTCPeer, conne
} }
} }
//
// Get current WebRTC peer. Nil if not connected.
//
func (session *SessionCtx) GetWebRTCPeer() types.WebRTCPeer { func (session *SessionCtx) GetWebRTCPeer() types.WebRTCPeer {
session.webrtcMu.Lock() session.webrtcMu.Lock()
defer session.webrtcMu.Unlock() defer session.webrtcMu.Unlock()

View File

@ -207,19 +207,18 @@ func (manager *WebSocketManagerCtx) Upgrade(checkOrigin types.CheckOrigin) types
} }
func (manager *WebSocketManagerCtx) connect(connection *websocket.Conn, r *http.Request) { func (manager *WebSocketManagerCtx) connect(connection *websocket.Conn, r *http.Request) {
// create new peer
peer := newPeer(connection)
session, err := manager.sessions.Authenticate(r) session, err := manager.sessions.Authenticate(r)
if err != nil { if err != nil {
manager.logger.Warn().Err(err).Msg("authentication failed") manager.logger.Warn().Err(err).Msg("authentication failed")
peer.Destroy(err.Error()) newPeer(manager.logger, connection).Destroy(err.Error())
return return
} }
// add session id to all log messages // add session id to all log messages
logger := manager.logger.With().Str("session_id", session.ID()).Logger() logger := manager.logger.With().Str("session_id", session.ID()).Logger()
peer.setSessionID(session.ID())
// create new peer
peer := newPeer(logger, connection)
if !session.Profile().CanConnect { if !session.Profile().CanConnect {
logger.Warn().Msg("connection disabled") logger.Warn().Msg("connection disabled")
@ -238,14 +237,12 @@ func (manager *WebSocketManagerCtx) connect(connection *websocket.Conn, r *http.
logger.Info().Msg("replacing peer connection") logger.Info().Msg("replacing peer connection")
} }
session.SetWebSocketPeer(peer)
logger.Info(). logger.Info().
Str("address", connection.RemoteAddr().String()). Str("address", connection.RemoteAddr().String()).
Str("agent", r.UserAgent()). Str("agent", r.UserAgent()).
Msg("connection started") Msg("connection started")
session.SetWebSocketConnected(peer, true, false) session.ConnectWebSocketPeer(peer)
// this is a blocking function that lives // this is a blocking function that lives
// throughout whole websocket connection // throughout whole websocket connection
@ -277,7 +274,7 @@ func (manager *WebSocketManagerCtx) connect(connection *websocket.Conn, r *http.
} }
} }
session.SetWebSocketConnected(peer, false, delayedDisconnect) session.DisconnectWebSocketPeer(peer, delayedDisconnect)
} }
func (manager *WebSocketManagerCtx) handle(connection *websocket.Conn, peer types.WebSocketPeer, session types.Session) error { func (manager *WebSocketManagerCtx) handle(connection *websocket.Conn, peer types.WebSocketPeer, session types.Session) error {

View File

@ -2,12 +2,10 @@ package websocket
import ( import (
"encoding/json" "encoding/json"
"errors"
"sync" "sync"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/demodesk/neko/pkg/types" "github.com/demodesk/neko/pkg/types"
"github.com/demodesk/neko/pkg/types/event" "github.com/demodesk/neko/pkg/types/event"
@ -21,30 +19,17 @@ type WebSocketPeerCtx struct {
connection *websocket.Conn connection *websocket.Conn
} }
func newPeer(connection *websocket.Conn) *WebSocketPeerCtx { func newPeer(logger zerolog.Logger, connection *websocket.Conn) *WebSocketPeerCtx {
logger := log.With().
Str("module", "websocket").
Str("submodule", "peer").
Logger()
return &WebSocketPeerCtx{ return &WebSocketPeerCtx{
logger: logger, logger: logger.With().Str("submodule", "peer").Logger(),
connection: connection, connection: connection,
} }
} }
func (peer *WebSocketPeerCtx) setSessionID(sessionId string) {
peer.logger = peer.logger.With().Str("session_id", sessionId).Logger()
}
func (peer *WebSocketPeerCtx) Send(event string, payload any) { func (peer *WebSocketPeerCtx) Send(event string, payload any) {
peer.mu.Lock() peer.mu.Lock()
defer peer.mu.Unlock() defer peer.mu.Unlock()
if peer.connection == nil {
return
}
raw, err := json.Marshal(payload) raw, err := json.Marshal(payload)
if err != nil { if err != nil {
peer.logger.Err(err).Str("event", event).Msg("message marshalling has failed") peer.logger.Err(err).Str("event", event).Msg("message marshalling has failed")
@ -79,10 +64,6 @@ func (peer *WebSocketPeerCtx) Ping() error {
peer.mu.Lock() peer.mu.Lock()
defer peer.mu.Unlock() defer peer.mu.Unlock()
if peer.connection == nil {
return errors.New("peer connection not found")
}
// application level heartbeat // application level heartbeat
if err := peer.connection.WriteJSON(types.WebSocketMessage{ if err := peer.connection.WriteJSON(types.WebSocketMessage{
Event: event.SYSTEM_HEARTBEAT, Event: event.SYSTEM_HEARTBEAT,
@ -103,9 +84,6 @@ func (peer *WebSocketPeerCtx) Destroy(reason string) {
peer.mu.Lock() peer.mu.Lock()
defer peer.mu.Unlock() defer peer.mu.Unlock()
if peer.connection != nil {
err := peer.connection.Close() err := peer.connection.Close()
peer.logger.Err(err).Msg("peer connection destroyed") peer.logger.Err(err).Msg("peer connection destroyed")
peer.connection = nil
}
} }

View File

@ -46,9 +46,9 @@ type Session interface {
SetCursor(cursor Cursor) SetCursor(cursor Cursor)
// websocket // websocket
SetWebSocketPeer(websocketPeer WebSocketPeer) ConnectWebSocketPeer(websocketPeer WebSocketPeer)
SetWebSocketConnected(websocketPeer WebSocketPeer, connected bool, delayed bool) DisconnectWebSocketPeer(websocketPeer WebSocketPeer, delayed bool)
GetWebSocketPeer() WebSocketPeer DestroyWebSocketPeer(reason string)
Send(event string, payload any) Send(event string, payload any)
// webrtc // webrtc