|
1 | 1 | # Tests of http client with custom Connector |
2 | 2 | import asyncio |
| 3 | +import contextlib |
3 | 4 | import gc |
4 | 5 | import hashlib |
5 | 6 | import platform |
|
16 | 17 | from unittest import mock |
17 | 18 |
|
18 | 19 | import pytest |
| 20 | +from multidict import CIMultiDict |
19 | 21 | from pytest_mock import MockerFixture |
20 | 22 | from yarl import URL |
21 | 23 |
|
|
25 | 27 | ClientSession, |
26 | 28 | ClientTimeout, |
27 | 29 | connector as connector_module, |
| 30 | + hdrs, |
28 | 31 | web, |
29 | 32 | ) |
30 | 33 | from aiohttp.abc import ResolveResult |
@@ -3299,6 +3302,93 @@ async def test_connect_reuseconn_tracing( |
3299 | 3302 | await conn.close() |
3300 | 3303 |
|
3301 | 3304 |
|
| 3305 | +@pytest.mark.parametrize( |
| 3306 | + "test_case,wait_for_con,expect_proxy_auth_header", |
| 3307 | + [ |
| 3308 | + ("use_proxy_with_embedded_auth", False, True), |
| 3309 | + ("use_proxy_with_auth_headers", True, True), |
| 3310 | + ("use_proxy_no_auth", False, False), |
| 3311 | + ("dont_use_proxy", False, False), |
| 3312 | + ], |
| 3313 | +) |
| 3314 | +async def test_connect_reuse_proxy_headers( # type: ignore[misc] |
| 3315 | + loop: asyncio.AbstractEventLoop, |
| 3316 | + make_client_request: _RequestMaker, |
| 3317 | + test_case: str, |
| 3318 | + wait_for_con: bool, |
| 3319 | + expect_proxy_auth_header: bool, |
| 3320 | +) -> None: |
| 3321 | + proto = create_mocked_conn(loop) |
| 3322 | + proto.is_connected.return_value = True |
| 3323 | + |
| 3324 | + if test_case != "dont_use_proxy": |
| 3325 | + proxy = ( |
| 3326 | + URL( "http://user:[email protected]") |
| 3327 | + if test_case == "use_proxy_with_embedded_auth" |
| 3328 | + else URL("http://example.com") |
| 3329 | + ) |
| 3330 | + proxy_headers = ( |
| 3331 | + CIMultiDict({hdrs.AUTHORIZATION: "Basic dXNlcjpwYXNzd29yZA=="}) |
| 3332 | + if test_case == "use_proxy_with_auth_headers" |
| 3333 | + else None |
| 3334 | + ) |
| 3335 | + else: |
| 3336 | + proxy = None |
| 3337 | + proxy_headers = None |
| 3338 | + key = ConnectionKey( |
| 3339 | + "localhost", |
| 3340 | + 80, |
| 3341 | + False, |
| 3342 | + True, |
| 3343 | + proxy, |
| 3344 | + None, |
| 3345 | + hash(tuple(proxy_headers.items())) if proxy_headers else None, |
| 3346 | + ) |
| 3347 | + req = make_client_request( |
| 3348 | + "GET", |
| 3349 | + URL("http://localhost:80"), |
| 3350 | + loop=loop, |
| 3351 | + response_class=mock.Mock(), |
| 3352 | + proxy=proxy, |
| 3353 | + proxy_headers=proxy_headers, |
| 3354 | + ) |
| 3355 | + |
| 3356 | + conn = aiohttp.BaseConnector(limit=1) |
| 3357 | + |
| 3358 | + async def _create_con(*args: Any, **kwargs: Any) -> None: |
| 3359 | + conn._conns[key] = deque([(proto, loop.time())]) |
| 3360 | + |
| 3361 | + with contextlib.ExitStack() as stack: |
| 3362 | + if wait_for_con: |
| 3363 | + # Simulate no available connections |
| 3364 | + stack.enter_context( |
| 3365 | + mock.patch.object( |
| 3366 | + conn, "_available_connections", autospec=True, return_value=0 |
| 3367 | + ) |
| 3368 | + ) |
| 3369 | + # Upon waiting for a connection, populate _conns with our proto, |
| 3370 | + # mocking a connection becoming immediately available |
| 3371 | + stack.enter_context( |
| 3372 | + mock.patch.object( |
| 3373 | + conn, |
| 3374 | + "_wait_for_available_connection", |
| 3375 | + autospec=True, |
| 3376 | + side_effect=_create_con, |
| 3377 | + ) |
| 3378 | + ) |
| 3379 | + else: |
| 3380 | + await _create_con() |
| 3381 | + # Call function to test |
| 3382 | + conn2 = await conn.connect(req, [], ClientTimeout()) |
| 3383 | + conn2.release() |
| 3384 | + await conn.close() |
| 3385 | + |
| 3386 | + if expect_proxy_auth_header: |
| 3387 | + assert req.headers[hdrs.PROXY_AUTHORIZATION] == "Basic dXNlcjpwYXNzd29yZA==" |
| 3388 | + else: |
| 3389 | + assert hdrs.PROXY_AUTHORIZATION not in req.headers |
| 3390 | + |
| 3391 | + |
3302 | 3392 | async def test_connect_with_limit_and_limit_per_host( |
3303 | 3393 | loop: asyncio.AbstractEventLoop, |
3304 | 3394 | key: ConnectionKey, |
|
0 commit comments