Read one message from a socket. Args: sock: Socket to read from Returns: Decoded message dictionary Raises: ProtocolError: If message is malformed ConnectionError: If connection is closed
(sock)
| 76 | |
| 77 | @staticmethod |
| 78 | def read_message(sock) -> dict: |
| 79 | """ |
| 80 | Read one message from a socket. |
| 81 | |
| 82 | Args: |
| 83 | sock: Socket to read from |
| 84 | |
| 85 | Returns: |
| 86 | Decoded message dictionary |
| 87 | |
| 88 | Raises: |
| 89 | ProtocolError: If message is malformed |
| 90 | ConnectionError: If connection is closed |
| 91 | """ |
| 92 | # Read length prefix |
| 93 | length_data = b'' |
| 94 | while len(length_data) < 4: |
| 95 | chunk = sock.recv(4 - len(length_data)) |
| 96 | if not chunk: |
| 97 | if not length_data: |
| 98 | raise ConnectionError("Connection closed") |
| 99 | raise ProtocolError("Incomplete length prefix") |
| 100 | length_data += chunk |
| 101 | |
| 102 | length = struct.unpack('>I', length_data)[0] |
| 103 | |
| 104 | if length > ControlProtocol.MAX_MESSAGE_SIZE: |
| 105 | raise ProtocolError(f"Message too large: {length}") |
| 106 | |
| 107 | # Read payload |
| 108 | payload_data = b'' |
| 109 | while len(payload_data) < length: |
| 110 | chunk = sock.recv(min(length - len(payload_data), 65536)) |
| 111 | if not chunk: |
| 112 | raise ProtocolError("Incomplete payload") |
| 113 | payload_data += chunk |
| 114 | |
| 115 | try: |
| 116 | return json.loads(payload_data.decode('utf-8')) |
| 117 | except json.JSONDecodeError as e: |
| 118 | raise ProtocolError(f"Invalid JSON: {e}") |
| 119 | |
| 120 | @staticmethod |
| 121 | def write_message(sock, data: dict): |