@@ -1332,3 +1332,116 @@ async def handler(request: web.Request) -> web.WebSocketResponse:
1332
1332
)
1333
1333
await client .server .close ()
1334
1334
assert close_code == WSCloseCode .OK
1335
+
1336
+
1337
+ async def test_websocket_prepare_timeout_close_issue (
1338
+ loop : asyncio .AbstractEventLoop , aiohttp_client : AiohttpClient
1339
+ ) -> None :
1340
+ """Test that WebSocket can handle prepare with early returns.
1341
+
1342
+ This is a regression test for issue #6009 where the prepared property
1343
+ incorrectly checked _payload_writer instead of _writer.
1344
+ """
1345
+
1346
+ async def handler (request : web .Request ) -> web .WebSocketResponse :
1347
+ ws = web .WebSocketResponse ()
1348
+ assert ws .can_prepare (request )
1349
+ await ws .prepare (request )
1350
+ await ws .send_str ("test" )
1351
+ await ws .close ()
1352
+ return ws
1353
+
1354
+ app = web .Application ()
1355
+ app .router .add_route ("GET" , "/ws" , handler )
1356
+ client = await aiohttp_client (app )
1357
+
1358
+ # Connect via websocket
1359
+ ws = await client .ws_connect ("/ws" )
1360
+ msg = await ws .receive ()
1361
+ assert msg .type is WSMsgType .TEXT
1362
+ assert msg .data == "test"
1363
+ await ws .close ()
1364
+
1365
+
1366
+ async def test_websocket_prepare_timeout_from_issue_reproducer (
1367
+ loop : asyncio .AbstractEventLoop , aiohttp_client : AiohttpClient
1368
+ ) -> None :
1369
+ """Test websocket behavior when prepare is interrupted.
1370
+
1371
+ This test verifies the fix for issue #6009 where close() would
1372
+ fail after prepare() was interrupted.
1373
+ """
1374
+ prepare_complete = asyncio .Event ()
1375
+ close_complete = asyncio .Event ()
1376
+
1377
+ async def handler (request : web .Request ) -> web .WebSocketResponse :
1378
+ ws = web .WebSocketResponse ()
1379
+
1380
+ # Prepare the websocket
1381
+ await ws .prepare (request )
1382
+ prepare_complete .set ()
1383
+
1384
+ # Send a message to confirm connection works
1385
+ await ws .send_str ("connected" )
1386
+
1387
+ # Wait for client to close
1388
+ msg = await ws .receive ()
1389
+ assert msg .type is WSMsgType .CLOSE
1390
+ await ws .close ()
1391
+ close_complete .set ()
1392
+
1393
+ return ws
1394
+
1395
+ app = web .Application ()
1396
+ app .router .add_route ("GET" , "/ws" , handler )
1397
+ client = await aiohttp_client (app )
1398
+
1399
+ # Connect and verify the connection works
1400
+ ws = await client .ws_connect ("/ws" )
1401
+ await prepare_complete .wait ()
1402
+
1403
+ msg = await ws .receive ()
1404
+ assert msg .type is WSMsgType .TEXT
1405
+ assert msg .data == "connected"
1406
+
1407
+ # Close the connection
1408
+ await ws .close ()
1409
+ await close_complete .wait ()
1410
+
1411
+
1412
+ async def test_websocket_prepared_property (
1413
+ loop : asyncio .AbstractEventLoop , aiohttp_client : AiohttpClient
1414
+ ) -> None :
1415
+ """Test that WebSocketResponse.prepared property correctly reflects state."""
1416
+ prepare_called = asyncio .Event ()
1417
+
1418
+ async def handler (request : web .Request ) -> web .WebSocketResponse :
1419
+ ws = web .WebSocketResponse ()
1420
+
1421
+ # Initially not prepared
1422
+ initial_state = ws .prepared
1423
+ assert not initial_state
1424
+
1425
+ # After prepare() is called, should be prepared
1426
+ await ws .prepare (request )
1427
+ prepare_called .set ()
1428
+
1429
+ # Check prepared state
1430
+ prepared_state = ws .prepared
1431
+ assert prepared_state
1432
+
1433
+ # Send a message to verify the connection works
1434
+ await ws .send_str ("test" )
1435
+ await ws .close ()
1436
+ return ws
1437
+
1438
+ app = web .Application ()
1439
+ app .router .add_route ("GET" , "/" , handler )
1440
+ client = await aiohttp_client (app )
1441
+
1442
+ ws = await client .ws_connect ("/" )
1443
+ await prepare_called .wait ()
1444
+ msg = await ws .receive ()
1445
+ assert msg .type is WSMsgType .TEXT
1446
+ assert msg .data == "test"
1447
+ await ws .close ()
0 commit comments