fix(protocol): retain completed DTLS handshakes

This commit is contained in:
Jason Morcos
2026-08-15 11:42:28 -07:00
parent bc4465b274
commit 31be87061a
4 changed files with 23 additions and 11 deletions
+4
View File
@@ -57,6 +57,10 @@ retransmissions within that same deadline:
sess.connect(timeout=4.0)
```
The deadline stops further setup, retries, and network waits. If OpenSSL
reports that the handshake completed at the deadline boundary, the completed
session is retained rather than torn down as a timeout.
Connection attempts can also use a one-way cancellation signal. The signal is
backed by a socketpair, so setting it wakes the network wait immediately while
OpenSSL retains control of DTLS retransmission timing:
+5 -3
View File
@@ -34,14 +34,16 @@ def _drive_dtls_handshake(
bounded only by its deadline, while the diagnostic probe retains its
explicit retry budget.
Return ``True`` only when the handshake completes before the deadline.
TLS and socket failures are left to the caller to classify.
Return ``True`` once OpenSSL reports the handshake complete. The deadline
prevents another setup, retry, or network-wait iteration; it does not tear
down a session that completed while ``do_handshake()`` was running. TLS and
socket failures are left to the caller to classify.
"""
retransmits = 0
while time.monotonic() < deadline:
try:
connection.do_handshake()
return time.monotonic() < deadline
return True
except SSL.WantReadError:
pass
+5 -3
View File
@@ -322,13 +322,15 @@ class DtlsCoapSession:
timeout: float | None = None,
cancel: ConnectCancellation | None = None,
):
"""Perform a cancellable DTLS handshake within a monotonic deadline.
"""Perform a cancellable DTLS handshake using a monotonic deadline.
``timeout`` overrides ``HANDSHAKE_TIMEOUT_S`` for this call. OpenSSL
owns DTLS retransmission timing while every receive is capped by the
remaining budget, so wall-clock adjustments cannot change the bound.
A ``ConnectCancellation`` wakes the network wait immediately and does
not alter an already established session.
Once OpenSSL reports completion, that completed session is retained
even if the call returns just after the deadline. A
``ConnectCancellation`` wakes the network wait immediately and does not
alter an already established session.
"""
handshake_timeout = _validate_handshake_timeout(
timeout, self.HANDSHAKE_TIMEOUT_S)
+9 -5
View File
@@ -323,12 +323,12 @@ def test_connect_services_openssl_retransmit_timer(monkeypatch):
@pytest.mark.parametrize("success_delay", (0.1, 0.2))
def test_handshake_success_at_or_after_deadline_is_rejected(
def test_handshake_success_at_or_after_deadline_is_retained(
monkeypatch,
success_delay,
):
clock = _Clock()
connection, sock, _endpoint, _open_calls = _install_handshake(
connection, sock, endpoint, _open_calls = _install_handshake(
monkeypatch,
clock,
)
@@ -338,7 +338,11 @@ def test_handshake_success_at_or_after_deadline_is_rejected(
connection.do_handshake = late_success
with pytest.raises(SessionTimeoutError):
_session().connect(timeout=0.1)
session = _session()
session.connect(timeout=0.1)
assert sock.closed
assert session.conn is connection
assert session.sock is sock
assert session.endpoint is endpoint
assert session.dest == endpoint.sockaddr
assert not sock.closed