Skip to content

Commit

Permalink
🔖 Release 2.1.900
Browse files Browse the repository at this point in the history
  • Loading branch information
Ousret committed Oct 7, 2023
1 parent d78f586 commit fb5f09f
Show file tree
Hide file tree
Showing 34 changed files with 237 additions and 3,080 deletions.
5 changes: 1 addition & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
os:
- macos-latest
- windows-latest
Expand Down Expand Up @@ -89,9 +89,6 @@ jobs:
os: ubuntu-22.04
- python-version: "3.8"
os: ubuntu-22.04
# Testing with non-final CPython on macOS is too slow for CI.
- python-version: "3.12-dev"
os: macos-latest

runs-on: ${{ matrix.os }}
name: ${{ fromJson('{"macos-latest":"macOS","windows-latest":"Windows","ubuntu-latest":"Ubuntu","ubuntu-20.04":"Ubuntu 20.04 (OpenSSL 1.1.1)","ubuntu-22.04":"Ubuntu 22.04 (OpenSSL 3.0)"}')[matrix.os] }} ${{ matrix.python-version }} ${{ matrix.nox-session }}
Expand Down
29 changes: 29 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
2.1.900 (2023-10-07)
====================

- Added ``cipher`` in ``ConnectionInfo`` when using HTTP/3 over QUIC.
- Added ``issuer_certificate_der``, ``issuer_certificate_dict`` into ``ConnectionInfo``.

By default, it is set to ``None``. This property is filled automatically on a QUIC connection.
It cannot be done automatically when using native Python capabilities.

- Removed support for SecureTransport.
- Removed support for PyOpenSSL.

This module is not delete but rendered ineffective. An explicit warning still appear.

- Improved automated exchange between the socket and the HTTP state machines.
- Removed all dependencies in the ``secure`` extra.
- Fixed disabling HTTP/3 over QUIC if specified settings were incompatible with TLS over QUIC.

Previously if ``ssl_context`` was set and specifying a list of ciphers it was discarded on upgrade.
Also, if ``ssl_maximum_version`` was set to TLS v1.2.
Now those parameters are correctly forwarded to the custom QUIC/TLS layer.

- Fixed ``ConnectionInfo`` repr that did not shown the ``http_version`` property.
- Undeprecated 'ssl_version' option in create_urllib3_context.
- Undeprecated 'format_header_param_rfc2231'.
- Removed warning about the 'strict' parameter.
- Removed constant ``IS_PYOPENSSL`` and ``IS_SECURETRANSPORT`` from ``urllib3.utils``.
- Added raise warning when using environment variables ``SSLKEYLOGFILE``, and ``QUICLOGDIR``.

2.0.936 (2023-10-01)
====================

