1717 InvalidProxy ,
1818 InvalidStatus ,
1919 InvalidURI ,
20+ ProxyError ,
2021 SecurityError ,
2122)
2223from websockets .extensions .permessage_deflate import PerMessageDeflate
@@ -379,24 +380,16 @@ def remove_accept_header(self, request, response):
379380
380381 async def test_timeout_during_handshake (self ):
381382 """Client times out before receiving handshake response from server."""
382- gate = asyncio .get_running_loop ().create_future ()
383-
384- async def stall_connection (self , request ):
385- await gate
386-
387- # The connection will be open for the server but failed for the client.
388- # Use a connection handler that exits immediately to avoid an exception.
389- async with serve (* args , process_request = stall_connection ) as server :
390- try :
391- with self .assertRaises (TimeoutError ) as raised :
392- async with connect (get_uri (server ) + "/no-op" , open_timeout = 2 * MS ):
393- self .fail ("did not raise" )
394- self .assertEqual (
395- str (raised .exception ),
396- "timed out during handshake" ,
397- )
398- finally :
399- gate .set_result (None )
383+ # Replace the WebSocket server with a TCP server that does't respond.
384+ with socket .create_server (("localhost" , 0 )) as sock :
385+ host , port = sock .getsockname ()
386+ with self .assertRaises (TimeoutError ) as raised :
387+ async with connect (f"ws://{ host } :{ port } " , open_timeout = MS ):
388+ self .fail ("did not raise" )
389+ self .assertEqual (
390+ str (raised .exception ),
391+ "timed out during handshake" ,
392+ )
400393
401394 async def test_connection_closed_during_handshake (self ):
402395 """Client reads EOF before receiving handshake response from server."""
@@ -570,11 +563,14 @@ class ProxyClientTests(unittest.IsolatedAsyncioTestCase):
570563 async def socks_proxy (self , auth = None ):
571564 if auth :
572565 proxyauth = "hello:iloveyou"
573- proxy_uri = "http://hello:iloveyou@localhost:1080 "
566+ proxy_uri = "http://hello:iloveyou@localhost:51080 "
574567 else :
575568 proxyauth = None
576- proxy_uri = "http://localhost:1080"
577- async with async_proxy (mode = ["socks5" ], proxyauth = proxyauth ) as record_flows :
569+ proxy_uri = "http://localhost:51080"
570+ async with async_proxy (
571+ mode = ["socks5@51080" ],
572+ proxyauth = proxyauth ,
573+ ) as record_flows :
578574 with patch_environ ({"socks_proxy" : proxy_uri }):
579575 yield record_flows
580576
@@ -602,14 +598,62 @@ async def test_authenticated_socks_proxy(self):
602598 self .assertEqual (client .protocol .state .name , "OPEN" )
603599 self .assertEqual (len (proxy .get_flows ()), 1 )
604600
601+ async def test_socks_proxy_connection_error (self ):
602+ """Client receives an error when connecting to the SOCKS5 proxy."""
603+ from python_socks import ProxyError as SocksProxyError
604+
605+ async with self .socks_proxy (auth = True ) as proxy :
606+ with self .assertRaises (ProxyError ) as raised :
607+ async with connect (
608+ "ws://example.com/" ,
609+ proxy = "socks5h://localhost:51080" , # remove credentials
610+ ):
611+ self .fail ("did not raise" )
612+ self .assertEqual (
613+ str (raised .exception ),
614+ "failed to connect to SOCKS proxy" ,
615+ )
616+ self .assertIsInstance (raised .exception .__cause__ , SocksProxyError )
617+ self .assertEqual (len (proxy .get_flows ()), 0 )
618+
619+ async def test_socks_proxy_connection_fails (self ):
620+ """Client fails to connect to the SOCKS5 proxy."""
621+ from python_socks import ProxyConnectionError as SocksProxyConnectionError
622+
623+ with self .assertRaises (OSError ) as raised :
624+ async with connect (
625+ "ws://example.com/" ,
626+ proxy = "socks5h://localhost:51080" , # nothing at this address
627+ ):
628+ self .fail ("did not raise" )
629+ # Don't test str(raised.exception) because we don't control it.
630+ self .assertIsInstance (raised .exception , SocksProxyConnectionError )
631+
632+ async def test_socks_proxy_connection_timeout (self ):
633+ """Client times out while connecting to the SOCKS5 proxy."""
634+ # Replace the proxy with a TCP server that does't respond.
635+ with socket .create_server (("localhost" , 0 )) as sock :
636+ host , port = sock .getsockname ()
637+ with self .assertRaises (TimeoutError ) as raised :
638+ async with connect (
639+ "ws://example.com/" ,
640+ proxy = f"socks5h://{ host } :{ port } /" ,
641+ open_timeout = MS ,
642+ ):
643+ self .fail ("did not raise" )
644+ self .assertEqual (
645+ str (raised .exception ),
646+ "timed out during handshake" ,
647+ )
648+
605649 async def test_explicit_proxy (self ):
606650 """Client connects to server through a proxy set explicitly."""
607- async with async_proxy (mode = ["socks5" ]) as proxy :
651+ async with async_proxy (mode = ["socks5@51080 " ]) as proxy :
608652 async with serve (* args ) as server :
609653 async with connect (
610654 get_uri (server ),
611655 # Take this opportunity to test socks5 instead of socks5h.
612- proxy = "socks5://localhost:1080 " ,
656+ proxy = "socks5://localhost:51080 " ,
613657 ) as client :
614658 self .assertEqual (client .protocol .state .name , "OPEN" )
615659 self .assertEqual (len (proxy .get_flows ()), 1 )
@@ -626,13 +670,13 @@ async def test_ignore_proxy_with_existing_socket(self):
626670
627671 async def test_unsupported_proxy (self ):
628672 """Client connects to server through an unsupported proxy."""
629- with patch_environ ({"ws_proxy" : "other://localhost:1080 " }):
673+ with patch_environ ({"ws_proxy" : "other://localhost:51080 " }):
630674 with self .assertRaises (InvalidProxy ) as raised :
631675 async with connect ("ws://example.com/" ):
632676 self .fail ("did not raise" )
633677 self .assertEqual (
634678 str (raised .exception ),
635- "other://localhost:1080 isn't a valid proxy: scheme other isn't supported" ,
679+ "other://localhost:51080 isn't a valid proxy: scheme other isn't supported" ,
636680 )
637681
638682
0 commit comments