Browse Source

Improved handling of rejected connections (#391 #487 #447)

pull/494/head
Miguel Grinberg 5 years ago
parent
commit
8d08096dc4
No known key found for this signature in database GPG Key ID: 36848B262DF5F06C
  1. 3
      socketio/asyncio_client.py
  2. 7
      socketio/asyncio_server.py
  3. 3
      socketio/client.py
  4. 7
      socketio/server.py
  5. 55
      tests/asyncio/test_asyncio_server.py
  6. 55
      tests/common/test_server.py

3
socketio/asyncio_client.py

@ -110,6 +110,9 @@ class AsyncClient(client.Client):
transports=transports, transports=transports,
engineio_path=socketio_path) engineio_path=socketio_path)
except engineio.exceptions.ConnectionError as exc: except engineio.exceptions.ConnectionError as exc:
await self._trigger_event(
'connect_error', '/',
exc.args[1] if len(exc.args) > 1 else exc.args[0])
six.raise_from(exceptions.ConnectionError(exc.args[0]), None) six.raise_from(exceptions.ConnectionError(exc.args[0]), None)
self.connected = True self.connected = True

7
socketio/asyncio_server.py

@ -426,12 +426,13 @@ class AsyncServer(server.Server):
self.manager.pre_disconnect(sid, namespace) self.manager.pre_disconnect(sid, namespace)
await self._send_packet(sid, packet.Packet( await self._send_packet(sid, packet.Packet(
packet.DISCONNECT, data=fail_reason, namespace=namespace)) packet.DISCONNECT, data=fail_reason, namespace=namespace))
self.manager.disconnect(sid, namespace) elif namespace != '/':
if not self.always_connect:
await self._send_packet(sid, packet.Packet( await self._send_packet(sid, packet.Packet(
packet.ERROR, data=fail_reason, namespace=namespace)) packet.ERROR, data=fail_reason, namespace=namespace))
if sid in self.environ: # pragma: no cover self.manager.disconnect(sid, namespace)
if namespace == '/' and sid in self.environ: # pragma: no cover
del self.environ[sid] del self.environ[sid]
return fail_reason or False
elif not self.always_connect: elif not self.always_connect:
await self._send_packet(sid, packet.Packet(packet.CONNECT, await self._send_packet(sid, packet.Packet(packet.CONNECT,
namespace=namespace)) namespace=namespace))

3
socketio/client.py

@ -276,6 +276,9 @@ class Client(object):
self.eio.connect(url, headers=headers, transports=transports, self.eio.connect(url, headers=headers, transports=transports,
engineio_path=socketio_path) engineio_path=socketio_path)
except engineio.exceptions.ConnectionError as exc: except engineio.exceptions.ConnectionError as exc:
self._trigger_event(
'connect_error', '/',
exc.args[1] if len(exc.args) > 1 else exc.args[0])
six.raise_from(exceptions.ConnectionError(exc.args[0]), None) six.raise_from(exceptions.ConnectionError(exc.args[0]), None)
self.connected = True self.connected = True

7
socketio/server.py

@ -635,12 +635,13 @@ class Server(object):
self.manager.pre_disconnect(sid, namespace) self.manager.pre_disconnect(sid, namespace)
self._send_packet(sid, packet.Packet( self._send_packet(sid, packet.Packet(
packet.DISCONNECT, data=fail_reason, namespace=namespace)) packet.DISCONNECT, data=fail_reason, namespace=namespace))
self.manager.disconnect(sid, namespace) elif namespace != '/':
if not self.always_connect:
self._send_packet(sid, packet.Packet( self._send_packet(sid, packet.Packet(
packet.ERROR, data=fail_reason, namespace=namespace)) packet.ERROR, data=fail_reason, namespace=namespace))
if sid in self.environ: # pragma: no cover self.manager.disconnect(sid, namespace)
if namespace == '/' and sid in self.environ: # pragma: no cover
del self.environ[sid] del self.environ[sid]
return fail_reason or False
elif not self.always_connect: elif not self.always_connect:
self._send_packet(sid, packet.Packet(packet.CONNECT, self._send_packet(sid, packet.Packet(packet.CONNECT,
namespace=namespace)) namespace=namespace))

55
tests/asyncio/test_asyncio_server.py

@ -313,17 +313,16 @@ class TestAsyncServer(unittest.TestCase):
s.eio.send.mock.assert_any_call('123', '0/foo', binary=False) s.eio.send.mock.assert_any_call('123', '0/foo', binary=False)
def test_handle_connect_rejected(self, eio): def test_handle_connect_rejected(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager() mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr) s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock(return_value=False) handler = mock.MagicMock(return_value=False)
s.on('connect', handler) s.on('connect', handler)
_run(s._handle_eio_connect('123', 'environ')) ret = _run(s._handle_eio_connect('123', 'environ'))
self.assertFalse(ret)
handler.assert_called_once_with('123', 'environ') handler.assert_called_once_with('123', 'environ')
self.assertEqual(s.manager.connect.call_count, 1) self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {}) self.assertEqual(s.environ, {})
s.eio.send.mock.assert_called_once_with('123', '4', binary=False)
def test_handle_connect_namespace_rejected(self, eio): def test_handle_connect_namespace_rejected(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
@ -331,11 +330,12 @@ class TestAsyncServer(unittest.TestCase):
s = asyncio_server.AsyncServer(client_manager=mgr) s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock(return_value=False) handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo') s.on('connect', handler, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ')) ret = _run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo')) _run(s._handle_eio_message('123', '0/foo'))
self.assertIsNone(ret)
self.assertEqual(s.manager.connect.call_count, 2) self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {}) self.assertEqual(s.environ, {'123': 'environ'})
s.eio.send.mock.assert_any_call('123', '4/foo', binary=False) s.eio.send.mock.assert_any_call('123', '4/foo', binary=False)
def test_handle_connect_rejected_always_connect(self, eio): def test_handle_connect_rejected_always_connect(self, eio):
@ -345,7 +345,8 @@ class TestAsyncServer(unittest.TestCase):
always_connect=True) always_connect=True)
handler = mock.MagicMock(return_value=False) handler = mock.MagicMock(return_value=False)
s.on('connect', handler) s.on('connect', handler)
_run(s._handle_eio_connect('123', 'environ')) ret = _run(s._handle_eio_connect('123', 'environ'))
self.assertFalse(ret)
handler.assert_called_once_with('123', 'environ') handler.assert_called_once_with('123', 'environ')
self.assertEqual(s.manager.connect.call_count, 1) self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
@ -360,11 +361,12 @@ class TestAsyncServer(unittest.TestCase):
always_connect=True) always_connect=True)
handler = mock.MagicMock(return_value=False) handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo') s.on('connect', handler, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ')) ret = _run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo')) _run(s._handle_eio_message('123', '0/foo'))
self.assertFalse(ret)
self.assertEqual(s.manager.connect.call_count, 2) self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {}) self.assertEqual(s.environ, {'123': 'environ'})
s.eio.send.mock.assert_any_call('123', '0/foo', binary=False) s.eio.send.mock.assert_any_call('123', '0/foo', binary=False)
s.eio.send.mock.assert_any_call('123', '1/foo', binary=False) s.eio.send.mock.assert_any_call('123', '1/foo', binary=False)
@ -375,11 +377,24 @@ class TestAsyncServer(unittest.TestCase):
handler = mock.MagicMock( handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError('fail_reason')) side_effect=exceptions.ConnectionRefusedError('fail_reason'))
s.on('connect', handler) s.on('connect', handler)
_run(s._handle_eio_connect('123', 'environ')) ret = _run(s._handle_eio_connect('123', 'environ'))
self.assertEqual(ret, 'fail_reason')
self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {})
def test_handle_connect_rejected_with_empty_exception(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError())
s.on('connect', handler)
ret = _run(s._handle_eio_connect('123', 'environ'))
self.assertFalse(ret)
self.assertEqual(s.manager.connect.call_count, 1) self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {}) self.assertEqual(s.environ, {})
s.eio.send.mock.assert_any_call('123', '4"fail_reason"', binary=False)
def test_handle_connect_namespace_rejected_with_exception(self, eio): def test_handle_connect_namespace_rejected_with_exception(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
@ -388,14 +403,30 @@ class TestAsyncServer(unittest.TestCase):
handler = mock.MagicMock( handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError('fail_reason', 1)) side_effect=exceptions.ConnectionRefusedError('fail_reason', 1))
s.on('connect', handler, namespace='/foo') s.on('connect', handler, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ')) ret = _run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo')) _run(s._handle_eio_message('123', '0/foo'))
self.assertIsNone(ret)
self.assertEqual(s.manager.connect.call_count, 2) self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {}) self.assertEqual(s.environ, {'123': 'environ'})
s.eio.send.mock.assert_any_call('123', '4/foo,["fail_reason",1]', s.eio.send.mock.assert_any_call('123', '4/foo,["fail_reason",1]',
binary=False) binary=False)
def test_handle_connect_namespace_rejected_with_empty_exception(self, eio):
eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError())
s.on('connect', handler, namespace='/foo')
ret = _run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo'))
self.assertIsNone(ret)
self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {'123': 'environ'})
s.eio.send.mock.assert_any_call('123', '4/foo', binary=False)
def test_handle_disconnect(self, eio): def test_handle_disconnect(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager() mgr = self._get_mock_manager()

55
tests/common/test_server.py

@ -285,22 +285,24 @@ class TestServer(unittest.TestCase):
s = server.Server(client_manager=mgr) s = server.Server(client_manager=mgr)
handler = mock.MagicMock(return_value=False) handler = mock.MagicMock(return_value=False)
s.on('connect', handler) s.on('connect', handler)
s._handle_eio_connect('123', 'environ') ret = s._handle_eio_connect('123', 'environ')
self.assertFalse(ret)
handler.assert_called_once_with('123', 'environ') handler.assert_called_once_with('123', 'environ')
self.assertEqual(s.manager.connect.call_count, 1) self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {}) self.assertEqual(s.environ, {})
s.eio.send.assert_called_once_with('123', '4', binary=False)
def test_handle_connect_namespace_rejected(self, eio): def test_handle_connect_namespace_rejected(self, eio):
mgr = mock.MagicMock() mgr = mock.MagicMock()
s = server.Server(client_manager=mgr) s = server.Server(client_manager=mgr)
handler = mock.MagicMock(return_value=False) handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo') s.on('connect', handler, namespace='/foo')
s._handle_eio_connect('123', 'environ') ret = s._handle_eio_connect('123', 'environ')
s._handle_eio_message('123', '0/foo') s._handle_eio_message('123', '0/foo')
self.assertIsNone(ret)
self.assertEqual(s.manager.connect.call_count, 2) self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {'123': 'environ'})
s.eio.send.assert_any_call('123', '4/foo', binary=False) s.eio.send.assert_any_call('123', '4/foo', binary=False)
def test_handle_connect_rejected_always_connect(self, eio): def test_handle_connect_rejected_always_connect(self, eio):
@ -308,7 +310,8 @@ class TestServer(unittest.TestCase):
s = server.Server(client_manager=mgr, always_connect=True) s = server.Server(client_manager=mgr, always_connect=True)
handler = mock.MagicMock(return_value=False) handler = mock.MagicMock(return_value=False)
s.on('connect', handler) s.on('connect', handler)
s._handle_eio_connect('123', 'environ') ret = s._handle_eio_connect('123', 'environ')
self.assertFalse(ret)
handler.assert_called_once_with('123', 'environ') handler.assert_called_once_with('123', 'environ')
self.assertEqual(s.manager.connect.call_count, 1) self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
@ -321,37 +324,69 @@ class TestServer(unittest.TestCase):
s = server.Server(client_manager=mgr, always_connect=True) s = server.Server(client_manager=mgr, always_connect=True)
handler = mock.MagicMock(return_value=False) handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo') s.on('connect', handler, namespace='/foo')
s._handle_eio_connect('123', 'environ') ret = s._handle_eio_connect('123', 'environ')
s._handle_eio_message('123', '0/foo') s._handle_eio_message('123', '0/foo')
self.assertIsNone(ret)
self.assertEqual(s.manager.connect.call_count, 2) self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {'123': 'environ'})
s.eio.send.assert_any_call('123', '0/foo', binary=False) s.eio.send.assert_any_call('123', '0/foo', binary=False)
s.eio.send.assert_any_call('123', '1/foo', binary=False) s.eio.send.assert_any_call('123', '1/foo', binary=False)
def test_handle_connect_rejected_with_exception(self, eio): def test_handle_connect_rejected_with_exception(self, eio):
mgr = mock.MagicMock()
s = server.Server(client_manager=mgr)
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError('fail_reason'))
s.on('connect', handler)
ret = s._handle_eio_connect('123', 'environ')
self.assertEqual(ret, 'fail_reason')
handler.assert_called_once_with('123', 'environ')
self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {})
def test_handle_connect_rejected_with_empty_exception(self, eio):
mgr = mock.MagicMock() mgr = mock.MagicMock()
s = server.Server(client_manager=mgr) s = server.Server(client_manager=mgr)
handler = mock.MagicMock( handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError()) side_effect=exceptions.ConnectionRefusedError())
s.on('connect', handler) s.on('connect', handler)
s._handle_eio_connect('123', 'environ') ret = s._handle_eio_connect('123', 'environ')
self.assertFalse(ret)
handler.assert_called_once_with('123', 'environ') handler.assert_called_once_with('123', 'environ')
self.assertEqual(s.manager.connect.call_count, 1) self.assertEqual(s.manager.connect.call_count, 1)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {}) self.assertEqual(s.environ, {})
s.eio.send.assert_any_call('123', '4', binary=False)
def test_handle_connect_namespace_rejected_with_exception(self, eio): def test_handle_connect_namespace_rejected_with_exception(self, eio):
mgr = mock.MagicMock() mgr = mock.MagicMock()
s = server.Server(client_manager=mgr) s = server.Server(client_manager=mgr)
handler = mock.MagicMock( handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError(u'fail_reason')) side_effect=exceptions.ConnectionRefusedError(u'fail_reason', 1))
s.on('connect', handler, namespace='/foo') s.on('connect', handler, namespace='/foo')
s._handle_eio_connect('123', 'environ') ret = s._handle_eio_connect('123', 'environ')
s._handle_eio_message('123', '0/foo')
self.assertIsNone(ret)
self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1)
self.assertEqual(s.environ, {'123': 'environ'})
s.eio.send.assert_any_call('123', '4/foo,["fail_reason",1]',
binary=False)
def test_handle_connect_namespace_rejected_with_empty_exception(self, eio):
mgr = mock.MagicMock()
s = server.Server(client_manager=mgr)
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError())
s.on('connect', handler, namespace='/foo')
ret = s._handle_eio_connect('123', 'environ')
s._handle_eio_message('123', '0/foo') s._handle_eio_message('123', '0/foo')
self.assertIsNone(ret)
self.assertEqual(s.manager.connect.call_count, 2) self.assertEqual(s.manager.connect.call_count, 2)
self.assertEqual(s.manager.disconnect.call_count, 1) self.assertEqual(s.manager.disconnect.call_count, 1)
s.eio.send.assert_any_call('123', '4/foo,"fail_reason"', binary=False) self.assertEqual(s.environ, {'123': 'environ'})
s.eio.send.assert_any_call('123', '4/foo', binary=False)
def test_handle_disconnect(self, eio): def test_handle_disconnect(self, eio):
mgr = mock.MagicMock() mgr = mock.MagicMock()

Loading…
Cancel
Save