From cd8977ee8e2680349b6636811f8e389b509ece6e Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui Date: Fri, 10 Jan 2025 10:23:33 +0100 Subject: [PATCH] hub: implement listen as native The function is largely inspired by the one in eventlet. That is said, there are nothing exceptional. Related-bug: #2087939 Change-Id: Ife65518531393fd6ad06730ef8fead02cdb1eb0f Signed-off-by: Sahid Orentino Ferdjaoui --- os_ken/lib/hub.py | 21 ++++++++ os_ken/tests/unit/lib/test_hub.py | 85 +++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/os_ken/lib/hub.py b/os_ken/lib/hub.py index a869ca39..69d9e608 100644 --- a/os_ken/lib/hub.py +++ b/os_ken/lib/hub.py @@ -18,6 +18,7 @@ import logging import os import ssl import socket +import sys import traceback from os_ken.lib import ip @@ -365,6 +366,26 @@ elif HUB_TYPE == 'native': except queue.Empty: pass + def listen(addr, family=socket.AF_INET, backlog=50, reuse_addr=True, + reuse_port=None): + """ + Largely inspired by: + https://github.com/eventlet/eventlet/../eventlet/convenience.py + """ + sock = socket.socket(family, socket.SOCK_STREAM) + if reuse_addr and sys.platform[:3] != 'win': + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + elif reuse_port is None: + reuse_port = True + if reuse_port and hasattr(socket, 'SO_REUSEPORT'): + try: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + except Exception: + # Not supported by the platform + pass + sock.bind(addr) + sock.listen(backlog) + return sock else: raise NotImplementedError( "Invalid OSKEN_HUB_TYPE. Expected one of ('eventlet', 'native').") diff --git a/os_ken/tests/unit/lib/test_hub.py b/os_ken/tests/unit/lib/test_hub.py index 0623a5cd..e2652f27 100644 --- a/os_ken/tests/unit/lib/test_hub.py +++ b/os_ken/tests/unit/lib/test_hub.py @@ -264,3 +264,88 @@ class TestThreadManagementNative(unittest.TestCase): @mock.patch.dict(os.environ, {"OSKEN_HUB_TYPE": "native"}) def setUp(self): self.hub = importlib.reload(os_ken.lib.hub) + + +class TestListenEventlet(unittest.TestCase): + + @mock.patch.dict(os.environ, {"OSKEN_HUB_TYPE": "eventlet"}) + def setUp(self): + self.hub = importlib.reload(os_ken.lib.hub) + + self.patcher = mock.patch('eventlet.green.socket.socket') + self.mock_socket = self.patcher.start() + + self.sock = mock.MagicMock() + self.mock_socket.return_value = self.sock + + +class TestListenEventlet(unittest.TestCase): + + @mock.patch.dict(os.environ, {"OSKEN_HUB_TYPE": "eventlet"}) + def setUp(self): + self.hub = importlib.reload(os_ken.lib.hub) + + self.patcher = mock.patch('eventlet.green.socket.socket') + self.mock_socket = self.patcher.start() + + self.sock = mock.MagicMock() + self.mock_socket.return_value = self.sock + + def tearDown(self): + self.patcher.stop() + + def test_default_socket_creation(self): + addr = ('127.0.0.1', 8080) + sock = self.hub.listen(addr) + + self.mock_socket.assert_called_once_with( + socket.AF_INET, socket.SOCK_STREAM) + + self.sock.setsockopt.assert_has_calls([ + mock.call(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1), + mock.call(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1), + ]) + + self.sock.bind.assert_called_once_with(addr) + self.sock.listen.assert_called_once_with(50) + + self.assertEqual(self.sock, sock) + + def test_socket_with_ipv6(self): + addr = ('::1', 8080) + sock = self.hub.listen(addr, family=socket.AF_INET6) + + self.mock_socket.assert_called_once_with( + socket.AF_INET6, socket.SOCK_STREAM) + + self.sock.setsockopt.assert_has_calls([ + mock.call(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1), + mock.call(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1), + ]) + + self.sock.bind.assert_called_once_with(addr) + self.sock.listen.assert_called_once_with(50) + + self.assertEqual(self.sock, sock) + + def test_reuse_port_enabled(self): + addr = ('127.0.0.1', 8081) + + sock = self.hub.listen(addr, reuse_port=True) + + self.sock.setsockopt.assert_any_call( + socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + self.sock.bind.assert_called_once_with(addr) + + +class TestListenNative(unittest.TestCase): + + @mock.patch.dict(os.environ, {"OSKEN_HUB_TYPE": "native"}) + def setUp(self): + self.hub = importlib.reload(os_ken.lib.hub) + + self.patcher = mock.patch('socket.socket') + self.mock_socket = self.patcher.start() + + self.sock = mock.MagicMock() + self.mock_socket.return_value = self.sock