OASIS MQTT v3.1.1 — Spec Coverage

Spec: OASIS MQTT Version 3.1.1 — OASIS Standard, 29 October 2014 →

Context: MQTT 3.1.1 (protocol level 4) shares the control-packet framing (§2.1) with MQTT 5.0 but differs in the variable header / payload: no property blocks (introduced in 5.0), a 2-byte CONNACK, a UNSUBACK without reason codes, an empty DISCONNECT body, and no AUTH packet. ZeroDDS implements 3.1.1 as a version-aware path in the same crates/mqtt-bridge codec (ProtocolVersion::V311); the broker (server.rs) and client (net.rs) negotiate the version per connection from the CONNECT protocol level. Every item is backed by a repo path + a test; interop is proven live against Eclipse Mosquitto 2.0 (-V mqttv311), both directions and both roles (client against a Mosquitto broker, real Mosquitto clients against the ZeroDDS broker).

Implementation:

  • crates/mqtt-bridge/ · docs.rs — version-aware 3.1.1 codec (ProtocolVersion::V311)
    • broker (server.rs) + client (net.rs); 165 lib tests + 6 broker E2E + 5 Mosquitto-live green.

§3.1.2.2 Protocol level + version negotiation

Spec: §3.1.2.1-§3.1.2.2 — protocol name "MQTT" + protocol level 4. The server accepts/rejects based on the level.

Repo: crates/mqtt-bridge/src/version.rs::ProtocolVersion (V311/V5, level/from_level/has_properties/protocol_name). The broker reads the level from the CONNECT body and replies in the matching dialect (server.rs::handle_connection).

Tests: version::tests::{level_round_trip, unsupported_levels_rejected, only_v5_has_properties}.

Status: done

§2.1 Frame format (shared with 5.0)

Spec: §2 — fixed header (type nibble + flags nibble + Remaining Length VBI). Identical to MQTT 5.0.

Repo: crates/mqtt-bridge/src/packet.rs::{FixedHeader, ControlPacketType} + vbi.rs (Remaining-Length VBI) + net.rs::{read_packet, frame_packet}.

Tests: packet::tests::*, vbi::tests::*, net::tests::{ frame_and_read_round_trip, read_packet_handles_multibyte_remaining_length}.

Status: done

§3.1 CONNECT

Spec: §3.1 v3.1.1 — like 5.0 but without the CONNECT and Will property blocks.

Repo: control_packets::{encode_connect_body_v, decode_connect_body_v} with ProtocolVersion::V311 (drops both property blocks).

Tests: control_packets::tests::v311_connect_omits_property_blocks; live mosquitto_interop_e2e.rs (3.1.1 handshake against Mosquitto + ZeroDDS broker).

Status: done

§3.2 CONNACK

Spec: §3.2 v3.1.1 — exactly 2 bytes (acknowledge flags + return code), no property block.

Repo: control_packets::{encode_connack_body_v, decode_connack_body_v}.

Tests: control_packets::tests::v311_connack_is_exactly_two_bytes.

Status: done

§3.3 PUBLISH

Spec: §3.3 v3.1.1 — topic + (at QoS>0) packet identifier + payload, without a property block.

Repo: codec::{encode_publish_v, decode_publish_v} with V311.

Tests: codec::tests::{v311_publish_has_no_property_block, v311_publish_qos0_round_trip}; live mosquitto_interop_e2e.rs.

Status: done

§3.4-§3.7 PUBACK / PUBREC / PUBREL / PUBCOMP

Spec: §3.4-§3.7 v3.1.1 — exactly the 2-byte packet identifier (no reason code, no properties).

Repo: control_packets::{encode_ack_body_v, decode_ack_body} (V311 emits only the packet identifier; the shared decoder reads the short form). The QoS-2 flow in the broker: server.rs (PUBREC→PUBREL→PUBCOMP).

