@@ -1726,6 +1726,113 @@ async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
17261726 ]
17271727
17281728
1729+ @pytest .mark .parametrize ("origin_first" , [True , False ])
1730+ def test_direct_vm_redirect_evicts_originating_cache_when_http_client_is_shared (
1731+ monkeypatch : pytest .MonkeyPatch ,
1732+ origin_first : bool ,
1733+ ) -> None :
1734+ monkeypatch .delenv ("KERNEL_BROWSER_ROUTING_SUBRESOURCES" , raising = False )
1735+ requests : list [tuple [httpx .URL , bytes ]] = []
1736+
1737+ class Transport (httpx .BaseTransport ):
1738+ @override
1739+ def handle_request (self , request : httpx .Request ) -> httpx .Response :
1740+ body = b"" .join (cast (Iterator [bytes ], request .stream ))
1741+ requests .append ((request .url , body ))
1742+ return httpx .Response (
1743+ 307 ,
1744+ headers = {"location" : "http://other-vm.test/browser/kernel/fs/write_file" },
1745+ )
1746+
1747+ http_client = httpx .Client (transport = Transport (), follow_redirects = True )
1748+
1749+ def make_client () -> Kernel :
1750+ return Kernel (
1751+ base_url = base_url ,
1752+ api_key = api_key ,
1753+ http_client = http_client ,
1754+ _strict_response_validation = True ,
1755+ )
1756+
1757+ if origin_first :
1758+ client , other_client = make_client (), make_client ()
1759+ else :
1760+ other_client , client = make_client (), make_client ()
1761+
1762+ try :
1763+ _cache_browser (client )
1764+ with pytest .raises (APIConnectionError ):
1765+ client .browsers .fs .write_file ("sess-1" , _UnseekableFile (b"payload" ), path = "/tmp/x" )
1766+ assert client .browser_route_cache .get ("sess-1" ) is None
1767+ assert other_client .browser_route_cache .get ("sess-1" ) is None
1768+ finally :
1769+ http_client .close ()
1770+
1771+ assert requests == [
1772+ (
1773+ httpx .URL ("http://browser-session.test/browser/kernel/fs/write_file?path=%2Ftmp%2Fx&jwt=token-abc" ),
1774+ b"payload" ,
1775+ )
1776+ ]
1777+
1778+
1779+ @pytest .mark .asyncio
1780+ @pytest .mark .parametrize ("origin_first" , [True , False ])
1781+ async def test_async_direct_vm_redirect_evicts_originating_cache_when_http_client_is_shared (
1782+ monkeypatch : pytest .MonkeyPatch ,
1783+ origin_first : bool ,
1784+ ) -> None :
1785+ monkeypatch .delenv ("KERNEL_BROWSER_ROUTING_SUBRESOURCES" , raising = False )
1786+ requests : list [tuple [httpx .URL , bytes ]] = []
1787+
1788+ class Transport (httpx .AsyncBaseTransport ):
1789+ @override
1790+ async def handle_async_request (self , request : httpx .Request ) -> httpx .Response :
1791+ body = b"" .join ([chunk async for chunk in cast (AsyncIterator [bytes ], request .stream )])
1792+ requests .append ((request .url , body ))
1793+ return httpx .Response (
1794+ 308 ,
1795+ headers = {"location" : "http://other-vm.test/browser/kernel/fs/write_file" },
1796+ )
1797+
1798+ http_client = httpx .AsyncClient (transport = Transport (), follow_redirects = True )
1799+
1800+ def make_client () -> AsyncKernel :
1801+ return AsyncKernel (
1802+ base_url = base_url ,
1803+ api_key = api_key ,
1804+ http_client = http_client ,
1805+ _strict_response_validation = True ,
1806+ )
1807+
1808+ if origin_first :
1809+ client , other_client = make_client (), make_client ()
1810+ else :
1811+ other_client , client = make_client (), make_client ()
1812+
1813+ try :
1814+ route = browser_route_from_browser (_fake_browser ())
1815+ assert route is not None
1816+ client .browser_route_cache .set (route )
1817+ with pytest .raises (APIConnectionError ):
1818+ await client .browsers .fs .write_file (
1819+ "sess-1" ,
1820+ cast (Any , _UnreplayableAsyncBody (b"payload" )),
1821+ path = "/tmp/x" ,
1822+ )
1823+ assert client .browser_route_cache .get ("sess-1" ) is None
1824+ assert other_client .browser_route_cache .get ("sess-1" ) is None
1825+ finally :
1826+ await http_client .aclose ()
1827+
1828+ assert requests == [
1829+ (
1830+ httpx .URL ("http://browser-session.test/browser/kernel/fs/write_file?path=%2Ftmp%2Fx&jwt=token-abc" ),
1831+ b"payload" ,
1832+ )
1833+ ]
1834+
1835+
17291836def test_direct_vm_auth_is_removed_after_httpx_and_request_hooks (monkeypatch : pytest .MonkeyPatch ) -> None :
17301837 monkeypatch .delenv ("KERNEL_BROWSER_ROUTING_SUBRESOURCES" , raising = False )
17311838 requests : list [httpx .Request ] = []
0 commit comments