From 05d44ec1fad5f07a4ab1d5a4d3b41d32057ba609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Mon, 30 Dec 2024 13:09:43 +0100 Subject: [PATCH] implement client heartbeat #460. --- server/internal/config/session.go | 16 ++++++++++++++++ server/internal/http/legacy/event/events.go | 4 ++++ server/internal/http/legacy/message/messages.go | 9 +++++---- server/internal/http/legacy/wstobackend.go | 5 +++++ server/internal/http/legacy/wstoclient.go | 3 ++- server/internal/session/manager.go | 1 + server/internal/websocket/handler/handler.go | 4 ++++ server/internal/websocket/manager.go | 3 ++- server/pkg/types/event/events.go | 4 ++++ server/pkg/types/session.go | 1 + 10 files changed, 44 insertions(+), 6 deletions(-) diff --git a/server/internal/config/session.go b/server/internal/config/session.go index 08934ab4d..1414171e4 100644 --- a/server/internal/config/session.go +++ b/server/internal/config/session.go @@ -18,6 +18,7 @@ type Session struct { ImplicitHosting bool InactiveCursors bool MercifulReconnect bool + HeartbeatInterval int APIToken string CookieEnabled bool @@ -67,6 +68,11 @@ func (Session) Init(cmd *cobra.Command) error { return err } + cmd.PersistentFlags().Int("session.heartbeat_interval", 120, "interval in seconds for sending heartbeat messages") + if err := viper.BindPFlag("session.heartbeat_interval", cmd.PersistentFlags().Lookup("session.heartbeat_interval")); err != nil { + return err + } + cmd.PersistentFlags().String("session.api_token", "", "API token for interacting with external services") if err := viper.BindPFlag("session.api_token", cmd.PersistentFlags().Lookup("session.api_token")); err != nil { return err @@ -112,6 +118,11 @@ func (Session) InitV2(cmd *cobra.Command) error { return err } + cmd.PersistentFlags().Int("heartbeat_interval", 120, "heartbeat interval in seconds") + if err := viper.BindPFlag("heartbeat_interval", cmd.PersistentFlags().Lookup("heartbeat_interval")); err != nil { + return err + } + return nil } @@ -125,6 +136,7 @@ func (s *Session) Set() { s.ImplicitHosting = viper.GetBool("session.implicit_hosting") s.InactiveCursors = viper.GetBool("session.inactive_cursors") s.MercifulReconnect = viper.GetBool("session.merciful_reconnect") + s.HeartbeatInterval = viper.GetInt("session.heartbeat_interval") s.APIToken = viper.GetString("session.api_token") s.CookieEnabled = viper.GetBool("session.cookie.enabled") @@ -156,4 +168,8 @@ func (s *Session) SetV2() { s.ControlProtection = viper.GetBool("control_protection") log.Warn().Msg("you are using v2 configuration 'NEKO_CONTROL_PROTECTION' which is deprecated, please use 'NEKO_SESSION_CONTROL_PROTECTION' instead") } + if viper.IsSet("heartbeat_interval") { + s.HeartbeatInterval = viper.GetInt("heartbeat_interval") + log.Warn().Msg("you are using v2 configuration 'NEKO_HEARTBEAT_INTERVAL' which is deprecated, please use 'NEKO_SESSION_HEARTBEAT_INTERVAL' instead") + } } diff --git a/server/internal/http/legacy/event/events.go b/server/internal/http/legacy/event/events.go index 8032ba90f..3cc0b3aec 100644 --- a/server/internal/http/legacy/event/events.go +++ b/server/internal/http/legacy/event/events.go @@ -6,6 +6,10 @@ const ( SYSTEM_ERROR = "system/error" ) +const ( + CLIENT_HEARTBEAT = "client/heartbeat" +) + const ( SIGNAL_OFFER = "signal/offer" SIGNAL_ANSWER = "signal/answer" diff --git a/server/internal/http/legacy/message/messages.go b/server/internal/http/legacy/message/messages.go index 83921e669..708f89a9b 100644 --- a/server/internal/http/legacy/message/messages.go +++ b/server/internal/http/legacy/message/messages.go @@ -11,10 +11,11 @@ type Message struct { } type SystemInit struct { - Event string `json:"event"` - Locks map[string]string `json:"locks"` - ImplicitHosting bool `json:"implicit_hosting"` - FileTransfer bool `json:"file_transfer"` + Event string `json:"event"` + Locks map[string]string `json:"locks"` + ImplicitHosting bool `json:"implicit_hosting"` + FileTransfer bool `json:"file_transfer"` + HeartbeatInterval int `json:"heartbeat_interval"` } type SystemMessage struct { diff --git a/server/internal/http/legacy/wstobackend.go b/server/internal/http/legacy/wstobackend.go index 8ab4cd186..114fd5fbb 100644 --- a/server/internal/http/legacy/wstobackend.go +++ b/server/internal/http/legacy/wstobackend.go @@ -26,6 +26,11 @@ func (s *session) wsToBackend(msg []byte) error { } switch header.Event { + // Client Events + case oldEvent.CLIENT_HEARTBEAT: + // do nothing + return nil + // Signal Events case oldEvent.SIGNAL_OFFER: request := &oldMessage.SignalOffer{} diff --git a/server/internal/http/legacy/wstoclient.go b/server/internal/http/legacy/wstoclient.go index b3e877911..9e86d0556 100644 --- a/server/internal/http/legacy/wstoclient.go +++ b/server/internal/http/legacy/wstoclient.go @@ -259,7 +259,8 @@ func (s *session) wsToClient(msg []byte) error { ImplicitHosting: request.Settings.ImplicitHosting, Locks: locks, // TODO: hack - we don't know if file transfer is enabled, we would need to check the global config. - FileTransfer: viper.GetBool("filetransfer.enabled") || (viper.GetBool("legacy") && viper.GetBool("file_transfer_enabled")), + FileTransfer: viper.GetBool("filetransfer.enabled") || (viper.GetBool("legacy") && viper.GetBool("file_transfer_enabled")), + HeartbeatInterval: request.Settings.HeartbeatInterval, }) case event.SYSTEM_ADMIN: diff --git a/server/internal/session/manager.go b/server/internal/session/manager.go index 29bb8e4b1..40f1470d1 100644 --- a/server/internal/session/manager.go +++ b/server/internal/session/manager.go @@ -27,6 +27,7 @@ func New(config *config.Session) *SessionManagerCtx { ImplicitHosting: config.ImplicitHosting, InactiveCursors: config.InactiveCursors, MercifulReconnect: config.MercifulReconnect, + HeartbeatInterval: config.HeartbeatInterval, }, tokens: make(map[string]string), sessions: make(map[string]*SessionCtx), diff --git a/server/internal/websocket/handler/handler.go b/server/internal/websocket/handler/handler.go index 576584a56..22634ec0d 100644 --- a/server/internal/websocket/handler/handler.go +++ b/server/internal/websocket/handler/handler.go @@ -36,6 +36,10 @@ type MessageHandlerCtx struct { func (h *MessageHandlerCtx) Message(session types.Session, data types.WebSocketMessage) bool { var err error switch data.Event { + // Client Events + case event.CLIENT_HEARTBEAT: + // do nothing + // System Events case event.SYSTEM_LOGS: payload := &message.SystemLogs{} diff --git a/server/internal/websocket/manager.go b/server/internal/websocket/manager.go index 1d09fad45..b34e3eda6 100644 --- a/server/internal/websocket/manager.go +++ b/server/internal/websocket/manager.go @@ -31,8 +31,9 @@ const maxPayloadLogLength = 10_000 var nologEvents = []string{ // don't log twice event.SYSTEM_LOGS, - // don't log heartbeat + // don't log heartbeats event.SYSTEM_HEARTBEAT, + event.CLIENT_HEARTBEAT, // don't log every cursor update event.SESSION_CURSORS, } diff --git a/server/pkg/types/event/events.go b/server/pkg/types/event/events.go index 0259d4d79..91f005f87 100644 --- a/server/pkg/types/event/events.go +++ b/server/pkg/types/event/events.go @@ -9,6 +9,10 @@ const ( SYSTEM_HEARTBEAT = "system/heartbeat" ) +const ( + CLIENT_HEARTBEAT = "client/heartbeat" +) + const ( SIGNAL_REQUEST = "signal/request" SIGNAL_RESTART = "signal/restart" diff --git a/server/pkg/types/session.go b/server/pkg/types/session.go index fba64d8cb..25aa399af 100644 --- a/server/pkg/types/session.go +++ b/server/pkg/types/session.go @@ -47,6 +47,7 @@ type Settings struct { ImplicitHosting bool `json:"implicit_hosting"` InactiveCursors bool `json:"inactive_cursors"` MercifulReconnect bool `json:"merciful_reconnect"` + HeartbeatInterval int `json:"heartbeat_interval"` // plugin scope Plugins PluginSettings `json:"plugins"`