Summary
When the broker is configured with BrokerConfig.server_keep_alive and overrides the client's requested keep-alive via the CONNACK ServerKeepAlive property, the broker continues to use the client's original requested value for its own read-timeout instead of the value it just imposed.
This was flagged by @butterflyfish in #78 and confirmed during follow-up review. The client side was fixed in #78; this issue tracks the symmetric broker-side fix.
Where
crates/mqtt5/src/broker/client_handler/connect.rs:95 — self.keep_alive = Duration::from_secs(u64::from(connect.keep_alive)); sets the broker's internal value from the CONNECT packet (client's request).
crates/mqtt5/src/broker/client_handler/connect.rs:403-407 — the broker writes ServerKeepAlive to CONNACK from config.server_keep_alive, but never updates self.keep_alive to the overridden value.
crates/mqtt5/src/broker/client_handler/mod.rs:616 — read_timeout = self.keep_alive.mul_f32(1.5) consequently uses the client's original value.
Effect
If the broker is configured with server_keep_alive = Some(10s) and the client requests 60s:
Functionally safe in the spec-compliant case — the broker is just more permissive than its own policy implies. But:
- With a non-compliant or older client that ignores
ServerKeepAlive, the broker would fail to detect a hung connection within the intended window.
- It defeats the operational intent of
server_keep_alive (typically set to detect dead connections sooner than the client's preferred cadence).
Suggested fix
In connect.rs after writing the ServerKeepAlive CONNACK property (line 403-407), also update self.keep_alive:
if let Some(keep_alive) = self.config.server_keep_alive {
let secs = u16::try_from(keep_alive.as_secs()).unwrap_or(u16::MAX);
connack.properties.set_server_keep_alive(secs);
self.keep_alive = Duration::from_secs(u64::from(secs));
}
(The u16 clamp logic should probably share a helper with the client-side configured_keep_alive_u16 introduced in #78.)
Test
Add a broker-side integration test that:
- Configures broker with
server_keep_alive = Some(small_value).
- Connects a client that requests a much larger keep-alive but doesn't send PINGREQ (simulate a non-compliant client by disabling its keepalive task).
- Asserts the broker disconnects the client within
~small_value * 1.5, not within ~large_value * 1.5.
References
Summary
When the broker is configured with
BrokerConfig.server_keep_aliveand overrides the client's requested keep-alive via the CONNACKServerKeepAliveproperty, the broker continues to use the client's original requested value for its own read-timeout instead of the value it just imposed.This was flagged by @butterflyfish in #78 and confirmed during follow-up review. The client side was fixed in #78; this issue tracks the symmetric broker-side fix.
Where
crates/mqtt5/src/broker/client_handler/connect.rs:95—self.keep_alive = Duration::from_secs(u64::from(connect.keep_alive));sets the broker's internal value from the CONNECT packet (client's request).crates/mqtt5/src/broker/client_handler/connect.rs:403-407— the broker writesServerKeepAliveto CONNACK fromconfig.server_keep_alive, but never updatesself.keep_aliveto the overridden value.crates/mqtt5/src/broker/client_handler/mod.rs:616—read_timeout = self.keep_alive.mul_f32(1.5)consequently uses the client's original value.Effect
If the broker is configured with
server_keep_alive = Some(10s)and the client requests60s:ServerKeepAlive = 10.60 * 1.5 = 90sfor its read timeout instead of10 * 1.5 = 15s.Functionally safe in the spec-compliant case — the broker is just more permissive than its own policy implies. But:
ServerKeepAlive, the broker would fail to detect a hung connection within the intended window.server_keep_alive(typically set to detect dead connections sooner than the client's preferred cadence).Suggested fix
In
connect.rsafter writing theServerKeepAliveCONNACK property (line 403-407), also updateself.keep_alive:(The
u16clamp logic should probably share a helper with the client-sideconfigured_keep_alive_u16introduced in #78.)Test
Add a broker-side integration test that:
server_keep_alive = Some(small_value).~small_value * 1.5, not within~large_value * 1.5.References