Skip to content

Commit

Permalink
Merge pull request #2203 from Fuyukai/fix-ci-once-and-for-all
Browse files Browse the repository at this point in the history
Fix CI and add compatibility with OpenSSL 3.0.
  • Loading branch information
oremanj committed Dec 24, 2021
2 parents f8cb817 + a43b832 commit fc98849
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 26 deletions.
8 changes: 4 additions & 4 deletions docs/source/reference-io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ library socket into a Trio socket:

.. autofunction:: from_stdlib_socket

Unlike :func:`socket.socket`, :func:`trio.socket.socket` is a
Unlike :class:`socket.socket`, :func:`trio.socket.socket` is a
function, not a class; if you want to check whether an object is a
Trio socket, use ``isinstance(obj, trio.socket.SocketType)``.

Expand Down Expand Up @@ -380,7 +380,7 @@ Socket objects
additional error checking.

In addition, the following methods are similar to the equivalents
in :func:`socket.socket`, but have some Trio-specific quirks:
in :class:`socket.socket`, but have some Trio-specific quirks:

.. method:: connect
:async:
Expand Down Expand Up @@ -421,7 +421,7 @@ Socket objects
False otherwise.

The following methods are identical to their equivalents in
:func:`socket.socket`, except async, and the ones that take address
:class:`socket.socket`, except async, and the ones that take address
arguments require pre-resolved addresses:

* :meth:`~socket.socket.accept`
Expand All @@ -437,7 +437,7 @@ Socket objects
* :meth:`~socket.socket.sendmsg` (if available)

All methods and attributes *not* mentioned above are identical to
their equivalents in :func:`socket.socket`:
their equivalents in :class:`socket.socket`:

* :attr:`~socket.socket.family`
* :attr:`~socket.socket.type`
Expand Down
2 changes: 2 additions & 0 deletions newsfragments/2203.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add compatibility with OpenSSL 3.0 on newer Python and PyPy versions by working
around ``SSLEOFError`` not being raised properly.
16 changes: 9 additions & 7 deletions test-requirements.in
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# For tests
pytest >= 5.0 # for faulthandler in core
pytest >= 5.0 # for faulthandler in core
pytest-cov >= 2.6.0
ipython # for the IPython traceback integration tests
pyOpenSSL # for the ssl tests
trustme # for the ssl tests
pylint # for pylint finding all symbols tests
jedi # for jedi code completion tests
ipython # for the IPython traceback integration tests
pyOpenSSL # for the ssl tests
trustme # for the ssl tests
pylint # for pylint finding all symbols tests
jedi # for jedi code completion tests
cryptography>=36.0.0 # 35.0.0 is transitive but fails

# Tools
black; implementation_name == "cpython"
Expand All @@ -14,7 +15,8 @@ flake8
astor # code generation

# https://github.com/python-trio/trio/pull/654#issuecomment-420518745
typed_ast; implementation_name == "cpython"
# typed_ast is deprecated as of 3.8, and straight up doesn't compile on 3.10-dev as of 2021-12-13
typed_ast; implementation_name == "cpython" and python_version < "3.8"
mypy-extensions; implementation_name == "cpython"
typing-extensions; implementation_name == "cpython"

Expand Down
30 changes: 25 additions & 5 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#
# This file is autogenerated by pip-compile
# This file is autogenerated by pip-compile with python 3.7
# To update, run:
#
# pip-compile --output-file test-requirements.txt test-requirements.in
# pip-compile --output-file=test-requirements.txt test-requirements.in
#
astor==0.8.1
# via -r test-requirements.in
Expand All @@ -25,8 +25,9 @@ click==8.0.3
# via black
coverage[toml]==6.0.2
# via pytest-cov
cryptography==35.0.0
cryptography==36.0.1
# via
# -r test-requirements.in
# pyopenssl
# trustme
decorator==5.1.0
Expand All @@ -39,6 +40,12 @@ idna==3.3
# trustme
immutables==0.16
# via -r test-requirements.in
importlib-metadata==4.2.0
# via
# click
# flake8
# pluggy
# pytest
iniconfig==1.1.1
# via pytest
ipython==7.29.0
Expand Down Expand Up @@ -131,14 +138,27 @@ traitlets==5.1.1
# matplotlib-inline
trustme==0.9.0
# via -r test-requirements.in
typed-ast==1.4.3 ; implementation_name == "cpython"
# via -r test-requirements.in
typed-ast==1.4.3 ; implementation_name == "cpython" and python_version < "3.8"
# via
# -r test-requirements.in
# astroid
# black
# mypy
typing-extensions==3.10.0.2 ; implementation_name == "cpython"
# via
# -r test-requirements.in
# astroid
# black
# immutables
# importlib-metadata
# mypy
# pylint
wcwidth==0.2.5
# via prompt-toolkit
wrapt==1.13.3
# via astroid
zipp==3.6.0
# via importlib-metadata

