diff options
| author | Ratan Kulshreshtha <ratan.shreshtha@gmail.com> | 2019-05-25 03:13:49 +0530 |
|---|---|---|
| committer | Ratan Kulshreshtha <ratan.shreshtha@gmail.com> | 2019-05-25 03:13:49 +0530 |
| commit | d8a63d0ae7b89d69709309fdbd038a414a5ec63e (patch) | |
| tree | 7c692cde117be7a5414695c7a64508459bc79819 | |
| parent | 266b347d393a16684f2b5abb87aeb5b13ca4f0b7 (diff) | |
| parent | 337992aba77104fb84e7b14f5a2c9aa1d3039415 (diff) | |
| download | urllib3-d8a63d0ae7b89d69709309fdbd038a414a5ec63e.tar.gz | |
Merge branch 'master' into blacken
| -rw-r--r-- | .github/FUNDING.yml | 1 | ||||
| -rw-r--r-- | CHANGES.rst | 10 | ||||
| -rw-r--r-- | docs/contributing.rst | 4 | ||||
| -rw-r--r-- | src/urllib3/__init__.py | 2 | ||||
| -rw-r--r-- | src/urllib3/connection.py | 31 | ||||
| -rw-r--r-- | test/with_dummyserver/test_socketlevel.py | 88 |
6 files changed, 129 insertions, 7 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..554e65fb --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +tidelift: pypi/urllib3 diff --git a/CHANGES.rst b/CHANGES.rst index 09409b66..a7724471 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,10 +1,14 @@ Changes ======= -dev (master) ------------- +1.25.3 (2019-05-23) +------------------- + +* Change ``HTTPSConnection`` to load system CA certificates + when ``ca_certs``, ``ca_cert_dir``, and ``ssl_context`` are + unspecified. (Pull #1608, Issue #1603) -* Upgrade bundled rfc3986 to 1.3.2. (Pull #1609, Issue #1605) +* Upgrade bundled rfc3986 to v1.3.2. (Pull #1609, Issue #1605) 1.25.2 (2019-04-28) diff --git a/docs/contributing.rst b/docs/contributing.rst index 39e47d7e..c28bc479 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -77,8 +77,8 @@ named ``release-x.x`` where ``x.x`` is the version of the proposed release. - Integration tests are run against the release candidate on Travis. From here on all the steps below will be handled by a maintainer so unless you receive review comments you are done here. -- Once the pull request is squash merged into master the merging maintainer the - pull request will tag the merge commit with the version number: +- Once the pull request is squash merged into master the merging maintainer + will tag the merge commit with the version number: - ``git tag -a 1.24.1 [commit sha]`` - ``git push origin master --tags`` diff --git a/src/urllib3/__init__.py b/src/urllib3/__init__.py index 5a23d503..63e5026f 100644 --- a/src/urllib3/__init__.py +++ b/src/urllib3/__init__.py @@ -22,7 +22,7 @@ from logging import NullHandler __author__ = "Andrey Petrov (andrey.petrov@shazow.net)" __license__ = "MIT" -__version__ = "1.25.2" +__version__ = "1.25.3" __all__ = ( "HTTPConnectionPool", diff --git a/src/urllib3/connection.py b/src/urllib3/connection.py index 94e95c67..f0396f25 100644 --- a/src/urllib3/connection.py +++ b/src/urllib3/connection.py @@ -257,11 +257,27 @@ class HTTPSConnection(HTTPConnection): conn = self._new_conn() self._prepare_conn(conn) + # Wrap socket using verification with the root certs in + # trusted_root_certs + default_ssl_context = False if self.ssl_context is None: + default_ssl_context = True self.ssl_context = create_urllib3_context( - ssl_version=resolve_ssl_version(None), cert_reqs=resolve_cert_reqs(None) + ssl_version=resolve_ssl_version(self.ssl_version), + cert_reqs=resolve_cert_reqs(self.cert_reqs), ) + # Try to load OS default certs if none are given. + # Works well on Windows (requires Python3.4+) + context = self.ssl_context + if ( + not self.ca_certs + and not self.ca_cert_dir + and default_ssl_context + and hasattr(context, "load_default_certs") + ): + context.load_default_certs() + self.sock = ssl_wrap_socket( sock=conn, keyfile=self.key_file, @@ -348,7 +364,9 @@ class VerifiedHTTPSConnection(HTTPSConnection): # Wrap socket using verification with the root certs in # trusted_root_certs + default_ssl_context = False if self.ssl_context is None: + default_ssl_context = True self.ssl_context = create_urllib3_context( ssl_version=resolve_ssl_version(self.ssl_version), cert_reqs=resolve_cert_reqs(self.cert_reqs), @@ -356,6 +374,17 @@ class VerifiedHTTPSConnection(HTTPSConnection): context = self.ssl_context context.verify_mode = resolve_cert_reqs(self.cert_reqs) + + # Try to load OS default certs if none are given. + # Works well on Windows (requires Python3.4+) + if ( + not self.ca_certs + and not self.ca_cert_dir + and default_ssl_context + and hasattr(context, "load_default_certs") + ): + context.load_default_certs() + self.sock = ssl_wrap_socket( sock=conn, keyfile=self.key_file, diff --git a/test/with_dummyserver/test_socketlevel.py b/test/with_dummyserver/test_socketlevel.py index 30e7ee75..76637a38 100644 --- a/test/with_dummyserver/test_socketlevel.py +++ b/test/with_dummyserver/test_socketlevel.py @@ -41,6 +41,7 @@ from threading import Event import select import socket import ssl +import mock import pytest @@ -1331,6 +1332,93 @@ class TestSSL(SocketDummyServerTestCase): response = pool.urlopen("GET", "/", retries=1) self.assertEqual(response.data, b"Success") + def test_ssl_load_default_certs_when_empty(self): + def socket_handler(listener): + sock = listener.accept()[0] + ssl_sock = ssl.wrap_socket( + sock, + server_side=True, + keyfile=DEFAULT_CERTS["keyfile"], + certfile=DEFAULT_CERTS["certfile"], + ca_certs=DEFAULT_CA, + ) + + buf = b"" + while not buf.endswith(b"\r\n\r\n"): + buf += ssl_sock.recv(65536) + + ssl_sock.send( + b"HTTP/1.1 200 OK\r\n" + b"Content-Type: text/plain\r\n" + b"Content-Length: 5\r\n\r\n" + b"Hello" + ) + + ssl_sock.close() + sock.close() + + context = mock.create_autospec(ssl_.SSLContext) + context.load_default_certs = mock.Mock() + context.options = 0 + + with mock.patch("urllib3.util.ssl_.SSLContext", lambda *_, **__: context): + + self._start_server(socket_handler) + pool = HTTPSConnectionPool(self.host, self.port) + self.addCleanup(pool.close) + + with self.assertRaises(MaxRetryError): + pool.request("GET", "/", timeout=0.01) + + context.load_default_certs.assert_called_with() + + def test_ssl_dont_load_default_certs_when_given(self): + def socket_handler(listener): + sock = listener.accept()[0] + ssl_sock = ssl.wrap_socket( + sock, + server_side=True, + keyfile=DEFAULT_CERTS["keyfile"], + certfile=DEFAULT_CERTS["certfile"], + ca_certs=DEFAULT_CA, + ) + + buf = b"" + while not buf.endswith(b"\r\n\r\n"): + buf += ssl_sock.recv(65536) + + ssl_sock.send( + b"HTTP/1.1 200 OK\r\n" + b"Content-Type: text/plain\r\n" + b"Content-Length: 5\r\n\r\n" + b"Hello" + ) + + ssl_sock.close() + sock.close() + + context = mock.create_autospec(ssl_.SSLContext) + context.load_default_certs = mock.Mock() + context.options = 0 + + with mock.patch("urllib3.util.ssl_.SSLContext", lambda *_, **__: context): + for kwargs in [ + {"ca_certs": "/a"}, + {"ca_cert_dir": "/a"}, + {"ca_certs": "a", "ca_cert_dir": "a"}, + {"ssl_context": context}, + ]: + + self._start_server(socket_handler) + + pool = HTTPSConnectionPool(self.host, self.port, **kwargs) + self.addCleanup(pool.close) + + with self.assertRaises(MaxRetryError): + pool.request("GET", "/", timeout=0.01) + + context.load_default_certs.assert_not_called() + class TestErrorWrapping(SocketDummyServerTestCase): def test_bad_statusline(self): |
