diff --git a/lib/urllib3/_version.py b/lib/urllib3/_version.py index 5141d980..fa8979d7 100644 --- a/lib/urllib3/_version.py +++ b/lib/urllib3/_version.py @@ -1,2 +1,2 @@ # This file is protected via CODEOWNERS -__version__ = "1.26.7" +__version__ = "1.26.8" diff --git a/lib/urllib3/connection.py b/lib/urllib3/connection.py index 60f70f79..4d92ac6d 100644 --- a/lib/urllib3/connection.py +++ b/lib/urllib3/connection.py @@ -51,7 +51,6 @@ from .exceptions import ( SubjectAltNameWarning, SystemTimeWarning, ) -from .packages.ssl_match_hostname import CertificateError, match_hostname from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection from .util.ssl_ import ( assert_fingerprint, @@ -61,6 +60,7 @@ from .util.ssl_ import ( resolve_ssl_version, ssl_wrap_socket, ) +from .util.ssl_match_hostname import CertificateError, match_hostname log = logging.getLogger(__name__) diff --git a/lib/urllib3/connectionpool.py b/lib/urllib3/connectionpool.py index 8dccf4bc..15bffcb2 100644 --- a/lib/urllib3/connectionpool.py +++ b/lib/urllib3/connectionpool.py @@ -2,6 +2,7 @@ from __future__ import absolute_import import errno import logging +import re import socket import sys import warnings @@ -35,7 +36,6 @@ from .exceptions import ( ) from .packages import six from .packages.six.moves import queue -from .packages.ssl_match_hostname import CertificateError from .request import RequestMethods from .response import HTTPResponse from .util.connection import is_connection_dropped @@ -44,6 +44,7 @@ from .util.queue import LifoQueue from .util.request import set_file_position from .util.response import assert_header_parsing from .util.retry import Retry +from .util.ssl_match_hostname import CertificateError from .util.timeout import Timeout from .util.url import Url, _encode_target from .util.url import _normalize_host as normalize_host @@ -301,8 +302,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): pass except queue.Full: # This should never happen if self.block == True - log.warning("Connection pool is full, discarding connection: %s", self.host) - + log.warning( + "Connection pool is full, discarding connection: %s. Connection pool size: %s", + self.host, + self.pool.qsize(), + ) # Connection never got put back into the pool, close it. if conn: conn.close() @@ -745,7 +749,33 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # Discard the connection for these exceptions. It will be # replaced during the next _get_conn() call. clean_exit = False - if isinstance(e, (BaseSSLError, CertificateError)): + + def _is_ssl_error_message_from_http_proxy(ssl_error): + # We're trying to detect the message 'WRONG_VERSION_NUMBER' but + # SSLErrors are kinda all over the place when it comes to the message, + # so we try to cover our bases here! + message = " ".join(re.split("[^a-z]", str(ssl_error).lower())) + return ( + "wrong version number" in message or "unknown protocol" in message + ) + + # Try to detect a common user error with proxies which is to + # set an HTTP proxy to be HTTPS when it should be 'http://' + # (ie {'http': 'http://proxy', 'https': 'https://proxy'}) + # Instead we add a nice error message and point to a URL. + if ( + isinstance(e, BaseSSLError) + and self.proxy + and _is_ssl_error_message_from_http_proxy(e) + ): + e = ProxyError( + "Your proxy appears to only use HTTP and not HTTPS, " + "try changing your proxy URL to be HTTP. See: " + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" + "#https-proxy-error-http-proxy", + SSLError(e), + ) + elif isinstance(e, (BaseSSLError, CertificateError)): e = SSLError(e) elif isinstance(e, (SocketError, NewConnectionError)) and self.proxy: e = ProxyError("Cannot connect to proxy.", e) diff --git a/lib/urllib3/contrib/_securetransport/bindings.py b/lib/urllib3/contrib/_securetransport/bindings.py index 11524d40..264d564d 100644 --- a/lib/urllib3/contrib/_securetransport/bindings.py +++ b/lib/urllib3/contrib/_securetransport/bindings.py @@ -48,7 +48,7 @@ from ctypes import ( ) from ctypes.util import find_library -from urllib3.packages.six import raise_from +from ...packages.six import raise_from if platform.system() != "Darwin": raise ImportError("Only macOS is supported") diff --git a/lib/urllib3/packages/__init__.py b/lib/urllib3/packages/__init__.py index fce4caa6..e69de29b 100644 --- a/lib/urllib3/packages/__init__.py +++ b/lib/urllib3/packages/__init__.py @@ -1,5 +0,0 @@ -from __future__ import absolute_import - -from . import ssl_match_hostname - -__all__ = ("ssl_match_hostname",) diff --git a/lib/urllib3/packages/ssl_match_hostname/__init__.py b/lib/urllib3/packages/ssl_match_hostname/__init__.py deleted file mode 100644 index ef3fde52..00000000 --- a/lib/urllib3/packages/ssl_match_hostname/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -import sys - -try: - # 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. - # 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") - - from ssl import CertificateError, match_hostname -except ImportError: - try: - # Backport of the function from a pypi module - from backports.ssl_match_hostname import ( # type: ignore - CertificateError, - match_hostname, - ) - except ImportError: - # Our vendored copy - from ._implementation import CertificateError, match_hostname # type: ignore - -# Not needed, but documenting what we provide. -__all__ = ("CertificateError", "match_hostname") diff --git a/lib/urllib3/util/connection.py b/lib/urllib3/util/connection.py index bdc240c5..6af1138f 100644 --- a/lib/urllib3/util/connection.py +++ b/lib/urllib3/util/connection.py @@ -2,9 +2,8 @@ from __future__ import absolute_import import socket -from urllib3.exceptions import LocationParseError - from ..contrib import _appengine_environ +from ..exceptions import LocationParseError from ..packages import six from .wait import NoWayToWaitForSocketError, wait_for_read diff --git a/lib/urllib3/util/retry.py b/lib/urllib3/util/retry.py index c7dc42f1..3398323f 100644 --- a/lib/urllib3/util/retry.py +++ b/lib/urllib3/util/retry.py @@ -69,6 +69,24 @@ class _RetryMeta(type): ) cls.DEFAULT_REMOVE_HEADERS_ON_REDIRECT = value + @property + def BACKOFF_MAX(cls): + warnings.warn( + "Using 'Retry.BACKOFF_MAX' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_BACKOFF_MAX' instead", + DeprecationWarning, + ) + return cls.DEFAULT_BACKOFF_MAX + + @BACKOFF_MAX.setter + def BACKOFF_MAX(cls, value): + warnings.warn( + "Using 'Retry.BACKOFF_MAX' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_BACKOFF_MAX' instead", + DeprecationWarning, + ) + cls.DEFAULT_BACKOFF_MAX = value + @six.add_metaclass(_RetryMeta) class Retry(object): @@ -181,7 +199,7 @@ class Retry(object): seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer - than :attr:`Retry.BACKOFF_MAX`. + than :attr:`Retry.DEFAULT_BACKOFF_MAX`. By default, backoff is disabled (set to 0). @@ -220,7 +238,7 @@ class Retry(object): DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset(["Authorization"]) #: Maximum backoff time. - BACKOFF_MAX = 120 + DEFAULT_BACKOFF_MAX = 120 def __init__( self, @@ -348,7 +366,7 @@ class Retry(object): return 0 backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) - return min(self.BACKOFF_MAX, backoff_value) + return min(self.DEFAULT_BACKOFF_MAX, backoff_value) def parse_retry_after(self, retry_after): # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 diff --git a/lib/urllib3/packages/ssl_match_hostname/_implementation.py b/lib/urllib3/util/ssl_match_hostname.py similarity index 96% rename from lib/urllib3/packages/ssl_match_hostname/_implementation.py rename to lib/urllib3/util/ssl_match_hostname.py index 689208d3..a4b4a569 100644 --- a/lib/urllib3/packages/ssl_match_hostname/_implementation.py +++ b/lib/urllib3/util/ssl_match_hostname.py @@ -9,7 +9,7 @@ import sys # ipaddress has been backported to 2.6+ in pypi. If it is installed on the # system, use it to handle IPAddress ServerAltnames (this was added in # python-3.5) otherwise only do DNS matching. This allows -# backports.ssl_match_hostname to continue to be used in Python 2.7. +# util.ssl_match_hostname to continue to be used in Python 2.7. try: import ipaddress except ImportError: @@ -78,7 +78,8 @@ def _dnsname_match(dn, hostname, max_wildcards=1): def _to_unicode(obj): if isinstance(obj, str) and sys.version_info < (3,): - obj = unicode(obj, encoding="ascii", errors="strict") + # ignored flake8 # F821 to support python 2.7 function + obj = unicode(obj, encoding="ascii", errors="strict") # noqa: F821 return obj diff --git a/lib/urllib3/util/ssltransport.py b/lib/urllib3/util/ssltransport.py index c2186bce..4a7105d1 100644 --- a/lib/urllib3/util/ssltransport.py +++ b/lib/urllib3/util/ssltransport.py @@ -2,8 +2,8 @@ import io import socket import ssl -from urllib3.exceptions import ProxySchemeUnsupported -from urllib3.packages import six +from ..exceptions import ProxySchemeUnsupported +from ..packages import six SSL_BLOCKSIZE = 16384 diff --git a/requirements.txt b/requirements.txt index 78093fcf..f32d7499 100644 --- a/requirements.txt +++ b/requirements.txt @@ -45,7 +45,7 @@ tempora==4.1.2 tokenize-rt==4.2.1 tzdata==2021.5 tzlocal==2.1 # apscheduler==3.8.0 requires tzlocal~=2.0 -urllib3==1.26.7 +urllib3==1.26.8 webencodings==0.5.1 websocket-client==1.2.3 xmltodict==0.12.0