Lifecycle
MqttBroker::new records configuration, connect waits for the
server's CONNACK, startup subscribes each filter, and shutdown sends a DISCONNECT.
The diagram as text
- Broker: MqttBroker::new (url, client_id); ConnectedMqttBroker; Closed: () nothing to report
- On MQTT: URL, client id and settings, no I/O; Check the settings: port, keep-alive, TLS; Start the task, wait 30 s for a CONNACK; Each subscription: SUBSCRIBE, SUBACK; Reconnect with backoff, resubscribe; Mark closed, send DISCONNECT, flush
- MqttBroker::new (url, client_id) → ConnectedMqttBroker: connect(self)
- ConnectedMqttBroker → Closed: () nothing to report: shutdown(self)
- URL, client id and settings, no I/O → Check the settings: port, keep-alive, TLS
- Check the settings: port, keep-alive, TLS → Start the task, wait 30 s for a CONNACK
- Start the task, wait 30 s for a CONNACK → Each subscription: SUBSCRIBE, SUBACK
- Each subscription: SUBSCRIBE, SUBACK → Reconnect with backoff, resubscribe
- Reconnect with backoff, resubscribe → Mark closed, send DISCONNECT, flush
- A publisher that outlives the connection answers MqttError::NotConnected, never a silent success
new does no I/O, so the service's builder stays synchronous. It takes the URL, mqtt://host:port
or mqtts://host:port, and the client id, and the builder's settings are recorded beside them.
The runtime connects the broker when the app starts.
Connect checks the settings first, before any I/O. A port that is not a number, a keep-alive under
the client's 5-second minimum and TLS without a certificate authority to trust each fail the start
with MqttError::Invalid, naming the setting.
Then connect starts the connection task and waits up to 30 seconds for the server's first
CONNACK. The task retries what the moment may cure, such as a
server not up yet. A refusal the connection cannot outgrow ends the wait at once with
MqttError::Connect: bad credentials, a client id the server rejects, a peer that does not speak
MQTT, a TLS handshake the certificates cannot satisfy. A wait that runs out reports the last
connection error it saw.
Each subscription's descriptor resolves once, against the
connected broker. It sends a SUBSCRIBE and waits for the SUBACK, so the server holds the
subscription before the app reports that it started. A filter the server refuses fails the start
with MqttError::Subscribe.
While the service runs, the task reconnects a lost connection.
It waits 100 milliseconds before the first attempt and doubles the wait up to 5 seconds. When the
CONNACK reports the session gone, it subscribes every filter again. A refusal the connection cannot
outgrow ends the task instead, and every subscription returns it as MqttError::Receive.
Shutdown marks the connection closed first, so a publisher that aliases it cannot slip a message in. It then queues a DISCONNECT behind every acknowledgement already queued, and waits up to 5 seconds for the task to write them all. The server sees a clean end of the connection and publishes no last will. Shutdown returns nothing more: the closed state carries no diagnostics.
A publisher paired before the shutdown is a handle the shutdown cannot take back. It answers
MqttError::NotConnected. A delivery acknowledged after the shutdown reports a broker error.
The in-process transport follows the same ladder. Its connect checks the settings as connect
does, so a configuration the server path refuses is refused in a test too.