Skip to content

Commit da2d141

Browse files
put clients in a pre-disconnect state while their disconnect handler runs
This avoids potential endless recursion. See miguelgrinberg/Flask-SocketIO#312
1 parent a58c184 commit da2d141

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed

socketio/base_manager.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def __init__(self):
1616
self.server = None
1717
self.rooms = {}
1818
self.callbacks = {}
19+
self.pending_disconnect = {}
1920

2021
def initialize(self, server):
2122
self.server = server
@@ -35,11 +36,26 @@ def connect(self, sid, namespace):
3536
self.enter_room(sid, namespace, sid)
3637

3738
def is_connected(self, sid, namespace):
39+
if namespace in self.pending_disconnect and \
40+
sid in self.pending_disconnect[namespace]:
41+
# the client is in the process of being disconnected
42+
return False
3843
try:
3944
return self.rooms[namespace][None][sid]
4045
except KeyError:
4146
pass
4247

48+
def pre_disconnect(self, sid, namespace):
49+
"""Put the client in the to-be-disconnected list.
50+
51+
This allows the client data structures to be present while the
52+
disconnect handler is invoked, but still recognize the fact that the
53+
client is soon going away.
54+
"""
55+
if namespace not in self.pending_disconnect:
56+
self.pending_disconnect[namespace] = []
57+
self.pending_disconnect[namespace].append(sid)
58+
4359
def disconnect(self, sid, namespace):
4460
"""Register a client disconnect from a namespace."""
4561
rooms = []
@@ -52,6 +68,11 @@ def disconnect(self, sid, namespace):
5268
del self.callbacks[sid][namespace]
5369
if len(self.callbacks[sid]) == 0:
5470
del self.callbacks[sid]
71+
if namespace in self.pending_disconnect and \
72+
sid in self.pending_disconnect[namespace]:
73+
self.pending_disconnect[namespace].remove(sid)
74+
if len(self.pending_disconnect[namespace]) == 0:
75+
del self.pending_disconnect[namespace]
5576

5677
def enter_room(self, sid, namespace, room):
5778
"""Add a client to a room."""

socketio/server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ def disconnect(self, sid, namespace=None):
298298
namespace = namespace or '/'
299299
if self.manager.is_connected(sid, namespace=namespace):
300300
self.logger.info('Disconnecting %s [%s]', sid, namespace)
301+
self.manager.pre_disconnect(sid, namespace=namespace)
301302
self._send_packet(sid, packet.Packet(packet.DISCONNECT,
302303
namespace=namespace))
303304
self._trigger_event('disconnect', namespace, sid)

tests/test_base_manager.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ def test_connect(self):
2424
self.assertEqual(self.bm.rooms['/foo'], {None: {'123': True},
2525
'123': {'123': True}})
2626

27+
def test_pre_disconnect(self):
28+
self.bm.connect('123', '/foo')
29+
self.bm.connect('456', '/foo')
30+
self.bm.pre_disconnect('123', '/foo')
31+
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['123']})
32+
self.assertFalse(self.bm.is_connected('123', '/foo'))
33+
self.bm.pre_disconnect('456', '/foo')
34+
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['123', '456']})
35+
self.assertFalse(self.bm.is_connected('456', '/foo'))
36+
self.bm.disconnect('123', '/foo')
37+
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['456']})
38+
self.bm.disconnect('456', '/foo')
39+
self.assertEqual(self.bm.pending_disconnect, {})
40+
2741
def test_disconnect(self):
2842
self.bm.connect('123', '/foo')
2943
self.bm.connect('456', '/foo')

0 commit comments

Comments
 (0)