Test all host formats in one fixture
MODIFY
test/conftest.py
test/conftest.py
@@ -5,7 +5,7 @@ import ssl
import sys
import threading
from pathlib import Path
-from typing import AbstractSet, Generator, NamedTuple, Optional
+from typing import AbstractSet, Any, Generator, NamedTuple, Optional
import pytest
import trustme
@@ -29,10 +29,18 @@ def configure_windows_event_loop() -> None:
class ServerConfig(NamedTuple):
+ scheme: str
host: str
port: int
ca_certs: str
+ @property
+ def base_url(self) -> str:
+ host = self.host
+ if ":" in host:
+ host = f"[{host}]"
+ return f"{self.scheme}://{host}:{self.port}"
+
@contextlib.contextmanager
def run_server_in_thread(
@@ -52,23 +60,43 @@ def run_server_in_thread(
server_thread = threading.Thread(target=io_loop.start)
server_thread.start()
- yield ServerConfig(host, port, ca_cert_path)
+ yield ServerConfig("https", host, port, ca_cert_path)
io_loop.add_callback(server.stop)
io_loop.add_callback(io_loop.stop)
server_thread.join()
-@pytest.fixture
+@pytest.fixture(params=["localhost", "127.0.0.1", "::1"])
+def loopback_host(request: Any) -> Generator[str, None, None]:
+ host = request.param
+ if host == "::1" and not HAS_IPV6:
+ pytest.skip("Test requires IPv6 on loopback")
+ yield host
+
+
+@pytest.fixture()
+def san_server(
+ loopback_host: str, tmp_path_factory: pytest.TempPathFactory
+) -> Generator[ServerConfig, None, None]:
+ tmpdir = tmp_path_factory.mktemp("certs")
+ ca = trustme.CA()
+
+ server_cert = ca.issue_cert(loopback_host)
+
+ with run_server_in_thread("https", loopback_host, tmpdir, ca, server_cert) as cfg:
+ yield cfg
+
+
+@pytest.fixture()
def no_san_server(
- tmp_path_factory: pytest.TempPathFactory,
+ loopback_host: str, tmp_path_factory: pytest.TempPathFactory
) -> Generator[ServerConfig, None, None]:
tmpdir = tmp_path_factory.mktemp("certs")
ca = trustme.CA()
- # only common name, no subject alternative names
- server_cert = ca.issue_cert(common_name="localhost")
+ server_cert = ca.issue_cert(common_name=loopback_host)
- with run_server_in_thread("https", "localhost", tmpdir, ca, server_cert) as cfg:
+ with run_server_in_thread("https", loopback_host, tmpdir, ca, server_cert) as cfg:
yield cfg

MODIFY
test/contrib/test_pyopenssl.py
test/contrib/test_pyopenssl.py
@@ -34,8 +34,7 @@ from ..test_ssl import TestSSL # noqa: E402, F401
from ..test_util import TestUtilSSL # noqa: E402, F401
from ..with_dummyserver.test_https import ( # noqa: E402, F401
TestHTTPS,
- TestHTTPS_IPSAN,
- TestHTTPS_IPV6SAN,
+ TestHTTPS_Hostname,
TestHTTPS_TLSv1,
TestHTTPS_TLSv1_1,
TestHTTPS_TLSv1_2,

MODIFY
test/with_dummyserver/test_https.py
test/with_dummyserver/test_https.py
@@ -32,7 +32,7 @@ from dummyserver.server import (
encrypt_key_pem,
)
from dummyserver.testcase import HTTPSDummyServerTestCase
-from urllib3 import HTTPSConnectionPool
+from urllib3 import HTTPSConnectionPool, PoolManager
from urllib3.connection import RECENT_DATE, VerifiedHTTPSConnection
from urllib3.exceptions import (
ConnectTimeoutError,
@@ -959,42 +959,27 @@ class TestHTTPS_TLSv1_3(TestHTTPS):
certs = TLSv1_3_CERTS
-class TestHTTPS_NoSAN:
- def test_common_name_without_san_fails(self, no_san_server: ServerConfig) -> None:
- with HTTPSConnectionPool(
- no_san_server.host,
- no_san_server.port,
- cert_reqs="CERT_REQUIRED",
- ca_certs=no_san_server.ca_certs,
- ) as https_pool:
- with pytest.raises(MaxRetryError, match="no appropriate subjectAltName"):
- https_pool.request("GET", "/")
-
-
-class TestHTTPS_IPSAN:
- def test_can_validate_ip_san(self, ip_san_server: ServerConfig) -> None:
+class TestHTTPS_Hostname:
+ def test_can_validate_san(self, san_server: ServerConfig) -> None:
"""Ensure that urllib3 can validate SANs with IP addresses in them."""
with HTTPSConnectionPool(
- ip_san_server.host,
- ip_san_server.port,
+ san_server.host,
+ san_server.port,
cert_reqs="CERT_REQUIRED",
- ca_certs=ip_san_server.ca_certs,
+ ca_certs=san_server.ca_certs,
) as https_pool:
r = https_pool.request("GET", "/")
assert r.status == 200
-
-class TestHTTPS_IPV6SAN:
- def test_can_validate_ipv6_san(self, ipv6_san_server: ServerConfig) -> None:
- """Ensure that urllib3 can validate SANs with IPv6 addresses in them."""
+ def test_common_name_without_san_fails(self, no_san_server: ServerConfig) -> None:
with HTTPSConnectionPool(
- "[::1]",
- ipv6_san_server.port,
+ no_san_server.host,
+ no_san_server.port,
cert_reqs="CERT_REQUIRED",
- ca_certs=ipv6_san_server.ca_certs,
+ ca_certs=no_san_server.ca_certs,
) as https_pool:
- r = https_pool.request("GET", "/")
- assert r.status == 200
+ with pytest.raises(MaxRetryError, match="no appropriate subjectAltName"):
+ https_pool.request("GET", "/")
def test_strip_square_brackets_before_validating(
self, ipv6_san_server: ServerConfig
@@ -1008,3 +993,11 @@ class TestHTTPS_IPV6SAN:
) as https_pool:
r = https_pool.request("GET", "/")
assert r.status == 200
+
+ def test_request_from_poolmanager(self, san_server: ServerConfig) -> None:
+ with PoolManager(
+ cert_reqs="CERT_REQUIRED",
+ ca_certs=san_server.ca_certs,
+ ) as pool:
+ r = pool.request("GET", san_server.base_url)
+ assert r.status == 200

Test all host formats in one fixture (#2421)