Tests: control_packets::tests::v311_ack_is_packet_id_only; in-process broker_server_e2e::qos2_exactly_once_delivery.

Status: done

§3.8 SUBSCRIBE

Spec: §3.8 v3.1.1 — packet identifier + (topic filter + QoS byte) list, no property block.

Repo: control_packets::{encode_subscribe_body_v, decode_subscribe_body_v}.

Tests: control_packets round-trip + live mosquitto_interop_e2e.rs.

Status: done

§3.9 SUBACK

Spec: §3.9 v3.1.1 — packet identifier + return codes (granted QoS 0/1/2 or 0x80 = failure), no property block.

Repo: control_packets::{encode_suback_body_v, decode_suback_body_v}.

Tests: control_packets::tests::v311_suback_has_no_property_block.

Status: done

§3.10-§3.11 UNSUBSCRIBE / UNSUBACK

Spec: §3.10-§3.11 v3.1.1 — UNSUBSCRIBE = packet identifier + filter list (no properties); UNSUBACK = the packet identifier only (no reason codes — those arrived in 5.0).

Repo: control_packets::{encode_unsubscribe_body_v, decode_unsubscribe_body_v, encode_unsuback_body_v, decode_unsuback_body_v}.

Tests: control_packets::tests::v311_unsuback_is_packet_id_only_no_reason_codes.

Status: done

§3.12-§3.13 PINGREQ / PINGRESP

Spec: §3.12-§3.13 — body-less keep-alive packets. Identical to 5.0.

Repo: server.rs (PINGREQ → PINGRESP); wire constants in packet.rs.

Tests: covered by the broker e2e (keep-alive path).

Status: done

§3.14 DISCONNECT

Spec: §3.14 v3.1.1 — an empty body (no reason code, no properties); the fixed-header pair 0xE0 0x00 is the whole packet.

Repo: control_packets::encode_disconnect_body_v (V311 → empty body) + net.rs::MqttClient::disconnect.

Tests: control_packets::tests::v311_disconnect_body_is_empty.

Status: done

§4 Operational behavior (3.1.1 at the broker + client)

Spec: §4 — session state, subscriptions, QoS flow, retained, will. Identical semantics to 5.0; only the wire encoding of the replies is 3.1.1.

Repo: server.rs::MqttBrokerServer + net.rs::MqttClient (both version-aware) over the shared broker.rs engine.

Tests: in-process broker_server_e2e::{cross_version_v5_publishes_v311_subscribes, cross_version_v311_publishes_v5_subscribes}; live mosquitto_interop_e2e::{ zerodds_311_client_receives_from_mosquitto, mosquitto_311_client_receives_from_zerodds, mosquitto_v311_clients_through_zerodds_broker}.

Status: done

§3.15 AUTH

Spec: — MQTT 3.1.1 has no AUTH packet (introduced only in 5.0, §3.15 v5.0).

Repo:

Tests:

Status: n/a (informative) — not part of the 3.1.1 spec.


Audit status

12 done / 0 partial / 0 open / 1 n/a (informative) / 0 n/a (rejected).

Test run: cargo test -p zerodds-mqtt-bridge --lib — 165 tests green (incl. the v311_* codec tests + the version module); in-process cargo test -p zerodds-mqtt-bridge --test broker_server_e2e — 6 green. Live interop (codepit, Eclipse Mosquitto 2.0): MQTT_MOSQUITTO=1 cargo test -p zerodds-mqtt-bridge --test mosquitto_interop_e2e -- --ignored — 5 green.

OASIS MQTT v3.1.1 — Spec-Coverage

Spec: OASIS MQTT Version 3.1.1 — OASIS Standard, 29 October 2014 →

