Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

irc: Improve output for common connection errors #2430

Merged
merged 5 commits into from
May 23, 2023
Merged
Changes from 3 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
74 changes: 48 additions & 26 deletions sopel/irc/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(
):
super().__init__(bot)

def is_connected(self) -> False:
def is_connected(self) -> bool:
Exirel marked this conversation as resolved.
Show resolved Hide resolved
"""Check if the backend is connected to an IRC server.

**Always returns False:** This backend type is never connected.
Expand Down Expand Up @@ -378,42 +378,40 @@ def get_connection_kwargs(self) -> Dict:
'local_addr': self._source_address,
}

async def _run_forever(self) -> None:
self._loop = asyncio.get_running_loop()
connection_kwargs = self.get_connection_kwargs()

# register signal handlers
for quit_signal in QUIT_SIGNALS:
self._loop.add_signal_handler(quit_signal, self._signal_quit)
for restart_signal in RESTART_SIGNALS:
self._loop.add_signal_handler(restart_signal, self._signal_restart)
async def _connect_to_server(
self, **connection_kwargs
) -> Tuple[
Optional[asyncio.StreamReader],
Optional[asyncio.StreamWriter],
]:
reader: Optional[asyncio.StreamReader] = None
writer: Optional[asyncio.StreamWriter] = None

# open connection
Exirel marked this conversation as resolved.
Show resolved Hide resolved
try:
self._reader, self._writer = await asyncio.open_connection(
reader, writer = await asyncio.open_connection(
**connection_kwargs,
)

# SSL Error
# SSL Errors (certificate verification and generic SSL errors)
except ssl.SSLCertVerificationError as err:
LOGGER.error(
'Unable to connect due to an SSL validation error: %s',
'Unable to connect due to '
'SSL certificate verification failure: %s',
err,
)
self.log_exception()
# tell the bot to quit without restart
self.bot.hasquit = True
self.bot.wantsrestart = False
return
except ssl.SSLError as err:
LOGGER.error('Unable to connect due to an SSL error: %s', err)
self.log_exception()
# tell the bot to quit without restart
self.bot.hasquit = True
self.bot.wantsrestart = False
return

# Specific connection error
# Specific connection error (invalid address and timeout)
except socket.gaierror as err:
LOGGER.error(
'Unable to connect due to invalid IRC server address: %s',
Expand All @@ -429,21 +427,24 @@ async def _run_forever(self) -> None:
# tell the bot to quit without restart
self.bot.hasquit = True
self.bot.wantsrestart = False
return
except TimeoutError as err:
LOGGER.error('Unable to connect due to a timeout: %s', err)
self.log_exception()
# tell the bot to quit without restart
self.bot.hasquit = True
self.bot.wantsrestart = False
return

# Generic connection error (OSError is used for any connection error)
# Generic connection error
except ConnectionError as err:
LOGGER.error('Unable to connect: %s', err)
self.log_exception()
# tell the bot to quit without restart
self.bot.hasquit = True
self.bot.wantsrestart = False

# Generic OSError (used for any unspecific connection error)
Exirel marked this conversation as resolved.
Show resolved Hide resolved
except OSError as err:
LOGGER.error(
'Unable to connect: %s',
err,
)
LOGGER.error('Unable to connect: %s', err)
LOGGER.error(
'You should verify that "%s:%s" is the correct address '
'to connect to the IRC server.',
Expand All @@ -454,7 +455,6 @@ async def _run_forever(self) -> None:
# tell the bot to quit without restart
self.bot.hasquit = True
self.bot.wantsrestart = False
return

# Unexpected error
except Exception as err:
Expand All @@ -468,13 +468,34 @@ async def _run_forever(self) -> None:
# TODO: prevent infinite connection failure loop
self.bot.hasquit = True
self.bot.wantsrestart = False
return

self._connected = True
return reader, writer

async def _run_forever(self) -> None:
self._loop = asyncio.get_running_loop()
connection_kwargs = self.get_connection_kwargs()

# register signal handlers
for quit_signal in QUIT_SIGNALS:
self._loop.add_signal_handler(quit_signal, self._signal_quit)
for restart_signal in RESTART_SIGNALS:
self._loop.add_signal_handler(restart_signal, self._signal_restart)

# connect to socket
LOGGER.debug('Attempt connection.')
self._reader, self._writer = await self._connect_to_server(
**connection_kwargs
)
if not self._reader or not self._writer:
LOGGER.debug('Connection attempt failed.')
return

# on socket connection
LOGGER.debug('Connection registered.')
self._connected = True
self.bot.on_connect()

# read forever
LOGGER.debug('Waiting for messages...')
self._read_task = asyncio.create_task(self.read_forever())
try:
Expand All @@ -484,6 +505,7 @@ async def _run_forever(self) -> None:
else:
LOGGER.debug('Reader received EOF.')

# on socket disconnection
self._connected = False

# cancel timeout tasks
Expand Down