Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion slack_sdk/socket_mode/aiohttp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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})")
Comment on lines +409 to +410

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪬 note: I'm surprised self.logger.debug doesn't check this condition itself but I think this log is quite useful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic follows the existing pattern in the file, but I'm not sure why we have this condition, I need to take a deeper dive into it

return
self.logger.exception(f"Failed to connect (error: {e}); Retrying...")
await asyncio.sleep(self.ping_interval)

Expand Down
37 changes: 37 additions & 0 deletions tests/slack_sdk_async/socket_mode/test_aiohttp.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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(
Expand Down
Loading