Browse Source

put clients in a pre-disconnect state while their disconnect handler runs

This avoids potential endless recursion.
See https://github.com/miguelgrinberg/Flask-SocketIO/issues/312
pull/56/head
Miguel Grinberg 9 years ago
parent
commit
da2d141e8d
  1. 21
      socketio/base_manager.py
  2. 1
      socketio/server.py
  3. 14
      tests/test_base_manager.py

21
socketio/base_manager.py

@ -16,6 +16,7 @@ class BaseManager(object):
self.server = None
self.rooms = {}
self.callbacks = {}
self.pending_disconnect = {}
def initialize(self, server):
self.server = server
@ -35,11 +36,26 @@ class BaseManager(object):
self.enter_room(sid, namespace, sid)
def is_connected(self, sid, namespace):
if namespace in self.pending_disconnect and \
sid in self.pending_disconnect[namespace]:
# the client is in the process of being disconnected
return False
try:
return self.rooms[namespace][None][sid]
except KeyError:
pass
def pre_disconnect(self, sid, namespace):
"""Put the client in the to-be-disconnected list.
This allows the client data structures to be present while the
disconnect handler is invoked, but still recognize the fact that the
client is soon going away.
"""
if namespace not in self.pending_disconnect:
self.pending_disconnect[namespace] = []
self.pending_disconnect[namespace].append(sid)
def disconnect(self, sid, namespace):
"""Register a client disconnect from a namespace."""
rooms = []
@ -52,6 +68,11 @@ class BaseManager(object):
del self.callbacks[sid][namespace]
if len(self.callbacks[sid]) == 0:
del self.callbacks[sid]
if namespace in self.pending_disconnect and \
sid in self.pending_disconnect[namespace]:
self.pending_disconnect[namespace].remove(sid)
if len(self.pending_disconnect[namespace]) == 0:
del self.pending_disconnect[namespace]
def enter_room(self, sid, namespace, room):
"""Add a client to a room."""

1
socketio/server.py

@ -298,6 +298,7 @@ class Server(object):
namespace = namespace or '/'
if self.manager.is_connected(sid, namespace=namespace):
self.logger.info('Disconnecting %s [%s]', sid, namespace)
self.manager.pre_disconnect(sid, namespace=namespace)
self._send_packet(sid, packet.Packet(packet.DISCONNECT,
namespace=namespace))
self._trigger_event('disconnect', namespace, sid)

14
tests/test_base_manager.py

@ -24,6 +24,20 @@ class TestBaseManager(unittest.TestCase):
self.assertEqual(self.bm.rooms['/foo'], {None: {'123': True},
'123': {'123': True}})
def test_pre_disconnect(self):
self.bm.connect('123', '/foo')
self.bm.connect('456', '/foo')
self.bm.pre_disconnect('123', '/foo')
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['123']})
self.assertFalse(self.bm.is_connected('123', '/foo'))
self.bm.pre_disconnect('456', '/foo')
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['123', '456']})
self.assertFalse(self.bm.is_connected('456', '/foo'))
self.bm.disconnect('123', '/foo')
self.assertEqual(self.bm.pending_disconnect, {'/foo': ['456']})
self.bm.disconnect('456', '/foo')
self.assertEqual(self.bm.pending_disconnect, {})
def test_disconnect(self):
self.bm.connect('123', '/foo')
self.bm.connect('456', '/foo')

Loading…
Cancel
Save