@@ -35,33 +35,42 @@ typealias Payload = Map<String, Any?>
35
35
/* * Data class that holds callbacks assigned to the socket */
36
36
internal class StateChangeCallbacks {
37
37
38
- var open: List < () -> Unit > = ArrayList ()
38
+ var open: List <Pair < String , () - > Unit > > = ArrayList ()
39
39
private set
40
- var close: List < () -> Unit > = ArrayList ()
40
+ var close: List <Pair < String , () - > Unit > > = ArrayList ()
41
41
private set
42
- var error: List < (Throwable , Response ? ) -> Unit > = ArrayList ()
42
+ var error: List <Pair < String , (Throwable , Response ?) - > Unit > > = ArrayList ()
43
43
private set
44
- var message: List < (Message ) -> Unit > = ArrayList ()
44
+ var message: List <Pair < String , (Message ) - > Unit > > = ArrayList ()
45
45
private set
46
46
47
47
/* * Safely adds an onOpen callback */
48
- fun onOpen (callback : () -> Unit ) {
49
- this .open = this .open + callback
48
+ fun onOpen (ref : String , callback : () -> Unit ) {
49
+ this .open = this .open + Pair (ref, callback)
50
50
}
51
51
52
52
/* * Safely adds an onClose callback */
53
- fun onClose (callback : () -> Unit ) {
54
- this .close = this .close + callback
53
+ fun onClose (ref : String , callback : () -> Unit ) {
54
+ this .close = this .close + Pair (ref, callback)
55
55
}
56
56
57
57
/* * Safely adds an onError callback */
58
- fun onError (callback : (Throwable , Response ? ) -> Unit ) {
59
- this .error = this .error + callback
58
+ fun onError (ref : String , callback : (Throwable , Response ? ) -> Unit ) {
59
+ this .error = this .error + Pair (ref, callback)
60
60
}
61
61
62
62
/* * Safely adds an onMessage callback */
63
- fun onMessage (callback : (Message ) -> Unit ) {
64
- this .message = this .message + callback
63
+ fun onMessage (ref : String , callback : (Message ) -> Unit ) {
64
+ this .message = this .message + Pair (ref, callback)
65
+ }
66
+
67
+ /* * Clears any callbacks with the matching refs */
68
+ fun release (refs : List <String >) {
69
+ open = open.filter { refs.contains(it.first) }
70
+ close = close.filter { refs.contains(it.first) }
71
+ error = error.filter { refs.contains(it.first) }
72
+ message = message.filter { refs.contains(it.first) }
73
+
65
74
}
66
75
67
76
/* * Clears all stored callbacks */
@@ -151,8 +160,11 @@ class Socket(
151
160
/* * Collection of unclosed channels created by the Socket */
152
161
internal var channels: List <Channel > = ArrayList ()
153
162
154
- /* * Buffers messages that need to be sent once the socket has connected */
155
- internal var sendBuffer: MutableList < () -> Unit > = ArrayList ()
163
+ /* *
164
+ * Buffers messages that need to be sent once the socket has connected. It is an array of Pairs
165
+ * that contain the ref of the message to send and the callback that will send the message.
166
+ */
167
+ internal var sendBuffer: MutableList <Pair <String ?, () - > Unit >> = ArrayList ()
156
168
157
169
/* * Ref counter for messages */
158
170
internal var ref: Int = 0
@@ -273,20 +285,20 @@ class Socket(
273
285
274
286
}
275
287
276
- fun onOpen (callback : (() -> Unit )) {
277
- this . stateChangeCallbacks.onOpen(callback)
288
+ fun onOpen (callback : (() -> Unit )): String {
289
+ return makeRef(). apply { stateChangeCallbacks.onOpen(this , callback) }
278
290
}
279
291
280
- fun onClose (callback : () -> Unit ) {
281
- this . stateChangeCallbacks.onClose(callback)
292
+ fun onClose (callback : () -> Unit ): String {
293
+ return makeRef(). apply { stateChangeCallbacks.onClose(this , callback) }
282
294
}
283
295
284
- fun onError (callback : (Throwable , Response ? ) -> Unit ) {
285
- this . stateChangeCallbacks.onError(callback)
296
+ fun onError (callback : (Throwable , Response ? ) -> Unit ): String {
297
+ return makeRef(). apply { stateChangeCallbacks.onError(this , callback) }
286
298
}
287
299
288
- fun onMessage (callback : (Message ) -> Unit ) {
289
- this . stateChangeCallbacks.onMessage(callback)
300
+ fun onMessage (callback : (Message ) -> Unit ): String {
301
+ return makeRef(). apply { stateChangeCallbacks.onMessage(this , callback) }
290
302
}
291
303
292
304
fun removeAllCallbacks () {
@@ -301,13 +313,24 @@ class Socket(
301
313
}
302
314
303
315
fun remove (channel : Channel ) {
316
+ this .off(channel.stateChangeRefs)
317
+
304
318
// To avoid a ConcurrentModificationException, filter out the channels to be
305
319
// removed instead of calling .remove() on the list, thus returning a new list
306
320
// that does not contain the channel that was removed.
307
321
this .channels = channels
308
322
.filter { it.joinRef != channel.joinRef }
309
323
}
310
324
325
+ /* *
326
+ * Removes [onOpen], [onClose], [onError], and [onMessage] registrations by their [ref] value.
327
+ *
328
+ * @param refs List of refs to remove
329
+ */
330
+ fun off (refs : List <String >) {
331
+ this .stateChangeCallbacks.release(refs)
332
+ }
333
+
311
334
// ------------------------------------------------------------------------------
312
335
// Internal
313
336
// ------------------------------------------------------------------------------
@@ -341,7 +364,7 @@ class Socket(
341
364
} else {
342
365
// If the socket is not connected, add the push to a buffer which will
343
366
// be sent immediately upon connection.
344
- sendBuffer.add(callback)
367
+ sendBuffer.add(Pair (ref, callback) )
345
368
}
346
369
}
347
370
@@ -374,7 +397,7 @@ class Socket(
374
397
375
398
// Since the connections onClose was null'd out, inform all state callbacks
376
399
// that the Socket has closed
377
- this .stateChangeCallbacks.close.forEach { it.invoke() }
400
+ this .stateChangeCallbacks.close.forEach { it.second. invoke() }
378
401
callback?.invoke()
379
402
}
380
403
@@ -391,11 +414,27 @@ class Socket(
391
414
/* * Send all messages that were buffered before the socket opened */
392
415
internal fun flushSendBuffer () {
393
416
if (isConnected && sendBuffer.isNotEmpty()) {
394
- this .sendBuffer.forEach { it.invoke() }
417
+ this .sendBuffer.forEach { it.second. invoke() }
395
418
this .sendBuffer.clear()
396
419
}
397
420
}
398
421
422
+ /* * Removes an item from the send buffer with the matching ref */
423
+ internal fun removeFromSendBuffer (ref : String ) {
424
+ this .sendBuffer = this .sendBuffer
425
+ .filter { it.first != ref }
426
+ .toMutableList()
427
+ }
428
+
429
+ internal fun leaveOpenTopic (topic : String ) {
430
+ this .channels
431
+ .firstOrNull { it.topic == topic && (it.isJoined || it.isJoining) }
432
+ ?.let {
433
+ logItems(" Transport: Leaving duplicate topic: [$topic ]" )
434
+ it.leave()
435
+ }
436
+ }
437
+
399
438
// ------------------------------------------------------------------------------
400
439
// Heartbeat
401
440
// ------------------------------------------------------------------------------
@@ -469,7 +508,7 @@ class Socket(
469
508
this .resetHeartbeat()
470
509
471
510
// Inform all onOpen callbacks that the Socket has opened
472
- this .stateChangeCallbacks.open.forEach { it.invoke() }
511
+ this .stateChangeCallbacks.open.forEach { it.second. invoke() }
473
512
}
474
513
475
514
internal fun onConnectionClosed (code : Int ) {
@@ -486,7 +525,7 @@ class Socket(
486
525
}
487
526
488
527
// Inform callbacks the socket closed
489
- this .stateChangeCallbacks.close.forEach { it.invoke() }
528
+ this .stateChangeCallbacks.close.forEach { it.second. invoke() }
490
529
}
491
530
492
531
internal fun onConnectionMessage (rawMessage : String ) {
@@ -504,7 +543,7 @@ class Socket(
504
543
.forEach { it.trigger(message) }
505
544
506
545
// Inform all onMessage callbacks of the message
507
- this .stateChangeCallbacks.message.forEach { it.invoke(message) }
546
+ this .stateChangeCallbacks.message.forEach { it.second. invoke(message) }
508
547
}
509
548
510
549
internal fun onConnectionError (t : Throwable , response : Response ? ) {
@@ -514,7 +553,7 @@ class Socket(
514
553
this .triggerChannelError()
515
554
516
555
// Inform any state callbacks of the error
517
- this .stateChangeCallbacks.error.forEach { it.invoke(t, response) }
556
+ this .stateChangeCallbacks.error.forEach { it.second. invoke(t, response) }
518
557
}
519
558
520
559
}
0 commit comments