fix: resolve intermittent mouse control loss and add permission logging

Root cause: Session pointer inconsistency during RPC/HID message processing.
The RPC and HID queue handlers were fetching a fresh session copy from the
session manager instead of using the original session pointer. This caused
permission checks to fail when the session was promoted to primary, because
the Mode field was updated in the manager's copy but not reflected in the
queue handler's copy.

Changes:
- Revert RPC queue handler to use original session pointer (webrtc.go:320)
- Revert HID queue handler to use original session pointer (webrtc.go:196)
- Add debug logging for permission check failures (hidrpc.go:31-34, 57-61, 71-75)

This ensures that when a session's Mode is updated in the session manager,
the change is immediately visible to all message handlers, preventing the
race condition where mouse/keyboard input would be silently dropped due to
HasPermission() checks failing on stale session state.

The permission logging will help diagnose any remaining edge cases where
input is blocked unexpectedly.
This commit is contained in:
Alex P 2025-10-14 23:35:36 +03:00
parent 8d51aaa8eb
commit 827decf803
2 changed files with 14 additions and 18 deletions

View File

@ -28,6 +28,10 @@ func handleHidRPCMessage(message hidrpc.Message, session *Session) {
session.hidRPCAvailable = true
case hidrpc.TypeKeypressReport, hidrpc.TypeKeyboardReport:
if !session.HasPermission(PermissionKeyboardInput) {
logger.Debug().
Str("sessionID", session.ID).
Str("mode", string(session.Mode)).
Msg("keyboard input blocked: session lacks PermissionKeyboardInput")
return
}
rpcErr = handleHidRPCKeyboardInput(message)
@ -54,6 +58,10 @@ func handleHidRPCMessage(message hidrpc.Message, session *Session) {
rpcErr = handleHidRPCKeypressKeepAlive(session)
case hidrpc.TypePointerReport:
if !session.HasPermission(PermissionMouseInput) {
logger.Debug().
Str("sessionID", session.ID).
Str("mode", string(session.Mode)).
Msg("pointer report blocked: session lacks PermissionMouseInput")
return
}
pointerReport, err := message.PointerReport()
@ -64,6 +72,10 @@ func handleHidRPCMessage(message hidrpc.Message, session *Session) {
rpcErr = rpcAbsMouseReport(int16(pointerReport.X), int16(pointerReport.Y), pointerReport.Button)
case hidrpc.TypeMouseReport:
if !session.HasPermission(PermissionMouseInput) {
logger.Debug().
Str("sessionID", session.ID).
Str("mode", string(session.Mode)).
Msg("mouse report blocked: session lacks PermissionMouseInput")
return
}
mouseReport, err := message.MouseReport()

View File

@ -193,16 +193,9 @@ func (s *Session) initQueues() {
func (s *Session) handleQueues(index int) {
for msg := range s.hidQueue[index] {
// Get current session from manager to ensure we have the latest state
currentSession := sessionManager.GetSession(s.ID)
if currentSession != nil {
onHidMessage(msg, currentSession)
} else {
// Session was removed, use original to avoid nil panic
onHidMessage(msg, s)
}
}
}
const keysDownStateQueueSize = 64
@ -324,16 +317,7 @@ func newSession(config SessionConfig) (*Session, error) {
go func() {
for msg := range session.rpcQueue {
// TODO: only use goroutine if the task is asynchronous
go func(m webrtc.DataChannelMessage) {
// Get current session from manager to ensure we have the latest state
currentSession := sessionManager.GetSession(session.ID)
if currentSession != nil {
onRPCMessage(m, currentSession)
} else {
// Session was removed, use original to avoid nil panic
onRPCMessage(m, session)
}
}(msg)
go onRPCMessage(msg, session)
}
}()