From b3da6695520aac16f3dfa157d049919566711cac Mon Sep 17 00:00:00 2001 From: gangj Date: Wed, 9 Mar 2022 08:19:38 +0000 Subject: [PATCH 01/19] Fix issue 6652: Raise `aiohttp.ServerFingerprintMismatch` exception on client-side if request through http proxy with mismatching server fingerprint digest --- CHANGES/6652.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/connector.py | 8 ++++++++ 3 files changed, 10 insertions(+) create mode 100644 CHANGES/6652.bugfix diff --git a/CHANGES/6652.bugfix b/CHANGES/6652.bugfix new file mode 100644 index 00000000000..4ce1f678792 --- /dev/null +++ b/CHANGES/6652.bugfix @@ -0,0 +1 @@ +Raise `aiohttp.ServerFingerprintMismatch` exception on client-side if request through http proxy with mismatching server fingerprint digest: `aiohttp.ClientSession(headers=headers, connector=TCPConnector(ssl=aiohttp.Fingerprint(mismatch_digest), trust_env=True).request(...)`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 0432d7b6d80..187c84a1073 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -126,6 +126,7 @@ Franek Magiera Frederik Gladhorn Frederik Peter Aalund Gabriel Tremblay +Gang Ji Gary Wilson Jr. Gennady Andreyev Georges Dubus diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 86399b84272..8596acdc371 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -1044,6 +1044,14 @@ async def _start_tls_connection( # chance to do this: underlying_transport.close() raise + if isinstance(tls_transport, asyncio.Transport): + fingerprint = self._get_fingerprint(req) + if fingerprint: + try: + fingerprint.check(tls_transport) + except ServerFingerprintMismatch: + tls_transport.close() + raise except cert_errors as exc: raise ClientConnectorCertificateError(req.connection_key, exc) from exc except ssl_errors as exc: From d677c44330f64732e4eec012788199304bdaf471 Mon Sep 17 00:00:00 2001 From: gangj Date: Fri, 18 Mar 2022 09:09:34 +0000 Subject: [PATCH 02/19] Fix issue 6652: Add test case to cover this case --- tests/test_proxy.py | 64 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index af869ee88f7..d12399cdf88 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -248,6 +248,70 @@ async def make_conn(): proxy_resp.close() self.loop.run_until_complete(req.close()) + @mock.patch("aiohttp.connector.ClientRequest") + def test_https_connect_fingerprint_mismatch(self, ClientRequestMock: Any) -> None: + proxy_req = ClientRequest( + "GET", URL("http://proxy.example.com"), loop=self.loop + ) + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=mock.Mock(), + continue100=None, + timer=TimerNoop(), + traces=[], + loop=self.loop, + session=mock.Mock(), + ) + proxy_req.send = make_mocked_coro(proxy_resp) + proxy_resp.start = make_mocked_coro(mock.Mock(status=200)) + + async def make_conn(): + return aiohttp.TCPConnector() + + connector = self.loop.run_until_complete(make_conn()) + connector._resolve_host = make_mocked_coro( + [ + { + "hostname": "hostname", + "host": "127.0.0.1", + "port": 80, + "family": socket.AF_INET, + "proto": 0, + "flags": 0, + } + ] + ) + fingerprint_mock = mock.Mock() + fingerprint_mock.check.side_effect = aiohttp.ServerFingerprintMismatch( + b"exp", b"got", "example.com", 8080 + ) + connector._get_fingerprint = mock.Mock(return_value=fingerprint_mock) + + # Called on connection to http://proxy.example.com + self.loop.create_connection = make_mocked_coro((mock.Mock(), mock.Mock())) + + # Called on connection to https://www.python.org + class TransportMock(asyncio.Transport): + def close(self): + pass + + self.loop.start_tls = make_mocked_coro(TransportMock()) + + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=self.loop, + ) + with self.assertRaises(aiohttp.ServerFingerprintMismatch): + self.loop.run_until_complete( + connector._create_connection(req, None, aiohttp.ClientTimeout()) + ) + @mock.patch("aiohttp.connector.ClientRequest") def test_https_connect_certificate_error(self, ClientRequestMock: Any) -> None: proxy_req = ClientRequest( From 30152553d9b74041fe36b20401357e7c0412c7ea Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 29 Sep 2024 17:08:08 +0100 Subject: [PATCH 03/19] Update connector.py --- aiohttp/connector.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 006536ae29f..36fb24bd4e5 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -1140,6 +1140,8 @@ async def _start_tls_connection( fingerprint.check(tls_transport) except ServerFingerprintMismatch: tls_transport.close() + if not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transp) raise except cert_errors as exc: raise ClientConnectorCertificateError(req.connection_key, exc) from exc From 0412b3f45238bba88fda2d8aed3e229ac5ab7f3c Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 29 Sep 2024 17:10:04 +0100 Subject: [PATCH 04/19] Typing --- tests/test_proxy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index e12fd829079..998cac3466a 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -416,7 +416,7 @@ async def make_conn() -> aiohttp.TCPConnector: self.loop.run_until_complete(connector.close()) @mock.patch("aiohttp.connector.ClientRequest") - def test_https_connect_fingerprint_mismatch(self, ClientRequestMock: Any) -> None: + def test_https_connect_fingerprint_mismatch(self, ClientRequestMock: mock.Mock) -> None: proxy_req = ClientRequest( "GET", URL("http://proxy.example.com"), loop=self.loop ) @@ -436,7 +436,7 @@ def test_https_connect_fingerprint_mismatch(self, ClientRequestMock: Any) -> Non proxy_req.send = make_mocked_coro(proxy_resp) proxy_resp.start = make_mocked_coro(mock.Mock(status=200)) - async def make_conn(): + async def make_conn() -> aiohttp.TCPConnector: return aiohttp.TCPConnector() connector = self.loop.run_until_complete(make_conn()) @@ -463,7 +463,7 @@ async def make_conn(): # Called on connection to https://www.python.org class TransportMock(asyncio.Transport): - def close(self): + def close(self) -> None: pass self.loop.start_tls = make_mocked_coro(TransportMock()) From 2f0c4d2fb220f338c53ec095730f992397175c22 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:10:35 +0000 Subject: [PATCH 05/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_proxy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 998cac3466a..92d02f47098 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -416,7 +416,9 @@ async def make_conn() -> aiohttp.TCPConnector: self.loop.run_until_complete(connector.close()) @mock.patch("aiohttp.connector.ClientRequest") - def test_https_connect_fingerprint_mismatch(self, ClientRequestMock: mock.Mock) -> None: + def test_https_connect_fingerprint_mismatch( + self, ClientRequestMock: mock.Mock + ) -> None: proxy_req = ClientRequest( "GET", URL("http://proxy.example.com"), loop=self.loop ) From e94e0e26ecb29eb53d95c252a2ec078f37d3bd2a Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 29 Sep 2024 17:20:23 +0100 Subject: [PATCH 06/19] Update aiohttp/connector.py --- aiohttp/connector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 36fb24bd4e5..45f766b0fcc 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -1141,7 +1141,7 @@ async def _start_tls_connection( except ServerFingerprintMismatch: tls_transport.close() if not self._cleanup_closed_disabled: - self._cleanup_closed_transports.append(transp) + self._cleanup_closed_transports.append(tls_transport) raise except cert_errors as exc: raise ClientConnectorCertificateError(req.connection_key, exc) from exc From 1e3cd6229c6736f63c8d6de8b40a4e2ee61aebbc Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 29 Sep 2024 17:32:44 +0100 Subject: [PATCH 07/19] Update test_proxy.py --- tests/test_proxy.py | 85 +++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 92d02f47098..06ecc9387a5 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -419,6 +419,9 @@ async def make_conn() -> aiohttp.TCPConnector: def test_https_connect_fingerprint_mismatch( self, ClientRequestMock: mock.Mock ) -> None: + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + proxy_req = ClientRequest( "GET", URL("http://proxy.example.com"), loop=self.loop ) @@ -435,51 +438,43 @@ def test_https_connect_fingerprint_mismatch( loop=self.loop, session=mock.Mock(), ) - proxy_req.send = make_mocked_coro(proxy_resp) - proxy_resp.start = make_mocked_coro(mock.Mock(status=200)) - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - connector._resolve_host = make_mocked_coro( - [ - { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } - ] - ) - fingerprint_mock = mock.Mock() - fingerprint_mock.check.side_effect = aiohttp.ServerFingerprintMismatch( - b"exp", b"got", "example.com", 8080 - ) - connector._get_fingerprint = mock.Mock(return_value=fingerprint_mock) - - # Called on connection to http://proxy.example.com - self.loop.create_connection = make_mocked_coro((mock.Mock(), mock.Mock())) - - # Called on connection to https://www.python.org - class TransportMock(asyncio.Transport): - def close(self) -> None: - pass - - self.loop.start_tls = make_mocked_coro(TransportMock()) - - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - with self.assertRaises(aiohttp.ServerFingerprintMismatch): - self.loop.run_until_complete( - connector._create_connection(req, None, aiohttp.ClientTimeout()) - ) + with mock.patch.object(proxy_req, "send", autospec=True, spec_set=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True, spec_set=True, return_value=mock.Mock(status=200)): + connector = self.loop.run_until_complete(make_conn()) + host = [ + { + "hostname": "hostname", + "host": "127.0.0.1", + "port": 80, + "family": socket.AF_INET, + "proto": 0, + "flags": 0, + } + ] + with mock.patch.object(connector, "_resolve_host", autospec=True, spec_set=True, return_value=host): + fingerprint_mock = mock.Mock() + fingerprint_mock.check.side_effect = aiohttp.ServerFingerprintMismatch( + b"exp", b"got", "example.com", 8080 + ) + with mock.patch.object(connector, "_get_fingerprint", autospec=True, spec_set=True, return_value=mock.Mock(return_value=fingerprint_mock)): + # Called on connection to http://proxy.example.com + with mock.patch.object(self.loop, "create_connection", autospec=True, spec_set=True, return_value=(mock.Mock(), mock.Mock())): + # Called on connection to https://www.python.org + class TransportMock(asyncio.Transport): + def close(self) -> None: + pass + + with mock.patch.object(self.loop, "start_tls", autospec=True, spec_set=True, return_value=TransportMock()): + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=self.loop, + ) + with self.assertRaises(aiohttp.ServerFingerprintMismatch): + self.loop.run_until_complete( + connector._create_connection(req, [], aiohttp.ClientTimeout()) + ) @mock.patch( "aiohttp.connector.aiohappyeyeballs.start_connection", From 95d9a9f26764cdde706dad567f9ec5c19e94116d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:33:14 +0000 Subject: [PATCH 08/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_proxy.py | 58 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 06ecc9387a5..2f0731f9df4 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -438,8 +438,16 @@ async def make_conn() -> aiohttp.TCPConnector: loop=self.loop, session=mock.Mock(), ) - with mock.patch.object(proxy_req, "send", autospec=True, spec_set=True, return_value=proxy_resp): - with mock.patch.object(proxy_resp, "start", autospec=True, spec_set=True, return_value=mock.Mock(status=200)): + with mock.patch.object( + proxy_req, "send", autospec=True, spec_set=True, return_value=proxy_resp + ): + with mock.patch.object( + proxy_resp, + "start", + autospec=True, + spec_set=True, + return_value=mock.Mock(status=200), + ): connector = self.loop.run_until_complete(make_conn()) host = [ { @@ -451,29 +459,59 @@ async def make_conn() -> aiohttp.TCPConnector: "flags": 0, } ] - with mock.patch.object(connector, "_resolve_host", autospec=True, spec_set=True, return_value=host): + with mock.patch.object( + connector, + "_resolve_host", + autospec=True, + spec_set=True, + return_value=host, + ): fingerprint_mock = mock.Mock() - fingerprint_mock.check.side_effect = aiohttp.ServerFingerprintMismatch( - b"exp", b"got", "example.com", 8080 + fingerprint_mock.check.side_effect = ( + aiohttp.ServerFingerprintMismatch( + b"exp", b"got", "example.com", 8080 + ) ) - with mock.patch.object(connector, "_get_fingerprint", autospec=True, spec_set=True, return_value=mock.Mock(return_value=fingerprint_mock)): + with mock.patch.object( + connector, + "_get_fingerprint", + autospec=True, + spec_set=True, + return_value=mock.Mock(return_value=fingerprint_mock), + ): # Called on connection to http://proxy.example.com - with mock.patch.object(self.loop, "create_connection", autospec=True, spec_set=True, return_value=(mock.Mock(), mock.Mock())): + with mock.patch.object( + self.loop, + "create_connection", + autospec=True, + spec_set=True, + return_value=(mock.Mock(), mock.Mock()), + ): # Called on connection to https://www.python.org class TransportMock(asyncio.Transport): def close(self) -> None: pass - with mock.patch.object(self.loop, "start_tls", autospec=True, spec_set=True, return_value=TransportMock()): + with mock.patch.object( + self.loop, + "start_tls", + autospec=True, + spec_set=True, + return_value=TransportMock(), + ): req = ClientRequest( "GET", URL("https://www.python.org"), proxy=URL("http://proxy.example.com"), loop=self.loop, ) - with self.assertRaises(aiohttp.ServerFingerprintMismatch): + with self.assertRaises( + aiohttp.ServerFingerprintMismatch + ): self.loop.run_until_complete( - connector._create_connection(req, [], aiohttp.ClientTimeout()) + connector._create_connection( + req, [], aiohttp.ClientTimeout() + ) ) @mock.patch( From 31f8842251c8efa016a08bfb8d274b252ae97b8f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 29 Sep 2024 12:51:56 -0500 Subject: [PATCH 09/19] fix test --- tests/test_proxy.py | 131 +++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 69 deletions(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 2f0731f9df4..ac7614e4fde 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -8,7 +8,7 @@ from yarl import URL import aiohttp -from aiohttp.client_reqrep import ClientRequest, ClientResponse +from aiohttp.client_reqrep import ClientRequest, ClientResponse, Fingerprint from aiohttp.connector import _SSL_CONTEXT_VERIFIED from aiohttp.helpers import TimerNoop from aiohttp.test_utils import make_mocked_coro @@ -415,9 +415,14 @@ async def make_conn() -> aiohttp.TCPConnector: self.loop.run_until_complete(req.close()) self.loop.run_until_complete(connector.close()) + @mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, + ) @mock.patch("aiohttp.connector.ClientRequest") def test_https_connect_fingerprint_mismatch( - self, ClientRequestMock: mock.Mock + self, ClientRequestMock: mock.Mock, start_connection: mock.Mock ) -> None: async def make_conn() -> aiohttp.TCPConnector: return aiohttp.TCPConnector() @@ -427,6 +432,10 @@ async def make_conn() -> aiohttp.TCPConnector: ) ClientRequestMock.return_value = proxy_req + class TransportMock(asyncio.Transport): + def close(self) -> None: + pass + proxy_resp = ClientResponse( "get", URL("http://proxy.example.com"), @@ -438,81 +447,65 @@ async def make_conn() -> aiohttp.TCPConnector: loop=self.loop, session=mock.Mock(), ) + fingerprint_mock = mock.Mock(spec=Fingerprint, auto_spec=True) + fingerprint_mock.check.side_effect = aiohttp.ServerFingerprintMismatch( + b"exp", b"got", "example.com", 8080 + ) with mock.patch.object( proxy_req, "send", autospec=True, spec_set=True, return_value=proxy_resp + ), mock.patch.object( + proxy_resp, + "start", + autospec=True, + spec_set=True, + return_value=mock.Mock(status=200), ): + connector = self.loop.run_until_complete(make_conn()) + host = [ + { + "hostname": "hostname", + "host": "127.0.0.1", + "port": 80, + "family": socket.AF_INET, + "proto": 0, + "flags": 0, + } + ] with mock.patch.object( - proxy_resp, - "start", + connector, + "_resolve_host", + autospec=True, + spec_set=True, + return_value=host, + ), mock.patch.object( + connector, + "_get_fingerprint", + autospec=True, + spec_set=True, + return_value=fingerprint_mock, + ), mock.patch.object( # Called on connection to http://proxy.example.com + self.loop, + "create_connection", + autospec=True, + spec_set=True, + return_value=(mock.Mock(), mock.Mock()), + ), mock.patch.object( # Called on connection to https://www.python.org + self.loop, + "start_tls", autospec=True, spec_set=True, - return_value=mock.Mock(status=200), + return_value=TransportMock(), ): - connector = self.loop.run_until_complete(make_conn()) - host = [ - { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } - ] - with mock.patch.object( - connector, - "_resolve_host", - autospec=True, - spec_set=True, - return_value=host, - ): - fingerprint_mock = mock.Mock() - fingerprint_mock.check.side_effect = ( - aiohttp.ServerFingerprintMismatch( - b"exp", b"got", "example.com", 8080 - ) + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=self.loop, + ) + with self.assertRaises(aiohttp.ServerFingerprintMismatch): + self.loop.run_until_complete( + connector._create_connection(req, [], aiohttp.ClientTimeout()) ) - with mock.patch.object( - connector, - "_get_fingerprint", - autospec=True, - spec_set=True, - return_value=mock.Mock(return_value=fingerprint_mock), - ): - # Called on connection to http://proxy.example.com - with mock.patch.object( - self.loop, - "create_connection", - autospec=True, - spec_set=True, - return_value=(mock.Mock(), mock.Mock()), - ): - # Called on connection to https://www.python.org - class TransportMock(asyncio.Transport): - def close(self) -> None: - pass - - with mock.patch.object( - self.loop, - "start_tls", - autospec=True, - spec_set=True, - return_value=TransportMock(), - ): - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - with self.assertRaises( - aiohttp.ServerFingerprintMismatch - ): - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) - ) @mock.patch( "aiohttp.connector.aiohappyeyeballs.start_connection", From c2cdefdb36ff290a2f6dd8c25281b1c887af671d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 29 Sep 2024 13:10:54 -0500 Subject: [PATCH 10/19] fix the other test --- tests/test_proxy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index ac7614e4fde..5ac1cf3fb40 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -512,8 +512,9 @@ def close(self) -> None: autospec=True, spec_set=True, ) + @mock.patch("aiohttp.connector.ClientRequest") def test_https_connect( - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock + self, ClientRequestMock: mock.Mock, start_connection: mock.Mock ) -> None: proxy_req = ClientRequest( "GET", URL("http://proxy.example.com"), loop=self.loop From becaac0f02ccc7803852049de17ac8a9f1bc12ac Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 29 Sep 2024 13:11:49 -0500 Subject: [PATCH 11/19] keep order --- tests/test_proxy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 5ac1cf3fb40..4bb326419f9 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -415,14 +415,14 @@ async def make_conn() -> aiohttp.TCPConnector: self.loop.run_until_complete(req.close()) self.loop.run_until_complete(connector.close()) + @mock.patch("aiohttp.connector.ClientRequest") @mock.patch( "aiohttp.connector.aiohappyeyeballs.start_connection", autospec=True, spec_set=True, ) - @mock.patch("aiohttp.connector.ClientRequest") def test_https_connect_fingerprint_mismatch( - self, ClientRequestMock: mock.Mock, start_connection: mock.Mock + self, start_connection: mock.Mock, ClientRequestMock: mock.Mock ) -> None: async def make_conn() -> aiohttp.TCPConnector: return aiohttp.TCPConnector() @@ -507,14 +507,14 @@ def close(self) -> None: connector._create_connection(req, [], aiohttp.ClientTimeout()) ) + @mock.patch("aiohttp.connector.ClientRequest") @mock.patch( "aiohttp.connector.aiohappyeyeballs.start_connection", autospec=True, spec_set=True, ) - @mock.patch("aiohttp.connector.ClientRequest") def test_https_connect( - self, ClientRequestMock: mock.Mock, start_connection: mock.Mock + self, start_connection: mock.Mock, ClientRequestMock: mock.Mock ) -> None: proxy_req = ClientRequest( "GET", URL("http://proxy.example.com"), loop=self.loop From 2e27c7ca1f7e4f499bdebf16c9297b827d856c4e Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 30 Sep 2024 00:22:37 +0100 Subject: [PATCH 12/19] Coverage --- tests/test_proxy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 4bb326419f9..d7b1f0b7020 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -415,6 +415,7 @@ async def make_conn() -> aiohttp.TCPConnector: self.loop.run_until_complete(req.close()) self.loop.run_until_complete(connector.close()) + @pytest.parametrize("cleanup", (True, False)) @mock.patch("aiohttp.connector.ClientRequest") @mock.patch( "aiohttp.connector.aiohappyeyeballs.start_connection", @@ -422,10 +423,10 @@ async def make_conn() -> aiohttp.TCPConnector: spec_set=True, ) def test_https_connect_fingerprint_mismatch( - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock + self, start_connection: mock.Mock, ClientRequestMock: mock.Mock, cleanup: bool ) -> None: async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() + return aiohttp.TCPConnector(enable_cleanup_closed=cleanup) proxy_req = ClientRequest( "GET", URL("http://proxy.example.com"), loop=self.loop From 2352b6e1046773658e1e9829988469d34edb6168 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 30 Sep 2024 00:25:57 +0100 Subject: [PATCH 13/19] Update test_proxy.py --- tests/test_proxy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index d7b1f0b7020..7181e65e8b7 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -5,6 +5,7 @@ import unittest from unittest import mock +import pytest from yarl import URL import aiohttp From bbe1d3cd39d5a398383b19212f87fe37fb82f066 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 30 Sep 2024 00:27:46 +0100 Subject: [PATCH 14/19] Update tests/test_proxy.py --- tests/test_proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 7181e65e8b7..650df310677 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -416,7 +416,7 @@ async def make_conn() -> aiohttp.TCPConnector: self.loop.run_until_complete(req.close()) self.loop.run_until_complete(connector.close()) - @pytest.parametrize("cleanup", (True, False)) + @pytest.mark.parametrize("cleanup", (True, False)) @mock.patch("aiohttp.connector.ClientRequest") @mock.patch( "aiohttp.connector.aiohappyeyeballs.start_connection", From 90e4b190a26b8a621a6446b7887fdaefc7be93d5 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 30 Sep 2024 00:45:52 +0100 Subject: [PATCH 15/19] Update test_proxy.py --- tests/test_proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 650df310677..d326e23956c 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -416,13 +416,13 @@ async def make_conn() -> aiohttp.TCPConnector: self.loop.run_until_complete(req.close()) self.loop.run_until_complete(connector.close()) - @pytest.mark.parametrize("cleanup", (True, False)) @mock.patch("aiohttp.connector.ClientRequest") @mock.patch( "aiohttp.connector.aiohappyeyeballs.start_connection", autospec=True, spec_set=True, ) + @pytest.mark.parametrize("cleanup", (True, False)) def test_https_connect_fingerprint_mismatch( self, start_connection: mock.Mock, ClientRequestMock: mock.Mock, cleanup: bool ) -> None: From 8b87bcebac3779d73ad22862cc6fece537172ac4 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 30 Sep 2024 13:29:58 +0100 Subject: [PATCH 16/19] subTest --- tests/test_proxy.py | 157 ++++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 78 deletions(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index d326e23956c..58c22d78715 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -422,92 +422,93 @@ async def make_conn() -> aiohttp.TCPConnector: autospec=True, spec_set=True, ) - @pytest.mark.parametrize("cleanup", (True, False)) def test_https_connect_fingerprint_mismatch( self, start_connection: mock.Mock, ClientRequestMock: mock.Mock, cleanup: bool ) -> None: async def make_conn() -> aiohttp.TCPConnector: return aiohttp.TCPConnector(enable_cleanup_closed=cleanup) - proxy_req = ClientRequest( - "GET", URL("http://proxy.example.com"), loop=self.loop - ) - ClientRequestMock.return_value = proxy_req - - class TransportMock(asyncio.Transport): - def close(self) -> None: - pass - - proxy_resp = ClientResponse( - "get", - URL("http://proxy.example.com"), - request_info=mock.Mock(), - writer=mock.Mock(), - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - fingerprint_mock = mock.Mock(spec=Fingerprint, auto_spec=True) - fingerprint_mock.check.side_effect = aiohttp.ServerFingerprintMismatch( - b"exp", b"got", "example.com", 8080 - ) - with mock.patch.object( - proxy_req, "send", autospec=True, spec_set=True, return_value=proxy_resp - ), mock.patch.object( - proxy_resp, - "start", - autospec=True, - spec_set=True, - return_value=mock.Mock(status=200), - ): - connector = self.loop.run_until_complete(make_conn()) - host = [ - { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } - ] - with mock.patch.object( - connector, - "_resolve_host", - autospec=True, - spec_set=True, - return_value=host, - ), mock.patch.object( - connector, - "_get_fingerprint", - autospec=True, - spec_set=True, - return_value=fingerprint_mock, - ), mock.patch.object( # Called on connection to http://proxy.example.com - self.loop, - "create_connection", - autospec=True, - spec_set=True, - return_value=(mock.Mock(), mock.Mock()), - ), mock.patch.object( # Called on connection to https://www.python.org - self.loop, - "start_tls", - autospec=True, - spec_set=True, - return_value=TransportMock(), - ): - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), + for cleanup in (True, False): + with self.subTest(cleanup=cleanup): + proxy_req = ClientRequest( + "GET", URL("http://proxy.example.com"), loop=self.loop + ) + ClientRequestMock.return_value = proxy_req + + class TransportMock(asyncio.Transport): + def close(self) -> None: + pass + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=mock.Mock(), + continue100=None, + timer=TimerNoop(), + traces=[], loop=self.loop, + session=mock.Mock(), ) - with self.assertRaises(aiohttp.ServerFingerprintMismatch): - self.loop.run_until_complete( - connector._create_connection(req, [], aiohttp.ClientTimeout()) - ) + fingerprint_mock = mock.Mock(spec=Fingerprint, auto_spec=True) + fingerprint_mock.check.side_effect = aiohttp.ServerFingerprintMismatch( + b"exp", b"got", "example.com", 8080 + ) + with mock.patch.object( + proxy_req, "send", autospec=True, spec_set=True, return_value=proxy_resp + ), mock.patch.object( + proxy_resp, + "start", + autospec=True, + spec_set=True, + return_value=mock.Mock(status=200), + ): + connector = self.loop.run_until_complete(make_conn()) + host = [ + { + "hostname": "hostname", + "host": "127.0.0.1", + "port": 80, + "family": socket.AF_INET, + "proto": 0, + "flags": 0, + } + ] + with mock.patch.object( + connector, + "_resolve_host", + autospec=True, + spec_set=True, + return_value=host, + ), mock.patch.object( + connector, + "_get_fingerprint", + autospec=True, + spec_set=True, + return_value=fingerprint_mock, + ), mock.patch.object( # Called on connection to http://proxy.example.com + self.loop, + "create_connection", + autospec=True, + spec_set=True, + return_value=(mock.Mock(), mock.Mock()), + ), mock.patch.object( # Called on connection to https://www.python.org + self.loop, + "start_tls", + autospec=True, + spec_set=True, + return_value=TransportMock(), + ): + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=self.loop, + ) + with self.assertRaises(aiohttp.ServerFingerprintMismatch): + self.loop.run_until_complete( + connector._create_connection(req, [], aiohttp.ClientTimeout()) + ) @mock.patch("aiohttp.connector.ClientRequest") @mock.patch( From a04a6c546ab09d963605de78606d9cd614f67c0b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:30:30 +0000 Subject: [PATCH 17/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_proxy.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 58c22d78715..8182eed78a0 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -434,11 +434,11 @@ async def make_conn() -> aiohttp.TCPConnector: "GET", URL("http://proxy.example.com"), loop=self.loop ) ClientRequestMock.return_value = proxy_req - + class TransportMock(asyncio.Transport): def close(self) -> None: pass - + proxy_resp = ClientResponse( "get", URL("http://proxy.example.com"), @@ -455,7 +455,11 @@ def close(self) -> None: b"exp", b"got", "example.com", 8080 ) with mock.patch.object( - proxy_req, "send", autospec=True, spec_set=True, return_value=proxy_resp + proxy_req, + "send", + autospec=True, + spec_set=True, + return_value=proxy_resp, ), mock.patch.object( proxy_resp, "start", @@ -507,7 +511,9 @@ def close(self) -> None: ) with self.assertRaises(aiohttp.ServerFingerprintMismatch): self.loop.run_until_complete( - connector._create_connection(req, [], aiohttp.ClientTimeout()) + connector._create_connection( + req, [], aiohttp.ClientTimeout() + ) ) @mock.patch("aiohttp.connector.ClientRequest") From 4c0ba020351b5be79eba8db0a71561507d34de3d Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 30 Sep 2024 13:34:15 +0100 Subject: [PATCH 18/19] Update tests/test_proxy.py --- tests/test_proxy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 8182eed78a0..a4d550cba0b 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -5,7 +5,6 @@ import unittest from unittest import mock -import pytest from yarl import URL import aiohttp From fd02612e49e6e718026539365502405123ce8212 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 30 Sep 2024 13:53:54 +0100 Subject: [PATCH 19/19] Update tests/test_proxy.py --- tests/test_proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index a4d550cba0b..4d58f05c904 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -422,7 +422,7 @@ async def make_conn() -> aiohttp.TCPConnector: spec_set=True, ) def test_https_connect_fingerprint_mismatch( - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock, cleanup: bool + self, start_connection: mock.Mock, ClientRequestMock: mock.Mock ) -> None: async def make_conn() -> aiohttp.TCPConnector: return aiohttp.TCPConnector(enable_cleanup_closed=cleanup)