Skip to content

Commit 8d08096

Browse files
Improved handling of rejected connections (#391 #487 #447)
1 parent b051893 commit 8d08096

File tree

6 files changed

+102
-28
lines changed

6 files changed

+102
-28
lines changed

socketio/asyncio_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ async def connect(self, url, headers={}, transports=None,
110110
transports=transports,
111111
engineio_path=socketio_path)
112112
except engineio.exceptions.ConnectionError as exc:
113+
await self._trigger_event(
114+
'connect_error', '/',
115+
exc.args[1] if len(exc.args) > 1 else exc.args[0])
113116
six.raise_from(exceptions.ConnectionError(exc.args[0]), None)
114117
self.connected = True
115118

socketio/asyncio_server.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,13 @@ async def _handle_connect(self, sid, namespace):
426426
self.manager.pre_disconnect(sid, namespace)
427427
await self._send_packet(sid, packet.Packet(
428428
packet.DISCONNECT, data=fail_reason, namespace=namespace))
429-
self.manager.disconnect(sid, namespace)
430-
if not self.always_connect:
429+
elif namespace != '/':
431430
await self._send_packet(sid, packet.Packet(
432431
packet.ERROR, data=fail_reason, namespace=namespace))
433-
if sid in self.environ: # pragma: no cover
432+
self.manager.disconnect(sid, namespace)
433+
if namespace == '/' and sid in self.environ: # pragma: no cover
434434
del self.environ[sid]
435+
return fail_reason or False
435436
elif not self.always_connect:
436437
await self._send_packet(sid, packet.Packet(packet.CONNECT,
437438
namespace=namespace))

socketio/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ def connect(self, url, headers={}, transports=None,
276276
self.eio.connect(url, headers=headers, transports=transports,
277277
engineio_path=socketio_path)
278278
except engineio.exceptions.ConnectionError as exc:
279+
self._trigger_event(
280+
'connect_error', '/',
281+
exc.args[1] if len(exc.args) > 1 else exc.args[0])
279282
six.raise_from(exceptions.ConnectionError(exc.args[0]), None)
280283
self.connected = True
281284

socketio/server.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -635,12 +635,13 @@ def _handle_connect(self, sid, namespace):
635635
self.manager.pre_disconnect(sid, namespace)
636636
self._send_packet(sid, packet.Packet(
637637
packet.DISCONNECT, data=fail_reason, namespace=namespace))
638-
self.manager.disconnect(sid, namespace)
639-
if not self.always_connect:
638+
elif namespace != '/':
640639
self._send_packet(sid, packet.Packet(
641640
packet.ERROR, data=fail_reason, namespace=namespace))
642-
if sid in self.environ: # pragma: no cover
641+
self.manager.disconnect(sid, namespace)
642+
if namespace == '/' and sid in self.environ: # pragma: no cover
643643
del self.environ[sid]
644+
return fail_reason or False
644645
elif not self.always_connect:
645646
self._send_packet(sid, packet.Packet(packet.CONNECT,
646647
namespace=namespace))

tests/asyncio/test_asyncio_server.py

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -313,29 +313,29 @@ def test_handle_connect_namespace(self, eio):
313313
s.eio.send.mock.assert_any_call('123', '0/foo', binary=False)
314314

315315
def test_handle_connect_rejected(self, eio):
316-
eio.return_value.send = AsyncMock()
317316
mgr = self._get_mock_manager()
318317
s = asyncio_server.AsyncServer(client_manager=mgr)
319318
handler = mock.MagicMock(return_value=False)
320319
s.on('connect', handler)
321-
_run(s._handle_eio_connect('123', 'environ'))
320+
ret = _run(s._handle_eio_connect('123', 'environ'))
321+
self.assertFalse(ret)
322322
handler.assert_called_once_with('123', 'environ')
323323
self.assertEqual(s.manager.connect.call_count, 1)
324324
self.assertEqual(s.manager.disconnect.call_count, 1)
325325
self.assertEqual(s.environ, {})
326-
s.eio.send.mock.assert_called_once_with('123', '4', binary=False)
327326

328327
def test_handle_connect_namespace_rejected(self, eio):
329328
eio.return_value.send = AsyncMock()
330329
mgr = self._get_mock_manager()
331330
s = asyncio_server.AsyncServer(client_manager=mgr)
332331
handler = mock.MagicMock(return_value=False)
333332
s.on('connect', handler, namespace='/foo')
334-
_run(s._handle_eio_connect('123', 'environ'))
333+
ret = _run(s._handle_eio_connect('123', 'environ'))
335334
_run(s._handle_eio_message('123', '0/foo'))
335+
self.assertIsNone(ret)
336336
self.assertEqual(s.manager.connect.call_count, 2)
337337
self.assertEqual(s.manager.disconnect.call_count, 1)
338-
self.assertEqual(s.environ, {})
338+
self.assertEqual(s.environ, {'123': 'environ'})
339339
s.eio.send.mock.assert_any_call('123', '4/foo', binary=False)
340340

341341
def test_handle_connect_rejected_always_connect(self, eio):
@@ -345,7 +345,8 @@ def test_handle_connect_rejected_always_connect(self, eio):
345345
always_connect=True)
346346
handler = mock.MagicMock(return_value=False)
347347
s.on('connect', handler)
348-
_run(s._handle_eio_connect('123', 'environ'))
348+
ret = _run(s._handle_eio_connect('123', 'environ'))
349+
self.assertFalse(ret)
349350
handler.assert_called_once_with('123', 'environ')
350351
self.assertEqual(s.manager.connect.call_count, 1)
351352
self.assertEqual(s.manager.disconnect.call_count, 1)
@@ -360,11 +361,12 @@ def test_handle_connect_namespace_rejected_always_connect(self, eio):
360361
always_connect=True)
361362
handler = mock.MagicMock(return_value=False)
362363
s.on('connect', handler, namespace='/foo')
363-
_run(s._handle_eio_connect('123', 'environ'))
364+
ret = _run(s._handle_eio_connect('123', 'environ'))
364365
_run(s._handle_eio_message('123', '0/foo'))
366+
self.assertFalse(ret)
365367
self.assertEqual(s.manager.connect.call_count, 2)
366368
self.assertEqual(s.manager.disconnect.call_count, 1)
367-
self.assertEqual(s.environ, {})
369+
self.assertEqual(s.environ, {'123': 'environ'})
368370
s.eio.send.mock.assert_any_call('123', '0/foo', binary=False)
369371
s.eio.send.mock.assert_any_call('123', '1/foo', binary=False)
370372

