Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-urllib3 for openSUSE:Factory checked in at 2022-04-23 19:45:20 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-urllib3 (Old) and /work/SRC/openSUSE:Factory/.python-urllib3.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-urllib3" Sat Apr 23 19:45:20 2022 rev:49 rq:970661 version:1.26.9 Changes: -------- --- /work/SRC/openSUSE:Factory/python-urllib3/python-urllib3.changes 2021-11-03 17:25:14.925303845 +0100 +++ /work/SRC/openSUSE:Factory/.python-urllib3.new.1538/python-urllib3.changes 2022-04-23 19:45:38.334959973 +0200 @@ -1,0 +2,47 @@ +Tue Apr 19 05:34:17 UTC 2022 - Steve Kowalik <steven.kowa...@suse.com> + +- Remove unneeded BuildRequires of mock. + +------------------------------------------------------------------- +Tue Apr 5 19:54:39 UTC 2022 - Ben Greiner <c...@bnavigator.de> + +- Remove unbundling off ssl.match_hostname. + * It was only done for the primary python3 flavor + * It is bundled for a reason gh#urllib3/urllib3#2439, + gh#urllib3/urllib3#2448 + * The tests (and probably urllib3 users) use wildcard patterns + not supported by the stdlib +- Fix undbundling of six for all flavors +- Replace brotlipy recommendation and test with python-Brotli + (see release notes below) + +------------------------------------------------------------------- +Tue Mar 29 11:48:27 UTC 2022 - Dirk M??ller <dmuel...@suse.com> + +- update to 1.26.9: + * Changed ``urllib3[brotli]`` extra to favor installing Brotli libraries that are still + receiving updates like ``brotli`` and ``brotlicffi`` instead of ``brotlipy``. + This change does not impact behavior of urllib3, only which dependencies are installed. + * Fixed a socket leaking when ``HTTPSConnection.connect()`` raises an exception. + * Fixed ``server_hostname`` being forwarded from ``PoolManager`` to ``HTTPConnectionPool`` + when requesting an HTTP URL. Should only be forwarded when requesting an HTTPS URL. + +------------------------------------------------------------------- +Mon Jan 10 22:41:38 UTC 2022 - Dirk M??ller <dmuel...@suse.com> + +- update to 1.26.8: + * Added extra message to``urllib3.exceptions.ProxyError`` when urllib3 detects that + a proxy is configured to use HTTPS but the proxy itself appears to only use HTTP. + * Added a mention of the size of the connection pool when discarding a + connection due to the pool being full. + * Added explicit support for Python 3.11. + * Deprecated the ``Retry.MAX_BACKOFF`` class property in favor of + ``Retry.DEFAULT_MAX_BACKOFF`` to better match the rest of the default parameter names. + ``Retry.MAX_BACKOFF`` is removed in v2.0. + * Changed location of the vendored ``ssl.match_hostname`` function from + ``urllib3.packages.ssl_match_hostname`` to + ``urllib3.util.ssl_match_hostname`` to ensure Python 3.10+ compatibility after + being repackaged by downstream distributors. + * Fixed absolute imports, all imports are now relative. + +------------------------------------------------------------------- Old: ---- ssl_match_hostname_py3.py urllib3-1.26.7.tar.gz New: ---- urllib3-1.26.9.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-urllib3.spec ++++++ --- /var/tmp/diff_new_pack.nC0Qht/_old 2022-04-23 19:45:38.974960734 +0200 +++ /var/tmp/diff_new_pack.nC0Qht/_new 2022-04-23 19:45:38.982960743 +0200 @@ -1,7 +1,7 @@ # # spec file # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,7 +16,7 @@ # -%{?!python_module:%define python_module() python-%{**} python3-%{**}} +%{?!python_module:%define python_module() python3-%{**}} %define skip_python2 1 %global flavor @BUILD_FLAVOR@%{nil} %if "%{flavor}" == "test" @@ -27,18 +27,17 @@ %bcond_with test %endif Name: python-urllib3%{psuffix} -Version: 1.26.7 +Version: 1.26.9 Release: 0 Summary: HTTP library with thread-safe connection pooling, file post, and more License: MIT Group: Development/Languages/Python URL: https://urllib3.readthedocs.org/ Source: https://files.pythonhosted.org/packages/source/u/urllib3/urllib3-%{version}.tar.gz -# Wrapper for ssl to unbundle ssl_match_hostname -Source1: ssl_match_hostname_py3.py # PATCH-FIX-UPSTREAM remove_mock.patch gh#urllib3/urllib3#2108 mc...@suse.com # remove dependency on the external module mock Patch0: remove_mock.patch +BuildRequires: %{python_module base >= 3.7} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module six} BuildRequires: fdupes @@ -50,18 +49,17 @@ Requires: python-idna >= 2.0.0 Requires: python-pyOpenSSL >= 0.14 Requires: python-six >= 1.12.0 +Recommends: python-Brotli >= 1.0.9 Recommends: python-PySocks >= 1.5.6 -Recommends: python-brotlipy >= 0.6.0 BuildArch: noarch %if %{with test} +BuildRequires: %{python_module Brotli >= 1.0.9} BuildRequires: %{python_module PySocks >= 1.5.6} -BuildRequires: %{python_module brotlipy >= 0.6.0} BuildRequires: %{python_module certifi} BuildRequires: %{python_module cryptography >= 1.3.4} BuildRequires: %{python_module dateutil} BuildRequires: %{python_module flaky} BuildRequires: %{python_module idna >= 2.0.0} -BuildRequires: %{python_module mock >= 1.3.0} BuildRequires: %{python_module psutil} BuildRequires: %{python_module pytest-freezegun} BuildRequires: %{python_module pytest-timeout} @@ -108,35 +106,26 @@ %if !%{with test} %python_install -# Unbundle the Python 3 build -rm %{buildroot}/%{python3_sitelib}/urllib3/packages/six.py -rm -r %{buildroot}/%{python3_sitelib}/urllib3/packages/ssl_match_hostname/ - -# Copy ssl_match_hostname.py before compilation, so we can have a pyc too -cp -a %{SOURCE1} %{buildroot}/%{python3_sitelib}/urllib3/packages/ssl_match_hostname.py - -%{python_expand \ -$python -m compileall -d %{$python_sitelib} %{buildroot}%{$python_sitelib}/urllib3/ -$python -O -m compileall -d %{$python_sitelib} %{buildroot}%{$python_sitelib}/urllib3/ -} +%{python_expand # Unbundle six +rm %{buildroot}/%{$python_sitelib}/urllib3/packages/six.py +rm %{buildroot}/%{$python_sitelib}/urllib3/packages/__pycache__/six*.pyc -ln -s %{python3_sitelib}/six.py %{buildroot}/%{python3_sitelib}/urllib3/packages/six.py -ln -sf %{python3_sitelib}/__pycache__/six.cpython-%{python3_version_nodots}.opt-1.pyc \ +ln -s %{$python_sitelib}/six.py %{buildroot}/%{$python_sitelib}/urllib3/packages/six.py +ln -sf %{$python_sitelib}/__pycache__/six.cpython-%{$python_version_nodots}.opt-1.pyc \ %{buildroot}/%{python3_sitelib}/urllib3/packages/__pycache__/ -ln -sf %{python3_sitelib}/__pycache__/six.cpython-%{python3_version_nodots}.pyc \ +ln -sf %{$python_sitelib}/__pycache__/six.cpython-%{$python_version_nodots}.pyc \ %{buildroot}/%{python3_sitelib}/urllib3/packages/__pycache__/ -%python_expand %fdupes %{buildroot}%{$python_sitelib} +%fdupes %{buildroot}%{$python_sitelib} +} %endif %if %{with test} %check # gh#urllib3/urllib3#2109 export CI="true" -# still broken with new ssl -skiplist='test_import_urllib3' # skip some randomly failing tests (mostly on i586, but sometimes they fail on other architectures) -skiplist+=" or test_ssl_read_timeout or test_ssl_failed_fingerprint_verification or test_ssl_custom_validation_failure_terminates" +skiplist="test_ssl_read_timeout or test_ssl_failed_fingerprint_verification or test_ssl_custom_validation_failure_terminates" # gh#urllib3/urllib3#1752 and others: upstream's way of checking that the build # system has a correct system time breaks (re-)building the package after too # many months have passed since the last release. ++++++ urllib3-1.26.7.tar.gz -> urllib3-1.26.9.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/CHANGES.rst new/urllib3-1.26.9/CHANGES.rst --- old/urllib3-1.26.7/CHANGES.rst 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/CHANGES.rst 2022-03-16 14:25:49.000000000 +0100 @@ -1,8 +1,35 @@ Changes ======= +1.26.9 (2022-03-16) +------------------- + +* Changed ``urllib3[brotli]`` extra to favor installing Brotli libraries that are still + receiving updates like ``brotli`` and ``brotlicffi`` instead of ``brotlipy``. + This change does not impact behavior of urllib3, only which dependencies are installed. +* Fixed a socket leaking when ``HTTPSConnection.connect()`` raises an exception. +* Fixed ``server_hostname`` being forwarded from ``PoolManager`` to ``HTTPConnectionPool`` + when requesting an HTTP URL. Should only be forwarded when requesting an HTTPS URL. + + +1.26.8 (2022-01-07) +------------------- + +* Added extra message to ``urllib3.exceptions.ProxyError`` when urllib3 detects that + a proxy is configured to use HTTPS but the proxy itself appears to only use HTTP. +* Added a mention of the size of the connection pool when discarding a connection due to the pool being full. +* Added explicit support for Python 3.11. +* Deprecated the ``Retry.MAX_BACKOFF`` class property in favor of ``Retry.DEFAULT_MAX_BACKOFF`` + to better match the rest of the default parameter names. ``Retry.MAX_BACKOFF`` is removed in v2.0. +* Changed location of the vendored ``ssl.match_hostname`` function from ``urllib3.packages.ssl_match_hostname`` + to ``urllib3.util.ssl_match_hostname`` to ensure Python 3.10+ compatibility after being repackaged + by downstream distributors. +* Fixed absolute imports, all imports are now relative. + + 1.26.7 (2021-09-22) ------------------- + * Fixed a bug with HTTPS hostname verification involving IP addresses and lack of SNI. (Issue #2400) * Fixed a bug where IPv6 braces weren't stripped during certificate hostname diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/PKG-INFO new/urllib3-1.26.9/PKG-INFO --- old/urllib3-1.26.7/PKG-INFO 2021-09-22 20:00:56.037445500 +0200 +++ new/urllib3-1.26.9/PKG-INFO 2022-03-16 14:27:36.687475400 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: urllib3 -Version: 1.26.7 +Version: 1.26.9 Summary: HTTP library with thread-safe connection pooling, file post, and more. Home-page: https://urllib3.readthedocs.io/ Author: Andrey Petrov @@ -25,6 +25,7 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Internet :: WWW/HTTP @@ -74,7 +75,7 @@ Alternatively, you can grab the latest source code from `GitHub <https://github.com/urllib3/urllib3>`_:: $ git clone git://github.com/urllib3/urllib3.git - $ python setup.py install + $ pip install . Documentation @@ -143,8 +144,35 @@ Changes ======= +1.26.9 (2022-03-16) +------------------- + +* Changed ``urllib3[brotli]`` extra to favor installing Brotli libraries that are still + receiving updates like ``brotli`` and ``brotlicffi`` instead of ``brotlipy``. + This change does not impact behavior of urllib3, only which dependencies are installed. +* Fixed a socket leaking when ``HTTPSConnection.connect()`` raises an exception. +* Fixed ``server_hostname`` being forwarded from ``PoolManager`` to ``HTTPConnectionPool`` + when requesting an HTTP URL. Should only be forwarded when requesting an HTTPS URL. + + +1.26.8 (2022-01-07) +------------------- + +* Added extra message to ``urllib3.exceptions.ProxyError`` when urllib3 detects that + a proxy is configured to use HTTPS but the proxy itself appears to only use HTTP. +* Added a mention of the size of the connection pool when discarding a connection due to the pool being full. +* Added explicit support for Python 3.11. +* Deprecated the ``Retry.MAX_BACKOFF`` class property in favor of ``Retry.DEFAULT_MAX_BACKOFF`` + to better match the rest of the default parameter names. ``Retry.MAX_BACKOFF`` is removed in v2.0. +* Changed location of the vendored ``ssl.match_hostname`` function from ``urllib3.packages.ssl_match_hostname`` + to ``urllib3.util.ssl_match_hostname`` to ensure Python 3.10+ compatibility after being repackaged + by downstream distributors. +* Fixed absolute imports, all imports are now relative. + + 1.26.7 (2021-09-22) ------------------- + * Fixed a bug with HTTPS hostname verification involving IP addresses and lack of SNI. (Issue #2400) * Fixed a bug where IPv6 braces weren't stripped during certificate hostname diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/README.rst new/urllib3-1.26.9/README.rst --- old/urllib3-1.26.7/README.rst 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/README.rst 2022-03-16 14:25:49.000000000 +0100 @@ -52,7 +52,7 @@ Alternatively, you can grab the latest source code from `GitHub <https://github.com/urllib3/urllib3>`_:: $ git clone git://github.com/urllib3/urllib3.git - $ python setup.py install + $ pip install . Documentation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/dev-requirements.txt new/urllib3-1.26.9/dev-requirements.txt --- old/urllib3-1.26.7/dev-requirements.txt 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/dev-requirements.txt 2022-03-16 14:25:49.000000000 +0100 @@ -13,9 +13,8 @@ trustme==0.7.0 cryptography==3.2.1;python_version<"3.6" cryptography==3.4.7;python_version>="3.6" -gcp-devrel-py-tools==0.0.16 - -# https://github.com/GoogleCloudPlatform/python-repo-tools/issues/23 -pylint<2.0;python_version<="2.7" - python-dateutil==2.8.1 + +# https://github.com/GrahamDumpleton/wrapt/issues/189 +wrapt==1.12.1; python_version<="2.7" and sys_platform=="win32" +gcp-devrel-py-tools==0.0.16 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/docs/advanced-usage.rst new/urllib3-1.26.9/docs/advanced-usage.rst --- old/urllib3-1.26.7/docs/advanced-usage.rst 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/docs/advanced-usage.rst 2022-03-16 14:25:49.000000000 +0100 @@ -168,6 +168,78 @@ **only use this option with trusted or corporate proxies** as the proxy will have full visibility of your requests. +.. _https_proxy_error_http_proxy: + +Your proxy appears to only use HTTP and not HTTPS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you're receiving the :class:`~urllib3.exceptions.ProxyError` and it mentions +your proxy only speaks HTTP and not HTTPS here's what to do to solve your issue: + +If you're using ``urllib3`` directly, make sure the URL you're passing into :class:`urllib3.ProxyManager` +starts with ``http://`` instead of ``https://``: + +.. code-block:: python + + # Do this: + http = urllib3.ProxyManager("http://...") + + # Not this: + http = urllib3.ProxyManager("https://...") + +If instead you're using ``urllib3`` through another library like Requests +there are multiple ways your proxy could be mis-configured. You need to figure out +where the configuration isn't correct and make the fix there. Some common places +to look are environment variables like ``HTTP_PROXY``, ``HTTPS_PROXY``, and ``ALL_PROXY``. + +Ensure that the values for all of these environment variables starts with ``http://`` +and not ``https://``: + +.. code-block:: bash + + # Check your existing environment variables in bash + $ env | grep "_PROXY" + HTTP_PROXY=http://127.0.0.1:8888 + HTTPS_PROXY=https://127.0.0.1:8888 # <--- This setting is the problem! + + # Make the fix in your current session and test your script + $ export HTTPS_PROXY="http://127.0.0.1:8888" + $ python test-proxy.py # This should now pass. + + # Persist your change in your shell 'profile' (~/.bashrc, ~/.profile, ~/.bash_profile, etc) + # You may need to logout and log back in to ensure this works across all programs. + $ vim ~/.bashrc + +If you're on Windows or macOS your proxy may be getting set at a system level. +To check this first ensure that the above environment variables aren't set +then run the following: + +.. code-block:: bash + + $ python -c 'import urllib.request; print(urllib.request.getproxies())' + +If the output of the above command isn't empty and looks like this: + +.. code-block:: python + + { + "http": "http://127.0.0.1:8888", + "https": "https://127.0.0.1:8888" # <--- This setting is the problem! + } + +Search how to configure proxies on your operating system and change the ``https://...`` URL into ``http://``. +After you make the change the return value of ``urllib.request.getproxies()`` should be: + +.. code-block:: python + + { # Everything is good here! :) + "http": "http://127.0.0.1:8888", + "https": "http://127.0.0.1:8888" + } + +If you still can't figure out how to configure your proxy after all these steps +please `join our community Discord <https://discord.gg/urllib3>`_ and we'll try to help you with your issue. + SOCKS Proxies ~~~~~~~~~~~~~ @@ -382,7 +454,8 @@ Brotli is a compression algorithm created by Google with better compression than gzip and deflate and is supported by urllib3 if the -`brotlipy <https://github.com/python-hyper/brotlipy>`_ package is installed. +`Brotli <https://pypi.org/Brotli>`_ package or +`brotlicffi <https://github.com/python-hyper/brotlicffi>`_ package is installed. You may also request the package be installed via the ``urllib3[brotli]`` extra: .. code-block:: bash diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/docs/index.rst new/urllib3-1.26.9/docs/index.rst --- old/urllib3-1.26.7/docs/index.rst 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/docs/index.rst 2022-03-16 14:25:49.000000000 +0100 @@ -86,7 +86,7 @@ .. code-block:: bash $ git clone git://github.com/urllib3/urllib3.git - $ python setup.py install + $ pip install . Usage ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/setup.cfg new/urllib3-1.26.9/setup.cfg --- old/urllib3-1.26.7/setup.cfg 2021-09-22 20:00:56.037445500 +0200 +++ new/urllib3-1.26.9/setup.cfg 2022-03-16 14:27:36.687475400 +0100 @@ -19,7 +19,9 @@ certifi; extra == 'secure' ipaddress; python_version=="2.7" and extra == 'secure' PySocks>=1.5.6,<2.0,!=1.5.7; extra == 'socks' - brotlipy>=0.6.0; extra == 'brotli' + brotli>=1.0.9; (os_name != 'nt' or python_version >= '3') and platform_python_implementation == 'CPython' and extra == 'brotli' + brotlicffi>=0.8.0; (os_name != 'nt' or python_version >= '3') and platform_python_implementation != 'CPython' and extra == 'brotli' + brotlipy>=0.6.0; os_name == 'nt' and python_version < '3' and extra == 'brotli' [tool:pytest] xfail_strict = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/setup.py new/urllib3-1.26.9/setup.py --- old/urllib3-1.26.7/setup.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/setup.py 2022-03-16 14:25:49.000000000 +0100 @@ -57,6 +57,7 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP", @@ -75,7 +76,6 @@ packages=[ "urllib3", "urllib3.packages", - "urllib3.packages.ssl_match_hostname", "urllib3.packages.backports", "urllib3.contrib", "urllib3.contrib._securetransport", @@ -85,7 +85,11 @@ requires=[], python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4", extras_require={ - "brotli": ["brotlipy>=0.6.0"], + "brotli": [ + "brotli>=1.0.9; (os_name != 'nt' or python_version >= '3') and platform_python_implementation == 'CPython'", + "brotlicffi>=0.8.0; (os_name != 'nt' or python_version >= '3') and platform_python_implementation != 'CPython'", + "brotlipy>=0.6.0; os_name == 'nt' and python_version < '3'", + ], "secure": [ "pyOpenSSL>=0.14", "cryptography>=1.3.4", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/_version.py new/urllib3-1.26.9/src/urllib3/_version.py --- old/urllib3-1.26.7/src/urllib3/_version.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/_version.py 2022-03-16 14:25:49.000000000 +0100 @@ -1,2 +1,2 @@ # This file is protected via CODEOWNERS -__version__ = "1.26.7" +__version__ = "1.26.9" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/connection.py new/urllib3-1.26.9/src/urllib3/connection.py --- old/urllib3-1.26.7/src/urllib3/connection.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/connection.py 2022-03-16 14:25:49.000000000 +0100 @@ -51,7 +51,6 @@ 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 @@ resolve_ssl_version, ssl_wrap_socket, ) +from .util.ssl_match_hostname import CertificateError, match_hostname log = logging.getLogger(__name__) @@ -355,17 +355,15 @@ def connect(self): # Add certificate verification - conn = self._new_conn() + self.sock = conn = self._new_conn() hostname = self.host tls_in_tls = False if self._is_using_tunnel(): if self.tls_in_tls_required: - conn = self._connect_tls_proxy(hostname, conn) + self.sock = conn = self._connect_tls_proxy(hostname, conn) tls_in_tls = True - self.sock = conn - # Calls self._set_hostport(), so self.host is # self._tunnel_host below. self._tunnel() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/connectionpool.py new/urllib3-1.26.9/src/urllib3/connectionpool.py --- old/urllib3-1.26.7/src/urllib3/connectionpool.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/connectionpool.py 2022-03-16 14:25:49.000000000 +0100 @@ -2,6 +2,7 @@ import errno import logging +import re import socket import sys import warnings @@ -35,7 +36,6 @@ ) 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.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 @@ 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 @@ # 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/contrib/_securetransport/bindings.py new/urllib3-1.26.9/src/urllib3/contrib/_securetransport/bindings.py --- old/urllib3-1.26.7/src/urllib3/contrib/_securetransport/bindings.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/contrib/_securetransport/bindings.py 2022-03-16 14:25:49.000000000 +0100 @@ -48,7 +48,7 @@ ) 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/packages/__init__.py new/urllib3-1.26.9/src/urllib3/packages/__init__.py --- old/urllib3-1.26.7/src/urllib3/packages/__init__.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/packages/__init__.py 2022-03-16 14:25:49.000000000 +0100 @@ -1,5 +0,0 @@ -from __future__ import absolute_import - -from . import ssl_match_hostname - -__all__ = ("ssl_match_hostname",) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/packages/ssl_match_hostname/__init__.py new/urllib3-1.26.9/src/urllib3/packages/ssl_match_hostname/__init__.py --- old/urllib3-1.26.7/src/urllib3/packages/ssl_match_hostname/__init__.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/packages/ssl_match_hostname/__init__.py 1970-01-01 01:00:00.000000000 +0100 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/packages/ssl_match_hostname/_implementation.py new/urllib3-1.26.9/src/urllib3/packages/ssl_match_hostname/_implementation.py --- old/urllib3-1.26.7/src/urllib3/packages/ssl_match_hostname/_implementation.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/packages/ssl_match_hostname/_implementation.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,160 +0,0 @@ -"""The match_hostname() function from Python 3.3.3, essential when using SSL.""" - -# Note: This file is under the PSF license as the code comes from the python -# stdlib. http://docs.python.org/3/license.html - -import re -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. -try: - import ipaddress -except ImportError: - ipaddress = None - -__version__ = "3.5.0.1" - - -class CertificateError(ValueError): - pass - - -def _dnsname_match(dn, hostname, max_wildcards=1): - """Matching according to RFC 6125, section 6.4.3 - - http://tools.ietf.org/html/rfc6125#section-6.4.3 - """ - pats = [] - if not dn: - return False - - # Ported from python3-syntax: - # leftmost, *remainder = dn.split(r'.') - parts = dn.split(r".") - leftmost = parts[0] - remainder = parts[1:] - - wildcards = leftmost.count("*") - if wildcards > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survey of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn) - ) - - # speed up common case w/o wildcards - if not wildcards: - return dn.lower() == hostname.lower() - - # RFC 6125, section 6.4.3, subitem 1. - # The client SHOULD NOT attempt to match a presented identifier in which - # the wildcard character comprises a label other than the left-most label. - if leftmost == "*": - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append("[^.]+") - elif leftmost.startswith("xn--") or hostname.startswith("xn--"): - # RFC 6125, section 6.4.3, subitem 3. - # The client SHOULD NOT attempt to match a presented identifier - # where the wildcard character is embedded within an A-label or - # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) - else: - # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) - - # add the remaining fragments, ignore any wildcards - for frag in remainder: - pats.append(re.escape(frag)) - - pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) - return pat.match(hostname) - - -def _to_unicode(obj): - if isinstance(obj, str) and sys.version_info < (3,): - obj = unicode(obj, encoding="ascii", errors="strict") - return obj - - -def _ipaddress_match(ipname, host_ip): - """Exact matching of IP addresses. - - RFC 6125 explicitly doesn't define an algorithm for this - (section 1.7.2 - "Out of Scope"). - """ - # OpenSSL may add a trailing newline to a subjectAltName's IP address - # Divergence from upstream: ipaddress can't handle byte str - ip = ipaddress.ip_address(_to_unicode(ipname).rstrip()) - return ip == host_ip - - -def match_hostname(cert, hostname): - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 - rules are followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError( - "empty or no certificate, match_hostname needs a " - "SSL socket or SSL context with either " - "CERT_OPTIONAL or CERT_REQUIRED" - ) - try: - # Divergence from upstream: ipaddress can't handle byte str - host_ip = ipaddress.ip_address(_to_unicode(hostname)) - except ValueError: - # Not an IP address (common case) - host_ip = None - except UnicodeError: - # Divergence from upstream: Have to deal with ipaddress not taking - # byte strings. addresses should be all ascii, so we consider it not - # an ipaddress in this case - host_ip = None - except AttributeError: - # Divergence from upstream: Make ipaddress library optional - if ipaddress is None: - host_ip = None - else: - raise - dnsnames = [] - san = cert.get("subjectAltName", ()) - for key, value in san: - if key == "DNS": - if host_ip is None and _dnsname_match(value, hostname): - return - dnsnames.append(value) - elif key == "IP Address": - if host_ip is not None and _ipaddress_match(value, host_ip): - return - dnsnames.append(value) - if not dnsnames: - # The subject is only checked when there is no dNSName entry - # in subjectAltName - for sub in cert.get("subject", ()): - for key, value in sub: - # XXX according to RFC 2818, the most specific Common Name - # must be used. - if key == "commonName": - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if len(dnsnames) > 1: - raise CertificateError( - "hostname %r " - "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) - ) - elif len(dnsnames) == 1: - raise CertificateError("hostname %r doesn't match %r" % (hostname, dnsnames[0])) - else: - raise CertificateError( - "no appropriate commonName or subjectAltName fields were found" - ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/poolmanager.py new/urllib3-1.26.9/src/urllib3/poolmanager.py --- old/urllib3-1.26.7/src/urllib3/poolmanager.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/poolmanager.py 2022-03-16 14:25:49.000000000 +0100 @@ -34,6 +34,7 @@ "ca_cert_dir", "ssl_context", "key_password", + "server_hostname", ) # All known keyword arguments that could be provided to the pool manager, its diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/response.py new/urllib3-1.26.9/src/urllib3/response.py --- old/urllib3-1.26.7/src/urllib3/response.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/response.py 2022-03-16 14:25:49.000000000 +0100 @@ -8,7 +8,10 @@ from socket import timeout as SocketTimeout try: - import brotli + try: + import brotlicffi as brotli + except ImportError: + import brotli except ImportError: brotli = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/util/connection.py new/urllib3-1.26.9/src/urllib3/util/connection.py --- old/urllib3-1.26.7/src/urllib3/util/connection.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/util/connection.py 2022-03-16 14:25:49.000000000 +0100 @@ -2,9 +2,8 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/util/request.py new/urllib3-1.26.9/src/urllib3/util/request.py --- old/urllib3-1.26.7/src/urllib3/util/request.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/util/request.py 2022-03-16 14:25:49.000000000 +0100 @@ -14,7 +14,10 @@ ACCEPT_ENCODING = "gzip,deflate" try: - import brotli as _unused_module_brotli # noqa: F401 + try: + import brotlicffi as _unused_module_brotli # noqa: F401 + except ImportError: + import brotli as _unused_module_brotli # noqa: F401 except ImportError: pass else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/util/retry.py new/urllib3-1.26.9/src/urllib3/util/retry.py --- old/urllib3-1.26.7/src/urllib3/util/retry.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/util/retry.py 2022-03-16 14:25:49.000000000 +0100 @@ -69,6 +69,24 @@ ) 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 @@ 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 @@ DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset(["Authorization"]) #: Maximum backoff time. - BACKOFF_MAX = 120 + DEFAULT_BACKOFF_MAX = 120 def __init__( self, @@ -348,7 +366,7 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/util/ssl_match_hostname.py new/urllib3-1.26.9/src/urllib3/util/ssl_match_hostname.py --- old/urllib3-1.26.7/src/urllib3/util/ssl_match_hostname.py 1970-01-01 01:00:00.000000000 +0100 +++ new/urllib3-1.26.9/src/urllib3/util/ssl_match_hostname.py 2022-03-16 14:25:49.000000000 +0100 @@ -0,0 +1,159 @@ +"""The match_hostname() function from Python 3.3.3, essential when using SSL.""" + +# Note: This file is under the PSF license as the code comes from the python +# stdlib. http://docs.python.org/3/license.html + +import re +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 +# util.ssl_match_hostname to continue to be used in Python 2.7. +try: + import ipaddress +except ImportError: + ipaddress = None + +__version__ = "3.5.0.1" + + +class CertificateError(ValueError): + pass + + +def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r".") + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count("*") + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn) + ) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == "*": + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append("[^.]+") + elif leftmost.startswith("xn--") or hostname.startswith("xn--"): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) + return pat.match(hostname) + + +def _to_unicode(obj): + if isinstance(obj, str) and sys.version_info < (3,): + # ignored flake8 # F821 to support python 2.7 function + obj = unicode(obj, encoding="ascii", errors="strict") # noqa: F821 + return obj + + +def _ipaddress_match(ipname, host_ip): + """Exact matching of IP addresses. + + RFC 6125 explicitly doesn't define an algorithm for this + (section 1.7.2 - "Out of Scope"). + """ + # OpenSSL may add a trailing newline to a subjectAltName's IP address + # Divergence from upstream: ipaddress can't handle byte str + ip = ipaddress.ip_address(_to_unicode(ipname).rstrip()) + return ip == host_ip + + +def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError( + "empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED" + ) + try: + # Divergence from upstream: ipaddress can't handle byte str + host_ip = ipaddress.ip_address(_to_unicode(hostname)) + except (UnicodeError, ValueError): + # ValueError: Not an IP address (common case) + # UnicodeError: Divergence from upstream: Have to deal with ipaddress not taking + # byte strings. addresses should be all ascii, so we consider it not + # an ipaddress in this case + host_ip = None + except AttributeError: + # Divergence from upstream: Make ipaddress library optional + if ipaddress is None: + host_ip = None + else: # Defensive + raise + dnsnames = [] + san = cert.get("subjectAltName", ()) + for key, value in san: + if key == "DNS": + if host_ip is None and _dnsname_match(value, hostname): + return + dnsnames.append(value) + elif key == "IP Address": + if host_ip is not None and _ipaddress_match(value, host_ip): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get("subject", ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == "commonName": + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError( + "hostname %r " + "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) + ) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r doesn't match %r" % (hostname, dnsnames[0])) + else: + raise CertificateError( + "no appropriate commonName or subjectAltName fields were found" + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3/util/ssltransport.py new/urllib3-1.26.9/src/urllib3/util/ssltransport.py --- old/urllib3-1.26.7/src/urllib3/util/ssltransport.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3/util/ssltransport.py 2022-03-16 14:25:49.000000000 +0100 @@ -2,8 +2,8 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3.egg-info/PKG-INFO new/urllib3-1.26.9/src/urllib3.egg-info/PKG-INFO --- old/urllib3-1.26.7/src/urllib3.egg-info/PKG-INFO 2021-09-22 20:00:55.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3.egg-info/PKG-INFO 2022-03-16 14:27:36.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: urllib3 -Version: 1.26.7 +Version: 1.26.9 Summary: HTTP library with thread-safe connection pooling, file post, and more. Home-page: https://urllib3.readthedocs.io/ Author: Andrey Petrov @@ -25,6 +25,7 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Internet :: WWW/HTTP @@ -74,7 +75,7 @@ Alternatively, you can grab the latest source code from `GitHub <https://github.com/urllib3/urllib3>`_:: $ git clone git://github.com/urllib3/urllib3.git - $ python setup.py install + $ pip install . Documentation @@ -143,8 +144,35 @@ Changes ======= +1.26.9 (2022-03-16) +------------------- + +* Changed ``urllib3[brotli]`` extra to favor installing Brotli libraries that are still + receiving updates like ``brotli`` and ``brotlicffi`` instead of ``brotlipy``. + This change does not impact behavior of urllib3, only which dependencies are installed. +* Fixed a socket leaking when ``HTTPSConnection.connect()`` raises an exception. +* Fixed ``server_hostname`` being forwarded from ``PoolManager`` to ``HTTPConnectionPool`` + when requesting an HTTP URL. Should only be forwarded when requesting an HTTPS URL. + + +1.26.8 (2022-01-07) +------------------- + +* Added extra message to ``urllib3.exceptions.ProxyError`` when urllib3 detects that + a proxy is configured to use HTTPS but the proxy itself appears to only use HTTP. +* Added a mention of the size of the connection pool when discarding a connection due to the pool being full. +* Added explicit support for Python 3.11. +* Deprecated the ``Retry.MAX_BACKOFF`` class property in favor of ``Retry.DEFAULT_MAX_BACKOFF`` + to better match the rest of the default parameter names. ``Retry.MAX_BACKOFF`` is removed in v2.0. +* Changed location of the vendored ``ssl.match_hostname`` function from ``urllib3.packages.ssl_match_hostname`` + to ``urllib3.util.ssl_match_hostname`` to ensure Python 3.10+ compatibility after being repackaged + by downstream distributors. +* Fixed absolute imports, all imports are now relative. + + 1.26.7 (2021-09-22) ------------------- + * Fixed a bug with HTTPS hostname verification involving IP addresses and lack of SNI. (Issue #2400) * Fixed a bug where IPv6 braces weren't stripped during certificate hostname diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3.egg-info/SOURCES.txt new/urllib3-1.26.9/src/urllib3.egg-info/SOURCES.txt --- old/urllib3-1.26.7/src/urllib3.egg-info/SOURCES.txt 2021-09-22 20:00:55.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3.egg-info/SOURCES.txt 2022-03-16 14:27:36.000000000 +0100 @@ -77,8 +77,6 @@ src/urllib3/packages/six.py src/urllib3/packages/backports/__init__.py src/urllib3/packages/backports/makefile.py -src/urllib3/packages/ssl_match_hostname/__init__.py -src/urllib3/packages/ssl_match_hostname/_implementation.py src/urllib3/util/__init__.py src/urllib3/util/connection.py src/urllib3/util/proxy.py @@ -87,6 +85,7 @@ src/urllib3/util/response.py src/urllib3/util/retry.py src/urllib3/util/ssl_.py +src/urllib3/util/ssl_match_hostname.py src/urllib3/util/ssltransport.py src/urllib3/util/timeout.py src/urllib3/util/url.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/src/urllib3.egg-info/requires.txt new/urllib3-1.26.9/src/urllib3.egg-info/requires.txt --- old/urllib3-1.26.7/src/urllib3.egg-info/requires.txt 2021-09-22 20:00:55.000000000 +0200 +++ new/urllib3-1.26.9/src/urllib3.egg-info/requires.txt 2022-03-16 14:27:36.000000000 +0100 @@ -1,5 +1,13 @@ [brotli] + +[brotli:(os_name != "nt" or python_version >= "3") and platform_python_implementation != "CPython"] +brotlicffi>=0.8.0 + +[brotli:(os_name != "nt" or python_version >= "3") and platform_python_implementation == "CPython"] +brotli>=1.0.9 + +[brotli:os_name == "nt" and python_version < "3"] brotlipy>=0.6.0 [secure] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/__init__.py new/urllib3-1.26.9/test/__init__.py --- old/urllib3-1.26.7/test/__init__.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/__init__.py 2022-03-16 14:25:49.000000000 +0100 @@ -10,7 +10,10 @@ import pytest try: - import brotli + try: + import brotlicffi as brotli + except ImportError: + import brotli except ImportError: brotli = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/conftest.py new/urllib3-1.26.9/test/conftest.py --- old/urllib3-1.26.7/test/conftest.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/conftest.py 2022-03-16 14:25:49.000000000 +0100 @@ -103,6 +103,16 @@ yield cfg +@pytest.fixture() +def no_san_server_with_different_commmon_name(tmp_path_factory): + tmpdir = tmp_path_factory.mktemp("certs") + ca = trustme.CA() + server_cert = ca.issue_cert(common_name=u"example.com") + + with run_server_in_thread("https", "localhost", tmpdir, ca, server_cert) as cfg: + yield cfg + + @pytest.fixture def no_san_proxy(tmp_path_factory): tmpdir = tmp_path_factory.mktemp("certs") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/test_connection.py new/urllib3-1.26.9/test/test_connection.py --- old/urllib3-1.26.7/test/test_connection.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/test_connection.py 2022-03-16 14:25:49.000000000 +0100 @@ -28,6 +28,12 @@ asserted_hostname = "foo" _match_hostname(cert, asserted_hostname) + def test_match_hostname_ipaddress_none(self): + cert = {"subjectAltName": [("DNS", "foo")]} + asserted_hostname = "foo" + with mock.patch("urllib3.util.ssl_match_hostname.ipaddress", None): + assert _match_hostname(cert, asserted_hostname) is None + def test_match_hostname_mismatch(self): cert = {"subjectAltName": [("DNS", "foo")]} asserted_hostname = "bar" @@ -79,6 +85,51 @@ # Assert no error is raised _match_hostname(cert, asserted_hostname) + def test_match_hostname_ip_address(self): + cert = {"subjectAltName": [("IP Address", "1.1.1.1")]} + asserted_hostname = "1.1.1.2" + try: + with mock.patch("urllib3.connection.log.warning") as mock_log: + _match_hostname(cert, asserted_hostname) + except CertificateError as e: + assert "hostname '1.1.1.2' doesn't match '1.1.1.1'" in str(e) + mock_log.assert_called_once_with( + "Certificate did not match expected hostname: %s. Certificate: %s", + "1.1.1.2", + {"subjectAltName": [("IP Address", "1.1.1.1")]}, + ) + assert e._peer_cert == cert + + def test_match_hostname_no_dns(self): + cert = {"subjectAltName": [("DNS", "")]} + asserted_hostname = "bar" + try: + with mock.patch("urllib3.connection.log.warning") as mock_log: + _match_hostname(cert, asserted_hostname) + except CertificateError as e: + assert "hostname 'bar' doesn't match ''" in str(e) + mock_log.assert_called_once_with( + "Certificate did not match expected hostname: %s. Certificate: %s", + "bar", + {"subjectAltName": [("DNS", "")]}, + ) + assert e._peer_cert == cert + + def test_match_hostname_startwith_wildcard(self): + cert = {"subjectAltName": [("DNS", "*")]} + asserted_hostname = "foo" + _match_hostname(cert, asserted_hostname) + + def test_match_hostname_dnsname(self): + cert = {"subjectAltName": [("DNS", "xn--p1b6ci4b4b3a*.xn--11b5bs8d")]} + asserted_hostname = "xn--p1b6ci4b4b3a*.xn--11b5bs8d" + _match_hostname(cert, asserted_hostname) + + def test_match_hostname_include_wildcard(self): + cert = {"subjectAltName": [("DNS", "foo*")]} + asserted_hostname = "foobar" + _match_hostname(cert, asserted_hostname) + def test_recent_date(self): # This test is to make sure that the RECENT_DATE value # doesn't get too far behind what the current date is. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/test_connectionpool.py new/urllib3-1.26.9/test/test_connectionpool.py --- old/urllib3-1.26.7/test/test_connectionpool.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/test_connectionpool.py 2022-03-16 14:25:49.000000000 +0100 @@ -29,8 +29,8 @@ from urllib3.packages.six.moves import http_client as httplib from urllib3.packages.six.moves.http_client import HTTPException from urllib3.packages.six.moves.queue import Empty -from urllib3.packages.ssl_match_hostname import CertificateError from urllib3.response import HTTPResponse +from urllib3.util.ssl_match_hostname import CertificateError from urllib3.util.timeout import Timeout from .test_response import MockChunkedEncodingResponse, MockSock @@ -225,7 +225,7 @@ assert pool.num_connections == 1 - def test_pool_edgecases(self): + def test_pool_edgecases(self, caplog): with HTTPConnectionPool(host="localhost", maxsize=1, block=False) as pool: conn1 = pool._get_conn() conn2 = pool._get_conn() # New because block=False @@ -237,6 +237,8 @@ assert conn2 != pool._get_conn() assert pool.num_connections == 3 + assert "Connection pool is full, discarding connection" in caplog.text + assert "Connection pool size: 1" in caplog.text def test_exception_str(self): assert ( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/test_retry.py new/urllib3-1.26.9/test/test_retry.py --- old/urllib3-1.26.7/test/test_retry.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/test_retry.py 2022-03-16 14:25:49.000000000 +0100 @@ -141,7 +141,7 @@ def test_backoff(self): """Backoff is computed correctly""" - max_backoff = Retry.BACKOFF_MAX + max_backoff = Retry.DEFAULT_BACKOFF_MAX retry = Retry(total=100, backoff_factor=0.2) assert retry.get_backoff_time() == 0 # First request diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/test_retry_deprecated.py new/urllib3-1.26.9/test/test_retry_deprecated.py --- old/urllib3-1.26.7/test/test_retry_deprecated.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/test_retry_deprecated.py 2022-03-16 14:25:49.000000000 +0100 @@ -143,7 +143,7 @@ def test_backoff(self): """Backoff is computed correctly""" - max_backoff = Retry.BACKOFF_MAX + max_backoff = Retry.DEFAULT_BACKOFF_MAX retry = Retry(total=100, backoff_factor=0.2) assert retry.get_backoff_time() == 0 # First request @@ -384,6 +384,9 @@ == Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST ) + def test_cls_get_default_backoff_max(self, expect_retry_deprecation): + assert Retry.DEFAULT_BACKOFF_MAX == Retry.BACKOFF_MAX + def test_cls_set_default_method_whitelist(self, expect_retry_deprecation): old_setting = Retry.DEFAULT_METHOD_WHITELIST try: @@ -429,6 +432,17 @@ Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST = old_setting assert Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST == old_setting + def test_cls_set_default_backoff_max(self, expect_retry_deprecation): + old_setting = Retry.BACKOFF_MAX + try: + Retry.BACKOFF_MAX = 99 + retry = Retry() + assert retry.DEFAULT_BACKOFF_MAX == 99 + assert retry.BACKOFF_MAX == 99 + finally: + Retry.BACKOFF_MAX = old_setting + assert Retry.BACKOFF_MAX == old_setting + @pytest.mark.parametrize( "options", [(None, None), ({"GET"}, None), (None, {"GET"}), ({"GET"}, {"GET"})] ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/with_dummyserver/test_https.py new/urllib3-1.26.9/test/with_dummyserver/test_https.py --- old/urllib3-1.26.7/test/with_dummyserver/test_https.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/with_dummyserver/test_https.py 2022-03-16 14:25:49.000000000 +0100 @@ -336,6 +336,22 @@ "Expected 'certificate verify failed', instead got: %r" % e.value.reason ) + def test_wrap_socket_failure_resource_leak(self): + with HTTPSConnectionPool( + self.host, + self.port, + cert_reqs="CERT_REQUIRED", + ca_certs=self.bad_ca_path, + ) as https_pool: + conn = https_pool._get_conn() + try: + with pytest.raises(ssl.SSLError): + conn.connect() + + assert conn.sock + finally: + conn.close() + def test_verified_without_ca_certs(self): # default is cert_reqs=None which is ssl.CERT_NONE with HTTPSConnectionPool( @@ -858,6 +874,19 @@ assert r.status == 200 assert warn.called + def test_common_name_without_san_with_different_common_name( + self, no_san_server_with_different_commmon_name + ): + with HTTPSConnectionPool( + no_san_server_with_different_commmon_name.host, + no_san_server_with_different_commmon_name.port, + cert_reqs="CERT_REQUIRED", + ca_certs=no_san_server_with_different_commmon_name.ca_certs, + ) as https_pool: + with pytest.raises(MaxRetryError) as cm: + https_pool.request("GET", "/") + assert isinstance(cm.value.reason, SSLError) + class TestHTTPS_IPV4SAN: def test_can_validate_ip_san(self, ipv4_san_server): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/with_dummyserver/test_poolmanager.py new/urllib3-1.26.9/test/with_dummyserver/test_poolmanager.py --- old/urllib3-1.26.7/test/with_dummyserver/test_poolmanager.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/with_dummyserver/test_poolmanager.py 2022-03-16 14:25:49.000000000 +0100 @@ -346,6 +346,11 @@ r = http.request("GET", "http://%s:%s/" % (self.host, self.port)) assert r.status == 200 + def test_http_with_server_hostname(self): + with PoolManager(server_hostname="example.com") as http: + r = http.request("GET", "http://%s:%s/" % (self.host, self.port)) + assert r.status == 200 + def test_http_with_ca_cert_dir(self): with PoolManager(ca_certs="REQUIRED", ca_cert_dir="/nosuchdir") as http: r = http.request("GET", "http://%s:%s/" % (self.host, self.port)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/with_dummyserver/test_proxy_poolmanager.py new/urllib3-1.26.9/test/with_dummyserver/test_proxy_poolmanager.py --- old/urllib3-1.26.7/test/with_dummyserver/test_proxy_poolmanager.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/with_dummyserver/test_proxy_poolmanager.py 2022-03-16 14:25:49.000000000 +0100 @@ -2,6 +2,8 @@ import os.path import shutil import socket +import ssl +import sys import tempfile import warnings from test import ( @@ -27,10 +29,12 @@ ProxyError, ProxySchemeUnknown, ProxySchemeUnsupported, + ReadTimeoutError, SSLError, SubjectAltNameWarning, ) from urllib3.poolmanager import ProxyManager, proxy_from_url +from urllib3.util import Timeout from urllib3.util.ssl_ import create_urllib3_context from .. import TARPIT_HOST, requires_network @@ -497,24 +501,160 @@ assert sc2 != sc3 assert sc3 == sc4 - @pytest.mark.timeout(0.5) @requires_network - def test_https_proxy_timeout(self): - with proxy_from_url("https://{host}".format(host=TARPIT_HOST)) as https: + @pytest.mark.parametrize( + ["proxy_scheme", "target_scheme", "use_forwarding_for_https"], + [ + ("http", "http", False), + ("https", "http", False), + # 'use_forwarding_for_https' is only valid for HTTPS+HTTPS. + ("https", "https", True), + ], + ) + def test_forwarding_proxy_request_timeout( + self, proxy_scheme, target_scheme, use_forwarding_for_https + ): + _should_skip_https_in_https( + proxy_scheme, target_scheme, use_forwarding_for_https + ) + + proxy_url = self.https_proxy_url if proxy_scheme == "https" else self.proxy_url + target_url = "%s://%s" % (target_scheme, TARPIT_HOST) + + with proxy_from_url( + proxy_url, + ca_certs=DEFAULT_CA, + use_forwarding_for_https=use_forwarding_for_https, + ) as proxy: + with pytest.raises(MaxRetryError) as e: + timeout = Timeout(connect=LONG_TIMEOUT, read=SHORT_TIMEOUT) + proxy.request("GET", target_url, timeout=timeout) + + # We sent the request to the proxy but didn't get any response + # so we're not sure if that's being caused by the proxy or the + # target so we put the blame on the target. + assert type(e.value.reason) == ReadTimeoutError + + @requires_network + @pytest.mark.parametrize( + ["proxy_scheme", "target_scheme"], [("http", "https"), ("https", "https")] + ) + def test_tunneling_proxy_request_timeout(self, proxy_scheme, target_scheme): + _should_skip_https_in_https(proxy_scheme, target_scheme) + + proxy_url = self.https_proxy_url if proxy_scheme == "https" else self.proxy_url + target_url = "%s://%s" % (target_scheme, TARPIT_HOST) + + with proxy_from_url( + proxy_url, + ca_certs=DEFAULT_CA, + ) as proxy: + with pytest.raises(MaxRetryError) as e: + timeout = Timeout(connect=LONG_TIMEOUT, read=SHORT_TIMEOUT) + proxy.request("GET", target_url, timeout=timeout) + + assert type(e.value.reason) == ProxyError + assert type(e.value.reason.original_error) == socket.timeout + + @requires_network + @pytest.mark.parametrize( + ["proxy_scheme", "target_scheme", "use_forwarding_for_https"], + [ + ("http", "http", False), + ("https", "http", False), + # 'use_forwarding_for_https' is only valid for HTTPS+HTTPS. + ("https", "https", True), + ], + ) + def test_forwarding_proxy_connect_timeout( + self, proxy_scheme, target_scheme, use_forwarding_for_https + ): + _should_skip_https_in_https( + proxy_scheme, target_scheme, use_forwarding_for_https + ) + + proxy_url = "%s://%s" % (proxy_scheme, TARPIT_HOST) + target_url = self.https_url if target_scheme == "https" else self.http_url + + with proxy_from_url( + proxy_url, + ca_certs=DEFAULT_CA, + timeout=SHORT_TIMEOUT, + use_forwarding_for_https=use_forwarding_for_https, + ) as proxy: with pytest.raises(MaxRetryError) as e: - https.request("GET", self.http_url, timeout=SHORT_TIMEOUT) + proxy.request("GET", target_url) + assert type(e.value.reason) == ConnectTimeoutError - @pytest.mark.timeout(0.5) @requires_network - def test_https_proxy_pool_timeout(self): + @pytest.mark.parametrize( + ["proxy_scheme", "target_scheme"], [("http", "https"), ("https", "https")] + ) + def test_tunneling_proxy_connect_timeout(self, proxy_scheme, target_scheme): + _should_skip_https_in_https(proxy_scheme, target_scheme) + + proxy_url = "%s://%s" % (proxy_scheme, TARPIT_HOST) + target_url = self.https_url if target_scheme == "https" else self.http_url + with proxy_from_url( - "https://{host}".format(host=TARPIT_HOST), timeout=SHORT_TIMEOUT - ) as https: + proxy_url, ca_certs=DEFAULT_CA, timeout=SHORT_TIMEOUT + ) as proxy: with pytest.raises(MaxRetryError) as e: - https.request("GET", self.http_url) + proxy.request("GET", target_url) + assert type(e.value.reason) == ConnectTimeoutError + @requires_network + @pytest.mark.parametrize( + ["target_scheme", "use_forwarding_for_https"], + [ + ("http", False), + ("https", False), + ("https", True), + ], + ) + def test_https_proxy_tls_error(self, target_scheme, use_forwarding_for_https): + _should_skip_https_in_https("https", target_scheme, use_forwarding_for_https) + + target_url = self.https_url if target_scheme == "https" else self.http_url + proxy_ctx = ssl.create_default_context() + with proxy_from_url( + self.https_proxy_url, + proxy_ssl_context=proxy_ctx, + use_forwarding_for_https=use_forwarding_for_https, + ) as proxy: + with pytest.raises(MaxRetryError) as e: + proxy.request("GET", target_url) + assert type(e.value.reason) == SSLError + + @requires_network + @pytest.mark.parametrize( + ["proxy_scheme", "use_forwarding_for_https"], + [ + ("http", False), + ("https", False), + ("https", True), + ], + ) + def test_proxy_https_target_tls_error(self, proxy_scheme, use_forwarding_for_https): + _should_skip_https_in_https(proxy_scheme, "https") + + proxy_url = self.https_proxy_url if proxy_scheme == "https" else self.proxy_url + proxy_ctx = ssl.create_default_context() + proxy_ctx.load_verify_locations(DEFAULT_CA) + ctx = ssl.create_default_context() + + with proxy_from_url( + proxy_url, + proxy_ssl_context=proxy_ctx, + ssl_context=ctx, + use_forwarding_for_https=use_forwarding_for_https, + ) as proxy: + with pytest.raises(MaxRetryError) as e: + proxy.request("GET", self.https_url) + assert type(e.value.reason) == SSLError + def test_scheme_host_case_insensitive(self): """Assert that upper-case schemes and hosts are normalized.""" with proxy_from_url(self.proxy_url.upper(), ca_certs=DEFAULT_CA) as http: @@ -618,3 +758,15 @@ assert len(w) == 1 assert w[0].category == SubjectAltNameWarning + + +def _should_skip_https_in_https( + proxy_scheme, target_scheme, use_forwarding_for_https=False +): + if ( + sys.version_info[0] == 2 + and proxy_scheme == "https" + and target_scheme == "https" + and use_forwarding_for_https is False + ): + pytest.skip("HTTPS-in-HTTPS isn't supported on Python 2") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.7/test/with_dummyserver/test_socketlevel.py new/urllib3-1.26.9/test/with_dummyserver/test_socketlevel.py --- old/urllib3-1.26.7/test/with_dummyserver/test_socketlevel.py 2021-09-22 20:00:21.000000000 +0200 +++ new/urllib3-1.26.9/test/with_dummyserver/test_socketlevel.py 2022-03-16 14:25:49.000000000 +0100 @@ -7,7 +7,7 @@ get_unreachable_address, ) from dummyserver.testcase import SocketDummyServerTestCase, consume_socket -from urllib3 import HTTPConnectionPool, HTTPSConnectionPool, util +from urllib3 import HTTPConnectionPool, HTTPSConnectionPool, ProxyManager, util from urllib3._collections import HTTPHeaderDict from urllib3.connection import HTTPConnection, _get_default_user_agent from urllib3.exceptions import ( @@ -39,6 +39,7 @@ import shutil import socket import ssl +import sys import tempfile from collections import OrderedDict from test import ( @@ -1160,6 +1161,33 @@ except MaxRetryError: self.fail("Invalid IPv6 format in HTTP CONNECT request") + @pytest.mark.parametrize("target_scheme", ["http", "https"]) + def test_https_proxymanager_connected_to_http_proxy(self, target_scheme): + if target_scheme == "https" and sys.version_info[0] == 2: + pytest.skip("HTTPS-in-HTTPS isn't supported on Python 2") + + errored = Event() + + def http_socket_handler(listener): + sock = listener.accept()[0] + sock.send(b"HTTP/1.0 501 Not Implemented\r\nConnection: close\r\n\r\n") + errored.wait() + sock.close() + + self._start_server(http_socket_handler) + base_url = "https://%s:%d" % (self.host, self.port) + + with ProxyManager(base_url, cert_reqs="NONE") as proxy: + with pytest.raises(MaxRetryError) as e: + proxy.request("GET", "%s://example.com" % target_scheme, retries=0) + + errored.set() # Avoid a ConnectionAbortedError on Windows. + + assert type(e.value.reason) == ProxyError + assert "Your proxy appears to only use HTTP and not HTTPS" in str( + e.value.reason + ) + class TestSSL(SocketDummyServerTestCase): def test_ssl_failure_midway_through_conn(self):