Send a message through the protocol. Applies outgoing middleware, serializes the message, and sends via transport. :param msg: Message to send (ClientMessage or ServerMessage) :raises: ConnectionError if transport not connected :raises: IOError if send fail
(self, msg: Any)
| 50 | self.logger = logging.getLogger(f"{__name__}.AIPProtocol") |
| 51 | |
| 52 | async def send_message(self, msg: Any) -> None: |
| 53 | """ |
| 54 | Send a message through the protocol. |
| 55 | |
| 56 | Applies outgoing middleware, serializes the message, and sends via transport. |
| 57 | |
| 58 | :param msg: Message to send (ClientMessage or ServerMessage) |
| 59 | :raises: ConnectionError if transport not connected |
| 60 | :raises: IOError if send fails |
| 61 | """ |
| 62 | try: |
| 63 | # Apply outgoing middleware |
| 64 | for middleware in self.middleware_chain: |
| 65 | msg = await middleware.process_outgoing(msg) |
| 66 | |
| 67 | # Serialize message |
| 68 | if hasattr(msg, "model_dump_json"): |
| 69 | # Pydantic model |
| 70 | serialized = msg.model_dump_json().encode("utf-8") |
| 71 | elif isinstance(msg, str): |
| 72 | serialized = msg.encode("utf-8") |
| 73 | elif isinstance(msg, bytes): |
| 74 | serialized = msg |
| 75 | else: |
| 76 | raise ValueError(f"Unsupported message type: {type(msg)}") |
| 77 | |
| 78 | # Send via transport |
| 79 | await self.transport.send(serialized) |
| 80 | self.logger.debug(f"Sent message: {msg.__class__.__name__}") |
| 81 | |
| 82 | except (ConnectionError, IOError, OSError) as e: |
| 83 | # Connection closed or I/O error - this is common during disconnection |
| 84 | # Log at DEBUG level to avoid alarming ERROR logs during normal shutdown |
| 85 | error_msg = str(e).lower() |
| 86 | if "closed" in error_msg or "not connected" in error_msg: |
| 87 | self.logger.debug(f"Cannot send message (connection closed): {e}") |
| 88 | else: |
| 89 | self.logger.warning(f"Connection error sending message: {e}") |
| 90 | raise |
| 91 | except Exception as e: |
| 92 | self.logger.error(f"Error sending message: {e}") |
| 93 | raise |
| 94 | |
| 95 | async def receive_message(self, message_type: type = ServerMessage) -> Any: |
| 96 | """ |