Skip to content

Commit

Permalink
Fix TLSListener.handle_handshake_error on asyncio failing to log th…
Browse files Browse the repository at this point in the history
…e error on CPython (#609)

Fixes #608.
  • Loading branch information
gschaffner authored Aug 30, 2023
1 parent 5f208ee commit e380c26
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 2 deletions.
3 changes: 3 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ This library adheres to `Semantic Versioning 2.0 <http://semver.org/>`_.
message) on asyncio to determine if a cancellation exception should be swallowed on scope exit,
to combat issues where third party libraries catch the ``CancelledError`` and raise another, thus
erasing the original cancel message
- Worked around a `CPython bug <https://github.com/python/cpython/issues/108668>`_ that
caused ``TLSListener.handle_handshake_error()`` on asyncio to log ``"NoneType: None"``
instead of the error (PR by Ganden Schaffner)

**4.0.0rc1**

Expand Down
8 changes: 7 additions & 1 deletion src/anyio/streams/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,13 @@ async def handle_handshake_error(exc: BaseException, stream: AnyByteStream) -> N

# Log all except cancellation exceptions
if not isinstance(exc, get_cancelled_exc_class()):
logging.getLogger(__name__).exception("Error during TLS handshake")
# CPython (as of 3.11.5) returns incorrect `sys.exc_info()` here when using
# any asyncio implementation, so we explicitly pass the exception to log
# (https://github.com/python/cpython/issues/108668). Trio does not have this
# issue because it works around the CPython bug.
logging.getLogger(__name__).exception(
"Error during TLS handshake", exc_info=exc
)

# Only reraise base exceptions and cancellation exceptions
if not isinstance(exc, Exception) or isinstance(exc, get_cancelled_exc_class()):
Expand Down
11 changes: 10 additions & 1 deletion tests/streams/test_tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,9 @@ async def test_default_context_ignore_unexpected_eof_flag_off(

class TestTLSListener:
@skip_on_broken_openssl
async def test_handshake_fail(self, server_context: ssl.SSLContext) -> None:
async def test_handshake_fail(
self, server_context: ssl.SSLContext, caplog: pytest.LogCaptureFixture
) -> None:
def handler(stream: object) -> NoReturn:
pytest.fail("This function should never be called in this scenario")

Expand All @@ -411,6 +413,13 @@ async def handle_handshake_error(
) -> None:
nonlocal exception
await TLSListener.handle_handshake_error(exc, stream)

# Regression test for #608
assert len(caplog.records) == 1
logged_exc_info = caplog.records[0].exc_info
logged_exc = logged_exc_info[1] if logged_exc_info is not None else None
assert logged_exc is exc

assert isinstance(stream, SocketStream)
exception = exc
event.set()
Expand Down

0 comments on commit e380c26

Please sign in to comment.