Path: blob/trunk/py/test/unit/selenium/webdriver/remote/remote_connection_tests.py
1990 views
# Licensed to the Software Freedom Conservancy (SFC) under one1# or more contributor license agreements. See the NOTICE file2# distributed with this work for additional information3# regarding copyright ownership. The SFC licenses this file4# to you under the Apache License, Version 2.0 (the5# "License"); you may not use this file except in compliance6# with the License. You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing,11# software distributed under the License is distributed on an12# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13# KIND, either express or implied. See the License for the14# specific language governing permissions and limitations15# under the License.1617import base6418import os19from unittest.mock import patch20from urllib import parse2122import pytest23from urllib3 import PoolManager, ProxyManager, make_headers24from urllib3.contrib.socks import SOCKSProxyManager25from urllib3.util import Retry, Timeout2627from selenium import __version__28from selenium.webdriver import Proxy29from selenium.webdriver.common.proxy import ProxyType30from selenium.webdriver.remote.client_config import AuthType31from selenium.webdriver.remote.remote_connection import ClientConfig, RemoteConnection323334@pytest.fixture35def remote_connection():36"""Fixture to create a RemoteConnection instance."""37return RemoteConnection("http://localhost:4444")383940def test_add_command(remote_connection):41"""Test adding a custom command to the connection."""42remote_connection.add_command("CUSTOM_COMMAND", "PUT", "/session/$sessionId/custom")43assert remote_connection.get_command("CUSTOM_COMMAND") == ("PUT", "/session/$sessionId/custom")444546@patch("selenium.webdriver.remote.remote_connection.RemoteConnection._request")47def test_execute_custom_command(mock_request, remote_connection):48"""Test executing a custom command through the connection."""49remote_connection.add_command("CUSTOM_COMMAND", "PUT", "/session/$sessionId/custom")50mock_request.return_value = {"status": 200, "value": "OK"}5152params = {"sessionId": "12345"}53response = remote_connection.execute("CUSTOM_COMMAND", params)5455mock_request.assert_called_once_with("PUT", "http://localhost:4444/session/12345/custom", body="{}")56assert response == {"status": 200, "value": "OK"}575859def test_get_remote_connection_headers_defaults():60url = "http://remote"61headers = RemoteConnection.get_remote_connection_headers(parse.urlparse(url))62assert "Authorization" not in headers63assert "Connection" not in headers64assert headers.get("Accept") == "application/json"65assert headers.get("Content-Type") == "application/json;charset=UTF-8"66assert headers.get("User-Agent").startswith(f"selenium/{__version__} (python ")67assert headers.get("User-Agent").split(" ")[-1].rstrip(")") in ("win32", "windows", "mac", "linux")686970def test_get_remote_connection_headers_adds_auth_header_if_pass(recwarn):71url = "http://user:pass@remote"72headers = RemoteConnection.get_remote_connection_headers(parse.urlparse(url))73assert headers.get("Authorization") == "Basic dXNlcjpwYXNz"74assert (75recwarn[0].message.args[0]76== "Embedding username and password in URL could be insecure, use ClientConfig instead"77)787980def test_get_remote_connection_headers_adds_keep_alive_if_requested():81url = "http://remote"82headers = RemoteConnection.get_remote_connection_headers(parse.urlparse(url), keep_alive=True)83assert headers.get("Connection") == "keep-alive"848586def test_get_proxy_url_http(mock_proxy_settings):87proxy = "http://http_proxy.com:8080"88remote_connection = RemoteConnection("http://remote", keep_alive=False)89proxy_url = remote_connection.client_config.get_proxy_url()90assert proxy_url == proxy919293def test_get_auth_header_if_client_config_pass_basic_auth():94custom_config = ClientConfig(95remote_server_addr="http://localhost:4444",96keep_alive=True,97username="user",98password="pass",99auth_type=AuthType.BASIC,100)101remote_connection = RemoteConnection(custom_config.remote_server_addr, client_config=custom_config)102headers = remote_connection.client_config.get_auth_header()103assert headers.get("Authorization") == "Basic dXNlcjpwYXNz"104105106def test_get_auth_header_if_client_config_pass_bearer_token():107custom_config = ClientConfig(108remote_server_addr="http://localhost:4444", keep_alive=True, auth_type=AuthType.BEARER, token="dXNlcjpwYXNz"109)110remote_connection = RemoteConnection(custom_config.remote_server_addr, client_config=custom_config)111headers = remote_connection.client_config.get_auth_header()112assert headers.get("Authorization") == "Bearer dXNlcjpwYXNz"113114115def test_get_auth_header_if_client_config_pass_x_api_key():116custom_config = ClientConfig(117remote_server_addr="http://localhost:4444",118keep_alive=True,119auth_type=AuthType.X_API_KEY,120token="abcdefgh123456789",121)122remote_connection = RemoteConnection(custom_config.remote_server_addr, client_config=custom_config)123headers = remote_connection.client_config.get_auth_header()124assert headers.get("X-API-Key") == "abcdefgh123456789"125126127def test_get_proxy_url_https(mock_proxy_settings):128proxy = "http://https_proxy.com:8080"129remote_connection = RemoteConnection("https://remote", keep_alive=False)130proxy_url = remote_connection.client_config.get_proxy_url()131assert proxy_url == proxy132133134def test_get_proxy_url_https_via_client_config():135client_config = ClientConfig(136remote_server_addr="https://localhost:4444",137proxy=Proxy({"proxyType": ProxyType.MANUAL, "sslProxy": "https://admin:admin@http_proxy.com:8080"}),138)139remote_connection = RemoteConnection(client_config=client_config)140conn = remote_connection._get_connection_manager()141assert isinstance(conn, ProxyManager)142conn.proxy_url = "https://http_proxy.com:8080"143conn.connection_pool_kw["proxy_headers"] = make_headers(proxy_basic_auth="admin:admin")144145146def test_get_proxy_url_http_via_client_config():147client_config = ClientConfig(148remote_server_addr="http://localhost:4444",149proxy=Proxy(150{151"proxyType": ProxyType.MANUAL,152"httpProxy": "http://admin:admin@http_proxy.com:8080",153"sslProxy": "https://admin:admin@http_proxy.com:8080",154}155),156)157remote_connection = RemoteConnection(client_config=client_config)158conn = remote_connection._get_connection_manager()159assert isinstance(conn, ProxyManager)160conn.proxy_url = "http://http_proxy.com:8080"161conn.connection_pool_kw["proxy_headers"] = make_headers(proxy_basic_auth="admin:admin")162163164def test_get_proxy_direct_via_client_config():165client_config = ClientConfig(166remote_server_addr="http://localhost:4444", proxy=Proxy({"proxyType": ProxyType.DIRECT})167)168remote_connection = RemoteConnection(client_config=client_config)169conn = remote_connection._get_connection_manager()170assert isinstance(conn, PoolManager)171proxy_url = remote_connection.client_config.get_proxy_url()172assert proxy_url is None173174175def test_get_proxy_system_matches_no_proxy_via_client_config():176with patch.dict(177"os.environ", {"HTTP_PROXY": "http://admin:admin@system_proxy.com:8080", "NO_PROXY": "localhost,127.0.0.1"}178):179client_config = ClientConfig(180remote_server_addr="http://localhost:4444", proxy=Proxy({"proxyType": ProxyType.SYSTEM})181)182remote_connection = RemoteConnection(client_config=client_config)183conn = remote_connection._get_connection_manager()184assert isinstance(conn, PoolManager)185proxy_url = remote_connection.client_config.get_proxy_url()186assert proxy_url is None187188189def test_get_proxy_url_none(mock_proxy_settings_missing):190remote_connection = RemoteConnection("https://remote", keep_alive=False)191proxy_url = remote_connection.client_config.get_proxy_url()192assert proxy_url is None193194195def test_get_proxy_url_http_auth(mock_proxy_auth_settings):196remote_connection = RemoteConnection("http://remote", keep_alive=False)197proxy_url = remote_connection.client_config.get_proxy_url()198raw_proxy_url, basic_auth_string = remote_connection._separate_http_proxy_auth()199assert proxy_url == "http://user:password@http_proxy.com:8080"200assert raw_proxy_url == "http://http_proxy.com:8080"201assert basic_auth_string == "user:password"202203204def test_get_proxy_url_https_auth(mock_proxy_auth_settings):205remote_connection = RemoteConnection("https://remote", keep_alive=False)206proxy_url = remote_connection.client_config.get_proxy_url()207raw_proxy_url, basic_auth_string = remote_connection._separate_http_proxy_auth()208assert proxy_url == "https://user:password@https_proxy.com:8080"209assert raw_proxy_url == "https://https_proxy.com:8080"210assert basic_auth_string == "user:password"211212213def test_get_connection_manager_without_proxy(mock_proxy_settings_missing):214remote_connection = RemoteConnection("http://remote", keep_alive=False)215conn = remote_connection._get_connection_manager()216assert isinstance(conn, PoolManager)217218219def test_get_connection_manager_for_certs_and_timeout():220remote_connection = RemoteConnection("http://remote", keep_alive=False)221remote_connection.set_timeout(10)222assert remote_connection.get_timeout() == 10223conn = remote_connection._get_connection_manager()224assert conn.connection_pool_kw["timeout"] == 10225assert conn.connection_pool_kw["cert_reqs"] == "CERT_REQUIRED"226assert f"certifi{os.path.sep}cacert.pem" in conn.connection_pool_kw["ca_certs"]227228229def test_default_socket_timeout_is_correct():230remote_connection = RemoteConnection("http://remote", keep_alive=True)231conn = remote_connection._get_connection_manager()232assert conn.connection_pool_kw["timeout"] is None233234235def test_get_connection_manager_with_http_proxy(mock_proxy_settings):236remote_connection = RemoteConnection("http://remote", keep_alive=False)237conn = remote_connection._get_connection_manager()238assert isinstance(conn, ProxyManager)239assert conn.proxy.scheme == "http"240assert conn.proxy.host == "http_proxy.com"241assert conn.proxy.port == 8080242243244def test_get_connection_manager_with_https_proxy(mock_proxy_settings):245remote_connection_https = RemoteConnection("https://remote", keep_alive=False)246conn = remote_connection_https._get_connection_manager()247assert isinstance(conn, ProxyManager)248assert conn.proxy.scheme == "http"249assert conn.proxy.host == "https_proxy.com"250assert conn.proxy.port == 8080251252253def test_get_connection_manager_with_auth_http_proxy(mock_proxy_auth_settings):254proxy_auth_header = make_headers(proxy_basic_auth="user:password")255remote_connection = RemoteConnection("http://remote", keep_alive=False)256conn = remote_connection._get_connection_manager()257assert isinstance(conn, ProxyManager)258assert conn.proxy.scheme == "http"259assert conn.proxy.host == "http_proxy.com"260assert conn.proxy.port == 8080261assert conn.proxy_headers == proxy_auth_header262263264def test_get_connection_manager_with_auth_https_proxy(mock_proxy_auth_settings):265proxy_auth_header = make_headers(proxy_basic_auth="user:password")266remote_connection_https = RemoteConnection("https://remote", keep_alive=False)267conn = remote_connection_https._get_connection_manager()268assert isinstance(conn, ProxyManager)269assert conn.proxy.scheme == "https"270assert conn.proxy.host == "https_proxy.com"271assert conn.proxy.port == 8080272assert conn.proxy_headers == proxy_auth_header273274275@pytest.mark.parametrize(276"url",277[278"*",279".localhost",280"localhost:80",281"localhost",282"LOCALHOST",283"LOCALHOST:80",284"http://localhost",285"https://localhost",286"test.localhost",287" localhost",288"127.0.0.1",289"127.0.0.2",290"::1",291],292)293def test_get_connection_manager_when_no_proxy_set(mock_no_proxy_settings, url):294remote_connection = RemoteConnection(url)295conn = remote_connection._get_connection_manager()296assert isinstance(conn, PoolManager)297298299def test_ignore_proxy_env_vars(mock_proxy_settings):300remote_connection = RemoteConnection("http://remote", ignore_proxy=True)301conn = remote_connection._get_connection_manager()302assert isinstance(conn, PoolManager)303304305def test_get_socks_proxy_when_set(mock_socks_proxy_settings):306remote_connection = RemoteConnection("http://remote")307conn = remote_connection._get_connection_manager()308309assert isinstance(conn, SOCKSProxyManager)310311312class MockResponse:313code = 200314headers = []315316def read(self):317return b"{}"318319def close(self):320pass321322def getheader(self, *args, **kwargs):323pass324325326@pytest.fixture(scope="function")327def mock_proxy_settings_missing(monkeypatch):328monkeypatch.delenv("HTTPS_PROXY", raising=False)329monkeypatch.delenv("HTTP_PROXY", raising=False)330monkeypatch.delenv("https_proxy", raising=False)331monkeypatch.delenv("http_proxy", raising=False)332333334@pytest.fixture(scope="function")335def mock_socks_proxy_settings(monkeypatch):336http_proxy = "SOCKS5://http_proxy.com:8080"337https_proxy = "SOCKS5://https_proxy.com:8080"338monkeypatch.setenv("HTTPS_PROXY", https_proxy)339monkeypatch.setenv("HTTP_PROXY", http_proxy)340monkeypatch.setenv("https_proxy", https_proxy)341monkeypatch.setenv("http_proxy", http_proxy)342343344@pytest.fixture(scope="function")345def mock_proxy_settings(monkeypatch):346http_proxy = "http://http_proxy.com:8080"347https_proxy = "http://https_proxy.com:8080"348monkeypatch.setenv("HTTPS_PROXY", https_proxy)349monkeypatch.setenv("HTTP_PROXY", http_proxy)350monkeypatch.setenv("https_proxy", https_proxy)351monkeypatch.setenv("http_proxy", http_proxy)352353354@pytest.fixture(scope="function")355def mock_proxy_auth_settings(monkeypatch):356http_proxy = "http://user:password@http_proxy.com:8080"357https_proxy = "https://user:password@https_proxy.com:8080"358monkeypatch.setenv("HTTPS_PROXY", https_proxy)359monkeypatch.setenv("HTTP_PROXY", http_proxy)360monkeypatch.setenv("https_proxy", https_proxy)361monkeypatch.setenv("http_proxy", http_proxy)362363364@pytest.fixture(scope="function")365def mock_no_proxy_settings(monkeypatch):366http_proxy = "http://http_proxy.com:8080"367https_proxy = "http://https_proxy.com:8080"368monkeypatch.setenv("HTTPS_PROXY", https_proxy)369monkeypatch.setenv("HTTP_PROXY", http_proxy)370monkeypatch.setenv("https_proxy", https_proxy)371monkeypatch.setenv("http_proxy", http_proxy)372monkeypatch.setenv("no_proxy", "65.253.214.253,localhost,127.0.0.1,*zyz.xx,::1")373monkeypatch.setenv("NO_PROXY", "65.253.214.253,localhost,127.0.0.1,*zyz.xx,::1,127.0.0.0/8")374375376@patch("selenium.webdriver.remote.remote_connection.RemoteConnection.get_remote_connection_headers")377def test_override_user_agent_in_headers(mock_get_remote_connection_headers, remote_connection):378RemoteConnection.user_agent = "custom-agent/1.0 (python 3.13)"379380mock_get_remote_connection_headers.return_value = {381"Accept": "application/json",382"Content-Type": "application/json;charset=UTF-8",383"User-Agent": "custom-agent/1.0 (python 3.13)",384}385386headers = RemoteConnection.get_remote_connection_headers(parse.urlparse("http://remote"))387388assert headers.get("User-Agent") == "custom-agent/1.0 (python 3.13)"389assert headers.get("Accept") == "application/json"390assert headers.get("Content-Type") == "application/json;charset=UTF-8"391392393@patch("selenium.webdriver.remote.remote_connection.RemoteConnection.get_remote_connection_headers")394def test_override_user_agent_via_client_config(mock_get_remote_connection_headers):395client_config = ClientConfig(396remote_server_addr="http://localhost:4444",397user_agent="custom-agent/1.0 (python 3.13)",398extra_headers={"Content-Type": "application/xml;charset=UTF-8"},399)400remote_connection = RemoteConnection(client_config=client_config)401402mock_get_remote_connection_headers.return_value = {403"Accept": "application/json",404"Content-Type": "application/xml;charset=UTF-8",405"User-Agent": "custom-agent/1.0 (python 3.13)",406}407408headers = remote_connection.get_remote_connection_headers(parse.urlparse("http://localhost:4444"))409410assert headers.get("User-Agent") == "custom-agent/1.0 (python 3.13)"411assert headers.get("Accept") == "application/json"412assert headers.get("Content-Type") == "application/xml;charset=UTF-8"413414415@patch("selenium.webdriver.remote.remote_connection.RemoteConnection._request")416def test_register_extra_headers(mock_request, remote_connection):417RemoteConnection.extra_headers = {"Foo": "bar"}418419mock_request.return_value = {"status": 200, "value": "OK"}420remote_connection.execute("newSession", {})421422mock_request.assert_called_once_with("POST", "http://localhost:4444/session", body="{}")423headers = RemoteConnection.get_remote_connection_headers(parse.urlparse("http://localhost:4444"), False)424assert headers["Foo"] == "bar"425426427@patch("selenium.webdriver.remote.remote_connection.RemoteConnection._request")428def test_register_extra_headers_via_client_config(mock_request):429client_config = ClientConfig(430remote_server_addr="http://localhost:4444",431extra_headers={432"Authorization": "AWS4-HMAC-SHA256",433"Credential": "abc/20200618/us-east-1/execute-api/aws4_request",434},435)436remote_connection = RemoteConnection(client_config=client_config)437438mock_request.return_value = {"status": 200, "value": "OK"}439remote_connection.execute("newSession", {})440441mock_request.assert_called_once_with("POST", "http://localhost:4444/session", body="{}")442headers = remote_connection.get_remote_connection_headers(parse.urlparse("http://localhost:4444"), False)443assert headers["Authorization"] == "AWS4-HMAC-SHA256"444assert headers["Credential"] == "abc/20200618/us-east-1/execute-api/aws4_request"445446447def test_backwards_compatibility_with_appium_connection():448# Keep backward compatibility for AppiumConnection - https://github.com/SeleniumHQ/selenium/issues/14694449client_config = ClientConfig(450remote_server_addr="http://localhost:4444", ca_certs="/path/to/cacert.pem", timeout=300451)452remote_connection = RemoteConnection(client_config=client_config)453assert remote_connection._ca_certs == "/path/to/cacert.pem"454assert remote_connection._timeout == 300455assert remote_connection._client_config == client_config456remote_connection.set_timeout(120)457assert remote_connection.get_timeout() == 120458remote_connection.set_certificate_bundle_path("/path/to/cacert2.pem")459assert remote_connection.get_certificate_bundle_path() == "/path/to/cacert2.pem"460461462def test_get_connection_manager_with_timeout_from_client_config():463remote_connection = RemoteConnection(remote_server_addr="http://localhost:4444", keep_alive=False)464remote_connection.set_timeout(10)465conn = remote_connection._get_connection_manager()466assert remote_connection.get_timeout() == 10467assert conn.connection_pool_kw["timeout"] == 10468assert isinstance(conn, PoolManager)469470471def test_connection_manager_with_timeout_via_client_config():472client_config = ClientConfig("http://remote", timeout=300)473remote_connection = RemoteConnection(client_config=client_config)474conn = remote_connection._get_connection_manager()475assert conn.connection_pool_kw["timeout"] == 300476assert isinstance(conn, PoolManager)477478479def test_get_connection_manager_with_ca_certs():480remote_connection = RemoteConnection(remote_server_addr="http://localhost:4444")481remote_connection.set_certificate_bundle_path("/path/to/cacert.pem")482conn = remote_connection._get_connection_manager()483assert conn.connection_pool_kw["timeout"] is None484assert conn.connection_pool_kw["cert_reqs"] == "CERT_REQUIRED"485assert conn.connection_pool_kw["ca_certs"] == "/path/to/cacert.pem"486assert isinstance(conn, PoolManager)487488489def test_connection_manager_with_ca_certs_via_client_config():490client_config = ClientConfig(remote_server_addr="http://localhost:4444", ca_certs="/path/to/cacert.pem")491remote_connection = RemoteConnection(client_config=client_config)492conn = remote_connection._get_connection_manager()493assert conn.connection_pool_kw["timeout"] is None494assert conn.connection_pool_kw["cert_reqs"] == "CERT_REQUIRED"495assert conn.connection_pool_kw["ca_certs"] == "/path/to/cacert.pem"496assert isinstance(conn, PoolManager)497498499def test_get_connection_manager_ignores_certificates():500remote_connection = RemoteConnection(501remote_server_addr="http://localhost:4444", keep_alive=False, ignore_certificates=True502)503remote_connection.set_timeout(10)504conn = remote_connection._get_connection_manager()505assert conn.connection_pool_kw["timeout"] == 10506assert conn.connection_pool_kw["cert_reqs"] == "CERT_NONE"507assert isinstance(conn, PoolManager)508remote_connection.reset_timeout()509assert remote_connection.get_timeout() is None510511512def test_connection_manager_ignores_certificates_via_client_config():513client_config = ClientConfig(remote_server_addr="http://localhost:4444", ignore_certificates=True, timeout=10)514remote_connection = RemoteConnection(client_config=client_config)515conn = remote_connection._get_connection_manager()516assert isinstance(conn, PoolManager)517assert conn.connection_pool_kw["timeout"] == 10518assert conn.connection_pool_kw["cert_reqs"] == "CERT_NONE"519520521def test_get_connection_manager_with_custom_args():522custom_args = {"init_args_for_pool_manager": {"retries": 3, "block": True}}523524remote_connection = RemoteConnection(525remote_server_addr="http://localhost:4444", keep_alive=False, init_args_for_pool_manager=custom_args526)527conn = remote_connection._get_connection_manager()528assert isinstance(conn, PoolManager)529assert isinstance(conn.connection_pool_kw["retries"], Retry)530assert conn.connection_pool_kw["retries"].total == 3531assert conn.connection_pool_kw["block"] is True532assert conn.connection_pool_kw["timeout"] is None533534535def test_connection_manager_with_custom_args_via_client_config():536retries = Retry(connect=2, read=2, redirect=2)537timeout = Timeout(connect=300, read=3600)538client_config = ClientConfig(539remote_server_addr="http://localhost:4444",540init_args_for_pool_manager={"init_args_for_pool_manager": {"retries": retries, "timeout": timeout}},541)542remote_connection = RemoteConnection(client_config=client_config)543conn = remote_connection._get_connection_manager()544assert isinstance(conn, PoolManager)545assert conn.connection_pool_kw["retries"] == retries546assert conn.connection_pool_kw["timeout"] == timeout547548549def test_proxy_auth_with_special_characters_url_encoded():550proxy_url = "http://user:passw%[email protected]:8080"551client_config = ClientConfig(552remote_server_addr="http://localhost:4444",553keep_alive=False,554proxy=Proxy({"proxyType": ProxyType.MANUAL, "httpProxy": proxy_url}),555)556remote_connection = RemoteConnection(client_config=client_config)557558proxy_without_auth, basic_auth = remote_connection._separate_http_proxy_auth()559560assert proxy_without_auth == "http://proxy.example.com:8080"561assert basic_auth == "user:passw%23rd" # Still URL-encoded562563conn = remote_connection._get_connection_manager()564assert isinstance(conn, ProxyManager)565566expected_auth = base64.b64encode("user:passw#rd".encode()).decode() # Decoded password567expected_headers = make_headers(proxy_basic_auth="user:passw#rd") # Unquoted password568569assert conn.proxy_headers == expected_headers570assert conn.proxy_headers["proxy-authorization"] == f"Basic {expected_auth}"571572573def test_proxy_auth_with_multiple_special_characters():574test_cases = [575("passw%23rd", "passw#rd"), # # character576("passw%40rd", "passw@rd"), # @ character577("passw%26rd", "passw&rd"), # & character578("passw%3Drd", "passw=rd"), # = character579("passw%2Brd", "passw+rd"), # + character580("passw%20rd", "passw rd"), # space character581("passw%21%40%23%24", "passw!@#$"), # Multiple special chars582]583584for encoded_password, decoded_password in test_cases:585proxy_url = f"http://testuser:{encoded_password}@proxy.example.com:8080"586client_config = ClientConfig(587remote_server_addr="http://localhost:4444",588keep_alive=False,589proxy=Proxy({"proxyType": ProxyType.MANUAL, "httpProxy": proxy_url}),590)591remote_connection = RemoteConnection(client_config=client_config)592593proxy_without_auth, basic_auth = remote_connection._separate_http_proxy_auth()594assert basic_auth == f"testuser:{encoded_password}"595596conn = remote_connection._get_connection_manager()597expected_auth = base64.b64encode(f"testuser:{decoded_password}".encode()).decode()598expected_headers = make_headers(proxy_basic_auth=f"testuser:{decoded_password}")599600assert conn.proxy_headers == expected_headers601assert conn.proxy_headers["proxy-authorization"] == f"Basic {expected_auth}"602603604