From b88bafdb110e6d9351050d10fffdfff001271317 Mon Sep 17 00:00:00 2001 From: Phlogi Date: Mon, 22 Dec 2025 10:57:15 +0100 Subject: [PATCH] implement read_exact method for reliable socket reading and enhance error handling --- librespot/core.py | 17 ++++++++++++++++- librespot/crypto.py | 11 +++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/librespot/core.py b/librespot/core.py index 46df5f9..08c30fe 100644 --- a/librespot/core.py +++ b/librespot/core.py @@ -1932,6 +1932,20 @@ def read(self, length: int) -> bytes: """ return self.__socket.recv(length) + def read_exact(self, length: int) -> bytes: + """Read exactly length bytes from socket or raise on EOF.""" + if length == 0: + return b"" + buffer = bytearray() + remaining = length + while remaining > 0: + chunk = self.__socket.recv(remaining) + if chunk == b"": + raise ConnectionError("EOF") + buffer.extend(chunk) + remaining -= len(chunk) + return bytes(buffer) + def read_int(self) -> int: """Read integer from socket @@ -2042,7 +2056,8 @@ def run(self) -> None: format(util.bytes_to_hex(packet.cmd), packet.payload)) continue - except (RuntimeError, ConnectionResetError) as ex: + except (RuntimeError, ConnectionResetError, + ConnectionError) as ex: if self.__running: self.__session.logger.fatal( "Failed reading packet! {}".format(ex)) diff --git a/librespot/crypto.py b/librespot/crypto.py index d09a4d5..13082e1 100644 --- a/librespot/crypto.py +++ b/librespot/crypto.py @@ -55,16 +55,19 @@ def receive_encoded(self, connection: Session.ConnectionHolder) -> Packet: try: self.__receive_cipher.nonce(self.__receive_nonce) self.__receive_nonce += 1 - header_bytes = self.__receive_cipher.decrypt(connection.read(3)) + header_bytes = self.__receive_cipher.decrypt( + connection.read_exact(3)) cmd = struct.pack(">s", bytes([header_bytes[0]])) payload_length = (header_bytes[1] << 8) | (header_bytes[2] & 0xff) payload_bytes = self.__receive_cipher.decrypt( - connection.read(payload_length)) - mac = connection.read(4) + connection.read_exact(payload_length)) + mac = connection.read_exact(4) expected_mac = self.__receive_cipher.finish(4) if mac != expected_mac: - raise RuntimeError() + raise RuntimeError("Bad MAC") return Packet(cmd, payload_bytes) + except ConnectionError: + raise except (IndexError, OSError): raise RuntimeError("Failed to receive packet")