@@ -687,6 +687,82 @@ async def test_connection_pool_closed_while_request_in_flight():
687687 await response .aread ()
688688
689689
690+ @pytest .mark .anyio
691+ async def test_connection_pool_with_idle_broken_connection ():
692+ """
693+ Pool gives a new connection when an idle connection gets readable (ie broken) while in the pool.
694+ """
695+
696+ class MockStream (httpcore .AsyncMockStream ):
697+ def __init__ (self , buffer : typing .List [bytes ]):
698+ super ().__init__ (buffer )
699+ self .mock_is_readable = False
700+
701+ def get_extra_info (self , info : str ) -> typing .Any :
702+ if info == "is_readable" :
703+ return self .mock_is_readable
704+ return super ().get_extra_info (info ) # pragma: nocover
705+
706+ streams = [
707+ MockStream (
708+ [
709+ b"HTTP/1.1 200 OK\r \n " ,
710+ b"Content-Type: plain/text\r \n " ,
711+ b"Content-Length: 15\r \n " ,
712+ b"\r \n " ,
713+ b"Hello, world 1!" ,
714+ b"HTTP/1.1 200 OK\r \n " ,
715+ b"Content-Type: plain/text\r \n " ,
716+ b"Content-Length: 15\r \n " ,
717+ b"\r \n " ,
718+ b"Hello, world 2!" ,
719+ ]
720+ ),
721+ MockStream (
722+ [
723+ b"HTTP/1.1 200 OK\r \n " ,
724+ b"Content-Type: plain/text\r \n " ,
725+ b"Content-Length: 29\r \n " ,
726+ b"\r \n " ,
727+ b"Hello, world from new stream!" ,
728+ ]
729+ ),
730+ ]
731+
732+ class MockBackend (httpcore .AsyncMockBackend ):
733+ async def connect_tcp (
734+ self , * args : typing .Any , ** kwargs : typing .Any
735+ ) -> MockStream :
736+ return streams .pop (0 )
737+
738+ async with httpcore .AsyncConnectionPool (
739+ network_backend = MockBackend ([]), max_connections = 1
740+ ) as pool :
741+ res = await pool .request ("GET" , "https://example.com/" )
742+ assert (await res .aread ()) == b"Hello, world 1!"
743+
744+ assert len (pool .connections ) == 1
745+ conn = pool .connections [0 ]
746+
747+ res = await pool .request ("GET" , "https://example.com/" )
748+ assert (await res .aread ()) == b"Hello, world 2!"
749+
750+ assert len (pool .connections ) == 1
751+ assert conn is pool .connections [0 ], "Should reuse connection"
752+
753+ # Simulate network breakage
754+ assert conn .is_idle ()
755+ conn ._connection ._network_stream .mock_is_readable = True # type: ignore[attr-defined]
756+
757+ res = await pool .request ("GET" , "https://example.com/" )
758+ assert (await res .aread ()) == b"Hello, world from new stream!"
759+
760+ assert len (pool .connections ) == 1
761+ new_conn = pool .connections [0 ]
762+ assert new_conn is not conn , "Should be a new connection"
763+ assert not new_conn ._connection ._network_stream .mock_is_readable # type: ignore[attr-defined]
764+
765+
690766@pytest .mark .anyio
691767async def test_connection_pool_timeout ():
692768 """
0 commit comments