@@ -375,11 +377,24 @@ def test_handle_connect_rejected_with_exception(self, eio):
375377
handler = mock.MagicMock(
376378
side_effect=exceptions.ConnectionRefusedError('fail_reason'))
377379
s.on('connect', handler)
378-
_run(s._handle_eio_connect('123', 'environ'))
380+
ret = _run(s._handle_eio_connect('123', 'environ'))
381+
self.assertEqual(ret, 'fail_reason')
382+
self.assertEqual(s.manager.connect.call_count, 1)
383+
self.assertEqual(s.manager.disconnect.call_count, 1)
384+
self.assertEqual(s.environ, {})
385+
386+
def test_handle_connect_rejected_with_empty_exception(self, eio):
387+
eio.return_value.send = AsyncMock()
388+
mgr = self._get_mock_manager()
389+
s = asyncio_server.AsyncServer(client_manager=mgr)
390+
handler = mock.MagicMock(
391+
side_effect=exceptions.ConnectionRefusedError())
392+
s.on('connect', handler)
393+
ret = _run(s._handle_eio_connect('123', 'environ'))
394+
self.assertFalse(ret)
379395
self.assertEqual(s.manager.connect.call_count, 1)
380396
self.assertEqual(s.manager.disconnect.call_count, 1)
381397
self.assertEqual(s.environ, {})
382-
s.eio.send.mock.assert_any_call('123', '4"fail_reason"', binary=False)
383398

384399
def test_handle_connect_namespace_rejected_with_exception(self, eio):
385400
eio.return_value.send = AsyncMock()
@@ -388,14 +403,30 @@ def test_handle_connect_namespace_rejected_with_exception(self, eio):
388403
handler = mock.MagicMock(
389404
side_effect=exceptions.ConnectionRefusedError('fail_reason', 1))
390405
s.on('connect', handler, namespace='/foo')
391-
_run(s._handle_eio_connect('123', 'environ'))
406+
ret = _run(s._handle_eio_connect('123', 'environ'))
392407
_run(s._handle_eio_message('123', '0/foo'))
408+
self.assertIsNone(ret)
393409
self.assertEqual(s.manager.connect.call_count, 2)
394410
self.assertEqual(s.manager.disconnect.call_count, 1)
395-
self.assertEqual(s.environ, {})
411+
self.assertEqual(s.environ, {'123': 'environ'})
396412
s.eio.send.mock.assert_any_call('123', '4/foo,["fail_reason",1]',
397413
binary=False)
398414

415+
def test_handle_connect_namespace_rejected_with_empty_exception(self, eio):
416+
eio.return_value.send = AsyncMock()
417+
mgr = self._get_mock_manager()
418+
s = asyncio_server.AsyncServer(client_manager=mgr)
419+
handler = mock.MagicMock(
420+
side_effect=exceptions.ConnectionRefusedError())
421+
s.on('connect', handler, namespace='/foo')
422+
ret = _run(s._handle_eio_connect('123', 'environ'))
423+
_run(s._handle_eio_message('123', '0/foo'))
424+
self.assertIsNone(ret)
425+
self.assertEqual(s.manager.connect.call_count, 2)
426+
self.assertEqual(s.manager.disconnect.call_count, 1)
427+
self.assertEqual(s.environ, {'123': 'environ'})
428+
s.eio.send.mock.assert_any_call('123', '4/foo', binary=False)
429+
399430
def test_handle_disconnect(self, eio):
400431
eio.return_value.send = AsyncMock()
401432
mgr = self._get_mock_manager()

tests/common/test_server.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -285,30 +285,33 @@ def test_handle_connect_rejected(self, eio):
285285
s = server.Server(client_manager=mgr)
286286
handler = mock.MagicMock(return_value=False)
287287
s.on('connect', handler)
288-
s._handle_eio_connect('123', 'environ')
288+
ret = s._handle_eio_connect('123', 'environ')
289+
self.assertFalse(ret)
289290
handler.assert_called_once_with('123', 'environ')
290291
self.assertEqual(s.manager.connect.call_count, 1)
291292
self.assertEqual(s.manager.disconnect.call_count, 1)
292293
self.assertEqual(s.environ, {})
293-
s.eio.send.assert_called_once_with('123', '4', binary=False)
294294

295295
def test_handle_connect_namespace_rejected(self, eio):
296296
mgr = mock.MagicMock()
297297
s = server.Server(client_manager=mgr)
298298
handler = mock.MagicMock(return_value=False)
299299
s.on('connect', handler, namespace='/foo')
300-
s._handle_eio_connect('123', 'environ')
300+
ret = s._handle_eio_connect('123', 'environ')
301301
s._handle_eio_message('123', '0/foo')
302+
self.assertIsNone(ret)
302303
self.assertEqual(s.manager.connect.call_count, 2)
303304
self.assertEqual(s.manager.disconnect.call_count, 1)
305+
self.assertEqual(s.environ, {'123': 'environ'})
304306
s.eio.send.assert_any_call('123', '4/foo', binary=False)
305307

306308
def test_handle_connect_rejected_always_connect(self, eio):
307309
mgr = mock.MagicMock()
308310
s = server.Server(client_manager=mgr, always_connect=True)
309311
handler = mock.MagicMock(return_value=False)
310312
s.on('connect', handler)
311-
s._handle_eio_connect('123', 'environ')
313+
ret = s._handle_eio_connect('123', 'environ')
314+
self.assertFalse(ret)
312315
handler.assert_called_once_with('123', 'environ')
313316
self.assertEqual(s.manager.connect.call_count, 1)
314317
self.assertEqual(s.manager.disconnect.call_count, 1)
@@ -321,37 +324,69 @@ def test_handle_connect_namespace_rejected_always_connect(self, eio):
321324
s = server.Server(client_manager=mgr, always_connect=True)
322325
handler = mock.MagicMock(return_value=False)
323326
s.on('connect', handler, namespace='/foo')
324-
s._handle_eio_connect('123', 'environ')
327+
ret = s._handle_eio_connect('123', 'environ')
325328
s._handle_eio_message('123', '0/foo')
329+
self.assertIsNone(ret)
326330
self.assertEqual(s.manager.connect.call_count, 2)
327331
self.assertEqual(s.manager.disconnect.call_count, 1)
332+
self.assertEqual(s.environ, {'123': 'environ'})
328333
s.eio.send.assert_any_call('123', '0/foo', binary=False)
329334
s.eio.send.assert_any_call('123', '1/foo', binary=False)
330335

331336
def test_handle_connect_rejected_with_exception(self, eio):
337+
mgr = mock.MagicMock()
338+
s = server.Server(client_manager=mgr)
339+
handler = mock.MagicMock(
340+
side_effect=exceptions.ConnectionRefusedError('fail_reason'))
341+
s.on('connect', handler)
342+
ret = s._handle_eio_connect('123', 'environ')
343+
self.assertEqual(ret, 'fail_reason')
344+
handler.assert_called_once_with('123', 'environ')
345+
self.assertEqual(s.manager.connect.call_count, 1)
346+
self.assertEqual(s.manager.disconnect.call_count, 1)
347+
self.assertEqual(s.environ, {})
348+
349+
def test_handle_connect_rejected_with_empty_exception(self, eio):
332350
mgr = mock.MagicMock()
333351
s = server.Server(client_manager=mgr)
334352
handler = mock.MagicMock(
335353
side_effect=exceptions.ConnectionRefusedError())
336354
s.on('connect', handler)
337-
s._handle_eio_connect('123', 'environ')
355+
ret = s._handle_eio_connect('123', 'environ')
356+
self.assertFalse(ret)
338357
handler.assert_called_once_with('123', 'environ')
339358
self.assertEqual(s.manager.connect.call_count, 1)
340359
self.assertEqual(s.manager.disconnect.call_count, 1)
341360
self.assertEqual(s.environ, {})
342-
s.eio.send.assert_any_call('123', '4', binary=False)
343361

344362
def test_handle_connect_namespace_rejected_with_exception(self, eio):
345363
mgr = mock.MagicMock()
346364
s = server.Server(client_manager=mgr)
347365
handler = mock.MagicMock(
348-
side_effect=exceptions.ConnectionRefusedError(u'fail_reason'))
366+
side_effect=exceptions.ConnectionRefusedError(u'fail_reason', 1))
349367
s.on('connect', handler, namespace='/foo')
350-
s._handle_eio_connect('123', 'environ')
368+
ret = s._handle_eio_connect('123', 'environ')
369+
s._handle_eio_message('123', '0/foo')
370+
self.assertIsNone(ret)
371+
self.assertEqual(s.manager.connect.call_count, 2)
372+
self.assertEqual(s.manager.disconnect.call_count, 1)
373+
self.assertEqual(s.environ, {'123': 'environ'})
374+
s.eio.send.assert_any_call('123', '4/foo,["fail_reason",1]',
375+
binary=False)
376+
377+
def test_handle_connect_namespace_rejected_with_empty_exception(self, eio):
378+
mgr = mock.MagicMock()
379+
s = server.Server(client_manager=mgr)
380+
handler = mock.MagicMock(
381+
side_effect=exceptions.ConnectionRefusedError())
382+
s.on('connect', handler, namespace='/foo')
383+
ret = s._handle_eio_connect('123', 'environ')
351384
s._handle_eio_message('123', '0/foo')
385+
self.assertIsNone(ret)
352386
self.assertEqual(s.manager.connect.call_count, 2)
353387
self.assertEqual(s.manager.disconnect.call_count, 1)
354-
s.eio.send.assert_any_call('123', '4/foo,"fail_reason"', binary=False)
388+
self.assertEqual(s.environ, {'123': 'environ'})
389+
s.eio.send.assert_any_call('123', '4/foo', binary=False)
355390

356391
def test_handle_disconnect(self, eio):
357392
mgr = mock.MagicMock()

0 commit comments

Comments
 (0)