From 49e020d9e5b555d2e84c8090a6dc3ff830f46f1a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 24 Jun 2022 08:58:30 +0200 Subject: [PATCH] gh-94172: urllib.request avoids deprecated check_hostname The urllib.request no longer uses the deprecated check_hostname parameter of the http.client module. Add http.client._create_https_context() helper to http.client, reused by urllib.request. Remove the now redundant check on check_hostname and verify_mode in http.client: the SSLContext.check_hostname setter already implements the check. --- Lib/http/client.py | 32 +++++++++++++++++--------------- Lib/urllib/request.py | 8 ++++++-- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py index f54172fd0deeae..4bef50e6474d49 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -786,6 +786,20 @@ def getcode(self): ''' return self.status + +def _create_https_context(http_version): + # Function also used by urllib.request to be able to set the check_hostname + # attribute on a context object. + context = ssl._create_default_https_context() + # send ALPN extension to indicate HTTP/1.1 protocol + if http_version == 11: + context.set_alpn_protocols(['http/1.1']) + # enable PHA for TLS 1.3 connections if available + if context.post_handshake_auth is not None: + context.post_handshake_auth = True + return context + + class HTTPConnection: _http_vsn = 11 @@ -1418,19 +1432,9 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, self.key_file = key_file self.cert_file = cert_file if context is None: - context = ssl._create_default_https_context() - # send ALPN extension to indicate HTTP/1.1 protocol - if self._http_vsn == 11: - context.set_alpn_protocols(['http/1.1']) - # enable PHA for TLS 1.3 connections if available - if context.post_handshake_auth is not None: - context.post_handshake_auth = True - will_verify = context.verify_mode != ssl.CERT_NONE - if check_hostname is None: - check_hostname = context.check_hostname - if check_hostname and not will_verify: - raise ValueError("check_hostname needs a SSL context with " - "either CERT_OPTIONAL or CERT_REQUIRED") + context = _create_https_context(self._http_vsn) + if check_hostname is not None: + context.check_hostname = check_hostname if key_file or cert_file: context.load_cert_chain(cert_file, key_file) # cert and key file means the user wants to authenticate. @@ -1438,8 +1442,6 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, if context.post_handshake_auth is not None: context.post_handshake_auth = True self._context = context - if check_hostname is not None: - self._context.check_hostname = check_hostname def connect(self): "Connect to a host on a given (SSL) port." diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index c352fb25ae2bc4..7878daacb52d08 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1383,12 +1383,16 @@ class HTTPSHandler(AbstractHTTPHandler): def __init__(self, debuglevel=0, context=None, check_hostname=None): AbstractHTTPHandler.__init__(self, debuglevel) + if context is None: + http_version = http.client.HTTPSConnection._http_vsn + context = http.client._create_https_context(http_version) + if check_hostname is not None: + context.check_hostname = check_hostname self._context = context - self._check_hostname = check_hostname def https_open(self, req): return self.do_open(http.client.HTTPSConnection, req, - context=self._context, check_hostname=self._check_hostname) + context=self._context) https_request = AbstractHTTPHandler.do_request_