Read framed message: 4-byte header + JSON payload. Args: sock: Connected socket. Returns: Parsed JSON payload as dictionary. Raises: ProtocolError: If header is incomplete, payload too large, or invalid JSON.
(self, sock: socket.socket)
| 174 | sock.sendall(header + payload_bytes) |
| 175 | |
| 176 | def _read_frame(self, sock: socket.socket) -> dict[str, Any]: |
| 177 | """Read framed message: 4-byte header + JSON payload. |
| 178 | |
| 179 | Args: |
| 180 | sock: Connected socket. |
| 181 | |
| 182 | Returns: |
| 183 | Parsed JSON payload as dictionary. |
| 184 | |
| 185 | Raises: |
| 186 | ProtocolError: If header is incomplete, payload too large, or invalid JSON. |
| 187 | """ |
| 188 | sock.settimeout(self.timeout) |
| 189 | |
| 190 | header = sock.recv(HEADER_SIZE) |
| 191 | if len(header) != HEADER_SIZE: |
| 192 | raise ProtocolError( |
| 193 | f"Expected {HEADER_SIZE}-byte header, got {len(header)} bytes", |
| 194 | "PROTOCOL_ERROR", |
| 195 | ) |
| 196 | |
| 197 | (length,) = struct.unpack(">I", header) |
| 198 | |
| 199 | if length > MAX_PAYLOAD_BYTES: |
| 200 | raise ProtocolError( |
| 201 | f"Payload too large: {length} > {MAX_PAYLOAD_BYTES}", |
| 202 | "PAYLOAD_TOO_LARGE", |
| 203 | ) |
| 204 | |
| 205 | chunks: list[bytes] = [] |
| 206 | remaining = length |
| 207 | while remaining > 0: |
| 208 | chunk_size = min(remaining, 65536) # 64KB buffer |
| 209 | chunk = sock.recv(chunk_size) |
| 210 | if not chunk: |
| 211 | raise ProtocolError( |
| 212 | "Connection closed while reading payload", |
| 213 | "PROTOCOL_ERROR", |
| 214 | ) |
| 215 | chunks.append(chunk) |
| 216 | remaining -= len(chunk) |
| 217 | payload = b"".join(chunks) # O(n) concatenation |
| 218 | |
| 219 | try: |
| 220 | result: dict[str, Any] = json.loads(payload.decode("utf-8")) |
| 221 | return result |
| 222 | except json.JSONDecodeError as e: |
| 223 | raise ProtocolError(f"Invalid JSON response: {e}", "MALFORMED_JSON") from e |
| 224 | |
| 225 | _RETRYABLE_CODES = frozenset({"INSTANCE_RELOADING", "INSTANCE_BUSY", "TIMEOUT", "INSTANCE_DISCONNECTED"}) |
| 226 |
no test coverage detected