How CasaOS Manages WebSocket Connections for Real-Time Updates
CasaOS uses the gorilla/websocket library to upgrade HTTP requests at GET /notify/ws, stores active connections in a global slice named WebSocketConns, and broadcasts JSON notifications via a background goroutine that polls the notification store every two seconds.
CasaOS is an open-source Home Cloud system that delivers real-time UI updates through a lightweight WebSocket layer. According to the IceWhaleTech/CasaOS source code, the implementation relies on a combination of connection pooling and lazy-initialized background broadcasting to push notifications to connected clients without blocking the main application thread.
WebSocket Upgrade and Connection Pooling
The HTTP Endpoint Handler
In route/v1/notify_old.go, the application exposes a WebSocket endpoint at GET /notify/ws. The handler uses a permissive Upgrader to convert the HTTP connection into a WebSocket connection.
// route/v1/notify_old.go (lines 28-30)
ws, err := upGrader.Upgrade(ctx.Response().Writer, ctx.Request(), nil)
if err != nil {
return err
}
Global Connection Registry
Each new *websocket.Conn is appended to the global slice WebSocketConns declared in service/service.go (lines 29-30). This slice acts as a connection pool for the broadcaster to iterate over.
// service/service.go
var WebSocketConns []*websocket.Conn
In notify_old.go (line 34), the handler registers the new connection:
service.WebSocketConns = append(service.WebSocketConns, ws)
Background Broadcasting Architecture
Lazy-Initialized Broadcaster
To conserve resources, CasaOS starts the broadcast goroutine only when the first client connects. The boolean flag service.SocketRun (declared in service/service.go) ensures that only one instance of SendMeg() runs at a time.
In route/v1/notify_old.go (lines 36-38):
if !service.SocketRun {
service.SocketRun = true
service.SendMeg()
}
The Notification Polling Loop
The SendMeg() function in service/notify.go implements a polling-based broadcaster. It repeatedly queries the Notify service for unread application notifications (lines 298-299):
list := MyService.Notify().GetList(types.NOTIFY_APP)
The function marshals the notification list to JSON and broadcasts it to every connection in WebSocketConns. Closed connections are filtered out during the broadcast loop (lines 303-311):
var temp []*websocket.Conn
for _, v := range WebSocketConns {
err := v.WriteMessage(1, json)
if err == nil {
temp = append(temp, v)
}
}
WebSocketConns = temp
Graceful Shutdown
When the connection pool becomes empty, the broadcaster terminates itself by setting SocketRun = false (lines 316-318):
if len(WebSocketConns) == 0 {
SocketRun = false
return
}
The next client connection will trigger a new broadcaster instance.
Optional Socket.io Integration
While the core implementation uses raw WebSockets, the codebase includes a go-socket.io server stub in service/notify.go (lines 39-41) for future event-driven extensions. The primary notification path, however, relies on the goroutine-based broadcast mechanism described above.
Summary
- Connection Upgrading: CasaOS upgrades HTTP requests to WebSockets at
route/v1/notify_old.gousing thegorilla/websocketlibrary. - Global Pool: Active connections are stored in the
WebSocketConnsslice defined inservice/service.go. - Lazy Broadcasting: The
SendMeg()function inservice/notify.gostarts only when the first client connects and runs as a background goroutine. - Dead Connection Cleanup: The broadcaster filters out failed connections during each broadcast cycle, preventing memory leaks.
- Resource Efficiency: The broadcaster automatically stops when no clients are connected, restarting only on the next connection.
Frequently Asked Questions
What WebSocket library does CasaOS use?
CasaOS uses the gorilla/websocket library for the core implementation. The upgrader is configured in route/v1/notify_old.go to handle the HTTP-to-WebSocket protocol upgrade, while the connection management relies on standard *websocket.Conn types from this library.
How does CasaOS handle disconnected WebSocket clients?
During each broadcast cycle in service/notify.go, the SendMeg() function attempts to write messages to all connections in WebSocketConns. Connections that return an error on WriteMessage are excluded from the temporary slice, effectively removing dead connections from the global pool without explicit close handlers.
What triggers real-time updates in CasaOS?
Real-time updates are triggered by a polling loop inside SendMeg() that queries MyService.Notify().GetList(types.NOTIFY_APP) every two seconds. When unread notifications exist, the function marshals them to JSON and broadcasts the payload to all active WebSocket connections before marking them as read.
Does CasaOS use Socket.io for notifications?
While the codebase includes a go-socket.io server stub in service/notify.go for future extensibility, the primary notification system uses raw WebSockets via the gorilla/websocket library. The Socket.io integration is not currently active in the core notification path.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →