home · contact · privacy
Handle more gracefully expected exceptions in BaseIrcConnection._read_lines.
authorChristian Heller <c.heller@plomlompom.de>
Sun, 10 Aug 2025 09:46:19 +0000 (11:46 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 10 Aug 2025 09:46:19 +0000 (11:46 +0200)
ircplom/client.py
ircplom/irc_conn.py

index 1c23695456135c65e96546f5d28412208caa8541..dff7db42936ccb4986df2e0c1432835d4a717222 100644 (file)
@@ -48,6 +48,11 @@ class _IrcConnection(BaseIrcConnection, ClientIdMixin):
         return ClientEvent.affector('handle_msg', client_id=self.client_id
                                     ).kw(msg=msg)
 
+    def _on_handled_loop_exception(self, e: IrcConnAbortException
+                                   ) -> ClientEvent:
+        return ClientEvent.affector('on_handled_loop_exception',
+                                    client_id=self.client_id).kw(e=e)
+
 
 @dataclass
 class ClientQueueMixin(QueueMixin, ClientIdMixin):
@@ -265,6 +270,11 @@ class Client(ABC, ClientQueueMixin):
         self.conn = None
         self.update_login(nick_confirmed=False)
 
+    def on_handled_loop_exception(self, e: IrcConnAbortException) -> None:
+        'Gracefully handle broken connection.'
+        self._log.alert(f'connection broken: {e}')
+        self.close()
+
     def handle_msg(self, msg: IrcMessage) -> None:
         'Process incoming msg towards appropriate client steps.'
         self._log.add(msg.raw, prefix=_LOG_PREFIX_RECV_RAW, stream=STREAM_RAW)
index 13af29113bdf64ac810a2787896fd83b37efa78a..0dcd113f22090058b3dd2e57384af28052b1b114 100644 (file)
@@ -156,35 +156,46 @@ class BaseIrcConnection(QueueMixin, ABC):
     def _make_recv_event(self, msg: IrcMessage) -> Event:
         pass
 
+    @abstractmethod
+    def _on_handled_loop_exception(self, _: IrcConnAbortException) -> Event:
+        pass
+
     def _read_lines(self) -> Iterator[Optional[Event]]:
         assert self._socket is not None
         bytes_total = b''
         buffer_linesep = b''
-        while True:
-            try:
-                bytes_new = self._socket.recv(_CONN_RECV_BUFSIZE)
-            except TimeoutError:
-                yield None
-                continue
-            except ConnectionResetError as e:
-                raise e
-            except OSError as e:
-                if e.errno == 9:
+        i = 0
+        try:
+            while True:
+                i += 1
+                try:
+                    bytes_new = self._socket.recv(_CONN_RECV_BUFSIZE)
+                    if i >= 10:
+                        raise IrcConnAbortException("DEBUG")
+                except TimeoutError:
+                    yield None
+                    continue
+                except ConnectionResetError as e:
+                    raise IrcConnAbortException(e) from e
+                except OSError as e:
+                    if e.errno == 9:
+                        raise IrcConnAbortException(e) from e
+                    raise e
+                if not bytes_new:
                     break
-                raise e
-            if not bytes_new:
-                break
-            for c in bytes_new:
-                c_byted = c.to_bytes()
-                if c not in _IRCSPEC_LINE_SEPARATOR:
-                    bytes_total += c_byted
-                    buffer_linesep = b''
-                elif c == _IRCSPEC_LINE_SEPARATOR[0]:
-                    buffer_linesep = c_byted
-                else:
-                    buffer_linesep += c_byted
-                if buffer_linesep == _IRCSPEC_LINE_SEPARATOR:
-                    buffer_linesep = b''
-                    yield self._make_recv_event(
+                for c in bytes_new:
+                    c_byted = c.to_bytes()
+                    if c not in _IRCSPEC_LINE_SEPARATOR:
+                        bytes_total += c_byted
+                        buffer_linesep = b''
+                    elif c == _IRCSPEC_LINE_SEPARATOR[0]:
+                        buffer_linesep = c_byted
+                    else:
+                        buffer_linesep += c_byted
+                    if buffer_linesep == _IRCSPEC_LINE_SEPARATOR:
+                        buffer_linesep = b''
+                        yield self._make_recv_event(
                             IrcMessage.from_raw(bytes_total.decode('utf-8')))
-                    bytes_total = b''
+                        bytes_total = b''
+        except IrcConnAbortException as e:
+            yield self._on_handled_loop_exception(e)