# The following packages are considered to be unsafe in a requirements file:
# setuptools
4 changes: 4 additions & 0 deletions trio/_highlevel_ssl_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ async def open_ssl_over_tcp_stream(
)
if ssl_context is None:
ssl_context = ssl.create_default_context()

if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"):
ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF

return trio.SSLStream(
tcp_stream, ssl_context, server_hostname=host, https_compatible=https_compatible
)
Expand Down
4 changes: 2 additions & 2 deletions trio/_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ async def getprotobyname(name):


def from_stdlib_socket(sock):
"""Convert a standard library :func:`socket.socket` object into a Trio
"""Convert a standard library :class:`socket.socket` object into a Trio
socket object.
"""
Expand Down Expand Up @@ -271,7 +271,7 @@ def socket(
proto=0,
fileno=None,
):
"""Create a new Trio socket, like :func:`socket.socket`.
"""Create a new Trio socket, like :class:`socket.socket`.
This function's behavior can be customized using
:func:`set_custom_socket_factory`.
Expand Down
21 changes: 15 additions & 6 deletions trio/_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,16 @@
STARTING_RECEIVE_SIZE = 16384


def _is_eof(exc):
# There appears to be a bug on Python 3.10, where SSLErrors
# aren't properly translated into SSLEOFErrors.
# This stringly-typed error check is borrowed from the AnyIO
# project.
return isinstance(exc, _stdlib_ssl.SSLEOFError) or (
hasattr(exc, "strerror") and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror
)


class NeedHandshakeError(Exception):
"""Some :class:`SSLStream` methods can't return any meaningful data until
after the handshake. If you call them before the handshake, they raise
Expand Down Expand Up @@ -658,9 +668,9 @@ async def receive_some(self, max_bytes=None):
# For some reason, EOF before handshake sometimes raises
# SSLSyscallError instead of SSLEOFError (e.g. on my linux
# laptop, but not on appveyor). Thanks openssl.
if self._https_compatible and isinstance(
exc.__cause__,
(_stdlib_ssl.SSLEOFError, _stdlib_ssl.SSLSyscallError),
if self._https_compatible and (
isinstance(exc.__cause__, _stdlib_ssl.SSLSyscallError)
or _is_eof(exc.__cause__)
):
await trio.lowlevel.checkpoint()
return b""
Expand All @@ -683,9 +693,8 @@ async def receive_some(self, max_bytes=None):
# BROKEN. But that's actually fine, because after getting an
# EOF on TLS then the only thing you can do is close the
# stream, and closing doesn't care about the state.
if self._https_compatible and isinstance(
exc.__cause__, _stdlib_ssl.SSLEOFError
):

if self._https_compatible and _is_eof(exc.__cause__):
await trio.lowlevel.checkpoint()
return b""
else:
Expand Down
12 changes: 10 additions & 2 deletions trio/tests/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from .._core import ClosedResourceError, BrokenResourceError
from .._highlevel_open_tcp_stream import open_tcp_stream
from .. import socket as tsocket
from .._ssl import SSLStream, SSLListener, NeedHandshakeError
from .._ssl import SSLStream, SSLListener, NeedHandshakeError, _is_eof
from .._util import ConflictDetector

from .._core.tests.tutil import slow
Expand Down Expand Up @@ -55,6 +55,9 @@
TRIO_TEST_1_CERT = TRIO_TEST_CA.issue_server_cert("trio-test-1.example.org")

SERVER_CTX = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"):
SERVER_CTX.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF

TRIO_TEST_1_CERT.configure_cert(SERVER_CTX)

# TLS 1.3 has a lot of changes from previous versions. So we want to run tests
Expand All @@ -76,6 +79,10 @@
@pytest.fixture(scope="module", params=client_ctx_params)
def client_ctx(request):
ctx = ssl.create_default_context()

if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"):
ctx.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF

TRIO_TEST_CA.configure_trust(ctx)
if request.param in ["default", "tls13"]:
return ctx
Expand Down Expand Up @@ -1105,7 +1112,8 @@ async def test_ssl_https_compatibility_disagreement(client_ctx):
async def receive_and_expect_error():
with pytest.raises(BrokenResourceError) as excinfo:
await server.receive_some(10)
assert isinstance(excinfo.value.__cause__, ssl.SSLEOFError)

assert _is_eof(excinfo.value.__cause__)

async with _core.open_nursery() as nursery:
nursery.start_soon(client.aclose)
Expand Down

0 comments on commit fc98849

Please sign in to comment.