Skip TLS versions if disabled by OpenSSL config
MODIFY
test/__init__.py
test/__init__.py
@@ -268,34 +268,6 @@ def requires_ssl_context_keyfile_password(test):
return wrapper
-def requiresTLSv1():
- """Test requires TLSv1 available"""
- return pytest.mark.skipif(
- not hasattr(ssl, "PROTOCOL_TLSv1"), reason="Test requires TLSv1"
- )
-
-
-def requiresTLSv1_1():
- """Test requires TLSv1.1 available"""
- return pytest.mark.skipif(
- not hasattr(ssl, "PROTOCOL_TLSv1_1"), reason="Test requires TLSv1.1"
- )
-
-
-def requiresTLSv1_2():
- """Test requires TLSv1.2 available"""
- return pytest.mark.skipif(
- not hasattr(ssl, "PROTOCOL_TLSv1_2"), reason="Test requires TLSv1.2"
- )
-
-
-def requiresTLSv1_3():
- """Test requires TLSv1.3 available"""
- return pytest.mark.skipif(
- not getattr(ssl, "HAS_TLSv1_3", False), reason="Test requires TLSv1.3"
- )
-
-
def resolvesLocalhostFQDN(test):
"""Test requires successful resolving of 'localhost.'"""

MODIFY
test/conftest.py
test/conftest.py
import collections
import contextlib
import platform
+import socket
+import ssl
import sys
import threading
@@ -10,6 +12,8 @@ from tornado import ioloop, web
from dummyserver.handlers import TestingApp
from dummyserver.server import HAS_IPV6, run_tornado_app
+from dummyserver.testcase import HTTPSDummyServerTestCase
+from urllib3.util import ssl_
from .tz_stub import stub_timezone_ctx
@@ -106,3 +110,67 @@ def stub_timezone(request):
"""
with stub_timezone_ctx(request.param):
yield
+
+
+@pytest.fixture(scope="session")
+def supported_tls_versions():
+ # We have to create an actual TLS connection
+ # to test if the TLS version is not disabled by
+ # OpenSSL config. Ubuntu 20.04 specifically
+ # disables TLSv1 and TLSv1.1.
+ tls_versions = set()
+
+ _server = HTTPSDummyServerTestCase()
+ _server._start_server()
+ for _ssl_version_name in (
+ "PROTOCOL_TLSv1",
+ "PROTOCOL_TLSv1_1",
+ "PROTOCOL_TLSv1_2",
+ "PROTOCOL_TLS",
+ ):
+ _ssl_version = getattr(ssl, _ssl_version_name, 0)
+ if _ssl_version == 0:
+ continue
+ _sock = socket.create_connection((_server.host, _server.port))
+ try:
+ _sock = ssl_.ssl_wrap_socket(
+ _sock, cert_reqs=ssl.CERT_NONE, ssl_version=_ssl_version
+ )
+ except ssl.SSLError:
+ pass
+ else:
+ tls_versions.add(_sock.version())
+ _sock.close()
+ _server._stop_server()
+ return tls_versions
+
+
+@pytest.fixture(scope="function")
+def requires_tlsv1(supported_tls_versions):
+ """Test requires TLSv1 available"""
+ if not hasattr(ssl, "PROTOCOL_TLSv1") or "TLSv1" not in supported_tls_versions:
+ pytest.skip("Test requires TLSv1")
+
+
+@pytest.fixture(scope="function")
+def requires_tlsv1_1(supported_tls_versions):
+ """Test requires TLSv1.1 available"""
+ if not hasattr(ssl, "PROTOCOL_TLSv1_1") or "TLSv1.1" not in supported_tls_versions:
+ pytest.skip("Test requires TLSv1.1")
+
+
+@pytest.fixture(scope="function")
+def requires_tlsv1_2(supported_tls_versions):
+ """Test requires TLSv1.2 available"""
+ if not hasattr(ssl, "PROTOCOL_TLSv1_2") or "TLSv1.2" not in supported_tls_versions:
+ pytest.skip("Test requires TLSv1.2")
+
+
+@pytest.fixture(scope="function")
+def requires_tlsv1_3(supported_tls_versions):
+ """Test requires TLSv1.3 available"""
+ if (
+ not getattr(ssl, "HAS_TLSv1_3", False)
+ or "TLSv1.3" not in supported_tls_versions
+ ):
+ pytest.skip("Test requires TLSv1.3")

MODIFY
test/with_dummyserver/test_https.py
test/with_dummyserver/test_https.py
@@ -16,10 +16,6 @@ from test import (
onlyPy279OrNewer,
requires_network,
requires_ssl_context_keyfile_password,
- requiresTLSv1,
- requiresTLSv1_1,
- requiresTLSv1_2,
- requiresTLSv1_3,
resolvesLocalhostFQDN,
)
@@ -823,25 +819,25 @@ class TestHTTPS(HTTPSDummyServerTestCase):
assert r.data.decode("utf-8") == util.ALPN_PROTOCOLS[0]
-@requiresTLSv1()
+@pytest.mark.usefixtures("requires_tlsv1")
class TestHTTPS_TLSv1(TestHTTPS):
tls_protocol_name = "TLSv1"
certs = TLSv1_CERTS
-@requiresTLSv1_1()
+@pytest.mark.usefixtures("requires_tlsv1_1")
class TestHTTPS_TLSv1_1(TestHTTPS):
tls_protocol_name = "TLSv1.1"
certs = TLSv1_1_CERTS
-@requiresTLSv1_2()
+@pytest.mark.usefixtures("requires_tlsv1_2")
class TestHTTPS_TLSv1_2(TestHTTPS):
tls_protocol_name = "TLSv1.2"
certs = TLSv1_2_CERTS
-@requiresTLSv1_3()
+@pytest.mark.usefixtures("requires_tlsv1_3")
class TestHTTPS_TLSv1_3(TestHTTPS):
tls_protocol_name = "TLSv1.3"
certs = TLSv1_3_CERTS

Skip TLS versions if disabled by OpenSSL config