Severity: major (MQTT v5 conformance / spurious Will publication).
Spec: MQTT v5 §3.1.3.2.2 (Will Delay Interval): "If a new Network Connection to this Session is made before the Will Delay Interval has passed, the Server MUST NOT send the Will Message" [MQTT-3.1.3-9]. The Will is published when the Will Delay Interval elapses or the Session ends, whichever is first.
Current behavior: In crates/mqtt5/src/broker/client_handler/lifecycle.rs, publish_will_message handles a will_delay_interval > 0 by spawning a detached task that tokio::time::sleep(delay)s, re-authorizes, and then routes the Will unconditionally:
if delay > 0 {
tokio::spawn(async move {
tokio::time::sleep(Duration::from_secs(u64::from(delay))).await;
// re-authorize, then route the will — no check that the client reconnected
...
});
}
There is no cancellation handle and no check of whether the same client_id re-established the session during the delay window. So a client that drops abnormally and reconnects (resumes its session) before the Will Delay Interval elapses still has its Will published — a spurious Will. This is exactly the case Will Delay exists to prevent (flaky/mobile clients using a delay to avoid transient-disconnect Wills).
Proposed fix: Track pending delayed-Will tasks keyed by client_id (e.g. a JoinHandle map on the broker/router), and cancel/abort the pending task when a new connection resumes that session before the delay elapses — the resume point is register_session / handle_session. On a clean-start takeover the Will handling should follow the same rules (the prior session ends, so its Will fires per spec unless a delay is pending and the session is resumed). Add a test: connect with a will + delay N, drop abnormally, reconnect within N, assert the Will is never published; and a second test asserting it IS published if no reconnect occurs within N.
Found while reviewing the broker's connect/disconnect event + LWT + timeout paths.
Severity: major (MQTT v5 conformance / spurious Will publication).
Spec: MQTT v5 §3.1.3.2.2 (Will Delay Interval): "If a new Network Connection to this Session is made before the Will Delay Interval has passed, the Server MUST NOT send the Will Message"
[MQTT-3.1.3-9]. The Will is published when the Will Delay Interval elapses or the Session ends, whichever is first.Current behavior: In
crates/mqtt5/src/broker/client_handler/lifecycle.rs,publish_will_messagehandles awill_delay_interval > 0by spawning a detached task thattokio::time::sleep(delay)s, re-authorizes, and then routes the Will unconditionally:There is no cancellation handle and no check of whether the same
client_idre-established the session during the delay window. So a client that drops abnormally and reconnects (resumes its session) before the Will Delay Interval elapses still has its Will published — a spurious Will. This is exactly the case Will Delay exists to prevent (flaky/mobile clients using a delay to avoid transient-disconnect Wills).Proposed fix: Track pending delayed-Will tasks keyed by
client_id(e.g. aJoinHandlemap on the broker/router), and cancel/abort the pending task when a new connection resumes that session before the delay elapses — the resume point isregister_session/handle_session. On a clean-start takeover the Will handling should follow the same rules (the prior session ends, so its Will fires per spec unless a delay is pending and the session is resumed). Add a test: connect with a will + delay N, drop abnormally, reconnect within N, assert the Will is never published; and a second test asserting it IS published if no reconnect occurs within N.Found while reviewing the broker's connect/disconnect event + LWT + timeout paths.