Update packaged urllib3 to version v1.26.6

This commit is contained in:
Jim Miller 2021-07-09 12:48:12 -05:00
parent f26bc481d4
commit b3126d3996
15 changed files with 164 additions and 65 deletions

View file

@ -1,2 +1,2 @@
# This file is protected via CODEOWNERS # This file is protected via CODEOWNERS
__version__ = "1.26.3" __version__ = "1.26.6"

View file

@ -201,7 +201,7 @@ class HTTPConnection(_HTTPConnection, object):
self._prepare_conn(conn) self._prepare_conn(conn)
def putrequest(self, method, url, *args, **kwargs): def putrequest(self, method, url, *args, **kwargs):
"""""" """ """
# Empty docstring because the indentation of CPython's implementation # Empty docstring because the indentation of CPython's implementation
# is broken but we don't want this method in our documentation. # is broken but we don't want this method in our documentation.
match = _CONTAINS_CONTROL_CHAR_RE.search(method) match = _CONTAINS_CONTROL_CHAR_RE.search(method)
@ -214,7 +214,7 @@ class HTTPConnection(_HTTPConnection, object):
return _HTTPConnection.putrequest(self, method, url, *args, **kwargs) return _HTTPConnection.putrequest(self, method, url, *args, **kwargs)
def putheader(self, header, *values): def putheader(self, header, *values):
"""""" """ """
if not any(isinstance(v, str) and v == SKIP_HEADER for v in values): if not any(isinstance(v, str) and v == SKIP_HEADER for v in values):
_HTTPConnection.putheader(self, header, *values) _HTTPConnection.putheader(self, header, *values)
elif six.ensure_str(header.lower()) not in SKIPPABLE_HEADERS: elif six.ensure_str(header.lower()) not in SKIPPABLE_HEADERS:
@ -249,7 +249,7 @@ class HTTPConnection(_HTTPConnection, object):
self.putheader("User-Agent", _get_default_user_agent()) self.putheader("User-Agent", _get_default_user_agent())
for header, value in headers.items(): for header, value in headers.items():
self.putheader(header, value) self.putheader(header, value)
if "transfer-encoding" not in headers: if "transfer-encoding" not in header_keys:
self.putheader("Transfer-Encoding", "chunked") self.putheader("Transfer-Encoding", "chunked")
self.endheaders() self.endheaders()
@ -490,6 +490,10 @@ class HTTPSConnection(HTTPConnection):
self.ca_cert_dir, self.ca_cert_dir,
self.ca_cert_data, self.ca_cert_data,
) )
# By default urllib3's SSLContext disables `check_hostname` and uses
# a custom check. For proxies we're good with relying on the default
# verification.
ssl_context.check_hostname = True
# If no cert was provided, use only the default options for server # If no cert was provided, use only the default options for server
# certificate validation # certificate validation

View file

@ -318,7 +318,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
pass pass
def _get_timeout(self, timeout): def _get_timeout(self, timeout):
""" Helper that always returns a :class:`urllib3.util.Timeout` """ """Helper that always returns a :class:`urllib3.util.Timeout`"""
if timeout is _Default: if timeout is _Default:
return self.timeout.clone() return self.timeout.clone()
@ -1014,7 +1014,7 @@ class HTTPSConnectionPool(HTTPConnectionPool):
( (
"Unverified HTTPS request is being made to host '%s'. " "Unverified HTTPS request is being made to host '%s'. "
"Adding certificate verification is strongly advised. See: " "Adding certificate verification is strongly advised. See: "
"https://urllib3.readthedocs.io/en/latest/advanced-usage.html" "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html"
"#ssl-warnings" % conn.host "#ssl-warnings" % conn.host
), ),
InsecureRequestWarning, InsecureRequestWarning,

View file

@ -111,7 +111,7 @@ class AppEngineManager(RequestMethods):
warnings.warn( warnings.warn(
"urllib3 is using URLFetch on Google App Engine sandbox instead " "urllib3 is using URLFetch on Google App Engine sandbox instead "
"of sockets. To use sockets directly instead of URLFetch see " "of sockets. To use sockets directly instead of URLFetch see "
"https://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html.", "https://urllib3.readthedocs.io/en/1.26.x/reference/urllib3.contrib.html.",
AppEnginePlatformWarning, AppEnginePlatformWarning,
) )

View file

