Skip to content

Added MQTT requests - #216

Merged
Julien-cpsn merged 12 commits into
Julien-cpsn:mainfrom
helenkwok:feature/mqtt
Oct 11, 2026
Merged

Julien-cpsn merged 12 commits into
Julien-cpsn:mainfrom
helenkwok:feature/mqtt

Conversation

@helenkwok

@helenkwok helenkwok commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Closes #215.

MQTT demo

Adds MQTT requests to ATAC, using upstream rumqttc 0.25.1 as agreed in the issue. Works with MQTT 3.1.1 and 5, over TCP (mqtt://, tcp://) and TLS (mqtts://, ssl://).

How it works

An MQTT request is a new protocol next to HTTP and WebSocket. The request URL is the broker, and the username and password come from basic auth. It reuses the WebSocket view: sending the request connects, messages arrive in the Messages tab, and it stays connected until you disconnect.

Param tabs:

  • Auth
  • Connect: version, clean session, client ID, keep alive, max packet size, MQTT 5 session expiry
  • Subscribe: key/value table of topics and QoS
  • Publish: topic, QoS, retain and payload (text or binary)
  • Scripts

Result tabs:

  • Messages: received and sent messages, plus events such as subscription results and disconnections
  • Headers: CONNACK session present and the MQTT 5 CONNACK properties
  • Console

The CONNACK reason code is shown as the status. A refused connection shows the error instead, such as the broker's reason code.

CLI:

  • request new -p MQTT takes --mqtt-version, --client-id, --no-clean-session, --session-expiry, --keep-alive, --max-packet-size, --add-subscription <TOPIC> <QOS> and --publish-topic/--publish-qos/--publish-retain.
  • request send prints messages as they arrive, and publishes each line read from stdin to the publish topic.
  • request info shows the MQTT settings.

Stability

rumqttc panics on a few inputs, so ATAC checks for them first and shows an error instead:

Other details:

  • Messages over the max packet size are refused before sending, since sending them would close the connection. The limit is the request's, or the MQTT 5 broker's if it advertises a lower one in its CONNACK.
  • The Accept invalid certs and Accept invalid hostnames request settings also apply to MQTT over TLS, with the same meaning as for HTTP.
  • rumqttc accepts brokers that send session present on a clean connect. That's a spec violation, but some real brokers do it.
  • MQTT 5 persistent sessions send a session expiry (default one hour, editable), so test sessions don't pile up forever on shared brokers.
  • Also fixes a TUI crash that already existed upstream: a response or an MQTT message arriving while no request is selected (e.g. after unselecting or deleting it) panicked in get_selected_request_as_local. A slow HTTP response could trigger it too.

Dependencies and size

  • New dependencies: rumqttc and rustls-native-certs.
  • Lockfile: adds 5 crates (rumqttc, flume, tokio-stream, rustls-pemfile, rustls-webpki 0.102).
  • Binary size: the release binary grows by about 0.37 MB (+3%) on macOS arm64.

Tested

  • Public brokers: EMQX, HiveMQ and test.mosquitto.org, with MQTT 3.1.1 and 5 over both 1883 and 8883. Each test connected, subscribed, published, and received its own message back.
  • Auth: test.mosquitto.org's authenticated port, with correct and wrong passwords.
  • TLS settings: untrusted certificate and hostname mismatch, each with the setting off (refused) and on (connects).
  • Local mosquitto: MQTT 5 session resume, where messages published while offline are delivered, and session expiry 0.
  • Error cases: wrong URL scheme, unsupported auth, unreachable broker, oversized message, timeout and cancel.
  • Load: 12 subscriptions with a burst of 50 publishes, all subscribed, sent and received back.
  • Request lifecycle: deleting a connected request (or its collection) disconnects it, and a duplicated request starts disconnected.
  • Real brokers: a production MQTT 5 broker that advertises a 64 KiB maximum packet size and sends session present on clean connects, and a local mosquitto with max_packet_size 1000: an oversized message is refused on its own and the connection stays up.
  • Misbehaving brokers: 23 scenarios against a scripted fake broker, on both MQTT versions where they apply: refused, silent, slow or duplicated CONNACKs; session present on a clean connect; garbage, malformed or oversized packets; invalid UTF-8 topics; unknown topic aliases; packet id 0; unsolicited acks; dropped connections; missing ping responses; a broker DISCONNECT with a reason; SUBACK failures; incoming QoS 2; retained, empty, binary and Unicode messages; 20,000 messages in a burst, including while nothing reads the CLI's output. None crash or hang, and the CLI exits when the connection ends.
  • Unit tests: 37 tests (cargo test, no new dependencies) for URL and port handling, the input checks, environment variables, the packet size limits (the request's and the broker's), the certificate verifier with the request settings (with an embedded test certificate authority), saving and loading requests, loading every example collection, and the request new flags.
  • TUI: connect, subscribe, publish and disconnect. WebSocket requests still work after the shared messages view change.

The GIF and an example collection are in gifs/mqtt.gif and example_resources/collections/mqtt.json.

Not included

  • MQTT over WebSocket: rumqttc has a feature for it, but it adds dependencies. It could come in a follow-up if wanted.
  • Export: the TUI doesn't offer export for MQTT requests, and the CLI answers "Export format not supported for the MQTT protocol". A mosquitto_pub/mosquitto_sub export would be possible if you'd like one.
  • Other auth: only basic auth, since that's what MQTT has. Bearer, JWT and digest give a clear error.

Noticed while testing, not changed here

These already exist upstream and aren't MQTT-specific, so I left them for you to decide:

  • When a post-request script fails, the request stays "pending", for HTTP and WebSocket too.
  • Duplicating a collection shares the same request objects with the original, so editing a request in one also changes it in the other until ATAC restarts.
  • OS environment variables override the selected environment's values with the same name, so {{USER}} in an environment file is replaced with the OS user.
  • Received messages are kept in memory without a limit, for WebSocket too. After 20,000 MQTT messages the TUI still responds to a key in about 60 ms.

Adds Protocol::MqttRequest with the MQTT version, client ID, clean session,
keep-alive, max packet size, subscriptions, publish options and message log.
The broker is the request URL, credentials come from basic auth.

Sending and the TUI view are temporary placeholders for now.
Uses rumqttc-next (MQTT 3.1.1 and 5, mqtt:// and mqtts://) behind a small
wrapper, the connection runs in its own task and receives publish and
disconnect commands through a channel.

- CONNACK is shown as the status, session present and MQTT 5 properties as headers
- Enabled subscriptions are subscribed on connect, messages and events are logged
- An empty client ID with clean session off is refused instead of panicking
- Messages over the max packet size are refused instead of closing the connection
- Explains the error when a broker answers a clean session with session present
- CLI send prints messages as they arrive and publishes stdin lines
- Param tabs for MQTT requests: Auth, Connect, Subscribe, Publish and Scripts
- Connect tab edits the version, client ID, clean session, keep alive and max packet size
- Subscribe tab lists the topic filters, QoS is changed with enter
- Publish tab sets the topic, QoS and retain, confirming the payload publishes it
- Messages result tab shows the published, received and connection events
- Headers result tab shows session present and the MQTT 5 CONNACK properties
- MQTT can be chosen when creating a new request
- The messages tab rendering is shared between websocket and MQTT requests
Replaces rumqttc-next with upstream rumqttc 0.25.1, as agreed in the issue.
The client and its event loop are kept apart since the event loop is not Sync.

- Option values rumqttc panics on are refused with a clear error first: an
  empty MQTT 3.1.1 client ID with clean session off, an MQTT 5 keep alive
  under 5 seconds
- TLS uses its own rustls configuration with the aws_lc_rs provider instead of
  the default one, which panics when system certificates cannot be loaded
- Accept invalid certs and accept invalid hostnames now apply to MQTT over TLS,
  with the same meaning as for HTTP
- Added an MQTT 5 session expiry field (default 3600 seconds), sent when clean
  session is off and a client ID is set
- The URL scheme error lists mqtt://, mqtts://, tcp:// and ssl://
- Removed the session present workaround, rumqttc accepts it
- request new takes --mqtt-version, --client-id, --no-clean-session,
  --session-expiry, --keep-alive, --max-packet-size, --add-subscription,
  --publish-topic, --publish-qos and --publish-retain
- MQTT options are refused on other protocols, and headers on MQTT requests
- request info shows the MQTT settings
- New MQTT requests no longer get the HTTP default headers, from the CLI or the TUI
- More than 10 subscriptions, or a burst of publishes, could block forever
  since rumqttc's request queue is only emptied by the event loop. Polling
  and sending now run in separate tasks
- A pre-request script changing the protocol is a script error instead of a panic
- request send --cookies no longer panics when there are no cookies
- Deleting a connected request, or its collection, disconnects it
- A duplicated request does not share the original's connection
- A max packet size of 0 is refused
- Accepting invalid certs no longer needs platform certificates
- The MQTT 5 packet size estimate includes the properties length
… output Julien-cpsn#215

- stdin is read from a plain thread, a pending tokio stdin read kept the
  process alive once the connection had ended
- Messages are copied before printing, printing under the request lock
  blocked the connection from polling when stdout was slow
Covers URL and port handling, input checks, environment variables, the
packet size limit, the certificate verifier with the request settings,
saving and loading requests, the example collections and the request new
flags. No new dependencies.
@Julien-cpsn

Copy link
Copy Markdown
Owner

Hello @helenkwok ,

Looks fantastic

@ me when the PR is ready for review. Sorry but it might take a long time as you have added 2900 lines of code lol

Have a great day!

…lien-cpsn#215

- The result view refresh after a response or a message assumed a selected
  request, so an MQTT message or disconnection after unselecting or deleting
  the request panicked. A slow HTTP response did the same
- A pre-request script changing the protocol of an MQTT request now says so
…psn#215

An MQTT 5 broker can advertise a lower maximum packet size than the
request's in its CONNACK. rumqttc then refuses to send a larger message by
closing the whole connection, so such a message is now refused on its own,
with the broker's limit in the event.
@helenkwok
helenkwok marked this pull request as ready for review September 25, 2026 04:06
@helenkwok

Copy link
Copy Markdown
Contributor Author

@Julien-cpsn it's ready for review, thank you!

To make the 2,950 lines less painful:

  • 584 of them are unit tests, mostly in the "Added MQTT unit tests" commit, which is easy to drop if you'd rather not have tests yet. Another ~170 are Cargo.lock, the example collection and the README.
  • That leaves about 2,200 lines of code:
    • src/app/business_logic/request/mqtt/ (~720): the rumqttc wrapper, TLS and the connection tasks. This is the part worth the closest look.
    • src/tui (~860): mostly connecting the new states and inputs to the existing lists, plus the Connect, Subscribe and Publish tabs.
    • src/models (~290) and src/cli (~240).
  • A suggested reading order: models/protocol/mqtt → business_logic/request/mqtt → cli → tui. The commits are in a logical order too, so reviewing commit by commit also works.
  • It also fixes an existing TUI crash when a response arrives while no request is selected (see the PR description).

Copilot AI lite review requested due to automatic review settings September 25, 2026 04:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Julien-cpsn
Julien-cpsn merged commit 54e3801 into Julien-cpsn:main Oct 11, 2026
@helenkwok
helenkwok deleted the feature/mqtt branch October 11, 2026 22:31
@helenkwok

Copy link
Copy Markdown
Contributor Author

Thank you so much for merging, and for the kind words in the release notes, Julien!

One small thing I noticed while trying 0.24.0 with a theme file from 0.23: besides the new [MQTT] section from the release notes, a custom theme also needs the new QUERY colour. Otherwise the TUI exits with Could not parse theme file / missing field `query` . Adding it under [HTTP.methods] fixes it:

QUERY = "LightMagenta"

It might be worth adding next to the MQTT snippet in the v0.24.0 release notes.

@Julien-cpsn

Copy link
Copy Markdown
Owner

@helenkwok yes you are totally right, I forgot lol

Thank you
Have a very good day!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature proposal: MQTT requests

3 participants