17
17
InvalidProxy ,
18
18
InvalidStatus ,
19
19
InvalidURI ,
20
+ ProxyError ,
20
21
SecurityError ,
21
22
)
22
23
from websockets .extensions .permessage_deflate import PerMessageDeflate
@@ -379,24 +380,16 @@ def remove_accept_header(self, request, response):
379
380
380
381
async def test_timeout_during_handshake (self ):
381
382
"""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
+ )
400
393
401
394
async def test_connection_closed_during_handshake (self ):
402
395
"""Client reads EOF before receiving handshake response from server."""
@@ -570,11 +563,13 @@ class ProxyClientTests(unittest.IsolatedAsyncioTestCase):
570
563
async def socks_proxy (self , auth = None ):
571
564
if auth :
572
565
proxyauth = "hello:iloveyou"
573
- proxy_uri = "http://hello:iloveyou@localhost:1080 "
566
+ proxy_uri = "http://hello:iloveyou@localhost:51080 "
574
567
else :
575
568
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" ], proxyauth = proxyauth
572
+ ) as record_flows :
578
573
with patch_environ ({"socks_proxy" : proxy_uri }):
579
574
yield record_flows
580
575
@@ -602,14 +597,62 @@ async def test_authenticated_socks_proxy(self):
602
597
self .assertEqual (client .protocol .state .name , "OPEN" )
603
598
self .assertEqual (len (proxy .get_flows ()), 1 )
604
599
600
+ async def test_socks_proxy_connection_error (self ):
601
+ """Client receives an error when connecting to the SOCKS5 proxy."""
602
+ from python_socks import ProxyError as SocksProxyError
603
+
604
+ async with self .socks_proxy (auth = True ) as proxy :
605
+ with self .assertRaises (ProxyError ) as raised :
606
+ async with connect (
607
+ "ws://example.com/" ,
608
+ proxy = "socks5h://localhost:51080" , # remove credentials
609
+ ):
610
+ self .fail ("did not raise" )
611
+ self .assertEqual (
612
+ str (raised .exception ),
613
+ "failed to connect to SOCKS proxy" ,
614
+ )
615
+ self .assertIsInstance (raised .exception .__cause__ , SocksProxyError )
616
+ self .assertEqual (len (proxy .get_flows ()), 0 )
617
+
618
+ async def test_socks_proxy_connection_fails (self ):
619
+ """Client fails to connect to the SOCKS5 proxy."""
620
+ from python_socks import ProxyConnectionError as SocksProxyConnectionError
621
+
622
+ with self .assertRaises (OSError ) as raised :
623
+ async with connect (
624
+ "ws://example.com/" ,
625
+ proxy = "socks5h://localhost:51080" , # nothing at this address
626
+ ):
627
+ self .fail ("did not raise" )
628
+ # Don't test str(raised.exception) because we don't control it.
629
+ self .assertIsInstance (raised .exception , SocksProxyConnectionError )
630
+
631
+ async def test_socks_proxy_connection_timeout (self ):
632
+ """Client times out while connecting to the SOCKS5 proxy."""
633
+ # Replace the proxy with a TCP server that does't respond.
634
+ with socket .create_server (("localhost" , 0 )) as sock :
635
+ host , port = sock .getsockname ()
636
+ with self .assertRaises (TimeoutError ) as raised :
637
+ async with connect (
638
+ "ws://example.com/" ,
639
+ proxy = f"socks5h://{ host } :{ port } /" ,
640
+ open_timeout = MS ,
641
+ ):
642
+ self .fail ("did not raise" )
643
+ self .assertEqual (
644
+ str (raised .exception ),
645
+ "timed out during handshake" ,
646
+ )
647
+
605
648
async def test_explicit_proxy (self ):
606
649
"""Client connects to server through a proxy set explicitly."""
607
- async with async_proxy (mode = ["socks5" ]) as proxy :
650
+ async with async_proxy (mode = ["socks5@51080 " ]) as proxy :
608
651
async with serve (* args ) as server :
609
652
async with connect (
610
653
get_uri (server ),
611
654
# Take this opportunity to test socks5 instead of socks5h.
612
- proxy = "socks5://localhost:1080 " ,
655
+ proxy = "socks5://localhost:51080 " ,
613
656
) as client :
614
657
self .assertEqual (client .protocol .state .name , "OPEN" )
615
658
self .assertEqual (len (proxy .get_flows ()), 1 )
@@ -626,13 +669,13 @@ async def test_ignore_proxy_with_existing_socket(self):
626
669
627
670
async def test_unsupported_proxy (self ):
628
671
"""Client connects to server through an unsupported proxy."""
629
- with patch_environ ({"ws_proxy" : "other://localhost:1080 " }):
672
+ with patch_environ ({"ws_proxy" : "other://localhost:51080 " }):
630
673
with self .assertRaises (InvalidProxy ) as raised :
631
674
async with connect ("ws://example.com/" ):
632
675
self .fail ("did not raise" )
633
676
self .assertEqual (
634
677
str (raised .exception ),
635
- "other://localhost:1080 isn't a valid proxy: scheme other isn't supported" ,
678
+ "other://localhost:51080 isn't a valid proxy: scheme other isn't supported" ,
636
679
)
637
680
638
681
0 commit comments