-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhandler.go
More file actions
397 lines (356 loc) · 10.5 KB
/
Copy pathhandler.go
File metadata and controls
397 lines (356 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
package socketio
import (
"errors"
"fmt"
"log"
"maps"
"reflect"
"sync"
"time"
)
type baseHandler struct {
events map[string]*caller
allEvents []*caller
name string
broadcast BroadcastAdaptor
lock sync.RWMutex
}
func newBaseHandler(name string, broadcast BroadcastAdaptor) *baseHandler {
return &baseHandler{
events: make(map[string]*caller),
allEvents: make([]*caller, 0, 5),
name: name,
broadcast: broadcast,
}
}
// On registers the function f to handle message.
func (h *baseHandler) On(message string, f any) error {
c, err := newCaller(f)
if err != nil {
return err
}
h.lock.Lock()
h.events[message] = c
h.lock.Unlock()
return nil
}
// OnAny registers f to be called for every application event, in addition to
// any handler registered via On for that specific message. Handlers registered
// with OnAny receive the same decoded arguments as On handlers but do not
// contribute to the ack response, and they do not fire for the synthetic
// connection/disconnect/error lifecycle messages.
func (h *baseHandler) OnAny(f any) error {
c, err := newCaller(f)
if err != nil {
return err
}
h.lock.Lock()
h.allEvents = append(h.allEvents, c)
h.lock.Unlock()
return nil
}
func (h *baseHandler) PrintEventsRespondedTo() {
fmt.Printf("\tEvents:[")
com := ""
for i := range h.events {
fmt.Printf("%s%s", com, i)
com = ", "
}
fmt.Printf(" ] AllEvents = %d", len(h.allEvents))
fmt.Printf("\n")
}
// maxOutstandingAcks caps how many un-acknowledged emit callbacks a single
// socket may have registered at once. A peer that never sends ack responses
// (or a server that emits acks faster than they are answered) would otherwise
// grow the acks map without bound, exhausting memory. When the cap is
// exceeded the oldest outstanding callbacks are evicted (they will never fire).
const maxOutstandingAcks = 10000
type socketHandler struct {
*baseHandler
acks map[int]*caller
ackOrder []int // ids in insertion order; may hold ids already taken/evicted
socket *socket
rooms map[string]struct{}
lastAckEvictLog time.Time
}
// registerAck records the ack callback for id. The cap is not enforced here so
// that a subsequent send failure (rolled back via unregisterAck) cannot cause
// an unrelated, still-valid callback to be evicted; commitAck enforces the cap
// once the frame has actually been written.
func (h *socketHandler) registerAck(id int, c *caller) {
h.lock.Lock()
h.acks[id] = c
h.ackOrder = append(h.ackOrder, id)
h.lock.Unlock()
}
// unregisterAck removes a pending ack callback by id (used to roll back a
// failed send). Because sendAck holds writeLock across register→encode→
// unregister, the rolled-back id is always the most recently appended entry,
// so it is popped from ackOrder here; otherwise a stream of failing sends
// (which never reach commitAck) would grow ackOrder without bound.
func (h *socketHandler) unregisterAck(id int) {
h.lock.Lock()
delete(h.acks, id)
if n := len(h.ackOrder); n > 0 && h.ackOrder[n-1] == id {
h.ackOrder = h.ackOrder[:n-1]
}
h.lock.Unlock()
}
// commitAck runs after a successful send: it enforces the outstanding-ack cap
// (evicting the oldest live callbacks) and compacts ackOrder so stale ids left
// by answered or evicted acks cannot accumulate without bound.
func (h *socketHandler) commitAck() {
h.lock.Lock()
defer h.lock.Unlock()
evicted := 0
if len(h.acks) > maxOutstandingAcks {
// ackOrder holds every live id in insertion order, so scanning from
// the front evicts the oldest outstanding callbacks first (FIFO).
for _, id := range h.ackOrder {
if len(h.acks) <= maxOutstandingAcks {
break
}
if _, ok := h.acks[id]; ok {
delete(h.acks, id)
evicted++
}
}
}
// Compact when stale ids (already answered or evicted) dominate. This
// reaps entries anywhere in the slice, not just a contiguous prefix, so an
// early un-answered ack (out-of-order acking) cannot pin growth.
if len(h.ackOrder) > 2*len(h.acks)+16 {
kept := h.ackOrder[:0]
for _, id := range h.ackOrder {
if _, ok := h.acks[id]; ok {
kept = append(kept, id)
}
}
h.ackOrder = kept
}
if evicted > 0 {
// Under a sustained flood this would fire on nearly every send; rate
// limit so logging does not become its own amplification vector.
if now := time.Now(); now.Sub(h.lastAckEvictLog) > time.Second {
h.lastAckEvictLog = now
log.Printf("socketio: evicting outstanding acks (more than %d unanswered)", maxOutstandingAcks)
}
}
}
// takeAck removes and returns the ack callback for id, if present.
func (h *socketHandler) takeAck(id int) (*caller, bool) {
h.lock.Lock()
defer h.lock.Unlock()
c, ok := h.acks[id]
if ok {
delete(h.acks, id)
}
return c, ok
}
// clearAcks drops all pending ack callbacks. Called on socket teardown so the
// read loop's exit releases any callbacks (and their captured state) still
// waiting on responses that will never arrive.
func (h *socketHandler) clearAcks() {
h.lock.Lock()
h.acks = make(map[int]*caller)
h.ackOrder = nil
h.lock.Unlock()
}
func newSocketHandler(s *socket, base *baseHandler) *socketHandler {
events := make(map[string]*caller)
allEvents := make([]*caller, 0, len(base.allEvents))
base.lock.Lock()
maps.Copy(events, base.events)
allEvents = append(allEvents, base.allEvents...)
name := base.name
base.lock.Unlock()
return &socketHandler{
baseHandler: &baseHandler{
events: events,
allEvents: allEvents,
name: name,
broadcast: base.broadcast,
},
acks: make(map[int]*caller),
socket: s,
rooms: make(map[string]struct{}),
}
}
func (h *socketHandler) Emit(message string, args ...any) error {
var c *caller
if l := len(args); l > 0 {
fv := reflect.ValueOf(args[l-1])
if fv.Kind() == reflect.Func {
var err error
c, err = newCaller(args[l-1])
if err != nil {
return err
}
args = args[:l-1]
}
}
args = append([]any{message}, args...)
if c != nil {
return h.socket.sendAck(args,
func(id int) { h.registerAck(id, c) },
h.unregisterAck,
h.commitAck,
)
}
return h.socket.send(args)
}
func (h *socketHandler) Rooms() []string {
h.lock.RLock()
defer h.lock.RUnlock()
ret := make([]string, len(h.rooms))
i := 0
for room := range h.rooms {
ret[i] = room
i++
}
return ret
}
func (h *socketHandler) Join(room string) error {
if err := h.broadcast.Join(h.broadcastName(room), h.socket); err != nil {
return err
}
h.lock.Lock()
h.rooms[room] = struct{}{}
h.lock.Unlock()
return nil
}
func (h *socketHandler) Leave(room string) error {
if err := h.broadcast.Leave(h.broadcastName(room), h.socket); err != nil {
return err
}
h.lock.Lock()
delete(h.rooms, room)
h.lock.Unlock()
return nil
}
func (h *socketHandler) LeaveAll() error {
h.lock.Lock()
rooms := make([]string, 0, len(h.rooms))
for room := range h.rooms {
rooms = append(rooms, room)
}
h.rooms = make(map[string]struct{})
h.lock.Unlock()
var errs []error
for _, room := range rooms {
if err := h.broadcast.Leave(h.broadcastName(room), h.socket); err != nil {
errs = append(errs, err)
}
}
return errors.Join(errs...)
}
func (h *baseHandler) BroadcastTo(room, message string, args ...any) error {
return h.broadcast.Send(nil, h.broadcastName(room), message, args...)
}
func (h *socketHandler) BroadcastTo(room, message string, args ...any) error {
return h.broadcast.Send(h.socket, h.broadcastName(room), message, args...)
}
func (h *baseHandler) broadcastName(room string) string {
return fmt.Sprintf("%s:%s", h.name, room)
}
func (h *socketHandler) onPacket(decoder *decoder, packet *packet) ([]any, error) {
var message string
switch packet.Type {
case Connect:
message = "connection"
case Disconnect:
message = "disconnect"
case Error:
message = "error"
case Ack, BinaryAck:
return nil, h.onAck(packet.ID, decoder, packet)
default:
message = decoder.Message()
}
// OnAny listeners observe application events only, matching socket.io's
// onAny semantics (they do not fire for the synthetic connection/
// disconnect/error lifecycle messages).
isAppEvent := packet.Type == Event || packet.Type == BinaryEvent
h.lock.RLock()
c, hasSpecific := h.events[message]
var anyCallers []*caller
if isAppEvent && len(h.allEvents) > 0 {
anyCallers = make([]*caller, len(h.allEvents))
copy(anyCallers, h.allEvents)
}
h.lock.RUnlock()
if !hasSpecific && len(anyCallers) == 0 {
// Nothing is listening for this message. Close the decoder so its
// underlying frame is released; otherwise the read loop can stall
// waiting on an open reader.
log.Printf("socketio: no handler registered for message %q", message)
decoder.Close()
return nil, nil
}
// Read the payload once; it is applied independently to each caller so a
// single-use frame stream is never consumed more than once.
data, err := decoder.ReadData(packet)
if err != nil {
return nil, err
}
// OnAny handlers observe the event; their return values are not used for
// the ack response, and neither a decode failure nor a panic in one
// observer must abort the primary handler or tear down the socket.
for _, ac := range anyCallers {
h.callObserver(ac, message, data)
}
if !hasSpecific {
return nil, nil
}
args, err := data.applyArgs(c)
if err != nil {
return nil, err
}
retV := c.Call(h.socket, args)
if len(retV) == 0 {
return nil, nil
}
var retErr error
if last, ok := retV[len(retV)-1].Interface().(error); ok {
retErr = last
retV = retV[0 : len(retV)-1]
}
ret := make([]any, len(retV))
for i, v := range retV {
ret[i] = v.Interface()
}
return ret, retErr
}
// callObserver decodes the buffered payload for an OnAny handler and invokes
// it, recovering from any panic (during decode or the call itself) so a buggy
// or mistyped observer cannot abort the primary handler or tear down the
// socket. Decode errors are logged and skipped.
func (h *socketHandler) callObserver(c *caller, message string, data *decodedData) {
defer func() {
if r := recover(); r != nil {
log.Printf("socketio: recovered from panic in OnAny handler for %q: %v", message, r)
}
}()
args, err := data.applyArgs(c)
if err != nil {
log.Printf("socketio: skipping OnAny handler for %q: %v", message, err)
return
}
c.Call(h.socket, args)
}
func (h *socketHandler) onAck(id int, decoder *decoder, packet *packet) error {
c, ok := h.takeAck(id)
if !ok {
// No handler is waiting on this ack id; close the decoder so the
// read loop does not stall on an open frame.
decoder.Close()
return nil
}
args := c.GetArgs()
packet.Data = &args
if err := decoder.DecodeData(packet); err != nil {
return err
}
c.Call(h.socket, args)
return nil
}