Skip to content

Should Error::HandshakePending be documented as an exception to fatal error handling? #158

Description

@HMBSbige

Hi, I may be misunderstanding the intended error contract.

The README says every Error returned by handle_packet, handle_timeout, send_application_data, or close is fatal and that the Dtls instance should always be dropped:

https://github.com/algesten/dimpl/blob/37f950984af1d0c2f86ef21b940c672fb27cd7c7/README.md#error-handling

However, Error::HandshakePending says callers should buffer the data and retry once the handshake advances:

dimpl/src/error.rs

Lines 46 to 51 in 37f9509

/// Application data cannot be sent because the handshake is not yet complete.
///
/// For auto-sense instances this means the version has not yet been
/// resolved. Callers should buffer the data and retry once the
/// handshake advances.
HandshakePending,

send_application_data repeats that guidance, and both it and close return HandshakePending from the pending branch before invoking the protocol-specific send or close logic:

dimpl/src/lib.rs

Lines 816 to 867 in 37f9509

/// Send application data over the established DTLS session.
///
/// Returns [`Error::HandshakePending`] if the DTLS version has not
/// yet been resolved (auto-sense pending). Callers should buffer
/// the data externally and retry after the handshake progresses.
pub fn send_application_data(&mut self, data: &[u8]) -> Result<(), Error> {
// unwrap is ok, we only have an Option to deal with pending auto.
let inner = self.inner.as_mut().unwrap();
if inner.is_pending() {
return Err(Error::HandshakePending);
}
match inner {
Inner::Client12(client) => client.send_application_data(data),
Inner::Server12(server) => server.send_application_data(data),
Inner::Client13(client) => client.send_application_data(data),
Inner::Server13(server) => server.send_application_data(data),
Inner::ClientPending(_) => Err(Error::HandshakePending),
}
}
/// Initiate graceful shutdown by sending a `close_notify` alert.
///
/// **Connected** (`AwaitApplicationData`): queues a `close_notify` alert;
/// the next [`poll_output`](Self::poll_output) cycle yields it as
/// [`Output::Packet`].
///
/// **Handshake in progress**: aborts immediately without sending an
/// alert (no authenticated channel exists). Subsequent calls to
/// [`send_application_data`](Self::send_application_data) will return
/// an error.
///
/// **Pending** (version not yet resolved): returns
/// [`Error::HandshakePending`]. Callers who want to discard a pending
/// connection can simply drop the [`Dtls`] value.
///
/// The alert is not retransmitted (per RFC 6347 §4.2.7 / RFC 9147 §5.10).
pub fn close(&mut self) -> Result<(), Error> {
let inner = self.inner.as_mut().unwrap();
if inner.is_pending() {
return Err(Error::HandshakePending);
}
match inner {
Inner::Client12(client) => client.close(),
Inner::Server12(server) => server.close(),
Inner::Client13(client) => client.close(),
Inner::Server13(server) => server.close(),
Inner::ClientPending(_) => Err(Error::HandshakePending),
}

Does this mean HandshakePending is recoverable and should be documented as an exception to the README’s fatal-error rule?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions