diff --git a/slack_sdk/socket_mode/aiohttp/__init__.py b/slack_sdk/socket_mode/aiohttp/__init__.py index a31fd2c15..da104b50e 100644 --- a/slack_sdk/socket_mode/aiohttp/__init__.py +++ b/slack_sdk/socket_mode/aiohttp/__init__.py @@ -349,7 +349,7 @@ async def connect(self): # a new monitor and a new message receiver are also created. # If a new session is created but we failed to create the new # monitor or the new message, we should try it. - while True: + while not self.closed: try: old_session: Optional[ClientWebSocketResponse] = ( None if self.current_session is None else self.current_session @@ -405,6 +405,10 @@ async def connect(self): self.logger.debug(f"A new receive_messages() executor has been recreated for {session_id}") break except Exception as e: + if self.closed: + if self.logger.level <= logging.DEBUG: + self.logger.debug(f"Stopped connecting because the client is closed (error: {e})") + return self.logger.exception(f"Failed to connect (error: {e}); Retrying...") await asyncio.sleep(self.ping_interval) diff --git a/tests/slack_sdk_async/socket_mode/test_aiohttp.py b/tests/slack_sdk_async/socket_mode/test_aiohttp.py index eda1fecdd..4834c0c84 100644 --- a/tests/slack_sdk_async/socket_mode/test_aiohttp.py +++ b/tests/slack_sdk_async/socket_mode/test_aiohttp.py @@ -1,5 +1,7 @@ import asyncio +import logging import unittest +from unittest.mock import MagicMock from slack_sdk.socket_mode.aiohttp import SocketModeClient from slack_sdk.web.async_client import AsyncWebClient @@ -31,6 +33,41 @@ async def test_init_close(self): finally: await client.close() + @async_test + async def test_connect_returns_when_closed(self): + # Regression test for #1913: connect() must not loop forever once the client is closed. + client = SocketModeClient( + app_token="xapp-A111-222-xyz", + web_client=self.web_client, + auto_reconnect_enabled=False, + ping_interval=0.01, + ) + client.wss_uri = "ws://localhost:8888/link" + await client.close() + await asyncio.wait_for(client.connect(), timeout=1.0) + self.assertTrue(client.closed) + + @async_test + async def test_connect_returns_when_exception_raised_after_close(self): + client = SocketModeClient( + app_token="xapp-A111-222-xyz", + web_client=self.web_client, + auto_reconnect_enabled=False, + ping_interval=0.01, + ) + client.logger = MagicMock() + client.logger.level = logging.DEBUG + + async def close_then_raise(*args, **kwargs): + await client.close() + raise RuntimeError("Session is closed") + + client.issue_new_wss_url = close_then_raise + + await asyncio.wait_for(client.connect(), timeout=1.0) + self.assertTrue(client.closed) + client.logger.exception.assert_not_called() + @async_test async def test_init_with_loop(self): client = SocketModeClient(