fix: one Closing behaviour for sm and peer
Merged
GO
13d ago+3707+3707+0123456789012345678901234567890123456789-184-184-012345678901234567890123456789
Part 2 of the protocol contract decided in #108. sm.StateMachine,
sm.Client and peer.Manager handled a connection in Closing (after a DPR
was sent or accepted) differently, and none of them followed RFC 6733
§5.6, whose Closing state defines only DPA, timeout and disconnect.
- Once Closing is latched, answers to requests sent before it still
complete. New requests and other base commands are dropped before
validation and dispatch, with no handler, hook, reply or watchdog
credit; the Manager's early error-answer path no longer answers them. - A request counts as sent before Closing if its write acquired the
connection's write gate first. Gate acquisition registers answer
ownership under the same lock as Closing, so a later request is
rejected with ErrConnectionClosing and never written, and frames stay
whole. Cancellation and request expiry remove that ownership. - A crossed or repeated valid DPR is answered without extending the
original deadline, and any correlated DPA closes the transport,
whatever its Result-Code (§5.4). - A DPR that arrives while this node's requests on the connection still
await answers is answered with DIAMETER_UNABLE_TO_COMPLY (5012), as
§5.4 recommends for messages likely in flight; the DPR's sender still
closes. - sm.Client stops its watchdog when Closing starts, so no DWR follows a
DPR, and the disconnect deadlines are armed before waiting for watchdog
work, so a stalled DWR write can no longer hold Closing open. - The Manager freezes which requests may still be answered when Closing
starts and disposes of reservations that were never written.
Refs #108
Validation
make checkpasses locally for all modules: format, test (Go 1.27.2 and
1.26.9), race, vet, lint, vuln, windows, secrets and codeql.- TCP captures on main show non-DPA replies during Closing, DWRs after a DPR,
early closure on a crossed DPR and 2001 DPAs while requests were in flight;
here there are none of the first three and the in-flight DPAs carry 5012. - Three review rounds found and fixed a Closing hang behind a stalled DWR, a
request boundary error in the Manager and inWriteToWithRetry, and a
frame-splitting bug in an intermediate design; the final version writes every
frame whole and mutating any guarded behaviour fails a named test.
Summary by cubic
Unifies Closing behaviour across sm.StateMachine, sm.Client, and peer.Manager so all follow RFC 6733 §5.6: once Closing is latched, new requests and other base commands are dropped before validation and dispatch, while answers to requests sent before Closing still complete.
Behaviour changes
- A request counts as sent before Closing only if its write acquired the connection's write gate first; later writes are rejected with
ErrConnectionClosing. - A crossed or repeated valid DPR receives a DPA without extending the original deadline, and any correlated DPA closes the transport regardless of its result code.
- A DPR arriving while this node's requests on the connection still await answers is answered with
DIAMETER_UNABLE_TO_COMPLY(5012).
Watchdog and peer.Manager
sm.Clientstops its watchdog when Closing starts so no DWR follows a DPR, and disconnect deadlines are armed before waiting for watchdog work.peer.Managerfreezes which requests may still be answered when Closing starts and disposes of admission reservations that were never written.