@@ -4382,3 +4382,128 @@ async def handler(request: web.Request) -> web.Response:
4382
4382
response .raise_for_status ()
4383
4383
4384
4384
assert len (client ._session .connector ._conns ) == 1
4385
+
4386
+
4387
+ async def test_post_content_exception_connection_kept (
4388
+ aiohttp_client : AiohttpClient ,
4389
+ ) -> None :
4390
+ """Test that connections are kept after content.set_exception() with POST."""
4391
+
4392
+ async def handler (request : web .Request ) -> web .Response :
4393
+ await request .read ()
4394
+ return web .Response (
4395
+ body = b"x" * 1000
4396
+ ) # Larger response to ensure it's not pre-buffered
4397
+
4398
+ app = web .Application ()
4399
+ app .router .add_post ("/" , handler )
4400
+ client = await aiohttp_client (app )
4401
+
4402
+ # POST request with body - connection should be closed after content exception
4403
+ resp = await client .post ("/" , data = b"request body" )
4404
+
4405
+ with pytest .raises (RuntimeError ):
4406
+ async with resp :
4407
+ assert resp .status == 200
4408
+ resp .content .set_exception (RuntimeError ("Simulated error" ))
4409
+ await resp .read ()
4410
+
4411
+ assert resp .closed
4412
+
4413
+ # Wait for any pending operations to complete
4414
+ await resp .wait_for_close ()
4415
+
4416
+ assert client ._session .connector is not None
4417
+ # Connection is kept because content.set_exception() is a client-side operation
4418
+ # that doesn't affect the underlying connection state
4419
+ assert len (client ._session .connector ._conns ) == 1
4420
+
4421
+
4422
+ async def test_network_error_connection_closed (
4423
+ aiohttp_client : AiohttpClient ,
4424
+ ) -> None :
4425
+ """Test that connections are closed after network errors."""
4426
+
4427
+ async def handler (request : web .Request ) -> NoReturn :
4428
+ # Read the request body
4429
+ await request .read ()
4430
+
4431
+ # Start sending response but close connection before completing
4432
+ response = web .StreamResponse ()
4433
+ response .content_length = 1000 # Promise 1000 bytes
4434
+ await response .prepare (request )
4435
+
4436
+ # Send partial data then force close the connection
4437
+ await response .write (b"x" * 100 ) # Only send 100 bytes
4438
+ # Force close the transport to simulate network error
4439
+ assert request .transport is not None
4440
+ request .transport .close ()
4441
+ assert False , "Will not return"
4442
+
4443
+ app = web .Application ()
4444
+ app .router .add_post ("/" , handler )
4445
+ client = await aiohttp_client (app )
4446
+
4447
+ # POST request that will fail due to network error
4448
+ with pytest .raises (aiohttp .ClientPayloadError ):
4449
+ resp = await client .post ("/" , data = b"request body" )
4450
+ async with resp :
4451
+ await resp .read () # This should fail
4452
+
4453
+ # Give event loop a chance to process connection cleanup
4454
+ await asyncio .sleep (0 )
4455
+
4456
+ assert client ._session .connector is not None
4457
+ # Connection should be closed due to network error
4458
+ assert len (client ._session .connector ._conns ) == 0
4459
+
4460
+
4461
+ async def test_client_side_network_error_connection_closed (
4462
+ aiohttp_client : AiohttpClient ,
4463
+ ) -> None :
4464
+ """Test that connections are closed after client-side network errors."""
4465
+ handler_done = asyncio .Event ()
4466
+
4467
+ async def handler (request : web .Request ) -> NoReturn :
4468
+ # Read the request body
4469
+ await request .read ()
4470
+
4471
+ # Start sending a large response
4472
+ response = web .StreamResponse ()
4473
+ response .content_length = 10000 # Promise 10KB
4474
+ await response .prepare (request )
4475
+
4476
+ # Send some data
4477
+ await response .write (b"x" * 1000 )
4478
+
4479
+ # Keep the response open - we'll interrupt from client side
4480
+ await asyncio .wait_for (handler_done .wait (), timeout = 5.0 )
4481
+ assert False , "Will not return"
4482
+
4483
+ app = web .Application ()
4484
+ app .router .add_post ("/" , handler )
4485
+ client = await aiohttp_client (app )
4486
+
4487
+ # POST request that will fail due to client-side network error
4488
+ with pytest .raises (aiohttp .ClientPayloadError ):
4489
+ resp = await client .post ("/" , data = b"request body" )
4490
+ async with resp :
4491
+ # Simulate client-side network error by closing the transport
4492
+ # This simulates connection reset, network failure, etc.
4493
+ assert resp .connection is not None
4494
+ assert resp .connection .protocol is not None
4495
+ assert resp .connection .protocol .transport is not None
4496
+ resp .connection .protocol .transport .close ()
4497
+
4498
+ # This should fail with connection error
4499
+ await resp .read ()
4500
+
4501
+ # Signal handler to finish
4502
+ handler_done .set ()
4503
+
4504
+ # Give event loop a chance to process connection cleanup
4505
+ await asyncio .sleep (0 )
4506
+
4507
+ assert client ._session .connector is not None
4508
+ # Connection should be closed due to client-side network error
4509
+ assert len (client ._session .connector ._conns ) == 0
0 commit comments