Expand Down
8 changes: 2 additions & 6 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ def tests_impl(
session.run("python", "--version")
session.run("python", "-c", "import struct; print(struct.calcsize('P') * 8)")

if "secure" in extras:
# Print OpenSSL information.
session.run("python", "-m", "OpenSSL.debug")

memray_supported = True
if (
sys.implementation.name != "cpython"
Expand Down Expand Up @@ -71,7 +67,7 @@ def test_brotlipy(session: nox.Session) -> None:
'brotlicffi' that we still don't blow up.
"""
session.install("brotlipy")
tests_impl(session, extras="socks,secure", byte_string_comparisons=False)
tests_impl(session, extras="socks", byte_string_comparisons=False)


def git_clone(session: nox.Session, git_url: str) -> None:
Expand Down Expand Up @@ -173,7 +169,7 @@ def mypy(session: nox.Session) -> None:
@nox.session
def docs(session: nox.Session) -> None:
session.install("-r", "docs/requirements.txt")
session.install(".[socks,secure,brotli,zstd]")
session.install(".[socks,brotli,zstd]")

session.chdir("docs")
if os.path.exists("_build"):
Expand Down
11 changes: 1 addition & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,7 @@ brotli = [
zstd = [
"zstandard>=0.18.0",
]
secure = [
"pyOpenSSL>=17.1.0",
"cryptography>=1.9",
"idna>=2.0.0",
"certifi",
"urllib3-secure-extra",
]
secure = []
socks = [
"PySocks>=1.5.6,<2.0,!=1.5.7",
]
Expand Down Expand Up @@ -99,8 +93,6 @@ filterwarnings = [
"error",
'''default:urllib3 v2.0 only supports OpenSSL 1.1.1+.*''',
'''default:'urllib3\[secure\]' extra is deprecated and will be removed in urllib3 v2\.1\.0.*:DeprecationWarning''',
'''default:'urllib3\.contrib\.pyopenssl' module is deprecated and will be removed in urllib3 v2\.1\.0.*:DeprecationWarning''',
'''default:'urllib3\.contrib\.securetransport' module is deprecated and will be removed in urllib3 v2\.1\.0.*:DeprecationWarning''',
'''default:No IPv6 support. Falling back to IPv4:urllib3.exceptions.HTTPWarning''',
'''default:No IPv6 support. skipping:urllib3.exceptions.HTTPWarning''',
'''default:ssl\.TLSVersion\.TLSv1 is deprecated:DeprecationWarning''',
Expand All @@ -114,7 +106,6 @@ filterwarnings = [
# https://github.com/pytest-dev/pytest/issues/10977
'''default:ast\.(Num|NameConstant|Str) is deprecated and will be removed in Python 3\.14; use ast\.Constant instead:DeprecationWarning:_pytest''',
'''default:Attribute s is deprecated and will be removed in Python 3\.14; use value instead:DeprecationWarning:_pytest''',
'''default:'HTTPResponse\.read_chunked\(\)' method is deprecated:DeprecationWarning''',
]

[tool.isort]
Expand Down
33 changes: 9 additions & 24 deletions src/urllib3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import typing
import warnings
from logging import NullHandler
from os import environ

from . import exceptions
from ._base_connection import _TYPE_BODY
Expand All @@ -31,36 +32,13 @@
except ImportError:
pass
else:
if not ssl.OPENSSL_VERSION.startswith("OpenSSL "): # Defensive:
warnings.warn(
"urllib3 v2.0 only supports OpenSSL 1.1.1+, currently "
f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. "
"See: https://github.com/urllib3/urllib3/issues/3020",
exceptions.NotOpenSSLWarning,
)
elif ssl.OPENSSL_VERSION_INFO < (1, 1, 1): # Defensive:
if ssl.OPENSSL_VERSION_INFO < (1, 1, 1): # Defensive:
raise ImportError(
"urllib3 v2.0 only supports OpenSSL 1.1.1+, currently "
f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. "
"See: https://github.com/urllib3/urllib3/issues/2168"
)

# === NOTE TO REPACKAGERS AND VENDORS ===
# Please delete this block, this logic is only
# for urllib3 being distributed via PyPI.
# See: https://github.com/urllib3/urllib3/issues/2680
try:
import urllib3_secure_extra # type: ignore # noqa: F401
except ModuleNotFoundError:
pass
else:
warnings.warn(
"'urllib3[secure]' extra is deprecated and will be removed "
"in urllib3 v2.1.0. Read more in this issue: "
"https://github.com/urllib3/urllib3/issues/2680",
category=DeprecationWarning,
stacklevel=2,
)

__author__ = "Andrey Petrov ([email protected])"
__license__ = "MIT"
Expand Down Expand Up @@ -114,6 +92,13 @@ def add_stderr_logger(
del NullHandler


if environ.get("SSHKEYLOGFILE", None) or environ.get("QUICLOGDIR", None):
warnings.warn(
"""urllib3 detected that development/debug environment variable are set. If you are not aware of it
please audit your environment. Look for variables 'SSHKEYLOGFILE' and 'QUICLOGDIR'.""",
exceptions.SecurityWarning,
)

# All warning filters *must* be appended unless you're really certain that they
# shouldn't be: otherwise, it's very hard for users to use most Python
# mechanisms to silence them.
Expand Down
2 changes: 1 addition & 1 deletion src/urllib3/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This file is protected via CODEOWNERS
from __future__ import annotations

__version__ = "2.0.936"
__version__ = "2.1.900"
5 changes: 5 additions & 0 deletions src/urllib3/backend/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ def __init__(self) -> None:
self.certificate_dict: dict[
str, int | tuple[tuple[str, str], ...] | tuple[str, ...] | str
] | None = None
self.issuer_certificate_der: bytes | None = None
self.issuer_certificate_dict: dict[
str, int | tuple[tuple[str, str], ...] | tuple[str, ...] | str
] | None = None
self.destination_address: tuple[str, int] | None = None
self.cipher: str | None = None
self.tls_version: TLSVersion | None = None
Expand All @@ -41,6 +45,7 @@ def __repr__(self) -> str:
"destination_address": self.destination_address,
"cipher": self.cipher,
"tls_version": self.tls_version,
"http_version": self.http_version,
}
)

Expand Down
24 changes: 17 additions & 7 deletions src/urllib3/backend/hface.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,14 @@ def _post_conn(self) -> None:
binary_form=False
)
self.conn_info.destination_address = self.sock.getpeername()[:2]
self.conn_info.cipher = (
None # todo: find a way to retrieve the actual cipher from qh3.
)
self.conn_info.cipher = self._protocol.cipher()
self.conn_info.tls_version = ssl.TLSVersion.TLSv1_3
self.conn_info.issuer_certificate_dict = self._protocol.getissuercert( # type: ignore[assignment]
binary_form=False
)
self.conn_info.issuer_certificate_der = self._protocol.getissuercert( # type: ignore[assignment]
binary_form=True
)

def set_tunnel(
self,
Expand Down Expand Up @@ -462,9 +466,12 @@ def __exchange_until(
while True:
if not self._protocol.has_pending_event():
if receive_first is False:
data_out = self._protocol.bytes_to_send()
while True:
data_out = self._protocol.bytes_to_send()

if not data_out:
break

if data_out:
self.sock.sendall(data_out)

data_in = self.sock.recv(maximal_data_in_read or self.blocksize)
Expand Down Expand Up @@ -498,9 +505,12 @@ def __exchange_until(
raise ProtocolError(e) from e # Defensive:

if receive_first is True:
data_out = self._protocol.bytes_to_send()
while True:
data_out = self._protocol.bytes_to_send()

if not data_out:
break

if data_out:
self.sock.sendall(data_out)

for event in iter(self._protocol.next_event, None): # type: Event
Expand Down
96 changes: 33 additions & 63 deletions src/urllib3/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,25 +416,6 @@ def request(
if chunked:
self.send(b"0\r\n\r\n")

def request_chunked(
self,
method: str,
url: str,
body: _TYPE_BODY | None = None,
headers: typing.Mapping[str, str] | None = None,
) -> None:
"""
Alternative to the common request method, which sends the
body with chunked encoding and not as one block
"""
warnings.warn(
"HTTPConnection.request_chunked() is deprecated and will be removed "
"in urllib3 v2.1.0. Instead use HTTPConnection.request(..., chunked=True).",
category=DeprecationWarning,
stacklevel=2,
)
self.request(method, url, body=body, headers=headers, chunked=True)

def getresponse( # type: ignore[override]
self,
) -> HTTPResponse:
Expand Down Expand Up @@ -529,6 +510,39 @@ def __init__(
key_file: str | None = None,
key_password: str | None = None,
) -> None:
# Some parameters may defacto exclude HTTP/3 over QUIC.
# Let's check all of those:
# -> TLS 1.3 required
# -> One of the three supported ciphers (listed bellow)
quic_disable: bool = False

if ssl_context is not None:
if (
ssl_context.maximum_version
and ssl_context.maximum_version <= ssl.TLSVersion.TLSv1_2
):
quic_disable = True
else:
any_capable_cipher: bool = False
for cipher_dict in ssl_context.get_ciphers():
if cipher_dict["name"] in [
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
]:
any_capable_cipher = True
if not any_capable_cipher:
quic_disable = True

if ssl_maximum_version and ssl_maximum_version <= ssl.TLSVersion.TLSv1_2:
quic_disable = True

if quic_disable:
if disabled_svn is None:
disabled_svn = set()

disabled_svn.add(HttpVersion.h3)

super().__init__(
host,
port=port,
Expand Down Expand Up @@ -564,47 +578,6 @@ def __init__(
cert_reqs = resolve_cert_reqs(None)
self.cert_reqs = cert_reqs

def set_cert(
self,
key_file: str | None = None,
cert_file: str | None = None,
cert_reqs: int | str | None = None,
key_password: str | None = None,
ca_certs: str | None = None,
assert_hostname: None | str | Literal[False] = None,
assert_fingerprint: str | None = None,
ca_cert_dir: str | None = None,
ca_cert_data: None | str | bytes = None,
) -> None:
"""
This method should only be called once, before the connection is used.
"""
warnings.warn(
"HTTPSConnection.set_cert() is deprecated and will be removed "
"in urllib3 v2.1.0. Instead provide the parameters to the "
"HTTPSConnection constructor.",
category=DeprecationWarning,
stacklevel=2,
)

# If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also
# have an SSLContext object in which case we'll use its verify_mode.
if cert_reqs is None:
if self.ssl_context is not None:
cert_reqs = self.ssl_context.verify_mode
else:
cert_reqs = resolve_cert_reqs(None)

self.key_file = key_file
self.cert_file = cert_file
self.cert_reqs = cert_reqs
self.key_password = key_password
self.assert_hostname = assert_hostname
self.assert_fingerprint = assert_fingerprint
self.ca_certs = ca_certs and os.path.expanduser(ca_certs)
self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir)
self.ca_cert_data = ca_cert_data

def connect(self) -> None:
sock: socket.socket | ssl.SSLSocket
self.sock = sock = self._new_conn()
Expand Down Expand Up @@ -789,9 +762,6 @@ def _ssl_wrap_socket_and_match_hostname(
or assert_hostname
# assert_hostname can be set to False to disable hostname checking
or assert_hostname is False
# We still support OpenSSL 1.0.2, which prevents us from verifying
# hostnames easily: https://github.com/pyca/pyopenssl/pull/933
or ssl_.IS_PYOPENSSL
or not ssl_.HAS_NEVER_CHECK_COMMON_NAME
):
context.check_hostname = False
Expand Down
Empty file.
Loading

0 comments on commit fb5f09f

Please sign in to comment.