Scope-Hinweis: MQTT 3.1.1 (Protocol Level 4) teilt das Control-Packet- Framing (§2.1) mit MQTT 5.0, unterscheidet sich aber im Variable Header / Payload: keine Property-Blocks (in 5.0 eingeführt), 2-Byte-CONNACK, UNSUBACK ohne Reason-Codes, leerer DISCONNECT-Body, kein AUTH-Packet. ZeroDDS implementiert 3.1.1 als version-aware Pfad im selben crates/mqtt-bridge-Codec (ProtocolVersion::V311); der Broker (server.rs) und Client (net.rs) handeln die Version pro Verbindung aus dem CONNECT-Protocol-Level aus. Jedes Item ist mit Repo + Test belegt; Interop ist live gegen Eclipse Mosquitto 2.0 (-V mqttv311) bewiesen, in beide Richtungen und in beiden Rollen (Client gegen Mosquitto-Broker, echte Mosquitto-Clients gegen ZeroDDS-Broker).

Implementation:

  • crates/mqtt-bridge/ · docs.rs — version-aware 3.1.1-Codec (ProtocolVersion::V311)
    • Broker (server.rs) + Client (net.rs); 165 lib-Tests + 6 Broker-E2E + 5 Mosquitto-Live grün.

§3.1.2.2 Protocol Level + Versions-Negotiation

Spec: §3.1.2.1-§3.1.2.2 — Protocol Name "MQTT" + Protocol Level 4. Der Server akzeptiert/verweigert anhand des Levels.

Repo: crates/mqtt-bridge/src/version.rs::ProtocolVersion (V311/V5, level/from_level/has_properties/protocol_name). Der Broker liest das Level aus dem CONNECT-Body und antwortet im passenden Dialekt (server.rs::handle_connection).

Tests: version::tests::{level_round_trip, unsupported_levels_rejected, only_v5_has_properties}.

Status: done

§2.1 Frame-Format (geteilt mit 5.0)

Spec: §2 — Fixed Header (type-Nibble + flags-Nibble + Remaining-Length- VBI). Identisch zu MQTT 5.0.

Repo: crates/mqtt-bridge/src/packet.rs::{FixedHeader, ControlPacketType} + vbi.rs (Remaining-Length-VBI) + net.rs::{read_packet, frame_packet}.

Tests: packet::tests::*, vbi::tests::*, net::tests::{ frame_and_read_round_trip, read_packet_handles_multibyte_remaining_length}.

Status: done

§3.1 CONNECT

Spec: §3.1 v3.1.1 — wie 5.0, aber ohne CONNECT- und Will-Property-Blocks.

Repo: control_packets::{encode_connect_body_v, decode_connect_body_v} mit ProtocolVersion::V311 (lässt beide Property-Blocks weg).

Tests: control_packets::tests::v311_connect_omits_property_blocks; Live mosquitto_interop_e2e.rs (3.1.1-Handshake gegen Mosquitto + ZeroDDS-Broker).

Status: done

§3.2 CONNACK

Spec: §3.2 v3.1.1 — genau 2 Byte (Acknowledge Flags + Return Code), kein Property-Block.

Repo: control_packets::{encode_connack_body_v, decode_connack_body_v}.

Tests: control_packets::tests::v311_connack_is_exactly_two_bytes.

Status: done

§3.3 PUBLISH

Spec: §3.3 v3.1.1 — Topic + (bei QoS>0) Packet-Identifier + Payload, ohne Property-Block.

Repo: codec::{encode_publish_v, decode_publish_v} mit V311.

Tests: codec::tests::{v311_publish_has_no_property_block, v311_publish_qos0_round_trip}; Live mosquitto_interop_e2e.rs.

Status: done

§3.4-§3.7 PUBACK / PUBREC / PUBREL / PUBCOMP

Spec: §3.4-§3.7 v3.1.1 — genau der 2-Byte Packet-Identifier (kein Reason- Code, keine Properties).

Repo: control_packets::{encode_ack_body_v, decode_ack_body} (V311 emittiert nur den Packet-Identifier; der gemeinsame Decoder liest die Short-Form). QoS-2-Flow im Broker: server.rs (PUBREC→PUBREL→PUBCOMP).