@ -5,6 +5,7 @@ Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10
""" """
from __future__ import absolute_import from __future__ import absolute_import
import warnings
from logging import getLogger from logging import getLogger
from ntlm import ntlm from ntlm import ntlm
@ -12,6 +13,14 @@ from ntlm import ntlm
from .. import HTTPSConnectionPool from .. import HTTPSConnectionPool
from ..packages.six.moves.http_client import HTTPSConnection from ..packages.six.moves.http_client import HTTPSConnection
warnings.warn(
"The 'urllib3.contrib.ntlmpool' module is deprecated and will be removed "
"in urllib3 v2.0 release, urllib3 is not able to support it properly due "
"to reasons listed in issue: https://github.com/urllib3/urllib3/issues/2282. "
"If you are a user of this module please comment in the mentioned issue.",
DeprecationWarning,
)
log = getLogger(__name__) log = getLogger(__name__)

View file

@ -76,6 +76,7 @@ import sys
from .. import util from .. import util
from ..packages import six from ..packages import six
from ..util.ssl_ import PROTOCOL_TLS_CLIENT
__all__ = ["inject_into_urllib3", "extract_from_urllib3"] __all__ = ["inject_into_urllib3", "extract_from_urllib3"]
@ -85,6 +86,7 @@ HAS_SNI = True
# Map from urllib3 to PyOpenSSL compatible parameter-values. # Map from urllib3 to PyOpenSSL compatible parameter-values.
_openssl_versions = { _openssl_versions = {
util.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, util.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD,
PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD,
ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
} }

View file

@ -67,6 +67,7 @@ import weakref
import six import six
from .. import util from .. import util
from ..util.ssl_ import PROTOCOL_TLS_CLIENT
from ._securetransport.bindings import CoreFoundation, Security, SecurityConst from ._securetransport.bindings import CoreFoundation, Security, SecurityConst
from ._securetransport.low_level import ( from ._securetransport.low_level import (
_assert_no_error, _assert_no_error,
@ -154,7 +155,8 @@ CIPHER_SUITES = [
# TLSv1 and a high of TLSv1.2. For everything else, we pin to that version. # TLSv1 and a high of TLSv1.2. For everything else, we pin to that version.
# TLSv1 to 1.2 are supported on macOS 10.8+ # TLSv1 to 1.2 are supported on macOS 10.8+
_protocol_to_min_max = { _protocol_to_min_max = {
util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12) util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12),
PROTOCOL_TLS_CLIENT: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12),
} }
if hasattr(ssl, "PROTOCOL_SSLv2"): if hasattr(ssl, "PROTOCOL_SSLv2"):

View file

@ -51,7 +51,7 @@ except ImportError:
( (
"SOCKS support in urllib3 requires the installation of optional " "SOCKS support in urllib3 requires the installation of optional "
"dependencies: specifically, PySocks. For more information, see " "dependencies: specifically, PySocks. For more information, see "
"https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies" "https://urllib3.readthedocs.io/en/1.26.x/contrib.html#socks-proxies"
), ),
DependencyWarning, DependencyWarning,
) )

View file

