|
22 | 22 | )
|
23 | 23 | from websockets.extensions.permessage_deflate import PerMessageDeflate
|
24 | 24 |
|
25 |
| -from ..proxy import async_proxy |
| 25 | +from ..proxy import ProxyMixin |
26 | 26 | from ..utils import (
|
27 | 27 | CLIENT_CONTEXT,
|
28 | 28 | MS,
|
@@ -388,7 +388,7 @@ def remove_accept_header(self, request, response):
|
388 | 388 |
|
389 | 389 | async def test_timeout_during_handshake(self):
|
390 | 390 | """Client times out before receiving handshake response from server."""
|
391 |
| - # Replace the WebSocket server with a TCP server that does't respond. |
| 391 | + # Replace the WebSocket server with a TCP server that doesn't respond. |
392 | 392 | with socket.create_server(("localhost", 0)) as sock:
|
393 | 393 | host, port = sock.getsockname()
|
394 | 394 | with self.assertRaises(TimeoutError) as raised:
|
@@ -508,7 +508,7 @@ async def test_reject_invalid_server_certificate(self):
|
508 | 508 | """Client rejects certificate where server certificate isn't trusted."""
|
509 | 509 | async with serve(*args, ssl=SERVER_CONTEXT) as server:
|
510 | 510 | with self.assertRaises(ssl.SSLCertVerificationError) as raised:
|
511 |
| - # The test certificate isn't trusted system-wide. |
| 511 | + # The test certificate is self-signed. |
512 | 512 | async with connect(get_uri(server)):
|
513 | 513 | self.fail("did not raise")
|
514 | 514 | self.assertIn(
|
@@ -566,126 +566,104 @@ def redirect(connection, request):
|
566 | 566 |
|
567 | 567 |
|
568 | 568 | @unittest.skipUnless("mitmproxy" in sys.modules, "mitmproxy not installed")
|
569 |
| -class ProxyClientTests(unittest.IsolatedAsyncioTestCase): |
570 |
| - @contextlib.asynccontextmanager |
571 |
| - async def socks_proxy(self, auth=None): |
572 |
| - if auth: |
573 |
| - proxyauth = "hello:iloveyou" |
574 |
| - proxy_uri = "http://hello:iloveyou@localhost:51080" |
575 |
| - else: |
576 |
| - proxyauth = None |
577 |
| - proxy_uri = "http://localhost:51080" |
578 |
| - async with async_proxy( |
579 |
| - mode=["socks5@51080"], |
580 |
| - proxyauth=proxyauth, |
581 |
| - ) as record_flows: |
582 |
| - with patch_environ({"socks_proxy": proxy_uri}): |
583 |
| - yield record_flows |
| 569 | +class SocksProxyClientTests(ProxyMixin, unittest.IsolatedAsyncioTestCase): |
| 570 | + proxy_mode = "socks5@51080" |
584 | 571 |
|
585 | 572 | async def test_socks_proxy(self):
|
586 | 573 | """Client connects to server through a SOCKS5 proxy."""
|
587 |
| - async with self.socks_proxy() as proxy: |
| 574 | + with patch_environ({"socks_proxy": "http://localhost:51080"}): |
588 | 575 | async with serve(*args) as server:
|
589 | 576 | async with connect(get_uri(server)) as client:
|
590 | 577 | self.assertEqual(client.protocol.state.name, "OPEN")
|
591 |
| - self.assertEqual(len(proxy.get_flows()), 1) |
| 578 | + self.assertNumFlows(1) |
592 | 579 |
|
593 | 580 | async def test_secure_socks_proxy(self):
|
594 | 581 | """Client connects to server securely through a SOCKS5 proxy."""
|
595 |
| - async with self.socks_proxy() as proxy: |
| 582 | + with patch_environ({"socks_proxy": "http://localhost:51080"}): |
596 | 583 | async with serve(*args, ssl=SERVER_CONTEXT) as server:
|
597 | 584 | async with connect(get_uri(server), ssl=CLIENT_CONTEXT) as client:
|
598 | 585 | self.assertEqual(client.protocol.state.name, "OPEN")
|
599 |
| - self.assertEqual(len(proxy.get_flows()), 1) |
| 586 | + self.assertNumFlows(1) |
600 | 587 |
|
601 | 588 | async def test_authenticated_socks_proxy(self):
|
602 | 589 | """Client connects to server through an authenticated SOCKS5 proxy."""
|
603 |
| - async with self.socks_proxy(auth=True) as proxy: |
604 |
| - async with serve(*args) as server: |
605 |
| - async with connect(get_uri(server)) as client: |
606 |
| - self.assertEqual(client.protocol.state.name, "OPEN") |
607 |
| - self.assertEqual(len(proxy.get_flows()), 1) |
| 590 | + try: |
| 591 | + self.proxy_options.update(proxyauth="hello:iloveyou") |
| 592 | + with patch_environ( |
| 593 | + {"socks_proxy": "http://hello:iloveyou@localhost:51080"} |
| 594 | + ): |
| 595 | + async with serve(*args) as server: |
| 596 | + async with connect(get_uri(server)) as client: |
| 597 | + self.assertEqual(client.protocol.state.name, "OPEN") |
| 598 | + finally: |
| 599 | + self.proxy_options.update(proxyauth=None) |
| 600 | + self.assertNumFlows(1) |
608 | 601 |
|
609 |
| - async def test_socks_proxy_connection_error(self): |
610 |
| - """Client receives an error when connecting to the SOCKS5 proxy.""" |
| 602 | + async def test_authenticated_socks_proxy_error(self): |
| 603 | + """Client fails to authenticate to the SOCKS5 proxy.""" |
611 | 604 | from python_socks import ProxyError as SocksProxyError
|
612 | 605 |
|
613 |
| - async with self.socks_proxy(auth=True) as proxy: |
614 |
| - with self.assertRaises(ProxyError) as raised: |
615 |
| - async with connect( |
616 |
| - "ws://example.com/", |
617 |
| - proxy="socks5h://localhost:51080", # remove credentials |
618 |
| - ): |
619 |
| - self.fail("did not raise") |
| 606 | + try: |
| 607 | + self.proxy_options.update(proxyauth="any") |
| 608 | + with patch_environ({"socks_proxy": "http://localhost:51080"}): |
| 609 | + with self.assertRaises(ProxyError) as raised: |
| 610 | + async with connect("ws://example.com/"): |
| 611 | + self.fail("did not raise") |
| 612 | + finally: |
| 613 | + self.proxy_options.update(proxyauth=None) |
620 | 614 | self.assertEqual(
|
621 | 615 | str(raised.exception),
|
622 | 616 | "failed to connect to SOCKS proxy",
|
623 | 617 | )
|
624 | 618 | self.assertIsInstance(raised.exception.__cause__, SocksProxyError)
|
625 |
| - self.assertEqual(len(proxy.get_flows()), 0) |
| 619 | + self.assertNumFlows(0) |
626 | 620 |
|
627 |
| - async def test_socks_proxy_connection_fails(self): |
| 621 | + async def test_socks_proxy_connection_failure(self): |
628 | 622 | """Client fails to connect to the SOCKS5 proxy."""
|
629 | 623 | from python_socks import ProxyConnectionError as SocksProxyConnectionError
|
630 | 624 |
|
631 |
| - with self.assertRaises(OSError) as raised: |
632 |
| - async with connect( |
633 |
| - "ws://example.com/", |
634 |
| - proxy="socks5h://localhost:51080", # nothing at this address |
635 |
| - ): |
636 |
| - self.fail("did not raise") |
| 625 | + with patch_environ({"socks_proxy": "http://localhost:61080"}): # bad port |
| 626 | + with self.assertRaises(OSError) as raised: |
| 627 | + async with connect("ws://example.com/"): |
| 628 | + self.fail("did not raise") |
637 | 629 | # Don't test str(raised.exception) because we don't control it.
|
638 | 630 | self.assertIsInstance(raised.exception, SocksProxyConnectionError)
|
| 631 | + self.assertNumFlows(0) |
639 | 632 |
|
640 | 633 | async def test_socks_proxy_connection_timeout(self):
|
641 | 634 | """Client times out while connecting to the SOCKS5 proxy."""
|
642 |
| - # Replace the proxy with a TCP server that does't respond. |
| 635 | + # Replace the proxy with a TCP server that doesn't respond. |
643 | 636 | with socket.create_server(("localhost", 0)) as sock:
|
644 | 637 | host, port = sock.getsockname()
|
645 |
| - with self.assertRaises(TimeoutError) as raised: |
646 |
| - async with connect( |
647 |
| - "ws://example.com/", |
648 |
| - proxy=f"socks5h://{host}:{port}/", |
649 |
| - open_timeout=MS, |
650 |
| - ): |
651 |
| - self.fail("did not raise") |
| 638 | + with patch_environ({"socks_proxy": f"http://{host}:{port}"}): |
| 639 | + with self.assertRaises(TimeoutError) as raised: |
| 640 | + async with connect("ws://example.com/", open_timeout=MS): |
| 641 | + self.fail("did not raise") |
652 | 642 | self.assertEqual(
|
653 | 643 | str(raised.exception),
|
654 | 644 | "timed out during handshake",
|
655 | 645 | )
|
| 646 | + self.assertNumFlows(0) |
656 | 647 |
|
657 |
| - async def test_explicit_proxy(self): |
658 |
| - """Client connects to server through a proxy set explicitly.""" |
659 |
| - async with async_proxy(mode=["socks5@51080"]) as proxy: |
660 |
| - async with serve(*args) as server: |
661 |
| - async with connect( |
662 |
| - get_uri(server), |
663 |
| - # Take this opportunity to test socks5 instead of socks5h. |
664 |
| - proxy="socks5://localhost:51080", |
665 |
| - ) as client: |
666 |
| - self.assertEqual(client.protocol.state.name, "OPEN") |
667 |
| - self.assertEqual(len(proxy.get_flows()), 1) |
| 648 | + async def test_explicit_socks_proxy(self): |
| 649 | + """Client connects to server through a SOCKS5 proxy set explicitly.""" |
| 650 | + async with serve(*args) as server: |
| 651 | + async with connect( |
| 652 | + get_uri(server), |
| 653 | + # Take this opportunity to test socks5 instead of socks5h. |
| 654 | + proxy="socks5://localhost:51080", |
| 655 | + ) as client: |
| 656 | + self.assertEqual(client.protocol.state.name, "OPEN") |
| 657 | + self.assertNumFlows(1) |
668 | 658 |
|
669 | 659 | async def test_ignore_proxy_with_existing_socket(self):
|
670 | 660 | """Client connects using a pre-existing socket."""
|
671 |
| - async with self.socks_proxy() as proxy: |
| 661 | + with patch_environ({"socks_proxy": "http://localhost:51080"}): |
672 | 662 | async with serve(*args) as server:
|
673 | 663 | with socket.create_connection(get_host_port(server)) as sock:
|
674 | 664 | # Use a non-existing domain to ensure we connect to sock.
|
675 | 665 | async with connect("ws://invalid/", sock=sock) as client:
|
676 | 666 | self.assertEqual(client.protocol.state.name, "OPEN")
|
677 |
| - self.assertEqual(len(proxy.get_flows()), 0) |
678 |
| - |
679 |
| - async def test_unsupported_proxy(self): |
680 |
| - """Client connects to server through an unsupported proxy.""" |
681 |
| - with patch_environ({"ws_proxy": "other://localhost:51080"}): |
682 |
| - with self.assertRaises(InvalidProxy) as raised: |
683 |
| - async with connect("ws://example.com/"): |
684 |
| - self.fail("did not raise") |
685 |
| - self.assertEqual( |
686 |
| - str(raised.exception), |
687 |
| - "other://localhost:51080 isn't a valid proxy: scheme other isn't supported", |
688 |
| - ) |
689 | 667 |
|
690 | 668 |
|
691 | 669 | @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "this test requires Unix sockets")
|
@@ -724,10 +702,7 @@ def redirect(connection, request):
|
724 | 702 | "cannot follow cross-origin redirect to ws://other/ with a Unix socket",
|
725 | 703 | )
|
726 | 704 |
|
727 |
| - |
728 |
| -@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "this test requires Unix sockets") |
729 |
| -class SecureUnixClientTests(unittest.IsolatedAsyncioTestCase): |
730 |
| - async def test_connection(self): |
| 705 | + async def test_secure_connection(self): |
731 | 706 | """Client connects to server securely over a Unix socket."""
|
732 | 707 | with temp_unix_socket_path() as path:
|
733 | 708 | async with unix_serve(handler, path, ssl=SERVER_CONTEXT):
|
@@ -769,6 +744,16 @@ async def test_secure_uri_without_ssl(self):
|
769 | 744 | "ssl=None is incompatible with a wss:// URI",
|
770 | 745 | )
|
771 | 746 |
|
| 747 | + async def test_unsupported_proxy(self): |
| 748 | + """Client rejects unsupported proxy.""" |
| 749 | + with self.assertRaises(InvalidProxy) as raised: |
| 750 | + async with connect("ws://example.com/", proxy="other://localhost:51080"): |
| 751 | + self.fail("did not raise") |
| 752 | + self.assertEqual( |
| 753 | + str(raised.exception), |
| 754 | + "other://localhost:51080 isn't a valid proxy: scheme other isn't supported", |
| 755 | + ) |
| 756 | + |
772 | 757 | async def test_unix_without_path_or_sock(self):
|
773 | 758 | """Unix client requires path when sock isn't provided."""
|
774 | 759 | with self.assertRaises(ValueError) as raised:
|
|
0 commit comments