Skip to content

fix: retry close_notify in poll_shutdown when the send buffer is full - #78

Open
ArniDagur wants to merge 1 commit into
rustls:mainfrom
ArniDagur:fix-shutdown-close-notify-retry
Open

fix: retry close_notify in poll_shutdown when the send buffer is full#78
ArniDagur wants to merge 1 commit into
rustls:mainfrom
ArniDagur:fix-shutdown-close-notify-retry

Conversation

@ArniDagur

@ArniDagur ArniDagur commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

poll_shutdown marked the write side closed before enforcing a successful close_notify. With a full send buffer this resulted in two bugs:

  1. Shutdown failed with WouldBlock, which is an error type that should not escape a poll-based API.
  2. Subsequent retries of the shutdown skipped the close_notify entirely, since write_closed was already set.

We fix this by only marking the write side closed once the alert is sent (or has failed fatally), and retrying on WouldBlock.

The retry uses a new AsyncWriteReady trait, which mirrors the preexisting AsyncReadReady. It exposes tokio's poll_write_ready and try_io. The latter clears write-readiness when send_close_notify returns WouldBlock, so the task parks until the socket becomes writable instead of busy-polling.

This PR introduces a minor breaking change, since the AsyncWrite impl for KtlsStream<IO> now requires IO: AsyncWriteReady. This change also lays the foundation for additional work, including in relation to properly implementing KeyUpdate (which I plan on doing).

`poll_shutdown` marked the write side closed _before_ enforcing a
successful `close_notify`. With a full send buffer this resulted
in two bugs:

1. Shutdown failed with `WouldBlock`, which is an error type that
should not escape a poll-based API.
2. Subsequent retries of the shutdown skipped the `close_notify`
entirely, since `write_closed` was already set.

We fix this by only marking the write side closed once the alert is
sent (or has failed fatally), and retrying on `WouldBlock`.

The retry uses a new `AsyncWriteReady` trait, which mirrors the
preexisting `AsyncReadReady`. It exposes tokio's `poll_write_ready` and
`try_io`. The latter `try_write_io` clears write-readiness when
`send_close_notify` returns `WouldBlock`, so the task parks until the
socket becomes writable instead of [busy-polling][1].

[1]: https://github.com/rustls/ktls/blob/5e3c7d6ceadbb1ae98d06908d559490723899aed/ktls/src/ktls_stream.rs#L268-L277

This PR introduces a minor breaking change, since the `AsyncWrite` impl
for `KtlsStream<IO>` now requires `IO: AsyncWriteReady`. This change also
lays the foundation for additional work, including in relation to properly
implementing `KeyUpdate`.
@ArniDagur
ArniDagur force-pushed the fix-shutdown-close-notify-retry branch from ee2dae6 to e862e48 Compare August 8, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant