Skip to content

Commit 4c234e3

Browse files
disconnect reasons for server
1 parent b6ee33e commit 4c234e3

File tree

8 files changed

+133
-39
lines changed

8 files changed

+133
-39
lines changed

src/socketio/async_namespace.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,15 @@ async def trigger_event(self, event, *args):
3434
handler = getattr(self, handler_name)
3535
if asyncio.iscoroutinefunction(handler) is True:
3636
try:
37-
ret = await handler(*args)
37+
try:
38+
ret = await handler(*args)
39+
except TypeError:
40+
# legacy disconnect events do not have a reason
41+
# argument
42+
if event == 'disconnect':
43+
ret = await handler(*args[:-1])
44+
else: # pragma: no cover
45+
raise
3846
except asyncio.CancelledError: # pragma: no cover
3947
ret = None
4048
else:

src/socketio/async_server.py

+25-8
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,8 @@ async def disconnect(self, sid, namespace=None, ignore_queue=False):
427427
eio_sid = self.manager.pre_disconnect(sid, namespace=namespace)
428428
await self._send_packet(eio_sid, self.packet_class(
429429
packet.DISCONNECT, namespace=namespace))
430-
await self._trigger_event('disconnect', namespace, sid)
430+
await self._trigger_event('disconnect', namespace, sid,
431+
self.reason.SERVER_DISCONNECT)
431432
await self.manager.disconnect(sid, namespace=namespace,
432433
ignore_queue=True)
433434

@@ -575,14 +576,15 @@ async def _handle_connect(self, eio_sid, namespace, data):
575576
await self._send_packet(eio_sid, self.packet_class(
576577
packet.CONNECT, {'sid': sid}, namespace=namespace))
577578

578-
async def _handle_disconnect(self, eio_sid, namespace):
579+
async def _handle_disconnect(self, eio_sid, namespace, reason=None):
579580
"""Handle a client disconnect."""
580581
namespace = namespace or '/'
581582
sid = self.manager.sid_from_eio_sid(eio_sid, namespace)
582583
if not self.manager.is_connected(sid, namespace): # pragma: no cover
583584
return
584585
self.manager.pre_disconnect(sid, namespace=namespace)
585-
await self._trigger_event('disconnect', namespace, sid)
586+
await self._trigger_event('disconnect', namespace, sid,
587+
reason or self.reason.UNKNOWN)
586588
await self.manager.disconnect(sid, namespace, ignore_queue=True)
587589

588590
async def _handle_event(self, eio_sid, namespace, id, data):
@@ -634,11 +636,25 @@ async def _trigger_event(self, event, namespace, *args):
634636
if handler:
635637
if asyncio.iscoroutinefunction(handler):
636638
try:
637-
ret = await handler(*args)
639+
try:
640+
ret = await handler(*args)
641+
except TypeError:
642+
# legacy disconnect events use only one argument
643+
if event == 'disconnect':
644+
ret = await handler(*args[:-1])
645+
else: # pragma: no cover
646+
raise
638647
except asyncio.CancelledError: # pragma: no cover
639648
ret = None
640649
else:
641-
ret = handler(*args)
650+
try:
651+
ret = handler(*args)
652+
except TypeError:
653+
# legacy disconnect events use only one argument
654+
if event == 'disconnect':
655+
ret = handler(*args[:-1])
656+
else: # pragma: no cover
657+
raise
642658
return ret
643659
# or else, forward the event to a namespace handler if one exists
644660
handler, args = self._get_namespace_handler(namespace, args)
@@ -671,7 +687,8 @@ async def _handle_eio_message(self, eio_sid, data):
671687
if pkt.packet_type == packet.CONNECT:
672688
await self._handle_connect(eio_sid, pkt.namespace, pkt.data)
673689
elif pkt.packet_type == packet.DISCONNECT:
674-
await self._handle_disconnect(eio_sid, pkt.namespace)
690+
await self._handle_disconnect(eio_sid, pkt.namespace,
691+
self.reason.CLIENT_DISCONNECT)
675692
elif pkt.packet_type == packet.EVENT:
676693
await self._handle_event(eio_sid, pkt.namespace, pkt.id,
677694
pkt.data)
@@ -686,10 +703,10 @@ async def _handle_eio_message(self, eio_sid, data):
686703
else:
687704
raise ValueError('Unknown packet type.')
688705

689-
async def _handle_eio_disconnect(self, eio_sid):
706+
async def _handle_eio_disconnect(self, eio_sid, reason):
690707
"""Handle Engine.IO disconnect event."""
691708
for n in list(self.manager.get_namespaces()).copy():
692-
await self._handle_disconnect(eio_sid, n)
709+
await self._handle_disconnect(eio_sid, n, reason)
693710
if eio_sid in self.environ:
694711
del self.environ[eio_sid]
695712

src/socketio/base_client.py

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import signal
44
import threading
55

6+
import engineio
7+
68
from . import base_namespace
79
from . import packet
810

@@ -31,6 +33,8 @@ def signal_handler(sig, frame): # pragma: no cover
3133
class BaseClient:
3234
reserved_events = ['connect', 'connect_error', 'disconnect',
3335
'__disconnect_final']
36+
print(dir(engineio.Client))
37+
reason = engineio.Client.reason
3438

3539
def __init__(self, reconnection=True, reconnection_attempts=0,
3640
reconnection_delay=1, reconnection_delay_max=5,

src/socketio/base_server.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import logging
22

3+
import engineio
4+
35
from . import manager
46
from . import base_namespace
57
from . import packet
@@ -9,6 +11,7 @@
911

1012
class BaseServer:
1113
reserved_events = ['connect', 'disconnect']
14+
reason = engineio.Server.reason
1215

1316
def __init__(self, client_manager=None, logger=False, serializer='default',
1417
json=None, async_handlers=True, always_connect=False,

src/socketio/namespace.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,14 @@ def trigger_event(self, event, *args):
2323
"""
2424
handler_name = 'on_' + (event or '')
2525
if hasattr(self, handler_name):
26-
return getattr(self, handler_name)(*args)
26+
try:
27+
return getattr(self, handler_name)(*args)
28+
except TypeError:
29+
# legacy disconnect events do not have a reason argument
30+
if event == 'disconnect':
31+
return getattr(self, handler_name)(*args[:-1])
32+
else: # pragma: no cover
33+
raise
2734

2835
def emit(self, event, data=None, to=None, room=None, skip_sid=None,
2936
namespace=None, callback=None, ignore_queue=False):

src/socketio/server.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ def disconnect(self, sid, namespace=None, ignore_queue=False):
403403
eio_sid = self.manager.pre_disconnect(sid, namespace=namespace)
404404
self._send_packet(eio_sid, self.packet_class(
405405
packet.DISCONNECT, namespace=namespace))
406-
self._trigger_event('disconnect', namespace, sid)
406+
self._trigger_event('disconnect', namespace, sid,
407+
self.reason.SERVER_DISCONNECT)
407408
self.manager.disconnect(sid, namespace=namespace,
408409
ignore_queue=True)
409410

@@ -557,14 +558,15 @@ def _handle_connect(self, eio_sid, namespace, data):
557558
self._send_packet(eio_sid, self.packet_class(
558559
packet.CONNECT, {'sid': sid}, namespace=namespace))
559560

560-
def _handle_disconnect(self, eio_sid, namespace):
561+
def _handle_disconnect(self, eio_sid, namespace, reason=None):
561562
"""Handle a client disconnect."""
562563
namespace = namespace or '/'
563564
sid = self.manager.sid_from_eio_sid(eio_sid, namespace)
564565
if not self.manager.is_connected(sid, namespace): # pragma: no cover
565566
return
566567
self.manager.pre_disconnect(sid, namespace=namespace)
567-
self._trigger_event('disconnect', namespace, sid)
568+
self._trigger_event('disconnect', namespace, sid,
569+
reason or self.reason.UNKNOWN)
568570
self.manager.disconnect(sid, namespace, ignore_queue=True)
569571

570572
def _handle_event(self, eio_sid, namespace, id, data):
@@ -611,7 +613,14 @@ def _trigger_event(self, event, namespace, *args):
611613
# first see if we have an explicit handler for the event
612614
handler, args = self._get_event_handler(event, namespace, args)
613615
if handler:
614-
return handler(*args)
616+
try:
617+
return handler(*args)
618+
except TypeError:
619+
# legacy disconnect events use only one argument
620+
if event == 'disconnect':
621+
return handler(*args[:-1])
622+
else: # pragma: no cover
623+
raise
615624
# or else, forward the event to a namespace handler if one exists
616625
handler, args = self._get_namespace_handler(namespace, args)
617626
if handler:
@@ -642,7 +651,8 @@ def _handle_eio_message(self, eio_sid, data):
642651
if pkt.packet_type == packet.CONNECT:
643652
self._handle_connect(eio_sid, pkt.namespace, pkt.data)
644653
elif pkt.packet_type == packet.DISCONNECT:
645-
self._handle_disconnect(eio_sid, pkt.namespace)
654+
self._handle_disconnect(eio_sid, pkt.namespace,
655+
self.reason.CLIENT_DISCONNECT)
646656
elif pkt.packet_type == packet.EVENT:
647657
self._handle_event(eio_sid, pkt.namespace, pkt.id, pkt.data)
648658
elif pkt.packet_type == packet.ACK:
@@ -655,10 +665,10 @@ def _handle_eio_message(self, eio_sid, data):
655665
else:
656666
raise ValueError('Unknown packet type.')
657667

658-
def _handle_eio_disconnect(self, eio_sid):
668+
def _handle_eio_disconnect(self, eio_sid, reason):
659669
"""Handle Engine.IO disconnect event."""
660670
for n in list(self.manager.get_namespaces()).copy():
661-
self._handle_disconnect(eio_sid, n)
671+
self._handle_disconnect(eio_sid, n, reason)
662672
if eio_sid in self.environ:
663673
del self.environ[eio_sid]
664674

tests/async/test_server.py

+41-11
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def test_on_event(self, eio):
5757
def foo():
5858
pass
5959

60-
def bar():
60+
def bar(reason):
6161
pass
6262

6363
s.on('disconnect', bar)
@@ -549,8 +549,36 @@ def test_handle_disconnect(self, eio):
549549
s.on('disconnect', handler)
550550
_run(s._handle_eio_connect('123', 'environ'))
551551
_run(s._handle_eio_message('123', '0'))
552-
_run(s._handle_eio_disconnect('123'))
553-
handler.assert_called_once_with('1')
552+
_run(s._handle_eio_disconnect('123', 'foo'))
553+
handler.assert_called_once_with('1', 'foo')
554+
s.manager.disconnect.assert_awaited_once_with(
555+
'1', '/', ignore_queue=True)
556+
assert s.environ == {}
557+
558+
def test_handle_legacy_disconnect(self, eio):
559+
eio.return_value.send = mock.AsyncMock()
560+
s = async_server.AsyncServer()
561+
s.manager.disconnect = mock.AsyncMock()
562+
handler = mock.MagicMock(side_effect=[TypeError, None])
563+
s.on('disconnect', handler)
564+
_run(s._handle_eio_connect('123', 'environ'))
565+
_run(s._handle_eio_message('123', '0'))
566+
_run(s._handle_eio_disconnect('123', 'foo'))
567+
handler.assert_called_with('1')
568+
s.manager.disconnect.assert_awaited_once_with(
569+
'1', '/', ignore_queue=True)
570+
assert s.environ == {}
571+
572+
def test_handle_legacy_disconnect_async(self, eio):
573+
eio.return_value.send = mock.AsyncMock()
574+
s = async_server.AsyncServer()
575+
s.manager.disconnect = mock.AsyncMock()
576+
handler = mock.AsyncMock(side_effect=[TypeError, None])
577+
s.on('disconnect', handler)
578+
_run(s._handle_eio_connect('123', 'environ'))
579+
_run(s._handle_eio_message('123', '0'))
580+
_run(s._handle_eio_disconnect('123', 'foo'))
581+
handler.assert_awaited_with('1')
554582
s.manager.disconnect.assert_awaited_once_with(
555583
'1', '/', ignore_queue=True)
556584
assert s.environ == {}
@@ -564,9 +592,9 @@ def test_handle_disconnect_namespace(self, eio):
564592
s.on('disconnect', handler_namespace, namespace='/foo')
565593
_run(s._handle_eio_connect('123', 'environ'))
566594
_run(s._handle_eio_message('123', '0/foo,'))
567-
_run(s._handle_eio_disconnect('123'))
595+
_run(s._handle_eio_disconnect('123', 'foo'))
568596
handler.assert_not_called()
569-
handler_namespace.assert_called_once_with('1')
597+
handler_namespace.assert_called_once_with('1', 'foo')
570598
assert s.environ == {}
571599

572600
def test_handle_disconnect_only_namespace(self, eio):
@@ -580,13 +608,14 @@ def test_handle_disconnect_only_namespace(self, eio):
580608
_run(s._handle_eio_message('123', '0/foo,'))
581609
_run(s._handle_eio_message('123', '1/foo,'))
582610
assert handler.call_count == 0
583-
handler_namespace.assert_called_once_with('1')
611+
handler_namespace.assert_called_once_with(
612+
'1', s.reason.CLIENT_DISCONNECT)
584613
assert s.environ == {'123': 'environ'}
585614

586615
def test_handle_disconnect_unknown_client(self, eio):
587616
mgr = self._get_mock_manager()
588617
s = async_server.AsyncServer(client_manager=mgr)
589-
_run(s._handle_eio_disconnect('123'))
618+
_run(s._handle_eio_disconnect('123', 'foo'))
590619

591620
def test_handle_event(self, eio):
592621
eio.return_value.send = mock.AsyncMock()
@@ -636,7 +665,7 @@ def test_handle_event_with_catchall_namespace(self, eio):
636665
_run(s._handle_eio_message('123', '2/bar,["msg","a","b"]'))
637666
_run(s._handle_eio_message('123', '2/foo,["my message","a","b","c"]'))
638667
_run(s._handle_eio_message('123', '2/bar,["my message","a","b","c"]'))
639-
_run(s._trigger_event('disconnect', '/bar', sid_bar))
668+
_run(s._trigger_event('disconnect', '/bar', sid_bar, s.reason.UNKNOWN))
640669
connect_star_handler.assert_called_once_with('/bar', sid_bar)
641670
msg_foo_handler.assert_called_once_with(sid_foo, 'a', 'b')
642671
msg_star_handler.assert_called_once_with('/bar', sid_bar, 'a', 'b')
@@ -902,8 +931,8 @@ class MyNamespace(async_namespace.AsyncNamespace):
902931
def on_connect(self, sid, environ):
903932
result['result'] = (sid, environ)
904933

905-
async def on_disconnect(self, sid):
906-
result['result'] = ('disconnect', sid)
934+
async def on_disconnect(self, sid, reason):
935+
result['result'] = ('disconnect', sid, reason)
907936

908937
async def on_foo(self, sid, data):
909938
result['result'] = (sid, data)
@@ -926,7 +955,8 @@ async def on_baz(self, sid, data1, data2):
926955
_run(s._handle_eio_message('123', '2/foo,["baz","a","b"]'))
927956
assert result['result'] == ('a', 'b')
928957
_run(s.disconnect('1', '/foo'))
929-
assert result['result'] == ('disconnect', '1')
958+
assert result['result'] == ('disconnect', '1',
959+
s.reason.SERVER_DISCONNECT)
930960

931961
def test_catchall_namespace_handler(self, eio):
932962
eio.return_value.send = mock.AsyncMock()

0 commit comments

Comments
 (0)