Skip to content

Commit 7816489

Browse files
intel352claude
andcommitted
fix(F1): add OnConnect/OnDisconnect callbacks to fire ws_connect/ws_disconnect pipelines
The WebSocket hub's register channel fires when a new connection is added, but the wsTrigger only listened for inbound messages. Add global connect/disconnect handler hooks (same pattern as the existing message handler) so the trigger can call TriggerCallback("connect", ...) and TriggerCallback("disconnect", ...). - trigger_websocket.go: globalWSConnectHandler + globalWSDisconnectHandler vars, set/call helpers, wsTrigger.Start wires both; Stop clears all three handlers. - module_ws_server.go: fire callGlobalWSConnectHandler in a goroutine after hub.register so the welcome message can be sent immediately on connect. - connection.go: fire callGlobalWSDisconnectHandler from readPump defer so the ws_disconnect pipeline triggers when the client's read loop exits. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d7f1a42 commit 7816489

3 files changed

Lines changed: 73 additions & 0 deletions

File tree

internal/connection.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type connection struct {
1818

1919
func (c *connection) readPump(onMessage func(connID string, msg []byte)) {
2020
defer func() {
21+
callGlobalWSDisconnectHandler(c.id)
2122
c.hub.unregister <- c
2223
c.close()
2324
}()

internal/module_ws_server.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ func (m *wsServerModule) ServeHTTP(w http.ResponseWriter, r *http.Request) {
139139
}
140140

141141
m.hub.register <- conn
142+
// Fire connect event after the connection is registered in the hub so that
143+
// ws_connect pipelines (e.g. welcome message) can send immediately.
144+
go callGlobalWSConnectHandler(connID)
142145
go conn.writePump()
143146
go conn.readPump(func(connID string, msg []byte) {
144147
callGlobalWSMessageHandler(connID, msg)

internal/trigger_websocket.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ import (
1515
var (
1616
globalWSMessageHandler func(connID string, msg []byte)
1717
globalWSMessageHandlerMu sync.RWMutex
18+
19+
// globalWSConnectHandler fires once per new connection, before the first message.
20+
globalWSConnectHandler func(connID string)
21+
globalWSConnectHandlerMu sync.RWMutex
22+
23+
// globalWSDisconnectHandler fires when a connection closes.
24+
globalWSDisconnectHandler func(connID string)
25+
globalWSDisconnectHandlerMu sync.RWMutex
1826
)
1927

2028
func setGlobalWSMessageHandler(h func(connID string, msg []byte)) {
@@ -34,6 +42,40 @@ func callGlobalWSMessageHandler(connID string, msg []byte) {
3442
}
3543
}
3644

45+
func setGlobalWSConnectHandler(h func(connID string)) {
46+
globalWSConnectHandlerMu.Lock()
47+
defer globalWSConnectHandlerMu.Unlock()
48+
globalWSConnectHandler = h
49+
}
50+
51+
// callGlobalWSConnectHandler is called by wsServerModule.ServeHTTP after a new
52+
// connection is registered in the hub.
53+
func callGlobalWSConnectHandler(connID string) {
54+
globalWSConnectHandlerMu.RLock()
55+
h := globalWSConnectHandler
56+
globalWSConnectHandlerMu.RUnlock()
57+
if h != nil {
58+
h(connID)
59+
}
60+
}
61+
62+
func setGlobalWSDisconnectHandler(h func(connID string)) {
63+
globalWSDisconnectHandlerMu.Lock()
64+
defer globalWSDisconnectHandlerMu.Unlock()
65+
globalWSDisconnectHandler = h
66+
}
67+
68+
// callGlobalWSDisconnectHandler is called by connection.readPump when a
69+
// connection's read loop exits (i.e. the client disconnected).
70+
func callGlobalWSDisconnectHandler(connID string) {
71+
globalWSDisconnectHandlerMu.RLock()
72+
h := globalWSDisconnectHandler
73+
globalWSDisconnectHandlerMu.RUnlock()
74+
if h != nil {
75+
h(connID)
76+
}
77+
}
78+
3779
// wsTrigger implements sdk.ModuleInstance for the "websocket" trigger type.
3880
// The host engine creates this via CreateModule("websocket", ...) and calls
3981
// Init/Start/Stop to manage its lifecycle (same pattern as RemoteTrigger).
@@ -46,6 +88,12 @@ func callGlobalWSMessageHandler(connID string, msg []byte) {
4688
// payload — decoded JSON object (if the message is valid JSON)
4789
// room — first room the connection belongs to (empty if none)
4890
// rooms — all rooms the connection belongs to
91+
//
92+
// Connect/disconnect lifecycle events are also fired:
93+
//
94+
// event "connect" — fired immediately after the connection is registered;
95+
// only connectionId is populated (no message/payload).
96+
// event "disconnect" — fired when the read loop exits (client gone).
4997
type wsTrigger struct {
5098
cb sdk.TriggerCallback
5199
}
@@ -97,10 +145,31 @@ func (t *wsTrigger) Start(_ context.Context) error {
97145

98146
_ = t.cb("message", data)
99147
})
148+
149+
setGlobalWSConnectHandler(func(connID string) {
150+
if t.cb == nil {
151+
return
152+
}
153+
_ = t.cb("connect", map[string]any{
154+
"connectionId": connID,
155+
})
156+
})
157+
158+
setGlobalWSDisconnectHandler(func(connID string) {
159+
if t.cb == nil {
160+
return
161+
}
162+
_ = t.cb("disconnect", map[string]any{
163+
"connectionId": connID,
164+
})
165+
})
166+
100167
return nil
101168
}
102169

103170
func (t *wsTrigger) Stop(_ context.Context) error {
104171
setGlobalWSMessageHandler(nil)
172+
setGlobalWSConnectHandler(nil)
173+
setGlobalWSDisconnectHandler(nil)
105174
return nil
106175
}

0 commit comments

Comments
 (0)