@ -1,4 +1,4 @@
# Copyright (c) 2010-2019 Benjamin Peterson # Copyright (c) 2010-2020 Benjamin Peterson
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@ -29,7 +29,7 @@ import sys
import types import types
__author__ = "Benjamin Peterson <benjamin@python.org>" __author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.12.0" __version__ = "1.16.0"
# Useful for very coarse version differentiation. # Useful for very coarse version differentiation.
@ -71,6 +71,11 @@ else:
MAXSIZE = int((1 << 63) - 1) MAXSIZE = int((1 << 63) - 1)
del X del X
if PY34:
from importlib.util import spec_from_loader
else:
spec_from_loader = None
def _add_doc(func, doc): def _add_doc(func, doc):
"""Add documentation to a function.""" """Add documentation to a function."""
@ -182,6 +187,11 @@ class _SixMetaPathImporter(object):
return self return self
return None return None
def find_spec(self, fullname, path, target=None):
if fullname in self.known_modules:
return spec_from_loader(fullname, self)
return None
def __get_module(self, fullname): def __get_module(self, fullname):
try: try:
return self.known_modules[fullname] return self.known_modules[fullname]
@ -220,6 +230,12 @@ class _SixMetaPathImporter(object):
get_source = get_code # same as get_code get_source = get_code # same as get_code
def create_module(self, spec):
return self.load_module(spec.name)
def exec_module(self, module):
pass
_importer = _SixMetaPathImporter(__name__) _importer = _SixMetaPathImporter(__name__)
@ -260,9 +276,19 @@ _moved_attributes = [
), ),
MovedModule("builtins", "__builtin__"), MovedModule("builtins", "__builtin__"),
MovedModule("configparser", "ConfigParser"), MovedModule("configparser", "ConfigParser"),
MovedModule(
"collections_abc",
"collections",
"collections.abc" if sys.version_info >= (3, 3) else "collections",
),
MovedModule("copyreg", "copy_reg"), MovedModule("copyreg", "copy_reg"),
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
MovedModule(
"_dummy_thread",
"dummy_thread",
"_dummy_thread" if sys.version_info < (3, 9) else "_thread",
),
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("http_cookies", "Cookie", "http.cookies"),
MovedModule("html_entities", "htmlentitydefs", "html.entities"), MovedModule("html_entities", "htmlentitydefs", "html.entities"),
@ -307,7 +333,9 @@ _moved_attributes = [
] ]
# Add windows specific modules. # Add windows specific modules.
if sys.platform == "win32": if sys.platform == "win32":
_moved_attributes += [MovedModule("winreg", "_winreg")] _moved_attributes += [
MovedModule("winreg", "_winreg"),
]
for attr in _moved_attributes: for attr in _moved_attributes:
setattr(_MovedItems, attr.name, attr) setattr(_MovedItems, attr.name, attr)
@ -476,7 +504,7 @@ class Module_six_moves_urllib_robotparser(_LazyModule):
_urllib_robotparser_moved_attributes = [ _urllib_robotparser_moved_attributes = [
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser") MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
] ]
for attr in _urllib_robotparser_moved_attributes: for attr in _urllib_robotparser_moved_attributes:
setattr(Module_six_moves_urllib_robotparser, attr.name, attr) setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
@ -678,9 +706,11 @@ if PY3:
if sys.version_info[1] <= 1: if sys.version_info[1] <= 1:
_assertRaisesRegex = "assertRaisesRegexp" _assertRaisesRegex = "assertRaisesRegexp"
_assertRegex = "assertRegexpMatches" _assertRegex = "assertRegexpMatches"
_assertNotRegex = "assertNotRegexpMatches"
else: else:
_assertRaisesRegex = "assertRaisesRegex" _assertRaisesRegex = "assertRaisesRegex"
_assertRegex = "assertRegex" _assertRegex = "assertRegex"
_assertNotRegex = "assertNotRegex"
else: else:
def b(s): def b(s):
@ -707,6 +737,7 @@ else:
_assertCountEqual = "assertItemsEqual" _assertCountEqual = "assertItemsEqual"
_assertRaisesRegex = "assertRaisesRegexp" _assertRaisesRegex = "assertRaisesRegexp"
_assertRegex = "assertRegexpMatches" _assertRegex = "assertRegexpMatches"
_assertNotRegex = "assertNotRegexpMatches"
_add_doc(b, """Byte literal""") _add_doc(b, """Byte literal""")
_add_doc(u, """Text literal""") _add_doc(u, """Text literal""")
@ -723,6 +754,10 @@ def assertRegex(self, *args, **kwargs):
return getattr(self, _assertRegex)(*args, **kwargs) return getattr(self, _assertRegex)(*args, **kwargs)
def assertNotRegex(self, *args, **kwargs):
return getattr(self, _assertNotRegex)(*args, **kwargs)
if PY3: if PY3:
exec_ = getattr(moves.builtins, "exec") exec_ = getattr(moves.builtins, "exec")
@ -750,7 +785,7 @@ else:
del frame del frame
elif _locs_ is None: elif _locs_ is None:
_locs_ = _globs_ _locs_ = _globs_
exec("""exec _code_ in _globs_, _locs_""") exec ("""exec _code_ in _globs_, _locs_""")
exec_( exec_(
"""def reraise(tp, value, tb=None): """def reraise(tp, value, tb=None):
@ -762,18 +797,7 @@ else:
) )
if sys.version_info[:2] == (3, 2): if sys.version_info[:2] > (3,):
exec_(
"""def raise_from(value, from_value):
try:
if from_value is None:
raise value
raise value from from_value
finally:
value = None
"""
)
elif sys.version_info[:2] > (3, 2):
exec_( exec_(
"""def raise_from(value, from_value): """def raise_from(value, from_value):
try: try:
@ -863,19 +887,41 @@ if sys.version_info[:2] < (3, 3):
_add_doc(reraise, """Reraise an exception.""") _add_doc(reraise, """Reraise an exception.""")
if sys.version_info[0:2] < (3, 4): if sys.version_info[0:2] < (3, 4):
# This does exactly the same what the :func:`py3:functools.update_wrapper`
# function does on Python versions after 3.2. It sets the ``__wrapped__``
# attribute on ``wrapper`` object and it doesn't raise an error if any of
# the attributes mentioned in ``assigned`` and ``updated`` are missing on
# ``wrapped`` object.
def _update_wrapper(
wrapper,
wrapped,
assigned=functools.WRAPPER_ASSIGNMENTS,
updated=functools.WRAPPER_UPDATES,
):
for attr in assigned:
try:
value = getattr(wrapped, attr)
except AttributeError:
continue
else:
setattr(wrapper, attr, value)
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
wrapper.__wrapped__ = wrapped
return wrapper
_update_wrapper.__doc__ = functools.update_wrapper.__doc__
def wraps( def wraps(
wrapped, wrapped,
assigned=functools.WRAPPER_ASSIGNMENTS, assigned=functools.WRAPPER_ASSIGNMENTS,
updated=functools.WRAPPER_UPDATES, updated=functools.WRAPPER_UPDATES,
): ):
def wrapper(f): return functools.partial(
f = functools.wraps(wrapped, assigned, updated)(f) _update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated
f.__wrapped__ = wrapped )
return f
return wrapper
wraps.__doc__ = functools.wraps.__doc__
else: else:
wraps = functools.wraps wraps = functools.wraps
@ -888,7 +934,15 @@ def with_metaclass(meta, *bases):
# the actual metaclass. # the actual metaclass.
class metaclass(type): class metaclass(type):
def __new__(cls, name, this_bases, d): def __new__(cls, name, this_bases, d):
return meta(name, bases, d) if sys.version_info[:2] >= (3, 7):
# This version introduced PEP 560 that requires a bit
# of extra care (we mimic what is done by __build_class__).
resolved_bases = types.resolve_bases(bases)
if resolved_bases is not bases:
d["__orig_bases__"] = bases
else:
resolved_bases = bases
return meta(name, resolved_bases, d)
@classmethod @classmethod
def __prepare__(cls, name, this_bases): def __prepare__(cls, name, this_bases):
@ -928,12 +982,11 @@ def ensure_binary(s, encoding="utf-8", errors="strict"):
- `str` -> encoded to `bytes` - `str` -> encoded to `bytes`
- `bytes` -> `bytes` - `bytes` -> `bytes`
""" """
if isinstance(s, binary_type):
return s
if isinstance(s, text_type): if isinstance(s, text_type):
return s.encode(encoding, errors) return s.encode(encoding, errors)
elif isinstance(s, binary_type): raise TypeError("not expecting type '%s'" % type(s))
return s
else:
raise TypeError("not expecting type '%s'" % type(s))
def ensure_str(s, encoding="utf-8", errors="strict"): def ensure_str(s, encoding="utf-8", errors="strict"):
@ -947,12 +1000,15 @@ def ensure_str(s, encoding="utf-8", errors="strict"):
- `str` -> `str` - `str` -> `str`
- `bytes` -> decoded to `str` - `bytes` -> decoded to `str`
""" """
if not isinstance(s, (text_type, binary_type)): # Optimization: Fast return for the common case.
raise TypeError("not expecting type '%s'" % type(s)) if type(s) is str:
return s
if PY2 and isinstance(s, text_type): if PY2 and isinstance(s, text_type):
s = s.encode(encoding, errors) return s.encode(encoding, errors)
elif PY3 and isinstance(s, binary_type): elif PY3 and isinstance(s, binary_type):
s = s.decode(encoding, errors) return s.decode(encoding, errors)
elif not isinstance(s, (text_type, binary_type)):
raise TypeError("not expecting type '%s'" % type(s))
return s return s
@ -977,7 +1033,7 @@ def ensure_text(s, encoding="utf-8", errors="strict"):
def python_2_unicode_compatible(klass): def python_2_unicode_compatible(klass):
""" """
A decorator that defines __unicode__ and __str__ methods under Python 2. A class decorator that defines __unicode__ and __str__ methods under Python 2.
Under Python 3 it does nothing. Under Python 3 it does nothing.
To support Python 2 and 3 with a single code base, define a __str__ method To support Python 2 and 3 with a single code base, define a __str__ method

View file

@ -1,9 +1,11 @@
import sys import sys
try: try:
# Our match_hostname function is the same as 3.5's, so we only want to # Our match_hostname function is the same as 3.10's, so we only want to
# import the match_hostname function if it's at least that good. # import the match_hostname function if it's at least that good.
if sys.version_info < (3, 5): # We also fallback on Python 3.10+ because our code doesn't emit
# deprecation warnings and is the same as Python 3.10 otherwise.
if sys.version_info < (3, 5) or sys.version_info >= (3, 10):
raise ImportError("Fallback to vendored code") raise ImportError("Fallback to vendored code")
from ssl import CertificateError, match_hostname from ssl import CertificateError, match_hostname

View file

@ -118,7 +118,7 @@ def allowed_gai_family():
def _has_ipv6(host): def _has_ipv6(host):
""" Returns True if the system can bind an IPv6 address. """ """Returns True if the system can bind an IPv6 address."""
sock = None sock = None
has_ipv6 = False has_ipv6 = False

View file

@ -37,7 +37,7 @@ class _RetryMeta(type):
def DEFAULT_METHOD_WHITELIST(cls): def DEFAULT_METHOD_WHITELIST(cls):
warnings.warn( warnings.warn(
"Using 'Retry.DEFAULT_METHOD_WHITELIST' is deprecated and " "Using 'Retry.DEFAULT_METHOD_WHITELIST' is deprecated and "
"will be removed in v2.0. Use 'Retry.DEFAULT_METHODS_ALLOWED' instead", "will be removed in v2.0. Use 'Retry.DEFAULT_ALLOWED_METHODS' instead",
DeprecationWarning, DeprecationWarning,
) )
return cls.DEFAULT_ALLOWED_METHODS return cls.DEFAULT_ALLOWED_METHODS
@ -253,6 +253,7 @@ class Retry(object):
"Using 'method_whitelist' with Retry is deprecated and " "Using 'method_whitelist' with Retry is deprecated and "
"will be removed in v2.0. Use 'allowed_methods' instead", "will be removed in v2.0. Use 'allowed_methods' instead",
DeprecationWarning, DeprecationWarning,
stacklevel=2,
) )
allowed_methods = method_whitelist allowed_methods = method_whitelist
if allowed_methods is _Default: if allowed_methods is _Default:
@ -320,7 +321,7 @@ class Retry(object):
@classmethod @classmethod
def from_int(cls, retries, redirect=True, default=None): def from_int(cls, retries, redirect=True, default=None):
""" Backwards-compatibility for the old retries format.""" """Backwards-compatibility for the old retries format."""
if retries is None: if retries is None:
retries = default if default is not None else cls.DEFAULT retries = default if default is not None else cls.DEFAULT
@ -373,7 +374,7 @@ class Retry(object):
return seconds return seconds
def get_retry_after(self, response): def get_retry_after(self, response):
""" Get the value of Retry-After in seconds. """ """Get the value of Retry-After in seconds."""
retry_after = response.getheader("Retry-After") retry_after = response.getheader("Retry-After")
@ -467,7 +468,7 @@ class Retry(object):
) )
def is_exhausted(self): def is_exhausted(self):
""" Are we out of retries? """ """Are we out of retries?"""
retry_counts = ( retry_counts = (
self.total, self.total,
self.connect, self.connect,

View file

@ -71,6 +71,11 @@ except ImportError:
except ImportError: except ImportError:
PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 PROTOCOL_SSLv23 = PROTOCOL_TLS = 2
try:
from ssl import PROTOCOL_TLS_CLIENT
except ImportError:
PROTOCOL_TLS_CLIENT = PROTOCOL_TLS
try: try:
from ssl import OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3 from ssl import OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3
@ -159,7 +164,7 @@ except ImportError:
"urllib3 from configuring SSL appropriately and may cause " "urllib3 from configuring SSL appropriately and may cause "
"certain SSL connections to fail. You can upgrade to a newer " "certain SSL connections to fail. You can upgrade to a newer "
"version of Python to solve this. For more information, see " "version of Python to solve this. For more information, see "
"https://urllib3.readthedocs.io/en/latest/advanced-usage.html" "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html"
"#ssl-warnings", "#ssl-warnings",
InsecurePlatformWarning, InsecurePlatformWarning,
) )
@ -278,7 +283,11 @@ def create_urllib3_context(
Constructed SSLContext object with specified options Constructed SSLContext object with specified options
:rtype: SSLContext :rtype: SSLContext
""" """
context = SSLContext(ssl_version or PROTOCOL_TLS) # PROTOCOL_TLS is deprecated in Python 3.10
if not ssl_version or ssl_version == PROTOCOL_TLS:
ssl_version = PROTOCOL_TLS_CLIENT
context = SSLContext(ssl_version)
context.set_ciphers(ciphers or DEFAULT_CIPHERS) context.set_ciphers(ciphers or DEFAULT_CIPHERS)
@ -313,13 +322,25 @@ def create_urllib3_context(
) is not None: ) is not None:
context.post_handshake_auth = True context.post_handshake_auth = True
context.verify_mode = cert_reqs def disable_check_hostname():
if ( if (
getattr(context, "check_hostname", None) is not None getattr(context, "check_hostname", None) is not None
): # Platform-specific: Python 3.2 ): # Platform-specific: Python 3.2
# We do our own verification, including fingerprints and alternative # We do our own verification, including fingerprints and alternative
# hostnames. So disable it here # hostnames. So disable it here
context.check_hostname = False context.check_hostname = False
# The order of the below lines setting verify_mode and check_hostname
# matter due to safe-guards SSLContext has to prevent an SSLContext with
# check_hostname=True, verify_mode=NONE/OPTIONAL. This is made even more
# complex because we don't know whether PROTOCOL_TLS_CLIENT will be used
# or not so we don't know the initial state of the freshly created SSLContext.
if cert_reqs == ssl.CERT_REQUIRED:
context.verify_mode = cert_reqs
disable_check_hostname()
else:
disable_check_hostname()
context.verify_mode = cert_reqs
# Enable logging of TLS session keys via defacto standard environment variable # Enable logging of TLS session keys via defacto standard environment variable
# 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values. # 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values.
@ -401,7 +422,7 @@ def ssl_wrap_socket(
try: try:
if hasattr(context, "set_alpn_protocols"): if hasattr(context, "set_alpn_protocols"):
context.set_alpn_protocols(ALPN_PROTOCOLS) context.set_alpn_protocols(ALPN_PROTOCOLS)
except NotImplementedError: except NotImplementedError: # Defensive: in CI, we always have set_alpn_protocols
pass pass
# If we detect server_hostname is an IP address then the SNI # If we detect server_hostname is an IP address then the SNI
@ -419,7 +440,7 @@ def ssl_wrap_socket(
"This may cause the server to present an incorrect TLS " "This may cause the server to present an incorrect TLS "
"certificate, which can cause validation failures. You can upgrade to " "certificate, which can cause validation failures. You can upgrade to "
"a newer version of Python to solve this. For more information, see " "a newer version of Python to solve this. For more information, see "
"https://urllib3.readthedocs.io/en/latest/advanced-usage.html" "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html"
"#ssl-warnings", "#ssl-warnings",
SNIMissingWarning, SNIMissingWarning,
) )

View file

@ -193,7 +193,7 @@ class SSLTransport:
raise raise
def _ssl_io_loop(self, func, *args): def _ssl_io_loop(self, func, *args):
""" Performs an I/O loop between incoming/outgoing and the socket.""" """Performs an I/O loop between incoming/outgoing and the socket."""
should_loop = True should_loop = True
ret = None ret = None

View file

@ -63,12 +63,12 @@ IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT + "$")
BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$") BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$")
ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$") ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$")
SUBAUTHORITY_PAT = (u"^(?:(.*)@)?(%s|%s|%s)(?::([0-9]{0,5}))?$") % ( _HOST_PORT_PAT = ("^(%s|%s|%s)(?::([0-9]{0,5}))?$") % (
REG_NAME_PAT, REG_NAME_PAT,
IPV4_PAT, IPV4_PAT,
IPV6_ADDRZ_PAT, IPV6_ADDRZ_PAT,
) )
SUBAUTHORITY_RE = re.compile(SUBAUTHORITY_PAT, re.UNICODE | re.DOTALL) _HOST_PORT_RE = re.compile(_HOST_PORT_PAT, re.UNICODE | re.DOTALL)
UNRESERVED_CHARS = set( UNRESERVED_CHARS = set(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~"
@ -365,7 +365,9 @@ def parse_url(url):
scheme = scheme.lower() scheme = scheme.lower()
if authority: if authority:
auth, host, port = SUBAUTHORITY_RE.match(authority).groups() auth, _, host_port = authority.rpartition("@")
auth = auth or None
host, port = _HOST_PORT_RE.match(host_port).groups()
if auth and normalize_uri: if auth and normalize_uri:
auth = _encode_invalid_chars(auth, USERINFO_CHARS) auth = _encode_invalid_chars(auth, USERINFO_CHARS)
if port == "": if port == "":