mirror of
https://github.com/m1k1o/neko.git
synced 2025-06-19 19:22:35 +02:00
implement client heartbeat #460.
This commit is contained in:
parent
5169e0aad3
commit
3082d3241b
14 changed files with 62 additions and 7 deletions
|
@ -20,6 +20,7 @@ export interface BaseEvents {
|
||||||
|
|
||||||
export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||||
protected _ws?: WebSocket
|
protected _ws?: WebSocket
|
||||||
|
protected _ws_heartbeat?: number
|
||||||
protected _peer?: RTCPeerConnection
|
protected _peer?: RTCPeerConnection
|
||||||
protected _channel?: RTCDataChannel
|
protected _channel?: RTCDataChannel
|
||||||
protected _timeout?: number
|
protected _timeout?: number
|
||||||
|
@ -82,6 +83,11 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||||
this._timeout = undefined
|
this._timeout = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._ws_heartbeat) {
|
||||||
|
clearInterval(this._ws_heartbeat)
|
||||||
|
this._ws_heartbeat = undefined
|
||||||
|
}
|
||||||
|
|
||||||
if (this._ws) {
|
if (this._ws) {
|
||||||
// reset all events
|
// reset all events
|
||||||
this._ws.onmessage = () => {}
|
this._ws.onmessage = () => {}
|
||||||
|
|
|
@ -14,6 +14,9 @@ export const EVENT = {
|
||||||
DISCONNECT: 'system/disconnect',
|
DISCONNECT: 'system/disconnect',
|
||||||
ERROR: 'system/error',
|
ERROR: 'system/error',
|
||||||
},
|
},
|
||||||
|
CLIENT: {
|
||||||
|
HEARTBEAT: 'client/heartbeat'
|
||||||
|
},
|
||||||
SIGNAL: {
|
SIGNAL: {
|
||||||
OFFER: 'signal/offer',
|
OFFER: 'signal/offer',
|
||||||
ANSWER: 'signal/answer',
|
ANSWER: 'signal/answer',
|
||||||
|
@ -69,6 +72,7 @@ export type Events = typeof EVENT
|
||||||
|
|
||||||
export type WebSocketEvents =
|
export type WebSocketEvents =
|
||||||
| SystemEvents
|
| SystemEvents
|
||||||
|
| ClientEvents
|
||||||
| ControlEvents
|
| ControlEvents
|
||||||
| MemberEvents
|
| MemberEvents
|
||||||
| SignalEvents
|
| SignalEvents
|
||||||
|
@ -87,6 +91,7 @@ export type ControlEvents =
|
||||||
| typeof EVENT.CONTROL.KEYBOARD
|
| typeof EVENT.CONTROL.KEYBOARD
|
||||||
|
|
||||||
export type SystemEvents = typeof EVENT.SYSTEM.DISCONNECT
|
export type SystemEvents = typeof EVENT.SYSTEM.DISCONNECT
|
||||||
|
export type ClientEvents = typeof EVENT.CLIENT.HEARTBEAT
|
||||||
export type MemberEvents = typeof EVENT.MEMBER.LIST | typeof EVENT.MEMBER.CONNECTED | typeof EVENT.MEMBER.DISCONNECTED
|
export type MemberEvents = typeof EVENT.MEMBER.LIST | typeof EVENT.MEMBER.CONNECTED | typeof EVENT.MEMBER.DISCONNECTED
|
||||||
|
|
||||||
export type SignalEvents =
|
export type SignalEvents =
|
||||||
|
|
|
@ -134,7 +134,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// System Events
|
// System Events
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
protected [EVENT.SYSTEM.INIT]({ implicit_hosting, locks, file_transfer }: SystemInitPayload) {
|
protected [EVENT.SYSTEM.INIT]({ implicit_hosting, locks, file_transfer, heartbeat_interval }: SystemInitPayload) {
|
||||||
this.$accessor.remote.setImplicitHosting(implicit_hosting)
|
this.$accessor.remote.setImplicitHosting(implicit_hosting)
|
||||||
this.$accessor.remote.setFileTransfer(file_transfer)
|
this.$accessor.remote.setFileTransfer(file_transfer)
|
||||||
|
|
||||||
|
@ -145,6 +145,11 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||||
id: locks[resource],
|
id: locks[resource],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (heartbeat_interval > 0) {
|
||||||
|
if (this._ws_heartbeat) clearInterval(this._ws_heartbeat)
|
||||||
|
this._ws_heartbeat = window.setInterval(() => this.sendMessage(EVENT.CLIENT.HEARTBEAT), heartbeat_interval * 1000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected [EVENT.SYSTEM.DISCONNECT]({ message }: SystemMessagePayload) {
|
protected [EVENT.SYSTEM.DISCONNECT]({ message }: SystemMessagePayload) {
|
||||||
|
|
|
@ -61,6 +61,7 @@ export interface SystemInitPayload {
|
||||||
implicit_hosting: boolean
|
implicit_hosting: boolean
|
||||||
locks: Record<string, string>
|
locks: Record<string, string>
|
||||||
file_transfer: boolean
|
file_transfer: boolean
|
||||||
|
heartbeat_interval: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// system/disconnect
|
// system/disconnect
|
||||||
|
|
|
@ -18,6 +18,7 @@ type Session struct {
|
||||||
ImplicitHosting bool
|
ImplicitHosting bool
|
||||||
InactiveCursors bool
|
InactiveCursors bool
|
||||||
MercifulReconnect bool
|
MercifulReconnect bool
|
||||||
|
HeartbeatInterval int
|
||||||
APIToken string
|
APIToken string
|
||||||
|
|
||||||
CookieEnabled bool
|
CookieEnabled bool
|
||||||
|
@ -67,6 +68,11 @@ func (Session) Init(cmd *cobra.Command) error {
|
||||||
return err
|
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")
|
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 {
|
if err := viper.BindPFlag("session.api_token", cmd.PersistentFlags().Lookup("session.api_token")); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -112,6 +118,11 @@ func (Session) InitV2(cmd *cobra.Command) error {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +136,7 @@ func (s *Session) Set() {
|
||||||
s.ImplicitHosting = viper.GetBool("session.implicit_hosting")
|
s.ImplicitHosting = viper.GetBool("session.implicit_hosting")
|
||||||
s.InactiveCursors = viper.GetBool("session.inactive_cursors")
|
s.InactiveCursors = viper.GetBool("session.inactive_cursors")
|
||||||
s.MercifulReconnect = viper.GetBool("session.merciful_reconnect")
|
s.MercifulReconnect = viper.GetBool("session.merciful_reconnect")
|
||||||
|
s.HeartbeatInterval = viper.GetInt("session.heartbeat_interval")
|
||||||
s.APIToken = viper.GetString("session.api_token")
|
s.APIToken = viper.GetString("session.api_token")
|
||||||
|
|
||||||
s.CookieEnabled = viper.GetBool("session.cookie.enabled")
|
s.CookieEnabled = viper.GetBool("session.cookie.enabled")
|
||||||
|
@ -156,4 +168,8 @@ func (s *Session) SetV2() {
|
||||||
s.ControlProtection = viper.GetBool("control_protection")
|
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")
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,10 @@ const (
|
||||||
SYSTEM_ERROR = "system/error"
|
SYSTEM_ERROR = "system/error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CLIENT_HEARTBEAT = "client/heartbeat"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SIGNAL_OFFER = "signal/offer"
|
SIGNAL_OFFER = "signal/offer"
|
||||||
SIGNAL_ANSWER = "signal/answer"
|
SIGNAL_ANSWER = "signal/answer"
|
||||||
|
|
|
@ -11,10 +11,11 @@ type Message struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SystemInit struct {
|
type SystemInit struct {
|
||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
Locks map[string]string `json:"locks"`
|
Locks map[string]string `json:"locks"`
|
||||||
ImplicitHosting bool `json:"implicit_hosting"`
|
ImplicitHosting bool `json:"implicit_hosting"`
|
||||||
FileTransfer bool `json:"file_transfer"`
|
FileTransfer bool `json:"file_transfer"`
|
||||||
|
HeartbeatInterval int `json:"heartbeat_interval"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SystemMessage struct {
|
type SystemMessage struct {
|
||||||
|
|
|
@ -26,6 +26,11 @@ func (s *session) wsToBackend(msg []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch header.Event {
|
switch header.Event {
|
||||||
|
// Client Events
|
||||||
|
case oldEvent.CLIENT_HEARTBEAT:
|
||||||
|
// do nothing
|
||||||
|
return nil
|
||||||
|
|
||||||
// Signal Events
|
// Signal Events
|
||||||
case oldEvent.SIGNAL_OFFER:
|
case oldEvent.SIGNAL_OFFER:
|
||||||
request := &oldMessage.SignalOffer{}
|
request := &oldMessage.SignalOffer{}
|
||||||
|
|
|
@ -259,7 +259,8 @@ func (s *session) wsToClient(msg []byte) error {
|
||||||
ImplicitHosting: request.Settings.ImplicitHosting,
|
ImplicitHosting: request.Settings.ImplicitHosting,
|
||||||
Locks: locks,
|
Locks: locks,
|
||||||
// TODO: hack - we don't know if file transfer is enabled, we would need to check the global config.
|
// 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:
|
case event.SYSTEM_ADMIN:
|
||||||
|
|
|
@ -27,6 +27,7 @@ func New(config *config.Session) *SessionManagerCtx {
|
||||||
ImplicitHosting: config.ImplicitHosting,
|
ImplicitHosting: config.ImplicitHosting,
|
||||||
InactiveCursors: config.InactiveCursors,
|
InactiveCursors: config.InactiveCursors,
|
||||||
MercifulReconnect: config.MercifulReconnect,
|
MercifulReconnect: config.MercifulReconnect,
|
||||||
|
HeartbeatInterval: config.HeartbeatInterval,
|
||||||
},
|
},
|
||||||
tokens: make(map[string]string),
|
tokens: make(map[string]string),
|
||||||
sessions: make(map[string]*SessionCtx),
|
sessions: make(map[string]*SessionCtx),
|
||||||
|
|
|
@ -36,6 +36,10 @@ type MessageHandlerCtx struct {
|
||||||
func (h *MessageHandlerCtx) Message(session types.Session, data types.WebSocketMessage) bool {
|
func (h *MessageHandlerCtx) Message(session types.Session, data types.WebSocketMessage) bool {
|
||||||
var err error
|
var err error
|
||||||
switch data.Event {
|
switch data.Event {
|
||||||
|
// Client Events
|
||||||
|
case event.CLIENT_HEARTBEAT:
|
||||||
|
// do nothing
|
||||||
|
|
||||||
// System Events
|
// System Events
|
||||||
case event.SYSTEM_LOGS:
|
case event.SYSTEM_LOGS:
|
||||||
payload := &message.SystemLogs{}
|
payload := &message.SystemLogs{}
|
||||||
|
|
|
@ -31,8 +31,9 @@ const maxPayloadLogLength = 10_000
|
||||||
var nologEvents = []string{
|
var nologEvents = []string{
|
||||||
// don't log twice
|
// don't log twice
|
||||||
event.SYSTEM_LOGS,
|
event.SYSTEM_LOGS,
|
||||||
// don't log heartbeat
|
// don't log heartbeats
|
||||||
event.SYSTEM_HEARTBEAT,
|
event.SYSTEM_HEARTBEAT,
|
||||||
|
event.CLIENT_HEARTBEAT,
|
||||||
// don't log every cursor update
|
// don't log every cursor update
|
||||||
event.SESSION_CURSORS,
|
event.SESSION_CURSORS,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,10 @@ const (
|
||||||
SYSTEM_HEARTBEAT = "system/heartbeat"
|
SYSTEM_HEARTBEAT = "system/heartbeat"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CLIENT_HEARTBEAT = "client/heartbeat"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SIGNAL_REQUEST = "signal/request"
|
SIGNAL_REQUEST = "signal/request"
|
||||||
SIGNAL_RESTART = "signal/restart"
|
SIGNAL_RESTART = "signal/restart"
|
||||||
|
|
|
@ -47,6 +47,7 @@ type Settings struct {
|
||||||
ImplicitHosting bool `json:"implicit_hosting"`
|
ImplicitHosting bool `json:"implicit_hosting"`
|
||||||
InactiveCursors bool `json:"inactive_cursors"`
|
InactiveCursors bool `json:"inactive_cursors"`
|
||||||
MercifulReconnect bool `json:"merciful_reconnect"`
|
MercifulReconnect bool `json:"merciful_reconnect"`
|
||||||
|
HeartbeatInterval int `json:"heartbeat_interval"`
|
||||||
|
|
||||||
// plugin scope
|
// plugin scope
|
||||||
Plugins PluginSettings `json:"plugins"`
|
Plugins PluginSettings `json:"plugins"`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue