@@ -5895,6 +5895,74 @@ async def handler(request: web.Request) -> web.Response:
58955895 await asyncio .to_thread (f .close )
58965896
58975897
5898+ async def test_file_upload_retry_persistent_connection (
5899+ aiohttp_client : AiohttpClient , tmp_path : pathlib .Path
5900+ ) -> None :
5901+ """A retried request must resend the whole file, not the unread remainder."""
5902+ received_bodies : list [bytes ] = []
5903+ num_requests = 0
5904+
5905+ async def handler (request : web .Request ) -> web .Response :
5906+ nonlocal num_requests
5907+ num_requests += 1
5908+ if num_requests == 1 :
5909+ assert request .transport is not None
5910+ request .transport .close ()
5911+ return web .Response ()
5912+
5913+ received_bodies .append (await request .read ())
5914+ return web .Response ()
5915+
5916+ app = web .Application ()
5917+ app .router .add_put ("/upload" , handler )
5918+
5919+ client = await aiohttp_client (app )
5920+ client .session ._retry_connection = True
5921+
5922+ test_file = tmp_path / "test_retry_upload.txt"
5923+ content = b"This is test file content for a retried upload."
5924+ await asyncio .to_thread (test_file .write_bytes , content )
5925+
5926+ f = await asyncio .to_thread (open , test_file , "rb" )
5927+ try :
5928+ async with client .put ("/upload" , data = f ) as resp :
5929+ assert resp .status == 200
5930+ finally :
5931+ await asyncio .to_thread (f .close )
5932+
5933+ assert num_requests == 2
5934+ assert received_bodies == [content ]
5935+
5936+
5937+ async def test_upload_retry_persistent_connection_unseekable_body (
5938+ aiohttp_client : AiohttpClient ,
5939+ ) -> None :
5940+ """An unreplayable body must not be silently resent truncated on retry."""
5941+ num_requests = 0
5942+
5943+ async def handler (request : web .Request ) -> web .Response :
5944+ nonlocal num_requests
5945+ num_requests += 1
5946+ assert request .transport is not None
5947+ request .transport .close ()
5948+ return web .Response ()
5949+
5950+ app = web .Application ()
5951+ app .router .add_put ("/upload" , handler )
5952+
5953+ client = await aiohttp_client (app )
5954+ client .session ._retry_connection = True
5955+
5956+ async def gen () -> AsyncIterator [bytes ]:
5957+ yield b"chunk1"
5958+ yield b"chunk2"
5959+
5960+ with pytest .raises ((aiohttp .ServerDisconnectedError , aiohttp .ClientOSError )):
5961+ await client .put ("/upload" , data = gen ())
5962+
5963+ assert num_requests == 1
5964+
5965+
58985966async def test_stream_reader_total_raw_bytes (aiohttp_client : AiohttpClient ) -> None :
58995967 """Test whether StreamReader.total_raw_bytes returns the number of bytes downloaded"""
59005968 source_data = b"@dKal^pH>1h|YW1:c2J$" * 4096
0 commit comments