Tests: control_packets::tests::v311_ack_is_packet_id_only; In-Process broker_server_e2e::qos2_exactly_once_delivery.

Status: done

§3.8 SUBSCRIBE

Spec: §3.8 v3.1.1 — Packet-Identifier + (Topic-Filter + QoS-Byte)-Liste, ohne Property-Block.

Repo: control_packets::{encode_subscribe_body_v, decode_subscribe_body_v}.

Tests: control_packets-Roundtrip + Live mosquitto_interop_e2e.rs.

Status: done

§3.9 SUBACK

Spec: §3.9 v3.1.1 — Packet-Identifier + Return-Codes (Granted QoS 0/1/2 oder 0x80 = Failure), ohne Property-Block.

Repo: control_packets::{encode_suback_body_v, decode_suback_body_v}.

Tests: control_packets::tests::v311_suback_has_no_property_block.

Status: done

§3.10-§3.11 UNSUBSCRIBE / UNSUBACK

Spec: §3.10-§3.11 v3.1.1 — UNSUBSCRIBE = Packet-Identifier + Filter-Liste (keine Properties); UNSUBACK = nur der Packet-Identifier (keine Reason-Codes — die kamen erst in 5.0).

Repo: control_packets::{encode_unsubscribe_body_v, decode_unsubscribe_body_v, encode_unsuback_body_v, decode_unsuback_body_v}.

Tests: control_packets::tests::v311_unsuback_is_packet_id_only_no_reason_codes.

Status: done

§3.12-§3.13 PINGREQ / PINGRESP

Spec: §3.12-§3.13 — body-lose Keep-Alive-Packets. Identisch zu 5.0.

Repo: server.rs (PINGREQ → PINGRESP); Wire-Konstanten in packet.rs.

Tests: abgedeckt durch die Broker-e2e (Keep-Alive-Pfad).

Status: done

§3.14 DISCONNECT

Spec: §3.14 v3.1.1 — leerer Body (kein Reason-Code, keine Properties); das Fixed-Header-Paar 0xE0 0x00 ist das ganze Packet.

Repo: control_packets::encode_disconnect_body_v (V311 → leerer Body) + net.rs::MqttClient::disconnect.

Tests: control_packets::tests::v311_disconnect_body_is_empty.

Status: done

§4 Operational Behavior (3.1.1 am Broker + Client)

Spec: §4 — Session-State, Subscriptions, QoS-Flow, Retained, Will. Identische Semantik zu 5.0; nur das Wire-Encoding der Replies ist 3.1.1.

Repo: server.rs::MqttBrokerServer + net.rs::MqttClient (beide version-aware) über der gemeinsamen broker.rs-Engine.

Tests: In-Process broker_server_e2e::{cross_version_v5_publishes_v311_subscribes, cross_version_v311_publishes_v5_subscribes}; Live mosquitto_interop_e2e::{ zerodds_311_client_receives_from_mosquitto, mosquitto_311_client_receives_from_zerodds, mosquitto_v311_clients_through_zerodds_broker}.

Status: done

§3.15 AUTH

Spec: — In MQTT 3.1.1 existiert kein AUTH-Packet (erst in 5.0, §3.15 v5.0 eingeführt).

Repo:

Tests:

Status: n/a (informative) — nicht Teil der 3.1.1-Spec.


Audit-Status

12 done / 0 partial / 0 open / 1 n/a (informative) / 0 n/a (rejected).

Test-Lauf: cargo test -p zerodds-mqtt-bridge --lib — 165 Tests grün (inkl. der v311_*-Codec-Tests + version-Modul); In-Process cargo test -p zerodds-mqtt-bridge --test broker_server_e2e — 6 grün. Live-Interop (codepit, Eclipse Mosquitto 2.0): MQTT_MOSQUITTO=1 cargo test -p zerodds-mqtt-bridge --test mosquitto_interop_e2e -- --ignored — 5 grün.