This document describes the protocol compliance features implemented in the MQTT library with configurable strict validation.
The library includes a feature flag strict-protocol-compliance that enables additional MQTT 5.0 protocol compliance validations. This feature is enabled by default but can be disabled if needed.
# In Cargo.toml - enable by default (current behavior)
[features]
default = ["strict-protocol-compliance"]
strict-protocol-compliance = []
# To disable strict compliance
cargo build --no-default-features
cargo test --no-default-featuresWhen strict-protocol-compliance is enabled, all UTF-8 strings are validated to ensure they comply with MQTT 5.0 specification:
- Null characters (U+0000) are rejected
- UTF-16 surrogate pairs (U+D800-U+DFFF) are rejected
- BOM (Byte Order Mark, U+FEFF) at the beginning of strings is rejected
Error Messages:
"UTF-8 string contains null character (U+0000)""UTF-8 string contains surrogate character (U+XXXX)""UTF-8 string starts with BOM (U+FEFF)"
Non-minimal VBI encodings are detected and rejected. A VBI is considered non-minimal if it uses more bytes than necessary to represent a value.
Error Message:
"Variable Byte Integer encoding is not minimal"
The reserved flag in Connect Flags must be 0.
Error Message:
"CONNECT packet reserved flag is not 0"
When Will Flag is not set (0):
- Will QoS must be 0
- Will Retain must be 0
When Will Flag is set:
- Will QoS cannot be 3 (invalid/malformed packet)
Error Messages:
"Will QoS must be 0 if Will Flag is 0""Will Retain must be 0 if Will Flag is 0""Will QoS cannot be 3"
The Connect Acknowledge Flags byte must have all reserved bits set to 0.
Error Message:
"CONNACK Connect Acknowledge Flags reserved bits must be 0"
- QoS level 3 (
11in binary) is invalid and rejected - DUP flag must be 0 for QoS 0 messages
Error Messages:
"PUBLISH QoS bits must not be set to 11 (invalid QoS 3)""PUBLISH DUP flag must be 0 for QoS 0 messages"
Topic names in PUBLISH packets must not contain wildcard characters (# or +).
Error Message:
"PUBLISH Topic Name must not contain wildcard characters (# or +)"
SUBSCRIBE packets must have fixed header flags set to 0010 (bit 1 = 1, others = 0).
Error Message:
"SUBSCRIBE fixed header flags must be 0010"
Packet identifier must be greater than 0.
Error Message:
"SUBSCRIBE packet_id must be > 0"
The payload must contain at least one subscription.
Error Message:
"SUBSCRIBE payload must contain at least one subscription"
Reserved bits in Subscription Options must be 0.
Error Message:
"SUBSCRIBE Subscription Options reserved bits must be 0"
The No Local flag cannot be set on shared subscriptions (topics starting with $share/).
Error Message:
"SUBSCRIBE No Local flag must not be set on Shared Subscriptions"
Comprehensive validation of topic filter syntax including:
- Empty topic filters are rejected
- Multi-level wildcards (
#) must be the only character in the level and the last level - Single-level wildcards (
+) must be the only character in the level - Maximum length validation (65535 bytes)
Error Messages:
"Topic filter cannot be empty""Multi-level wildcard (#) must be the only character in topic level""Multi-level wildcard (#) must be the last level in topic filter""Single-level wildcard (+) must be the only character in topic level"
Shared subscriptions must follow the format $share/ShareName/TopicFilter:
- ShareName cannot be empty
- TopicFilter cannot be empty
- The TopicFilter part is validated according to normal topic filter rules
Error Messages:
"Invalid shared subscription format: must be $share/ShareName/TopicFilter""Shared subscription ShareName cannot be empty""Shared subscription TopicFilter cannot be empty"
Packet identifier must be greater than 0.
Error Message:
"SUBACK packet_id must be > 0"
The payload must contain at least one reason code.
Error Message:
"SUBACK payload must contain at least one reason code"
UNSUBSCRIBE packets must have fixed header flags set to 0010 (bit 1 = 1, others = 0).
Error Message:
"UNSUBSCRIBE fixed header flags must be 0010"
Packet identifier must be greater than 0.
Error Message:
"UNSUBSCRIBE packet_id must be > 0"
The payload must contain at least one topic filter.
Error Message:
"UNSUBSCRIBE payload must contain at least one topic filter"
Packet identifier must be greater than 0.
Error Message:
"UNSUBACK packet_id must be > 0"
The payload must contain at least one reason code.
Error Message:
"UNSUBACK payload must contain at least one reason code"
PUBREL packets must have fixed header flags set to 0010 (bit 1 = 1, others = 0).
Error Message:
"Invalid PUBREL flags: expected 0x02, got 0x{flags:02x}"
PINGREQ packets must have remaining length of 0.
Error Message:
"PINGREQ packet must have remaining length of 0"
PINGREQ packets must have fixed header flags set to 0000 (all reserved bits = 0).
Error Message:
"PINGREQ packet has invalid fixed header flags"
PINGRESP packets must have remaining length of 0.
Error Message:
"PINGRESP packet must have remaining length of 0"
PINGRESP packets must have fixed header flags set to 0000 (all reserved bits = 0).
Error Message:
"PINGRESP packet has invalid fixed header flags"
DISCONNECT packets must have fixed header flags set to 0000 (all reserved bits = 0).
Error Message:
"DISCONNECT packet has invalid fixed header flags"
AUTH packets must have fixed header flags set to 0000 (all reserved bits = 0).
Error Message:
"AUTH packet has invalid fixed header flags"
AUTH packets with remaining length > 0 must contain a reason code.
Error Message:
"AUTH packet must contain a reason code"
Using request/response in a pub/sub system is error-prone, use HTTP-based request/response instead.
The library includes comprehensive tests for all validation features:
# Run all tests with strict compliance (default)
cargo test
# Run all tests without strict compliance
cargo test --no-default-features
# Run only protocol compliance tests
cargo test protocol_compliance_testsWhen strict-protocol-compliance is disabled, the library maintains backward compatibility and only performs basic validations that were already implemented. This ensures that existing code continues to work without changes.
When strict-protocol-compliance is enabled (default), the library provides maximum compliance with the MQTT 5.0 specification, which may reject some packets that were previously accepted.
use flowsdk::mqtt_serde::encode_utf8_string;
// With strict-protocol-compliance enabled (default):
let result = encode_utf8_string("hello\u{0000}world");
assert!(result.is_err()); // Rejected due to null character
// With strict-protocol-compliance disabled:
// The same string would be accepted (only length validation)use flowsdk::mqtt_serde::mqttv5::subscribe::TopicSubscription;
// With strict-protocol-compliance enabled:
let result = TopicSubscription::from_bytes(b"$share/group/topic\x80"); // Invalid subscription options
assert!(result.is_err()); // Rejected due to reserved bits
// With strict-protocol-compliance disabled:
// Reserved bits validation is not performed