diff --git a/tests/conftest.py b/tests/conftest.py index 62fb04f2e36..57a7e131a36 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -243,6 +243,11 @@ def assert_sock_fits(sock_path: str) -> None: return +@pytest.fixture +async def event_loop(loop: asyncio.AbstractEventLoop) -> asyncio.AbstractEventLoop: + return asyncio.get_running_loop() + + @pytest.fixture def selector_loop() -> Iterator[asyncio.AbstractEventLoop]: factory = asyncio.SelectorEventLoop diff --git a/tests/isolated/check_for_client_response_leak.py b/tests/isolated/check_for_client_response_leak.py index 67393c2c2d8..f386ad4bf42 100644 --- a/tests/isolated/check_for_client_response_leak.py +++ b/tests/isolated/check_for_client_response_leak.py @@ -1,10 +1,11 @@ import asyncio import contextlib import gc +import socket import sys from aiohttp import ClientError, ClientSession, web -from aiohttp.test_utils import get_unused_port_socket +from aiohttp.test_utils import REUSE_ADDRESS gc.set_debug(gc.DEBUG_LEAK) @@ -18,30 +19,32 @@ async def stream_handler(request: web.Request) -> web.Response: return web.Response() app.router.add_get("/stream", stream_handler) - sock = get_unused_port_socket("127.0.0.1") - port = sock.getsockname()[1] - - runner = web.AppRunner(app) - await runner.setup() - site = web.SockSite(runner, sock) - await site.start() - - session = ClientSession() - - async def fetch_stream(url: str) -> None: - """Fetch a stream and read a few bytes from it.""" - with contextlib.suppress(ClientError): - await session.get(url) - - client_task = asyncio.create_task(fetch_stream(f"http://localhost:{port}/stream")) - await client_task - gc.collect() - client_response_present = any( - type(obj).__name__ == "ClientResponse" for obj in gc.garbage - ) - await session.close() - await runner.cleanup() - sys.exit(1 if client_response_present else 0) + with socket.create_server(("127.0.0.1", 0), reuse_port=REUSE_ADDRESS) as sock: + port = sock.getsockname()[1] + + runner = web.AppRunner(app) + await runner.setup() + site = web.SockSite(runner, sock) + await site.start() + + session = ClientSession() + + async def fetch_stream(url: str) -> None: + """Fetch a stream and read a few bytes from it.""" + with contextlib.suppress(ClientError): + await session.get(url) + + client_task = asyncio.create_task( + fetch_stream(f"http://localhost:{port}/stream") + ) + await client_task + gc.collect() + client_response_present = any( + type(obj).__name__ == "ClientResponse" for obj in gc.garbage + ) + await session.close() + await runner.cleanup() + sys.exit(1 if client_response_present else 0) asyncio.run(main()) diff --git a/tests/isolated/check_for_request_leak.py b/tests/isolated/check_for_request_leak.py index 6f340a05277..b60273a251b 100644 --- a/tests/isolated/check_for_request_leak.py +++ b/tests/isolated/check_for_request_leak.py @@ -1,10 +1,11 @@ import asyncio import gc +import socket import sys from typing import NoReturn from aiohttp import ClientSession, web -from aiohttp.test_utils import get_unused_port_socket +from aiohttp.test_utils import REUSE_ADDRESS gc.set_debug(gc.DEBUG_LEAK) @@ -17,24 +18,24 @@ async def handler(request: web.Request) -> NoReturn: assert False app.router.add_route("GET", "/json", handler) - sock = get_unused_port_socket("127.0.0.1") - port = sock.getsockname()[1] - - runner = web.AppRunner(app) - await runner.setup() - site = web.SockSite(runner, sock) - await site.start() - - async with ClientSession() as session: - async with session.get(f"http://127.0.0.1:{port}/json") as resp: - await resp.read() - - # Give time for the cancelled task to be collected - await asyncio.sleep(0.5) - gc.collect() - request_present = any(type(obj).__name__ == "Request" for obj in gc.garbage) - await session.close() - await runner.cleanup() + with socket.create_server(("127.0.0.1", 0), reuse_port=REUSE_ADDRESS) as sock: + port = sock.getsockname()[1] + + runner = web.AppRunner(app) + await runner.setup() + site = web.SockSite(runner, sock) + await site.start() + + async with ClientSession() as session: + async with session.get(f"http://127.0.0.1:{port}/json") as resp: + await resp.read() + + # Give time for the cancelled task to be collected + await asyncio.sleep(0.5) + gc.collect() + request_present = any(type(obj).__name__ == "Request" for obj in gc.garbage) + await session.close() + await runner.cleanup() sys.exit(1 if request_present else 0) diff --git a/tests/test_cookiejar.py b/tests/test_cookiejar.py index 4f34e957a28..b3774140c63 100644 --- a/tests/test_cookiejar.py +++ b/tests/test_cookiejar.py @@ -1,10 +1,8 @@ -import asyncio import datetime import heapq import itertools import logging import pickle -import unittest from http.cookies import BaseCookie, Morsel, SimpleCookie from operator import not_ from pathlib import Path @@ -188,7 +186,6 @@ async def test_constructor_with_expired( def test_save_load( tmp_path: Path, - loop: asyncio.AbstractEventLoop, cookies_to_send: SimpleCookie, cookies_to_receive: SimpleCookie, ) -> None: @@ -209,9 +206,7 @@ def test_save_load( assert jar_test == cookies_to_receive -async def test_update_cookie_with_unicode_domain( - loop: asyncio.AbstractEventLoop, -) -> None: +async def test_update_cookie_with_unicode_domain() -> None: cookies = ( "idna-domain-first=first; Domain=xn--9caa.com; Path=/;", "idna-domain-second=second; Domain=xn--9caa.com; Path=/;", @@ -228,9 +223,7 @@ async def test_update_cookie_with_unicode_domain( assert jar_test == SimpleCookie(" ".join(cookies)) -async def test_filter_cookie_with_unicode_domain( - loop: asyncio.AbstractEventLoop, -) -> None: +async def test_filter_cookie_with_unicode_domain() -> None: jar = CookieJar() jar.update_cookies( SimpleCookie("idna-domain-first=first; Domain=xn--9caa.com; Path=/; ") @@ -239,7 +232,7 @@ async def test_filter_cookie_with_unicode_domain( assert len(jar.filter_cookies(URL("http://xn--9caa.com"))) == 1 -async def test_filter_cookies_str_deprecated(loop: asyncio.AbstractEventLoop) -> None: +async def test_filter_cookies_str_deprecated() -> None: jar = CookieJar() with pytest.deprecated_call( match="The method accepts yarl.URL instances only, got ", @@ -302,7 +295,6 @@ async def test_filter_cookies_str_deprecated(loop: asyncio.AbstractEventLoop) -> ), ) async def test_filter_cookies_with_domain_path_lookup_multilevelpath( - loop: asyncio.AbstractEventLoop, url: str, expected_cookies: Set[str], ) -> None: @@ -337,7 +329,7 @@ async def test_filter_cookies_with_domain_path_lookup_multilevelpath( assert c in expected_cookies -async def test_domain_filter_ip_cookie_send(loop: asyncio.AbstractEventLoop) -> None: +async def test_domain_filter_ip_cookie_send() -> None: jar = CookieJar() cookies = SimpleCookie( "shared-cookie=first; " @@ -402,7 +394,7 @@ async def test_domain_filter_ip_cookie_receive( ), ) async def test_quotes_correctly_based_on_input( - loop: asyncio.AbstractEventLoop, cookies: str, expected: str, quote_bool: bool + cookies: str, expected: str, quote_bool: bool ) -> None: jar = CookieJar(unsafe=True, quote_cookie=quote_bool) jar.update_cookies(SimpleCookie(cookies)) @@ -410,7 +402,7 @@ async def test_quotes_correctly_based_on_input( assert cookies_sent == expected -async def test_ignore_domain_ending_with_dot(loop: asyncio.AbstractEventLoop) -> None: +async def test_ignore_domain_ending_with_dot() -> None: jar = CookieJar(unsafe=True) jar.update_cookies( SimpleCookie("cookie=val; Domain=example.com.;"), URL("http://www.example.com") @@ -421,90 +413,38 @@ async def test_ignore_domain_ending_with_dot(loop: asyncio.AbstractEventLoop) -> assert cookies_sent.output(header="Cookie:") == "" -class TestCookieJarBase(unittest.TestCase): - cookies_to_receive: SimpleCookie - cookies_to_send: SimpleCookie - loop: asyncio.AbstractEventLoop - jar: CookieJar - - def setUp(self) -> None: - self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(None) - - # N.B. those need to be overridden in child test cases - async def make_jar() -> CookieJar: - return CookieJar() - - self.jar = self.loop.run_until_complete(make_jar()) - - def tearDown(self) -> None: - self.loop.close() +class TestCookieJarSafe: + @pytest.fixture(autouse=True) + def setup_cookies( + self, + cookies_to_send_with_expired: SimpleCookie, + cookies_to_receive: SimpleCookie, + ) -> None: + self.cookies_to_send = cookies_to_send_with_expired + self.cookies_to_receive = cookies_to_receive def request_reply_with_same_url( self, url: str ) -> Tuple["BaseCookie[str]", SimpleCookie]: - self.jar.update_cookies(self.cookies_to_send) - cookies_sent = self.jar.filter_cookies(URL(url)) + jar = CookieJar() + jar.update_cookies(self.cookies_to_send) + cookies_sent = jar.filter_cookies(URL(url)) - self.jar.clear() + jar.clear() - self.jar.update_cookies(self.cookies_to_receive, URL(url)) + jar.update_cookies(self.cookies_to_receive, URL(url)) cookies_received = SimpleCookie() - for cookie in self.jar: + for cookie in jar: dict.__setitem__(cookies_received, cookie.key, cookie) - self.jar.clear() + jar.clear() return cookies_sent, cookies_received - -class TestCookieJarSafe(TestCookieJarBase): - def setUp(self) -> None: - super().setUp() - - self.cookies_to_send = SimpleCookie( - "shared-cookie=first; " - "domain-cookie=second; Domain=example.com; " - "subdomain1-cookie=third; Domain=test1.example.com; " - "subdomain2-cookie=fourth; Domain=test2.example.com; " - "dotted-domain-cookie=fifth; Domain=.example.com; " - "different-domain-cookie=sixth; Domain=different.org; " - "secure-cookie=seventh; Domain=secure.com; Secure; " - "no-path-cookie=eighth; Domain=pathtest.com; " - "path1-cookie=ninth; Domain=pathtest.com; Path=/; " - "path2-cookie=tenth; Domain=pathtest.com; Path=/one; " - "path3-cookie=eleventh; Domain=pathtest.com; Path=/one/two; " - "path4-cookie=twelfth; Domain=pathtest.com; Path=/one/two/; " - "expires-cookie=thirteenth; Domain=expirestest.com; Path=/;" - " Expires=Tue, 1 Jan 1980 12:00:00 GMT; " - "max-age-cookie=fourteenth; Domain=maxagetest.com; Path=/;" - " Max-Age=60; " - "invalid-max-age-cookie=fifteenth; Domain=invalid-values.com; " - " Max-Age=string; " - "invalid-expires-cookie=sixteenth; Domain=invalid-values.com; " - " Expires=string;" - ) - - self.cookies_to_receive = SimpleCookie( - "unconstrained-cookie=first; Path=/; " - "domain-cookie=second; Domain=example.com; Path=/; " - "subdomain1-cookie=third; Domain=test1.example.com; Path=/; " - "subdomain2-cookie=fourth; Domain=test2.example.com; Path=/; " - "dotted-domain-cookie=fifth; Domain=.example.com; Path=/; " - "different-domain-cookie=sixth; Domain=different.org; Path=/; " - "no-path-cookie=seventh; Domain=pathtest.com; " - "path-cookie=eighth; Domain=pathtest.com; Path=/somepath; " - "wrong-path-cookie=ninth; Domain=pathtest.com; Path=somepath;" - ) - - async def make_jar() -> CookieJar: - return CookieJar() - - self.jar = self.loop.run_until_complete(make_jar()) - def timed_request( self, url: str, update_time: float, send_time: float ) -> "BaseCookie[str]": + jar = CookieJar() freeze_update_time: Union[datetime.datetime, datetime.timedelta] freeze_send_time: Union[datetime.datetime, datetime.timedelta] if isinstance(update_time, int): @@ -517,12 +457,12 @@ def timed_request( freeze_send_time = datetime.datetime.fromtimestamp(send_time) with freeze_time(freeze_update_time): - self.jar.update_cookies(self.cookies_to_send) + jar.update_cookies(self.cookies_to_send) with freeze_time(freeze_send_time): - cookies_sent = self.jar.filter_cookies(URL(url)) + cookies_sent = jar.filter_cookies(URL(url)) - self.jar.clear() + jar.clear() return cookies_sent @@ -531,180 +471,169 @@ def test_domain_filter_same_host(self) -> None: "http://example.com/" ) - self.assertEqual( - set(cookies_sent.keys()), - {"shared-cookie", "domain-cookie", "dotted-domain-cookie"}, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "domain-cookie", + "dotted-domain-cookie", + } - self.assertEqual( - set(cookies_received.keys()), - {"unconstrained-cookie", "domain-cookie", "dotted-domain-cookie"}, - ) + assert set(cookies_received.keys()) == { + "unconstrained-cookie", + "domain-cookie", + "dotted-domain-cookie", + } def test_domain_filter_same_host_and_subdomain(self) -> None: cookies_sent, cookies_received = self.request_reply_with_same_url( "http://test1.example.com/" ) - self.assertEqual( - set(cookies_sent.keys()), - { - "shared-cookie", - "domain-cookie", - "subdomain1-cookie", - "dotted-domain-cookie", - }, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "domain-cookie", + "subdomain1-cookie", + "dotted-domain-cookie", + } - self.assertEqual( - set(cookies_received.keys()), - { - "unconstrained-cookie", - "domain-cookie", - "subdomain1-cookie", - "dotted-domain-cookie", - }, - ) + assert set(cookies_received.keys()) == { + "unconstrained-cookie", + "domain-cookie", + "subdomain1-cookie", + "dotted-domain-cookie", + } def test_domain_filter_same_host_diff_subdomain(self) -> None: cookies_sent, cookies_received = self.request_reply_with_same_url( "http://different.example.com/" ) - self.assertEqual( - set(cookies_sent.keys()), - {"shared-cookie", "domain-cookie", "dotted-domain-cookie"}, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "domain-cookie", + "dotted-domain-cookie", + } - self.assertEqual( - set(cookies_received.keys()), - {"unconstrained-cookie", "domain-cookie", "dotted-domain-cookie"}, - ) + assert set(cookies_received.keys()) == { + "unconstrained-cookie", + "domain-cookie", + "dotted-domain-cookie", + } def test_domain_filter_diff_host(self) -> None: cookies_sent, cookies_received = self.request_reply_with_same_url( "http://different.org/" ) - self.assertEqual( - set(cookies_sent.keys()), {"shared-cookie", "different-domain-cookie"} - ) + assert set(cookies_sent.keys()) == {"shared-cookie", "different-domain-cookie"} - self.assertEqual( - set(cookies_received.keys()), - {"unconstrained-cookie", "different-domain-cookie"}, - ) + assert set(cookies_received.keys()) == { + "unconstrained-cookie", + "different-domain-cookie", + } - def test_domain_filter_host_only(self) -> None: - self.jar.update_cookies(self.cookies_to_receive, URL("http://example.com/")) + def test_domain_filter_host_only(self, cookies_to_receive: SimpleCookie) -> None: + jar = CookieJar() + jar.update_cookies(cookies_to_receive, URL("http://example.com/")) sub_cookie = SimpleCookie("subdomain=spam; Path=/;") - self.jar.update_cookies(sub_cookie, URL("http://foo.example.com/")) + jar.update_cookies(sub_cookie, URL("http://foo.example.com/")) - cookies_sent = self.jar.filter_cookies(URL("http://foo.example.com/")) - self.assertIn("subdomain", set(cookies_sent.keys())) - self.assertNotIn("unconstrained-cookie", set(cookies_sent.keys())) + cookies_sent = jar.filter_cookies(URL("http://foo.example.com/")) + assert "subdomain" in set(cookies_sent.keys()) + assert "unconstrained-cookie" not in set(cookies_sent.keys()) def test_secure_filter(self) -> None: cookies_sent, _ = self.request_reply_with_same_url("http://secure.com/") - self.assertEqual(set(cookies_sent.keys()), {"shared-cookie"}) + assert set(cookies_sent.keys()) == {"shared-cookie"} cookies_sent, _ = self.request_reply_with_same_url("https://secure.com/") - self.assertEqual(set(cookies_sent.keys()), {"shared-cookie", "secure-cookie"}) + assert set(cookies_sent.keys()) == {"shared-cookie", "secure-cookie"} def test_path_filter_root(self) -> None: cookies_sent, _ = self.request_reply_with_same_url("http://pathtest.com/") - self.assertEqual( - set(cookies_sent.keys()), - {"shared-cookie", "no-path-cookie", "path1-cookie"}, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "no-path-cookie", + "path1-cookie", + } def test_path_filter_folder(self) -> None: cookies_sent, _ = self.request_reply_with_same_url("http://pathtest.com/one/") - self.assertEqual( - set(cookies_sent.keys()), - {"shared-cookie", "no-path-cookie", "path1-cookie", "path2-cookie"}, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "no-path-cookie", + "path1-cookie", + "path2-cookie", + } def test_path_filter_file(self) -> None: cookies_sent, _ = self.request_reply_with_same_url( "http://pathtest.com/one/two" ) - self.assertEqual( - set(cookies_sent.keys()), - { - "shared-cookie", - "no-path-cookie", - "path1-cookie", - "path2-cookie", - "path3-cookie", - }, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "no-path-cookie", + "path1-cookie", + "path2-cookie", + "path3-cookie", + } def test_path_filter_subfolder(self) -> None: cookies_sent, _ = self.request_reply_with_same_url( "http://pathtest.com/one/two/" ) - self.assertEqual( - set(cookies_sent.keys()), - { - "shared-cookie", - "no-path-cookie", - "path1-cookie", - "path2-cookie", - "path3-cookie", - "path4-cookie", - }, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "no-path-cookie", + "path1-cookie", + "path2-cookie", + "path3-cookie", + "path4-cookie", + } def test_path_filter_subsubfolder(self) -> None: cookies_sent, _ = self.request_reply_with_same_url( "http://pathtest.com/one/two/three/" ) - self.assertEqual( - set(cookies_sent.keys()), - { - "shared-cookie", - "no-path-cookie", - "path1-cookie", - "path2-cookie", - "path3-cookie", - "path4-cookie", - }, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "no-path-cookie", + "path1-cookie", + "path2-cookie", + "path3-cookie", + "path4-cookie", + } def test_path_filter_different_folder(self) -> None: cookies_sent, _ = self.request_reply_with_same_url( "http://pathtest.com/hundred/" ) - self.assertEqual( - set(cookies_sent.keys()), - {"shared-cookie", "no-path-cookie", "path1-cookie"}, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "no-path-cookie", + "path1-cookie", + } def test_path_value(self) -> None: _, cookies_received = self.request_reply_with_same_url("http://pathtest.com/") - self.assertEqual( - set(cookies_received.keys()), - { - "unconstrained-cookie", - "no-path-cookie", - "path-cookie", - "wrong-path-cookie", - }, - ) + assert set(cookies_received.keys()) == { + "unconstrained-cookie", + "no-path-cookie", + "path-cookie", + "wrong-path-cookie", + } - self.assertEqual(cookies_received["no-path-cookie"]["path"], "/") - self.assertEqual(cookies_received["path-cookie"]["path"], "/somepath") - self.assertEqual(cookies_received["wrong-path-cookie"]["path"], "/") + assert cookies_received["no-path-cookie"]["path"] == "/" + assert cookies_received["path-cookie"]["path"] == "/somepath" + assert cookies_received["wrong-path-cookie"]["path"] == "/" def test_expires(self) -> None: ts_before = datetime.datetime( @@ -719,40 +648,41 @@ def test_expires(self) -> None: "http://expirestest.com/", ts_before, ts_before ) - self.assertEqual(set(cookies_sent.keys()), {"shared-cookie", "expires-cookie"}) + assert set(cookies_sent.keys()) == {"shared-cookie", "expires-cookie"} cookies_sent = self.timed_request( "http://expirestest.com/", ts_before, ts_after ) - self.assertEqual(set(cookies_sent.keys()), {"shared-cookie"}) + assert set(cookies_sent.keys()) == {"shared-cookie"} def test_max_age(self) -> None: cookies_sent = self.timed_request("http://maxagetest.com/", 1000, 1000) - self.assertEqual(set(cookies_sent.keys()), {"shared-cookie", "max-age-cookie"}) + assert set(cookies_sent.keys()) == {"shared-cookie", "max-age-cookie"} cookies_sent = self.timed_request("http://maxagetest.com/", 1000, 2000) - self.assertEqual(set(cookies_sent.keys()), {"shared-cookie"}) + assert set(cookies_sent.keys()) == {"shared-cookie"} def test_invalid_values(self) -> None: cookies_sent, cookies_received = self.request_reply_with_same_url( "http://invalid-values.com/" ) - self.assertEqual( - set(cookies_sent.keys()), - {"shared-cookie", "invalid-max-age-cookie", "invalid-expires-cookie"}, - ) + assert set(cookies_sent.keys()) == { + "shared-cookie", + "invalid-max-age-cookie", + "invalid-expires-cookie", + } cookie = cookies_sent["invalid-max-age-cookie"] - self.assertEqual(cookie["max-age"], "") + assert cookie["max-age"] == "" cookie = cookies_sent["invalid-expires-cookie"] - self.assertEqual(cookie["expires"], "") + assert cookie["expires"] == "" - def test_cookie_not_expired_when_added_after_removal(self) -> None: + async def test_cookie_not_expired_when_added_after_removal(self) -> None: # Test case for https://github.com/aio-libs/aiohttp/issues/2084 timestamps = [ 533588.993, @@ -768,10 +698,7 @@ def test_cookie_not_expired_when_added_after_removal(self) -> None: timestamps, itertools.cycle([timestamps[-1]]) ) - async def make_jar() -> CookieJar: - return CookieJar(unsafe=True) - - jar = self.loop.run_until_complete(make_jar()) + jar = CookieJar(unsafe=True) # Remove `foo` cookie. jar.update_cookies(SimpleCookie('foo=""; Max-Age=0')) # Set `foo` cookie to `bar`. @@ -780,11 +707,8 @@ async def make_jar() -> CookieJar: # Assert that there is a cookie. assert len(jar) == 1 - def test_path_filter_diff_folder_same_name(self) -> None: - async def make_jar() -> CookieJar: - return CookieJar(unsafe=True) - - jar = self.loop.run_until_complete(make_jar()) + async def test_path_filter_diff_folder_same_name(self) -> None: + jar = CookieJar(unsafe=True) jar.update_cookies( SimpleCookie("path-cookie=zero; Domain=pathtest.com; Path=/; ") @@ -792,23 +716,20 @@ async def make_jar() -> CookieJar: jar.update_cookies( SimpleCookie("path-cookie=one; Domain=pathtest.com; Path=/one; ") ) - self.assertEqual(len(jar), 2) + assert len(jar) == 2 jar_filtered = jar.filter_cookies(URL("http://pathtest.com/")) - self.assertEqual(len(jar_filtered), 1) - self.assertEqual(jar_filtered["path-cookie"].value, "zero") + assert len(jar_filtered) == 1 + assert jar_filtered["path-cookie"].value == "zero" jar_filtered = jar.filter_cookies(URL("http://pathtest.com/one")) - self.assertEqual(len(jar_filtered), 1) - self.assertEqual(jar_filtered["path-cookie"].value, "one") + assert len(jar_filtered) == 1 + assert jar_filtered["path-cookie"].value == "one" - def test_path_filter_diff_folder_same_name_return_best_match_independent_from_put_order( + async def test_path_filter_diff_folder_same_name_return_best_match_independent_from_put_order( self, ) -> None: - async def make_jar() -> CookieJar: - return CookieJar(unsafe=True) - - jar = self.loop.run_until_complete(make_jar()) + jar = CookieJar(unsafe=True) jar.update_cookies( SimpleCookie("path-cookie=one; Domain=pathtest.com; Path=/one; ") ) @@ -818,19 +739,19 @@ async def make_jar() -> CookieJar: jar.update_cookies( SimpleCookie("path-cookie=two; Domain=pathtest.com; Path=/second; ") ) - self.assertEqual(len(jar), 3) + assert len(jar) == 3 jar_filtered = jar.filter_cookies(URL("http://pathtest.com/")) - self.assertEqual(len(jar_filtered), 1) - self.assertEqual(jar_filtered["path-cookie"].value, "zero") + assert len(jar_filtered) == 1 + assert jar_filtered["path-cookie"].value == "zero" jar_filtered = jar.filter_cookies(URL("http://pathtest.com/second")) - self.assertEqual(len(jar_filtered), 1) - self.assertEqual(jar_filtered["path-cookie"].value, "two") + assert len(jar_filtered) == 1 + assert jar_filtered["path-cookie"].value == "two" jar_filtered = jar.filter_cookies(URL("http://pathtest.com/one")) - self.assertEqual(len(jar_filtered), 1) - self.assertEqual(jar_filtered["path-cookie"].value, "one") + assert len(jar_filtered) == 1 + assert jar_filtered["path-cookie"].value == "one" async def test_dummy_cookie_jar() -> None: diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 783af5a5089..fd22c3a9910 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -1,8 +1,6 @@ import asyncio -import gc import socket import ssl -import unittest from unittest import mock import pytest @@ -14,187 +12,213 @@ from aiohttp.helpers import TimerNoop -class TestProxy(unittest.TestCase): - response_mock_attrs = { - "status": 200, - } - mocked_response = mock.Mock(**response_mock_attrs) - clientrequest_mock_attrs = { - "return_value.send.return_value.start": mock.AsyncMock( - return_value=mocked_response - ), - } - - def setUp(self) -> None: - self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(None) - - def tearDown(self) -> None: - # just in case if we have transport close callbacks - self.loop.stop() - self.loop.run_forever() - self.loop.close() - gc.collect() - - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_connect( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + req = ClientRequest( + "GET", + URL("http://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, ) - def test_connect( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> None: - req = ClientRequest( - "GET", - URL("http://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, + assert str(req.proxy) == "http://proxy.example.com" + + # mock all the things! + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r]): + proto = mock.Mock( + **{ + "transport.get_extra_info.return_value": False, + } ) - self.assertEqual(str(req.proxy), "http://proxy.example.com") - - # mock all the things! - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "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, return_value=[r] + event_loop, + "create_connection", + autospec=True, + return_value=(proto.transport, proto), ): - proto = mock.Mock( - **{ - "transport.get_extra_info.return_value": False, - } + conn = event_loop.run_until_complete( + connector.connect(req, [], aiohttp.ClientTimeout()) ) - with mock.patch.object( - self.loop, - "create_connection", - autospec=True, - return_value=(proto.transport, proto), - ): - conn = self.loop.run_until_complete( - connector.connect(req, [], aiohttp.ClientTimeout()) - ) - self.assertEqual(req.url, URL("http://www.python.org")) - self.assertIs(conn._protocol, proto) - self.assertIs(conn.transport, proto.transport) - - ClientRequestMock.assert_called_with( - "GET", - URL("http://proxy.example.com"), - auth=None, - headers={"Host": "www.python.org"}, - loop=self.loop, - ssl=True, - ) + assert req.url == URL("http://www.python.org") + assert conn._protocol is proto + assert conn.transport is proto.transport - conn.close() - self.loop.run_until_complete(connector.close()) + ClientRequestMock.assert_called_with( + "GET", + URL("http://proxy.example.com"), + auth=None, + headers={"Host": "www.python.org"}, + loop=event_loop, + ssl=True, + ) - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, + conn.close() + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_proxy_headers( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + req = ClientRequest( + "GET", + URL("http://www.python.org"), + proxy=URL("http://proxy.example.com"), + proxy_headers={"Foo": "Bar"}, + loop=event_loop, ) - def test_proxy_headers( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> None: - req = ClientRequest( - "GET", - URL("http://www.python.org"), - proxy=URL("http://proxy.example.com"), - proxy_headers={"Foo": "Bar"}, - loop=self.loop, + assert str(req.proxy) == "http://proxy.example.com" + + # mock all the things! + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r]): + proto = mock.Mock( + **{ + "transport.get_extra_info.return_value": False, + } ) - self.assertEqual(str(req.proxy), "http://proxy.example.com") - - # mock all the things! - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "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, return_value=[r] + event_loop, + "create_connection", + autospec=True, + return_value=(proto.transport, proto), ): - proto = mock.Mock( - **{ - "transport.get_extra_info.return_value": False, - } + conn = event_loop.run_until_complete( + connector.connect(req, [], aiohttp.ClientTimeout()) ) - with mock.patch.object( - self.loop, - "create_connection", - autospec=True, - return_value=(proto.transport, proto), - ): - conn = self.loop.run_until_complete( - connector.connect(req, [], aiohttp.ClientTimeout()) - ) - self.assertEqual(req.url, URL("http://www.python.org")) - self.assertIs(conn._protocol, proto) - self.assertIs(conn.transport, proto.transport) - - ClientRequestMock.assert_called_with( - "GET", - URL("http://proxy.example.com"), - auth=None, - headers={"Host": "www.python.org", "Foo": "Bar"}, - loop=self.loop, - ssl=True, - ) - - conn.close() - self.loop.run_until_complete(connector.close()) + assert req.url == URL("http://www.python.org") + assert conn._protocol is proto + assert conn.transport is proto.transport - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, - ) - def test_proxy_auth(self, start_connection: mock.Mock) -> None: # type: ignore[misc] - with self.assertRaises(ValueError) as ctx: - ClientRequest( + ClientRequestMock.assert_called_with( "GET", - URL("http://python.org"), - proxy=URL("http://proxy.example.com"), - proxy_auth=("user", "pass"), # type: ignore[arg-type] - loop=mock.Mock(), + URL("http://proxy.example.com"), + auth=None, + headers={"Host": "www.python.org", "Foo": "Bar"}, + loop=event_loop, + ssl=True, ) - self.assertEqual( - ctx.exception.args[0], - "proxy_auth must be None or BasicAuth() tuple", + + conn.close() + event_loop.run_until_complete(connector.close()) + + +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_proxy_auth(start_connection: mock.Mock) -> None: # type: ignore[misc] + msg = r"proxy_auth must be None or BasicAuth\(\) tuple" + with pytest.raises(ValueError, match=msg): + ClientRequest( + "GET", + URL("http://python.org"), + proxy=URL("http://proxy.example.com"), + proxy_auth=("user", "pass"), # type: ignore[arg-type] + loop=mock.Mock(), ) - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, - ) - def test_proxy_dns_error(self, start_connection: mock.Mock) -> None: # type: ignore[misc] - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - connector = self.loop.run_until_complete(make_conn()) +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_proxy_dns_error( # type: ignore[misc] + start_connection: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + with mock.patch.object( + connector, + "_resolve_host", + autospec=True, + side_effect=OSError("dont take it serious"), + ): + req = ClientRequest( + "GET", + URL("http://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + expected_headers = dict(req.headers) + with pytest.raises(aiohttp.ClientConnectorError): + event_loop.run_until_complete( + connector.connect(req, [], aiohttp.ClientTimeout()) + ) + assert req.url.path == "/" + assert dict(req.headers) == expected_headers + event_loop.run_until_complete(connector.close()) + + +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, + return_value=mock.create_autospec(socket.socket, spec_set=True, instance=True), +) +def test_proxy_connection_error( # type: ignore[misc] + start_connection: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "hostname": "www.python.org", + "host": "127.0.0.1", + "port": 80, + "family": socket.AF_INET, + "proto": 0, + "flags": socket.AI_NUMERICHOST, + } + with mock.patch.object(connector, "_resolve_host", autospec=True, return_value=[r]): with mock.patch.object( - connector, - "_resolve_host", + connector._loop, + "create_connection", autospec=True, side_effect=OSError("dont take it serious"), ): @@ -202,958 +226,904 @@ async def make_conn() -> aiohttp.TCPConnector: "GET", URL("http://www.python.org"), proxy=URL("http://proxy.example.com"), - loop=self.loop, + loop=event_loop, ) - expected_headers = dict(req.headers) - with self.assertRaises(aiohttp.ClientConnectorError): - self.loop.run_until_complete( + with pytest.raises(aiohttp.ClientProxyConnectionError): + event_loop.run_until_complete( connector.connect(req, [], aiohttp.ClientTimeout()) ) - self.assertEqual(req.url.path, "/") - self.assertEqual(dict(req.headers), expected_headers) - self.loop.run_until_complete(connector.close()) - - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, - return_value=mock.create_autospec(socket.socket, spec_set=True, instance=True), + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_proxy_server_hostname_default( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_loop) + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), ) - def test_proxy_connection_error(self, start_connection: mock.Mock) -> None: # type: ignore[misc] - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "www.python.org", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": socket.AI_NUMERICHOST, - } - with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] - ): + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True) as m: + m.return_value.status = 200 + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "hostname": "hostname", + "host": "127.0.0.1", + "port": 80, + "family": socket.AF_INET, + "proto": 0, + "flags": 0, + } with mock.patch.object( - connector._loop, - "create_connection", - autospec=True, - side_effect=OSError("dont take it serious"), + connector, "_resolve_host", autospec=True, return_value=[r] ): - req = ClientRequest( - "GET", - URL("http://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - with self.assertRaises(aiohttp.ClientProxyConnectionError): - self.loop.run_until_complete( - connector.connect(req, [], aiohttp.ClientTimeout()) - ) - self.loop.run_until_complete(connector.close()) - - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, - ) - def test_proxy_server_hostname_default( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> 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=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp - ): - with mock.patch.object(proxy_resp, "start", autospec=True) as m: - m.return_value.status = 200 - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } + tr, proto = mock.Mock(), mock.Mock() with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), ): - tr, proto = mock.Mock(), mock.Mock() with mock.patch.object( - self.loop, - "create_connection", + event_loop, + "start_tls", autospec=True, - return_value=(tr, proto), - ): - with mock.patch.object( - self.loop, - "start_tls", - autospec=True, - return_value=mock.Mock(), - ) as tls_m: - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) - ) - - self.assertEqual( - tls_m.call_args.kwargs["server_hostname"], - "www.python.org", + return_value=mock.Mock(), + ) as tls_m: + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + event_loop.run_until_complete( + connector._create_connection( + req, [], aiohttp.ClientTimeout() ) + ) - self.loop.run_until_complete(proxy_req.close()) - proxy_resp.close() - self.loop.run_until_complete(req.close()) - self.loop.run_until_complete(connector.close()) + assert ( + tls_m.call_args.kwargs["server_hostname"] + == "www.python.org" + ) - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, + event_loop.run_until_complete(proxy_req.close()) + proxy_resp.close() + event_loop.run_until_complete(req.close()) + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_proxy_server_hostname_override( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest( + "GET", + URL("http://proxy.example.com"), + loop=event_loop, ) - def test_proxy_server_hostname_override( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> 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=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp - ): - with mock.patch.object(proxy_resp, "start", autospec=True) as m: - m.return_value.status = 200 - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), + ) + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True) as m: + m.return_value.status = 200 + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r] + ): + tr, proto = mock.Mock(), mock.Mock() with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), ): - tr, proto = mock.Mock(), mock.Mock() with mock.patch.object( - self.loop, - "create_connection", - autospec=True, - return_value=(tr, proto), - ): - with mock.patch.object( - self.loop, - "start_tls", - autospec=True, - return_value=mock.Mock(), - ) as tls_m: - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - server_hostname="server-hostname.example.com", - loop=self.loop, - ) - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) - ) - - self.assertEqual( - tls_m.call_args.kwargs["server_hostname"], - "server-hostname.example.com", - ) - - self.loop.run_until_complete(proxy_req.close()) - proxy_resp.close() - 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, - ) - @pytest.mark.usefixtures("enable_cleanup_closed") - def test_https_connect_fingerprint_mismatch( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> None: - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector(enable_cleanup_closed=cleanup) - - 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(), - ) - 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", + event_loop, + "start_tls", 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(), - ), - ): + return_value=mock.Mock(), + ) as tls_m: req = ClientRequest( "GET", URL("https://www.python.org"), proxy=URL("http://proxy.example.com"), - loop=self.loop, + server_hostname="server-hostname.example.com", + loop=event_loop, ) - with self.assertRaises(aiohttp.ServerFingerprintMismatch): - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) + event_loop.run_until_complete( + connector._create_connection( + req, [], aiohttp.ClientTimeout() ) + ) - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, + assert ( + tls_m.call_args.kwargs["server_hostname"] + == "server-hostname.example.com" + ) + + event_loop.run_until_complete(proxy_req.close()) + proxy_resp.close() + event_loop.run_until_complete(req.close()) + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +@pytest.mark.usefixtures("enable_cleanup_closed") +@pytest.mark.parametrize("cleanup", (True, False)) +def test_https_connect_fingerprint_mismatch( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + cleanup: bool, + event_loop: asyncio.AbstractEventLoop, +) -> None: + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector(enable_cleanup_closed=cleanup) + + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_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=event_loop, + session=mock.Mock(), ) - def test_https_connect( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> 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=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp + 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 = event_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 + event_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 + event_loop, + "start_tls", + autospec=True, + spec_set=True, + return_value=TransportMock(), + ), ): - with mock.patch.object(proxy_resp, "start", autospec=True) as m: - m.return_value.status = 200 - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "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, return_value=[r] - ): - tr, proto = mock.Mock(), mock.Mock() - with mock.patch.object( - self.loop, - "create_connection", - autospec=True, - return_value=(tr, proto), - ): - with mock.patch.object( - self.loop, - "start_tls", - autospec=True, - return_value=mock.Mock(), - ): - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) - ) - - self.assertEqual(req.url.path, "/") - self.assertEqual(proxy_req.method, "CONNECT") - self.assertEqual( - proxy_req.url, URL("https://www.python.org") - ) + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + with pytest.raises(aiohttp.ServerFingerprintMismatch): + event_loop.run_until_complete( + connector._create_connection(req, [], aiohttp.ClientTimeout()) + ) - self.loop.run_until_complete(proxy_req.close()) - proxy_resp.close() - 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") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_https_connect( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_loop) + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), ) - def test_https_connect_certificate_error( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> 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=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp - ): - with mock.patch.object(proxy_resp, "start", autospec=True) as m: - m.return_value.status = 200 - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True) as m: + m.return_value.status = 200 + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r] + ): + tr, proto = mock.Mock(), mock.Mock() with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), ): - tr, proto = mock.Mock(), mock.Mock() - # Called on connection to http://proxy.example.com with mock.patch.object( - self.loop, - "create_connection", + event_loop, + "start_tls", autospec=True, - return_value=(tr, proto), + return_value=mock.Mock(), ): - # Called on connection to https://www.python.org - with mock.patch.object( - self.loop, - "start_tls", - autospec=True, - side_effect=ssl.CertificateError, - ): - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + event_loop.run_until_complete( + connector._create_connection( + req, [], aiohttp.ClientTimeout() ) - with self.assertRaises( - aiohttp.ClientConnectorCertificateError - ): - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) - ) - self.loop.run_until_complete(connector.close()) + ) - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, - ) - def test_https_connect_ssl_error( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> 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=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp - ): - with mock.patch.object(proxy_resp, "start", autospec=True) as m: - m.return_value.status = 200 - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "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, return_value=[r] - ): - tr, proto = mock.Mock(), mock.Mock() - # Called on connection to http://proxy.example.com - with mock.patch.object( - self.loop, - "create_connection", - autospec=True, - return_value=(tr, proto), - ): - # Called on connection to https://www.python.org - with mock.patch.object( - self.loop, - "start_tls", - autospec=True, - side_effect=ssl.SSLError, - ): - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - with self.assertRaises(aiohttp.ClientConnectorSSLError): - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) - ) - self.loop.run_until_complete(connector.close()) + assert req.url.path == "/" + assert proxy_req.method == "CONNECT" + assert proxy_req.url == URL("https://www.python.org") - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, + event_loop.run_until_complete(proxy_req.close()) + proxy_resp.close() + event_loop.run_until_complete(req.close()) + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_https_connect_certificate_error( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_loop) + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), ) - def test_https_connect_http_proxy_error( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> 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=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp - ): - with mock.patch.object(proxy_resp, "start", autospec=True) as m: - m.return_value.status = 400 - m.return_value.reason = "bad request" - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True) as m: + m.return_value.status = 200 + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r] + ): + tr, proto = mock.Mock(), mock.Mock() + # Called on connection to http://proxy.example.com with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), ): - tr, proto = mock.Mock(), mock.Mock() - tr.get_extra_info.return_value = None - # Called on connection to http://proxy.example.com + # Called on connection to https://www.python.org with mock.patch.object( - self.loop, - "create_connection", + event_loop, + "start_tls", autospec=True, - return_value=(tr, proto), + side_effect=ssl.CertificateError, ): req = ClientRequest( "GET", URL("https://www.python.org"), proxy=URL("http://proxy.example.com"), - loop=self.loop, + loop=event_loop, ) - with self.assertRaisesRegex( - aiohttp.ClientHttpProxyError, "400, message='bad request'" - ): - self.loop.run_until_complete( + with pytest.raises(aiohttp.ClientConnectorCertificateError): + event_loop.run_until_complete( connector._create_connection( req, [], aiohttp.ClientTimeout() ) ) - - self.loop.run_until_complete(proxy_req.close()) - proxy_resp.close() - 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, + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_https_connect_ssl_error( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_loop) + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), ) - def test_https_connect_resp_start_error( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> 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=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp - ): + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True) as m: + m.return_value.status = 200 + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "hostname": "hostname", + "host": "127.0.0.1", + "port": 80, + "family": socket.AF_INET, + "proto": 0, + "flags": 0, + } with mock.patch.object( - proxy_resp, "start", autospec=True, side_effect=OSError("error message") + connector, "_resolve_host", autospec=True, return_value=[r] ): - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } + tr, proto = mock.Mock(), mock.Mock() + # Called on connection to http://proxy.example.com with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), ): - tr, proto = mock.Mock(), mock.Mock() - tr.get_extra_info.return_value = None - # Called on connection to http://proxy.example.com + # Called on connection to https://www.python.org with mock.patch.object( - self.loop, - "create_connection", + event_loop, + "start_tls", autospec=True, - return_value=(tr, proto), + side_effect=ssl.SSLError, ): req = ClientRequest( "GET", URL("https://www.python.org"), proxy=URL("http://proxy.example.com"), - loop=self.loop, + loop=event_loop, ) - with self.assertRaisesRegex(OSError, "error message"): - self.loop.run_until_complete( + with pytest.raises(aiohttp.ClientConnectorSSLError): + event_loop.run_until_complete( connector._create_connection( req, [], aiohttp.ClientTimeout() ) ) - self.loop.run_until_complete(connector.close()) + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_https_connect_http_proxy_error( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_loop) + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), + ) + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True) as m: + m.return_value.status = 400 + m.return_value.reason = "bad request" + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r] + ): + tr, proto = mock.Mock(), mock.Mock() + tr.get_extra_info.return_value = None + # Called on connection to http://proxy.example.com + with mock.patch.object( + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), + ): + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + with pytest.raises( + aiohttp.ClientHttpProxyError, match="400, message='bad request'" + ): + event_loop.run_until_complete( + connector._create_connection( + req, [], aiohttp.ClientTimeout() + ) + ) - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, + event_loop.run_until_complete(proxy_req.close()) + proxy_resp.close() + event_loop.run_until_complete(req.close()) + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_https_connect_resp_start_error( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_loop) + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), ) - def test_request_port( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> None: - proxy_req = ClientRequest( - "GET", URL("http://proxy.example.com"), loop=self.loop - ) - ClientRequestMock.return_value = proxy_req - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] + proxy_resp, "start", autospec=True, side_effect=OSError("error message") ): - tr, proto = mock.Mock(), mock.Mock() - tr.get_extra_info.return_value = None - # Called on connection to http://proxy.example.com + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "hostname": "hostname", + "host": "127.0.0.1", + "port": 80, + "family": socket.AF_INET, + "proto": 0, + "flags": 0, + } with mock.patch.object( - self.loop, "create_connection", autospec=True, return_value=(tr, proto) + connector, "_resolve_host", autospec=True, return_value=[r] ): - req = ClientRequest( - "GET", - URL("http://localhost:1234/path"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - self.loop.run_until_complete( - connector._create_connection(req, [], aiohttp.ClientTimeout()) - ) - self.assertEqual(req.url, URL("http://localhost:1234/path")) - self.loop.run_until_complete(connector.close()) + tr, proto = mock.Mock(), mock.Mock() + tr.get_extra_info.return_value = None + # Called on connection to http://proxy.example.com + with mock.patch.object( + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), + ): + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + with pytest.raises(OSError, match="error message"): + event_loop.run_until_complete( + connector._create_connection( + req, [], aiohttp.ClientTimeout() + ) + ) + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_request_port( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_loop) + ClientRequestMock.return_value = proxy_req + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r]): + tr, proto = mock.Mock(), mock.Mock() + tr.get_extra_info.return_value = None + # Called on connection to http://proxy.example.com + with mock.patch.object( + event_loop, "create_connection", autospec=True, return_value=(tr, proto) + ): + req = ClientRequest( + "GET", + URL("http://localhost:1234/path"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + event_loop.run_until_complete( + connector._create_connection(req, [], aiohttp.ClientTimeout()) + ) + assert req.url == URL("http://localhost:1234/path") + event_loop.run_until_complete(connector.close()) - def test_proxy_auth_property(self) -> None: - req = aiohttp.ClientRequest( - "GET", - URL("http://localhost:1234/path"), - proxy=URL("http://proxy.example.com"), - proxy_auth=aiohttp.helpers.BasicAuth("user", "pass"), - loop=self.loop, - ) - self.assertEqual(("user", "pass", "latin1"), req.proxy_auth) - def test_proxy_auth_property_default(self) -> None: - req = aiohttp.ClientRequest( - "GET", - URL("http://localhost:1234/path"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - self.assertIsNone(req.proxy_auth) +def test_proxy_auth_property(event_loop: asyncio.AbstractEventLoop) -> None: + req = aiohttp.ClientRequest( + "GET", + URL("http://localhost:1234/path"), + proxy=URL("http://proxy.example.com"), + proxy_auth=aiohttp.helpers.BasicAuth("user", "pass"), + loop=event_loop, + ) + assert ("user", "pass", "latin1") == req.proxy_auth - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, + +def test_proxy_auth_property_default(event_loop: asyncio.AbstractEventLoop) -> None: + req = aiohttp.ClientRequest( + "GET", + URL("http://localhost:1234/path"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, ) - def test_https_connect_pass_ssl_context( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> 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=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp - ): - with mock.patch.object(proxy_resp, "start", autospec=True) as m: - m.return_value.status = 200 - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } + assert req.proxy_auth is None + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_https_connect_pass_ssl_context( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest("GET", URL("http://proxy.example.com"), loop=event_loop) + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), + ) + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True) as m: + m.return_value.status = 200 + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r] + ): + tr, proto = mock.Mock(), mock.Mock() with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), ): - tr, proto = mock.Mock(), mock.Mock() with mock.patch.object( - self.loop, - "create_connection", + event_loop, + "start_tls", autospec=True, - return_value=(tr, proto), - ): - with mock.patch.object( - self.loop, - "start_tls", - autospec=True, - return_value=mock.Mock(), - ) as tls_m: - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) - ) - - # ssl_shutdown_timeout=0 is not passed to start_tls - tls_m.assert_called_with( - mock.ANY, - mock.ANY, - _SSL_CONTEXT_VERIFIED, - server_hostname="www.python.org", - ssl_handshake_timeout=mock.ANY, + return_value=mock.Mock(), + ) as tls_m: + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + event_loop.run_until_complete( + connector._create_connection( + req, [], aiohttp.ClientTimeout() ) + ) - self.assertEqual(req.url.path, "/") - self.assertEqual(proxy_req.method, "CONNECT") - self.assertEqual( - proxy_req.url, URL("https://www.python.org") - ) + # ssl_shutdown_timeout=0 is not passed to start_tls + tls_m.assert_called_with( + mock.ANY, + mock.ANY, + _SSL_CONTEXT_VERIFIED, + server_hostname="www.python.org", + ssl_handshake_timeout=mock.ANY, + ) - self.loop.run_until_complete(proxy_req.close()) - proxy_resp.close() - self.loop.run_until_complete(req.close()) - self.loop.run_until_complete(connector.close()) + assert req.url.path == "/" + assert proxy_req.method == "CONNECT" + assert proxy_req.url == URL("https://www.python.org") - @mock.patch("aiohttp.connector.ClientRequest") - @mock.patch( - "aiohttp.connector.aiohappyeyeballs.start_connection", - autospec=True, - spec_set=True, + event_loop.run_until_complete(proxy_req.close()) + proxy_resp.close() + event_loop.run_until_complete(req.close()) + event_loop.run_until_complete(connector.close()) + + +@mock.patch("aiohttp.connector.ClientRequest") +@mock.patch( + "aiohttp.connector.aiohappyeyeballs.start_connection", + autospec=True, + spec_set=True, +) +def test_https_auth( # type: ignore[misc] + start_connection: mock.Mock, + ClientRequestMock: mock.Mock, + event_loop: asyncio.AbstractEventLoop, +) -> None: + proxy_req = ClientRequest( + "GET", + URL("http://proxy.example.com"), + auth=aiohttp.helpers.BasicAuth("user", "pass"), + loop=event_loop, ) - def test_https_auth( # type: ignore[misc] - self, start_connection: mock.Mock, ClientRequestMock: mock.Mock - ) -> None: - proxy_req = ClientRequest( - "GET", - URL("http://proxy.example.com"), - auth=aiohttp.helpers.BasicAuth("user", "pass"), - loop=self.loop, - ) - ClientRequestMock.return_value = proxy_req - - proxy_resp = ClientResponse( - "get", - URL("http://proxy.example.com"), - request_info=mock.Mock(), - writer=None, - continue100=None, - timer=TimerNoop(), - traces=[], - loop=self.loop, - session=mock.Mock(), - ) - with mock.patch.object( - proxy_req, "send", autospec=True, return_value=proxy_resp - ): - with mock.patch.object(proxy_resp, "start", autospec=True) as m: - m.return_value.status = 200 - - async def make_conn() -> aiohttp.TCPConnector: - return aiohttp.TCPConnector() - - connector = self.loop.run_until_complete(make_conn()) - r = { - "hostname": "hostname", - "host": "127.0.0.1", - "port": 80, - "family": socket.AF_INET, - "proto": 0, - "flags": 0, - } + ClientRequestMock.return_value = proxy_req + + proxy_resp = ClientResponse( + "get", + URL("http://proxy.example.com"), + request_info=mock.Mock(), + writer=None, + continue100=None, + timer=TimerNoop(), + traces=[], + loop=event_loop, + session=mock.Mock(), + ) + with mock.patch.object(proxy_req, "send", autospec=True, return_value=proxy_resp): + with mock.patch.object(proxy_resp, "start", autospec=True) as m: + m.return_value.status = 200 + + async def make_conn() -> aiohttp.TCPConnector: + return aiohttp.TCPConnector() + + connector = event_loop.run_until_complete(make_conn()) + r = { + "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, return_value=[r] + ) as host_m: + tr, proto = mock.Mock(), mock.Mock() with mock.patch.object( - connector, "_resolve_host", autospec=True, return_value=[r] - ) as host_m: - tr, proto = mock.Mock(), mock.Mock() + event_loop, + "create_connection", + autospec=True, + return_value=(tr, proto), + ): with mock.patch.object( - self.loop, - "create_connection", + event_loop, + "start_tls", autospec=True, - return_value=(tr, proto), + return_value=mock.Mock(), ): - with mock.patch.object( - self.loop, - "start_tls", - autospec=True, - return_value=mock.Mock(), - ): - self.assertIn("AUTHORIZATION", proxy_req.headers) - self.assertNotIn("PROXY-AUTHORIZATION", proxy_req.headers) - - req = ClientRequest( - "GET", - URL("https://www.python.org"), - proxy=URL("http://proxy.example.com"), - loop=self.loop, - ) - self.assertNotIn("AUTHORIZATION", req.headers) - self.assertNotIn("PROXY-AUTHORIZATION", req.headers) - self.loop.run_until_complete( - connector._create_connection( - req, [], aiohttp.ClientTimeout() - ) + assert "AUTHORIZATION" in proxy_req.headers + assert "PROXY-AUTHORIZATION" not in proxy_req.headers + + req = ClientRequest( + "GET", + URL("https://www.python.org"), + proxy=URL("http://proxy.example.com"), + loop=event_loop, + ) + assert "AUTHORIZATION" not in req.headers + assert "PROXY-AUTHORIZATION" not in req.headers + event_loop.run_until_complete( + connector._create_connection( + req, [], aiohttp.ClientTimeout() ) + ) - self.assertEqual(req.url.path, "/") - self.assertNotIn("AUTHORIZATION", req.headers) - self.assertNotIn("PROXY-AUTHORIZATION", req.headers) - self.assertNotIn("AUTHORIZATION", proxy_req.headers) - self.assertIn("PROXY-AUTHORIZATION", proxy_req.headers) + assert req.url.path == "/" + assert "AUTHORIZATION" not in req.headers + assert "PROXY-AUTHORIZATION" not in req.headers + assert "AUTHORIZATION" not in proxy_req.headers + assert "PROXY-AUTHORIZATION" in proxy_req.headers - host_m.assert_called_with( - "proxy.example.com", 80, traces=mock.ANY - ) + host_m.assert_called_with( + "proxy.example.com", 80, traces=mock.ANY + ) - self.loop.run_until_complete(proxy_req.close()) - proxy_resp.close() - self.loop.run_until_complete(req.close()) - self.loop.run_until_complete(connector.close()) + event_loop.run_until_complete(proxy_req.close()) + proxy_resp.close() + event_loop.run_until_complete(req.close()) + event_loop.run_until_complete(connector.close())