@@ -63,6 +63,40 @@ async def test_stdio_server():
6363 assert received_responses [1 ] == JSONRPCResponse (jsonrpc = "2.0" , id = 4 , result = {})
6464
6565
66+ @pytest .mark .anyio
67+ async def test_stdio_server_no_crlf (monkeypatch : pytest .MonkeyPatch ):
68+ """Raw bytes written to stdout must use LF (\\ n), never CRLF (\\ r\\ n).
69+
70+ On Windows, TextIOWrapper with the default newline=None translates \\ n to
71+ \\ r\\ n on write, which corrupts NDJSON framing for JSON-RPC. The fix is to
72+ pass newline="" to TextIOWrapper so no translation occurs.
73+ """
74+ raw_stdout = io .BytesIO ()
75+ # Wrap with newline="" so we can inspect the exact bytes that
76+ # stdio_server writes. The key assertion is that the raw bytes
77+ # contain \n and never \r\n.
78+ stdout_wrapper = TextIOWrapper (raw_stdout , encoding = "utf-8" , newline = "" )
79+ stdin_wrapper = TextIOWrapper (io .BytesIO (b"" ), encoding = "utf-8" )
80+
81+ message = JSONRPCRequest (jsonrpc = "2.0" , id = 1 , method = "ping" )
82+
83+ with anyio .fail_after (5 ):
84+ async with stdio_server (
85+ stdin = anyio .AsyncFile (stdin_wrapper ),
86+ stdout = anyio .AsyncFile (stdout_wrapper ),
87+ ) as (read_stream , write_stream ):
88+ async with write_stream :
89+ await write_stream .send (SessionMessage (message ))
90+ async with read_stream :
91+ pass
92+
93+ stdout_wrapper .flush ()
94+ raw_bytes = raw_stdout .getvalue ()
95+ assert len (raw_bytes ) > 0 , "expected output bytes"
96+ assert raw_bytes .endswith (b"\n " ), "output must end with LF"
97+ assert b"\r \n " not in raw_bytes , "output must not contain CRLF"
98+
99+
66100@pytest .mark .anyio
67101async def test_stdio_server_invalid_utf8 (monkeypatch : pytest .MonkeyPatch ):
68102 """Non-UTF-8 bytes on stdin must not crash the server.
0 commit comments