From 332526969856c822f32e7a796e800236a8a62865 Mon Sep 17 00:00:00 2001 From: tbkizle Date: Thu, 7 May 2026 04:16:00 -0400 Subject: [PATCH] Update --- deluge_client/client.py | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/deluge_client/client.py b/deluge_client/client.py index 4f0fe86..294f1b4 100644 --- a/deluge_client/client.py +++ b/deluge_client/client.py @@ -2,6 +2,7 @@ import socket import ssl import struct +import time import warnings import zlib import io @@ -70,6 +71,20 @@ def __init__(self, host, port, username, password, decode_utf8=False, automatic_ self._create_socket() + def _safe_recv(self, size, retries=5): + """ + Retry-safe recv wrapper to handle Deluge 2.2.0's tendency + to close or stall SSL sockets briefly. + """ + while True: + try: + return self._socket.recv(size) + except (ssl.SSLError, socket.timeout, socket.error): + if retries <= 0: + raise CallTimeoutException() + retries -= 1 + time.sleep(0.2) + def _create_socket(self, ssl_version=None, ciphers=None): # Insecure context without remote certificate verification # This logic is a bit messy to try and support various configurations @@ -132,7 +147,7 @@ def _detect_deluge_version(self): self._send_call(1, None, 'daemon.info') self._send_call(2, None, 'daemon.info') self._send_call(2, 1, 'daemon.info') - result = self._socket.recv(1) + result = self._safe_recv(1) if result[:1] == b'D': # This is a protocol deluge 2.0 was using before release self.deluge_version = 2 @@ -179,7 +194,7 @@ def _receive_response(self, deluge_version, protocol_version, partial_data=b''): data = partial_data while True: try: - d = self._socket.recv(READ_SIZE) + d = self._safe_recv(READ_SIZE) except ssl.SSLError: raise CallTimeoutException() @@ -247,13 +262,23 @@ def _receive_response(self, deluge_version, protocol_version, partial_data=b''): retval = data[0] return retval - def reconnect(self): + def reconnect(self, retries=5, delay=1): """ - Reconnect + More tolerant reconnect logic for Deluge 2.2.0. + Handles SSL renegotiation, idle socket closes, and + transient RPC stalls. """ - self.disconnect() - self._create_socket() - self.connect() + for _ in range(retries): + try: + self.disconnect() + self._create_socket() + self.connect() + return + except (ssl.SSLError, socket.error, ConnectionLostException, CallTimeoutException): + time.sleep(delay) + delay = min(delay * 2, 10) + + raise FailedToReconnectException("Could not reconnect after multiple attempts") def call(self, method, *args, **kwargs): """