From 5b5fb203b332b208dd60954b28c88e099bc8e726 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Thu, 2 Oct 2025 20:03:51 +0200 Subject: [PATCH] Implement on-timeout reconnect-wait only hinted at in previous commit. --- src/ircplom/client.py | 28 ++++++++++++++++++++++------ src/tests/pingpong.txt | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/ircplom/client.py b/src/ircplom/client.py index b27f72f..bf8c28b 100644 --- a/src/ircplom/client.py +++ b/src/ircplom/client.py @@ -5,8 +5,10 @@ from base64 import b64encode from dataclasses import dataclass, InitVar from getpass import getuser from threading import Thread +from time import sleep from typing import (Any, Callable, Collection, Generic, Iterable, Iterator, Optional, Self, Set, TypeVar) +from uuid import UUID, uuid4 # ourselves from ircplom.events import ( AffectiveEvent, CrashingException, ExceptionEvent, QueueMixin) @@ -818,7 +820,8 @@ class Client(ABC, ClientQueueMixin): conn: Optional[IrcConnection] = None _cls_conn: type[IrcConnection] = IrcConnection _expected_pong: str - _retry_in_s: int + _retry_connect_in_s: int + _retry_connect_id: Optional[UUID] = None def __init__(self, conn_setup: IrcConnSetup, channels: set[str], **kwargs ) -> None: @@ -838,6 +841,7 @@ class Client(ABC, ClientQueueMixin): def connect(self) -> None: 'Attempt to open connection, on success perform session init steps.' + self._retry_connect_id = None self.db.connection_state = 'connecting' def connect(self) -> None: @@ -851,7 +855,7 @@ class Client(ABC, ClientQueueMixin): self._put(ExceptionEvent(CrashingException(e))) else: self._expected_pong = '' - self._retry_in_s = -1 + self._retry_connect_in_s = -1 self.db.connection_state = 'connected' self.caps.start_negotation() self.send('USER', self.db.user_wanted, @@ -870,9 +874,21 @@ class Client(ABC, ClientQueueMixin): if self.conn: self.conn.close() self.conn = None - if self._retry_in_s > 0: - self._retry_in_s *= 2 - self.connect() + if self._retry_connect_in_s > 0: + def delayed_connect(self, wait_s: int, retry_connect_id: UUID + ) -> None: + sleep(wait_s) + if self._retry_connect_id == retry_connect_id: + self._client_trigger('connect') + + self._retry_connect_id = uuid4() + self._alert( + f'will retry connecting in {self._retry_connect_in_s} seconds') + Thread(target=delayed_connect, daemon=True, + args=(self, self._retry_connect_in_s, + self._retry_connect_id) + ).start() + self._retry_connect_in_s *= 2 def on_handled_loop_exception(self, e: IrcConnException) -> None: 'On …AbortException, call .close(), on …Timeout… first (!) try PING.' @@ -881,7 +897,7 @@ class Client(ABC, ClientQueueMixin): self.close() elif isinstance(e, IrcConnTimeoutException): if self._expected_pong: - self._retry_in_s = 1 + self._retry_connect_in_s = 1 self.on_handled_loop_exception( IrcConnAbortException('no timely PONG from server')) else: diff --git a/src/tests/pingpong.txt b/src/tests/pingpong.txt index 1811cd4..a468f57 100644 --- a/src/tests/pingpong.txt +++ b/src/tests/pingpong.txt @@ -52,6 +52,7 @@ repeat isupport-clear-in isupport-clear-out # on (automatic!) re-connect, ensure timer cleared, so won't trigger already on 1st timeout | conn2-in +1 .!$ will retry connecting in 1 seconds repeat conn0-in conn0-out 2 .$ CONNECTED repeat conn1-in conn1-out -- 2.30.2