fix: resolve concurrent write crash on gorilla/websocket by adding wsWriteMu mutex
All checks were successful
Deploy on Master Change / deploy (push) Successful in 42s

This commit is contained in:
2026-07-01 13:42:13 +07:00
parent 604ce9ea16
commit a3b986674e
3 changed files with 36 additions and 40 deletions

BIN
client/.DS_Store vendored

Binary file not shown.

View File

@@ -140,6 +140,7 @@ type App struct {
chatUnread int chatUnread int
replyStaffID uint replyStaffID uint
fetchAppsMu sync.Mutex fetchAppsMu sync.Mutex
wsWriteMu sync.Mutex
} }
type LocalStats struct { type LocalStats struct {
@@ -608,23 +609,32 @@ func (a *App) GetStats() map[string]any {
} }
} }
// safeWriteWS writes a message to the WebSocket connection thread-safely
func (a *App) safeWriteWS(msg any) error {
a.mu.Lock()
conn := a.wsConn
a.mu.Unlock()
if conn == nil {
return errors.New("websocket connection is nil")
}
a.wsWriteMu.Lock()
defer a.wsWriteMu.Unlock()
return conn.WriteJSON(msg)
}
// SendWebcamFrame truyền webcam frame từ JS frontend lên máy chủ qua WS // SendWebcamFrame truyền webcam frame từ JS frontend lên máy chủ qua WS
func (a *App) SendWebcamFrame(frameBase64 string) { func (a *App) SendWebcamFrame(frameBase64 string) {
if !a.isMonitoringActive() { if !a.isMonitoringActive() {
return return
} }
a.mu.Lock() _ = a.safeWriteWS(map[string]any{
conn := a.wsConn "event": "webcam_stream_frame",
a.mu.Unlock() "data": map[string]any{
"imageBuffer": frameBase64,
if conn != nil { },
_ = conn.WriteJSON(map[string]any{ })
"event": "webcam_stream_frame",
"data": map[string]any{
"imageBuffer": frameBase64,
},
})
}
} }
// authStorageScanner chỉ quét localStorage khi người dùng chủ động mở trang đăng nhập // authStorageScanner chỉ quét localStorage khi người dùng chủ động mở trang đăng nhập
@@ -1225,18 +1235,12 @@ func (a *App) startScreenshotStream() {
continue continue
} }
a.mu.Lock() _ = a.safeWriteWS(map[string]any{
conn := a.wsConn "event": "screenshot_stream_frame",
a.mu.Unlock() "data": map[string]any{
"imageBuffer": frame,
if conn != nil { },
_ = conn.WriteJSON(map[string]any{ })
"event": "screenshot_stream_frame",
"data": map[string]any{
"imageBuffer": frame,
},
})
}
case <-a.streamScStop: case <-a.streamScStop:
return return
} }
@@ -1301,22 +1305,14 @@ func (a *App) startWebcamStream() {
emptyCount = 0 emptyCount = 0
sentCount++ sentCount++
a.mu.Lock() err := a.safeWriteWS(map[string]any{
conn := a.wsConn "event": "webcam_stream_frame",
a.mu.Unlock() "data": map[string]any{
"imageBuffer": frame,
if conn != nil { },
err := conn.WriteJSON(map[string]any{ })
"event": "webcam_stream_frame", if sentCount <= 5 {
"data": map[string]any{ log.Printf("[WS] Webcam frame #%d sent (len=%d, err=%v)", sentCount, len(frame), err)
"imageBuffer": frame,
},
})
if sentCount <= 5 {
log.Printf("[WS] Webcam frame #%d sent (len=%d, err=%v)", sentCount, len(frame), err)
}
} else if sentCount <= 5 {
log.Printf("[WS] Webcam frame #%d: no WS connection", sentCount)
} }
case <-a.streamCamStop: case <-a.streamCamStop:
return return

BIN
client/build/.DS_Store vendored

Binary file not shown.