Przeglądaj źródła

fix: preserve TCP receive order on codec errors

wuzilong 1 miesiąc temu
rodzic
commit
c2f88bbaba

+ 1 - 1
cq2_sdk/src/cq2_sdk/transport/tcp.py

@@ -107,7 +107,7 @@ class TcpClient:
         await self._writer.drain()
 
     async def recv(self, timeout: float = 30.0) -> tuple[FrameHeader, bytes]:
-        if self._recv_error is not None:
+        if self._msg_queue.empty() and self._recv_error is not None:
             raise self._recv_error
         result = await asyncio.wait_for(self._msg_queue.get(), timeout=timeout)
         if isinstance(result, Exception):

+ 29 - 0
cq2_sdk/tests/unit/test_tcp_client.py

@@ -149,3 +149,32 @@ async def test_recv_surfaces_unsupported_body_codec():
         await client.close()
         server.close()
         await server.wait_closed()
+
+
+@pytest.mark.asyncio
+async def test_recv_delivers_queued_frame_before_later_codec_error():
+    async def handler(reader, writer):
+        writer.write(encode_frame(529, 0, 0, 0, 0, b"valid"))
+        writer.write(encode_frame(530, 0, 0, 0, 0, b"compressed", codec=1))
+        await writer.drain()
+        writer.close()
+
+    server = await asyncio.start_server(handler, "127.0.0.1", 0)
+    port = server.sockets[0].getsockname()[1]
+    client = TcpClient("127.0.0.1", port)
+    await client.connect()
+    try:
+        for _ in range(100):
+            if client._recv_error is not None:
+                break
+            await asyncio.sleep(0.01)
+        assert client._recv_error is not None
+        header, body = await client.recv(timeout=1.0)
+        assert header.msg_id == 529
+        assert body == b"valid"
+        with pytest.raises(ValueError, match="unsupported body codec: 1"):
+            await client.recv(timeout=1.0)
+    finally:
+        await client.close()
+        server.close()
+        await server.wait_closed()