├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── src ├── broker.rs ├── config.rs ├── de │ ├── deserializer.rs │ ├── mod.rs │ ├── packet_reader.rs │ └── received_packet.rs ├── lib.rs ├── message_types.rs ├── mqtt_client.rs ├── network_manager.rs ├── packets.rs ├── properties.rs ├── publication.rs ├── reason_codes.rs ├── republication.rs ├── ring_buffer.rs ├── ser │ └── mod.rs ├── session_state.rs ├── types.rs ├── varint.rs └── will.rs └── tests ├── at_least_once_subscription.rs ├── deferred_payload.rs ├── exactly_once_subscription.rs ├── exactly_once_transmission.rs ├── integration_test.rs ├── poll_result.rs ├── reconnection.rs └── stack └── mod.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | style: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions-rs/toolchain@v1 18 | with: 19 | profile: minimal 20 | toolchain: stable 21 | override: true 22 | components: rustfmt 23 | - name: cargo fmt --check 24 | uses: actions-rs/cargo@v1 25 | with: 26 | command: fmt 27 | args: --all -- --check 28 | 29 | documentation: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | 34 | - uses: actions-rs/toolchain@v1 35 | with: 36 | profile: minimal 37 | toolchain: stable 38 | target: thumbv7em-none-eabihf 39 | override: true 40 | 41 | - name: Cargo Doc 42 | uses: actions-rs/cargo@v1 43 | with: 44 | command: doc 45 | 46 | clippy: 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v2 50 | - uses: actions-rs/toolchain@v1 51 | with: 52 | profile: minimal 53 | toolchain: stable 54 | target: thumbv7em-none-eabihf 55 | override: true 56 | components: clippy 57 | 58 | - name: cargo clippy 59 | uses: actions-rs/cargo@v1 60 | with: 61 | command: clippy 62 | 63 | audit: 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v2 67 | - name: Cargo Audit 68 | uses: actions-rs/audit-check@v1 69 | with: 70 | token: ${{ secrets.GITHUB_TOKEN }} 71 | 72 | compile: 73 | runs-on: ubuntu-latest 74 | strategy: 75 | matrix: 76 | toolchain: 77 | - stable 78 | - beta 79 | steps: 80 | - uses: actions/checkout@v2 81 | - name: Install Rust ${{ matrix.toolchain }} 82 | uses: actions-rs/toolchain@v1 83 | with: 84 | toolchain: ${{ matrix.toolchain }} 85 | target: thumbv7em-none-eabihf 86 | override: true 87 | - name: cargo check 88 | uses: actions-rs/cargo@v1 89 | with: 90 | command: check 91 | args: --verbose 92 | - name: cargo build 93 | uses: actions-rs/cargo@v1 94 | with: 95 | command: build 96 | args: --all-features 97 | - name: cargo build release 98 | uses: actions-rs/cargo@v1 99 | with: 100 | command: build 101 | args: --release --all-features 102 | 103 | test: 104 | runs-on: ubuntu-20.04 105 | steps: 106 | - uses: actions/checkout@v2 107 | 108 | - name: Start Mosquitto 109 | run: | 110 | sudo apt-get install mosquitto 111 | sudo service mosquitto start 112 | 113 | 114 | - name: Install Rust ${{ matrix.toolchain }} 115 | uses: actions-rs/toolchain@v1 116 | with: 117 | override: true 118 | toolchain: stable 119 | profile: minimal 120 | 121 | - name: Cargo Test 122 | uses: actions-rs/cargo@v1 123 | with: 124 | command: test 125 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ejabberd/*/ 2 | /target 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | 4 | All notable changes to this project will be documented in this file. 5 | 6 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 7 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 8 | 9 | ## [0.10.0](https://github.com/quartiq/minimq/compare/v0.9.0...v0.10.0) - 2025-01-27 10 | 11 | ## Changed 12 | 13 | * The `Publication::finish()` API was removed in favor of a new `Publication::respond()` API for 14 | constructing replies to previously received messages. 15 | * `DeferredPublication` has been removed: pass a `FnOnce(&mut [u8])` as payload. 16 | * [breaking] `embedded-nal` bumped. Now `core::net::SocketAddr` and related ip types are used. 17 | MSRV becomes 1.77.0. 18 | 19 | # [0.9.0] - 2024-04-29 20 | 21 | ## Fixed 22 | 23 | * Fixed an issue where a corrupted mqtt header length could result in a crash 24 | * [breaking] `embedded-nal` bumped 25 | 26 | # [0.8.0] - 2023-11-01 27 | 28 | ## Changed 29 | 30 | * [breaking] Const generics for message size and allowable in-flight messages have been removed. 31 | Instead, the user now supplies an RX buffer, a TX buffer, and a session state buffer. 32 | * Setup-only configuration APIs such as `set_will()` and `set_keepalive_interval()` have been moved 33 | to a new `Config` structure that is supplied to the `Minimq::new()` constructor to simplify the 34 | client. 35 | * Added a new `correlate()` API to publication builder to easily add correlation data. 36 | 37 | ## Added 38 | 39 | * Support for subscribing at `QoS::ExactlyOnce` 40 | * Support for downgrading the `QoS` to the maximum permitted by the server 41 | * Brokers may now be provided using domain-name syntax or static IP addresses. 42 | 43 | ## Fixed 44 | 45 | * Fixed an issue where PubComp was serialized with an incorrect control code 46 | * Fixed an issue where some response control packets would be improperly serialized 47 | * The client now respects the server max packet reception 48 | 49 | # [0.7.0] - 2023-06-22 50 | 51 | ## Fixed 52 | 53 | * [breaking] Embedded-nal version updated to 0.7 54 | * Fixed an issue where the MQTT client would become permanently inoperable when the broker 55 | disconnected under certain conditions. 56 | 57 | # [0.6.2] - 2023-04-05 58 | 59 | ## Fixed 60 | 61 | * `UserProperty` now properly serializes key-then-value. Serialization order was previously 62 | unintentionally inverted. 63 | 64 | # [0.6.1] - 2022-11-03 65 | 66 | ## Fixed 67 | 68 | * `PubAck` can now be deserialized when no properties are present, but a reason code is specified. 69 | 70 | # [0.6.0] - 2022-11-03 71 | 72 | ## Added 73 | 74 | * Allow configuration of non-default broker port numbers 75 | * Support added for QoS::ExactlyOnce transmission 76 | * `poll()` now supports returning from the closure. An `Option::Some()` will be generated whenever 77 | the `poll()` closure executes on an inbound `Publish` message. 78 | * `subscribe()` now supports subscription configuration, such as retain configuration, QoS 79 | specification, and no-local publications. 80 | * `subscribe()` modified to take a list of topic filters. 81 | * Subscriptions at QoS::AtLeastOnce are now supported. 82 | * Added a new `Publication` builder API to easily construct new messages or reply to received 83 | messages. 84 | 85 | ## Changed 86 | 87 | * [breaking] The client is no longer publicly exposed, and is instead accessible via `Minimq::client()` 88 | * Single MQTT packets are now processed per `Minimq::poll()` execution, reducing stack usage. 89 | * [breaking] External crate is now used for `varint` encoding. Varints changed to u32 format. 90 | * Deserialization and serialization is now handled directly by `serde`. 91 | * [breaking] Properties are now wrapped in MQTT-specific data types. 92 | * [breaking] Packets now support reason codes. Unused error codes were removed. 93 | * `poll()` updated such that the user should call it repeatedly until it returns `Ok(None)`. 94 | * [breaking] Property handling has been changed such that an arbitrary number can be received. 95 | Properties are deserialized as needed by the application. 96 | * [breaking] `publish` now accepts a single `Pub` message type, which can be easily created using 97 | the `Publication` builder utility. 98 | 99 | ## Fixed 100 | 101 | * All unacknowledged messages will be guaranteed to be retransmitted upon connection with the 102 | broker. 103 | * The `ReceiveMaximum` property is now sent in the connection request to the broker 104 | 105 | # [0.5.3] - 2022-02-14 106 | 107 | ## Added 108 | 109 | * Property comparison now implements PartialEq 110 | 111 | # [0.5.2] - 2021-12-14 112 | 113 | ## Fixed 114 | 115 | * Made `mqtt_client` module public to correct documentation 116 | * Partial packet writes no longer cause the connection to the broker to break down. 117 | [#74](https://github.com/quartiq/minimq/issues/74) 118 | 119 | # [0.5.1] - 2021-12-07 120 | 121 | ## Fixed 122 | 123 | * Fixed an issue where the keepalive interval could not be set properly. See 124 | [#69](https://github.com/quartiq/minimq/issues/69). 125 | * Fixed an issue where the keepalive interval was not set properly. See 126 | [#70](https://github.com/quartiq/minimq/issues/70). 127 | 128 | # [0.5.0] - 2021-12-06 129 | 130 | ## Added 131 | 132 | * Support for the `Will` message specification. 133 | * [breaking] Adding `retained` flag to `publish()` to allow messages to be published in a retained 134 | manner. 135 | 136 | # [0.4.0] - 2021-10-08 137 | 138 | * Updating to `std-embedded-nal` v0.1 (dev dependency only; now again used for integration tests) 139 | * Added support for PingReq/PingResp to handle broken TCP connections and configuration of the 140 | keep-alive interval 141 | * Updating tests to use `std-embedded-time` 142 | * Fixing main docs. 143 | * Added support for publishing with QoS 1 144 | * Refactoring network stack management into a separate container class 145 | * Keep-alive settings now take a u16 integer number of seconds 146 | 147 | # [0.3.0] - 2021-08-06 148 | 149 | * Client ID may now be unspecified to allow the broker to automatically assign an ID. 150 | * Strict client ID check removed to allow broker-validated IDs. 151 | * Updated `generic-array` dependencies to address security vulnerability. 152 | * Updating `new()` to allow the network stack to be non-functional during initialization. 153 | * `Property` updated to implement `Copy`/`Clone`. 154 | * Session state is now maintained 155 | * `Error::SessionReset` can be used to detect need to resubscribe to topics. 156 | * Refactoring client into `Minimq` and `MqttClient` to solve internal mutability issues. 157 | * Updating to `embedded-nal` v0.6 158 | * Removing using of `generic-array` in favor of const-generics. 159 | * Correcting an issue where the client would not reconnect if the broker was restarted. 160 | 161 | # [0.2.0] - 2021-02-15 162 | 163 | * Updating the `MqttClient::poll()` function to take a `FnMut` closure to allow internal state 164 | mutation. 165 | * Use the `std-embedded-nal` crate as a dependency to provide a standard `NetworkStack` for 166 | integration tests. 167 | * Updating `read()` to not block when no data is available. Updating `write()` to progate network 168 | stack errors out to the user. 169 | * Updating library to re-export `generic-array` publicly. 170 | 171 | ## [0.1.0] - 2020-08-27 172 | 173 | * Initial library release and publish to crates.io 174 | 175 | [0.9.0]: https://github.com/quartiq/minimq/releases/tag/0.9.0 176 | [0.8.0]: https://github.com/quartiq/minimq/releases/tag/0.8.0 177 | [0.7.0]: https://github.com/quartiq/minimq/releases/tag/0.7.0 178 | [0.6.2]: https://github.com/quartiq/minimq/releases/tag/0.6.2 179 | [0.6.1]: https://github.com/quartiq/minimq/releases/tag/0.6.1 180 | [0.6.0]: https://github.com/quartiq/minimq/releases/tag/0.6.0 181 | [0.5.3]: https://github.com/quartiq/minimq/releases/tag/0.5.3 182 | [0.5.2]: https://github.com/quartiq/minimq/releases/tag/0.5.2 183 | [0.5.1]: https://github.com/quartiq/minimq/releases/tag/0.5.1 184 | [0.5.0]: https://github.com/quartiq/minimq/releases/tag/0.5.0 185 | [0.4.0]: https://github.com/quartiq/minimq/releases/tag/0.4.0 186 | [0.3.0]: https://github.com/quartiq/minimq/releases/tag/0.3.0 187 | [0.2.0]: https://github.com/quartiq/minimq/releases/tag/0.2.0 188 | [0.1.0]: https://github.com/quartiq/minimq/releases/tag/0.1.0 189 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minimq" 3 | version = "0.10.0" 4 | authors = [ 5 | "Ryan Summers ", 6 | "Max Rottenkolber ", 7 | "Robert Jördens ", 8 | ] 9 | edition = "2021" 10 | 11 | description = "A minimal MQTT5 client designed for no_std platforms" 12 | homepage = "https://github.com/quartiq/minimq" 13 | repository = "https://github.com/quartiq/minimq" 14 | documentation = "https://docs.rs/minimq" 15 | 16 | readme = "README.md" 17 | categories = ["embedded", "no-std", "database", "encoding"] 18 | keywords = ["mqtt", "embedded", "client"] 19 | license = "MIT" 20 | rust-version = "1.77.0" 21 | 22 | [dependencies] 23 | bit_field = "0.10.0" 24 | num_enum = { version = "0.7", default-features = false } 25 | heapless = { version = "0.8", features = ["serde"] } 26 | log = { version = "0.4", optional = true } 27 | embedded-time = "0.12" 28 | varint-rs = { version = "2.2", default-features = false } 29 | serde = { version = "1", features = ["derive"], default-features = false } 30 | smlang = "0.8.0" 31 | embedded-nal = "0.9" 32 | 33 | [features] 34 | default = [] 35 | logging = ["log"] 36 | unsecure = [] 37 | 38 | [dev-dependencies] 39 | log = "0.4" 40 | env_logger = "0.11" 41 | std-embedded-time = "0.1" 42 | std-embedded-nal = "0.4.0" 43 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Quartiq GmbH 2 | Copyright (c) 2020 Ryan Summers 3 | Copyright (c) 2020 Max Rottenkolber 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![QUARTIQ Matrix Chat](https://img.shields.io/matrix/quartiq:matrix.org)](https://matrix.to/#/#quartiq:matrix.org) 2 | [![Continuous Integration](https://github.com/quartiq/minimq/actions/workflows/ci.yml/badge.svg)](https://github.com/quartiq/minimq/actions/workflows/ci.yml) 3 | 4 | # Minimq 5 | 6 | Minimq provides a minimal MQTTv5 client and message parsing for the MQTT version 5 protocol. It 7 | leverages the [`embedded-nal`](https://github.com/rust-embedded-community/embedded-nal) to operate 8 | on top of any TCP stack implementation and is actively used with `std-embedded-nal`, 9 | [`smoltcp`](https://github.com/smoltcp-rs/smoltcp), and the W5500 hardware network stack. 10 | 11 | Minimq provides a simple, `no_std` interface to connect to an MQTT broker to publish messages and 12 | subscribe to topics. 13 | 14 | ## Features 15 | 16 | Minimq supports all of the fundamental operations of MQTT, such as message subscription and 17 | publication. Below is a detailed list of features, indicating what aspects are supported: 18 | 19 | * Publication at all quality-of-service levels (at-most-once, at-least-once, and exactly-once) 20 | * Retained messages 21 | * Connection will messages 22 | * Session state reconnection and republication 23 | * Topic subscriptions at all quality-of-service levels 24 | * Subscription option flags 25 | * Zero-copy message deserialization 26 | * Serde-compatible MQTT message serialization and deserialization 27 | 28 | If there are features that you would like to have that are not yet supported, we are always 29 | accepting pull requests to extend Minimq's capabilities. 30 | 31 | Minimq also provides convenient APIs to implement request-response interfaces over MQTT leveraging 32 | the `ResponseTopic` and `CorrelationData` properties for in-bound and out-bound messages. 33 | 34 | ### Smoltcp Support 35 | 36 | If using `smoltcp`, check out the [`smoltcp-nal`](https://github.com/quartiq/smoltcp-nal) to quickly 37 | create an interface that can be used by Minimq. 38 | 39 | ## Examples 40 | 41 | An example usage of Minimq that can be run on a desktop PC can be found in 42 | [`tests/integration_test.rs`](https://github.com/quartiq/minimq/blob/master/tests/integration_test.rs) 43 | -------------------------------------------------------------------------------- /src/broker.rs: -------------------------------------------------------------------------------- 1 | use crate::{warn, MQTT_INSECURE_DEFAULT_PORT}; 2 | use core::{ 3 | convert::TryFrom, 4 | net::{IpAddr, Ipv4Addr, SocketAddr}, 5 | }; 6 | use embedded_nal::{nb, AddrType, Dns}; 7 | 8 | /// A type that allows us to (eventually) determine the broker address. 9 | pub trait Broker { 10 | /// Retrieve the broker address (if available). 11 | fn get_address(&mut self) -> Option; 12 | 13 | /// Set the port of the broker connection. 14 | fn set_port(&mut self, port: u16); 15 | } 16 | 17 | /// A broker that is specified using a qualified domain-name. The name will be resolved at some 18 | /// point in the future. 19 | #[derive(Debug)] 20 | pub struct NamedBroker { 21 | raw: heapless::String, 22 | resolver: R, 23 | addr: SocketAddr, 24 | } 25 | 26 | impl NamedBroker { 27 | /// Construct a new named broker. 28 | /// 29 | /// # Args 30 | /// * `broker` - The domain name of the broker, such as `broker.example.com` 31 | /// * `resolver` - A [embedded_nal::Dns] resolver to resolve the broker domain name to an IP 32 | /// address. 33 | pub fn new(broker: &str, resolver: R) -> Result { 34 | let addr: Ipv4Addr = broker.parse().unwrap_or(Ipv4Addr::UNSPECIFIED); 35 | 36 | Ok(Self { 37 | raw: heapless::String::try_from(broker).map_err(|_| "Broker domain name too long")?, 38 | resolver, 39 | addr: SocketAddr::new(IpAddr::V4(addr), MQTT_INSECURE_DEFAULT_PORT), 40 | }) 41 | } 42 | } 43 | 44 | impl Broker for NamedBroker { 45 | fn get_address(&mut self) -> Option { 46 | // Attempt to resolve the address. 47 | if self.addr.ip().is_unspecified() { 48 | match self.resolver.get_host_by_name(&self.raw, AddrType::IPv4) { 49 | Ok(ip) => self.addr.set_ip(ip), 50 | Err(nb::Error::WouldBlock) => {} 51 | Err(_other) => { 52 | warn!("DNS lookup failed: {_other:?}") 53 | } 54 | } 55 | } 56 | 57 | if !self.addr.ip().is_unspecified() { 58 | Some(self.addr) 59 | } else { 60 | None 61 | } 62 | } 63 | 64 | fn set_port(&mut self, port: u16) { 65 | self.addr.set_port(port) 66 | } 67 | } 68 | 69 | /// A simple broker specification where the address of the broker is known in advance. 70 | #[derive(Debug, Copy, Clone, PartialEq)] 71 | pub struct IpBroker { 72 | addr: SocketAddr, 73 | } 74 | 75 | impl IpBroker { 76 | /// Construct a broker with a known IP address. 77 | pub fn new(broker: IpAddr) -> Self { 78 | Self { 79 | addr: SocketAddr::new(broker, MQTT_INSECURE_DEFAULT_PORT), 80 | } 81 | } 82 | } 83 | impl Broker for IpBroker { 84 | fn get_address(&mut self) -> Option { 85 | Some(self.addr) 86 | } 87 | 88 | fn set_port(&mut self, port: u16) { 89 | self.addr.set_port(port) 90 | } 91 | } 92 | 93 | impl From for IpBroker { 94 | fn from(addr: IpAddr) -> Self { 95 | IpBroker::new(addr) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::{types::Auth, will::SerializedWill, ProtocolError, Will}; 2 | use core::convert::TryFrom; 3 | use embedded_time::duration::{Extensions, Milliseconds}; 4 | use heapless::String; 5 | 6 | #[derive(Debug, Copy, Clone, PartialEq)] 7 | pub enum BufferConfig { 8 | /// Specify a buffer as having a minimum size in bytes. 9 | Minimum(usize), 10 | 11 | /// Specify a buffer as having a maximum size in bytes. 12 | Maximum(usize), 13 | 14 | /// Specify a buffer as having exactly a size in bytes. 15 | Exactly(usize), 16 | } 17 | 18 | #[derive(Debug)] 19 | pub(crate) struct Config<'a, Broker> { 20 | pub(crate) broker: Broker, 21 | pub(crate) rx_buffer: &'a mut [u8], 22 | pub(crate) tx_buffer: &'a mut [u8], 23 | pub(crate) state_buffer: &'a mut [u8], 24 | pub(crate) will: Option>, 25 | pub(crate) client_id: String<64>, 26 | pub(crate) keepalive_interval: Milliseconds, 27 | pub(crate) downgrade_qos: bool, 28 | pub(crate) auth: Option>, 29 | } 30 | 31 | /// Configuration specifying the operational state of the MQTT client. 32 | #[derive(Debug)] 33 | pub struct ConfigBuilder<'a, Broker> { 34 | buffer: &'a mut [u8], 35 | broker: Broker, 36 | rx_config: Option, 37 | tx_config: Option, 38 | session_state_config: Option, 39 | will: Option>, 40 | client_id: String<64>, 41 | keepalive_interval: Milliseconds, 42 | downgrade_qos: bool, 43 | auth: Option>, 44 | } 45 | 46 | impl<'a, Broker: crate::Broker> ConfigBuilder<'a, Broker> { 47 | /// Construct configuration for the MQTT client. 48 | /// 49 | /// # Args 50 | /// * `buffer` - Memory used by the MQTT client. This memory is used for the will, the message 51 | /// receive buffer, the transmission buffer, and the client session state. 52 | pub fn new(broker: Broker, buffer: &'a mut [u8]) -> Self { 53 | Self { 54 | broker, 55 | buffer, 56 | session_state_config: None, 57 | rx_config: None, 58 | tx_config: None, 59 | client_id: String::new(), 60 | auth: None, 61 | keepalive_interval: 59_000.milliseconds(), 62 | downgrade_qos: false, 63 | will: None, 64 | } 65 | } 66 | 67 | /// Specify the authentication message used by the server. 68 | /// 69 | /// # Args 70 | /// * `user_name` - The user name 71 | /// * `password` - The password 72 | #[cfg(feature = "unsecure")] 73 | pub fn set_auth(mut self, user_name: &str, password: &str) -> Result { 74 | if self.auth.is_some() { 75 | return Err(ProtocolError::AuthAlreadySpecified); 76 | } 77 | 78 | let (username_bytes, tail) = self.buffer.split_at_mut(user_name.as_bytes().len()); 79 | username_bytes.copy_from_slice(user_name.as_bytes()); 80 | self.buffer = tail; 81 | 82 | let (password_bytes, tail) = self.buffer.split_at_mut(password.as_bytes().len()); 83 | password_bytes.copy_from_slice(password.as_bytes()); 84 | self.buffer = tail; 85 | 86 | self.auth.replace(Auth { 87 | // Note(unwrap): We are directly copying `str` types to these buffers, so we know they 88 | // are valid utf8. 89 | user_name: core::str::from_utf8(username_bytes).unwrap(), 90 | password: core::str::from_utf8(password_bytes).unwrap(), 91 | }); 92 | Ok(self) 93 | } 94 | 95 | /// Specify a specific configuration for the session state buffer. 96 | /// 97 | /// # Note 98 | /// The session state buffer is used for publications greater than [crate::QoS::AtMostOnce]. If 99 | /// these messages are unused, you can specify [BufferConfig::Exactly(0)]. 100 | /// 101 | /// # Args 102 | /// * `config` - The configuration for the size of the session state buffer. 103 | pub fn session_state(mut self, config: BufferConfig) -> Self { 104 | self.session_state_config.replace(config); 105 | self 106 | } 107 | 108 | /// Specify a specific configuration for the message receive buffer. 109 | /// 110 | /// # Args 111 | /// * `config` - The configuration for the size of the receive buffer. 112 | pub fn rx_buffer(mut self, config: BufferConfig) -> Self { 113 | self.rx_config.replace(config); 114 | self 115 | } 116 | 117 | /// Specify a specific configuration for the message transmit buffer. 118 | /// 119 | /// # Args 120 | /// * `config` - The configuration for the size of the message transmit buffer. 121 | pub fn tx_buffer(mut self, config: BufferConfig) -> Self { 122 | self.tx_config.replace(config); 123 | self 124 | } 125 | 126 | /// Specify a known client ID to use. If not assigned, the broker will auto assign an ID. 127 | pub fn client_id(mut self, id: &str) -> Result { 128 | self.client_id = 129 | String::try_from(id).map_err(|_| ProtocolError::ProvidedClientIdTooLong)?; 130 | Ok(self) 131 | } 132 | 133 | /// Configure the MQTT keep-alive interval. 134 | /// 135 | /// # Note 136 | /// The broker may override the requested keep-alive interval. Any value requested by the 137 | /// broker will be used instead. 138 | /// 139 | /// # Args 140 | /// * `interval` - The keep-alive interval in seconds. A ping will be transmitted if no other 141 | /// messages are sent within 50% of the keep-alive interval. 142 | pub fn keepalive_interval(mut self, seconds: u16) -> Self { 143 | self.keepalive_interval = Milliseconds(seconds as u32 * 1000); 144 | self 145 | } 146 | 147 | /// Specify if publication [crate::QoS] should be automatically downgraded to the maximum 148 | /// supported by the server if they exceed the server [crate::QoS] maximum. 149 | pub fn autodowngrade_qos(mut self) -> Self { 150 | self.downgrade_qos = true; 151 | self 152 | } 153 | 154 | /// Specify the Will message to be sent if the client disconnects. 155 | /// 156 | /// # Args 157 | /// * `will` - The will to use. 158 | pub fn will(mut self, will: Will<'_>) -> Result { 159 | if self.will.is_some() { 160 | return Err(ProtocolError::WillAlreadySpecified); 161 | } 162 | let will_len = will.serialized_len(); 163 | let (head, tail) = self.buffer.split_at_mut(will_len); 164 | self.buffer = tail; 165 | self.will = Some(will.serialize(head)?); 166 | 167 | Ok(self) 168 | } 169 | 170 | /// Consume the configuration and split the user-provided memory into the necessary buffers. 171 | pub(crate) fn build(self) -> Config<'a, Broker> { 172 | let configs = [self.rx_config, self.tx_config, self.session_state_config]; 173 | let mut buffers = [None, None, None]; 174 | 175 | let mut data = self.buffer; 176 | 177 | // First, check for any exact configurations. 178 | for (size, buffer) in configs 179 | .iter() 180 | .zip(buffers.iter_mut()) 181 | .filter_map(|(config, buf)| { 182 | if let Some(BufferConfig::Exactly(size)) = config { 183 | Some((size, buf)) 184 | } else { 185 | None 186 | } 187 | }) 188 | { 189 | let (head, tail) = data.split_at_mut(*size); 190 | data = tail; 191 | buffer.replace(head); 192 | } 193 | 194 | // Calculate the prescribed size for remaining buffers. This will be the base size used - 195 | // any buffers that specify a min/max size will use this size and their respective mins or 196 | // maxes. 197 | let size = { 198 | let remainder = buffers.iter().filter(|x| x.is_none()).count(); 199 | data.len().checked_div(remainder).unwrap_or(0) 200 | }; 201 | 202 | // Note: We intentionally are not dynamically updating the prescribed sizes as we allocate 203 | // buffers because this would invalidate any previous potential min/max calculations that 204 | // we conducted. 205 | for (config, buffer) in configs 206 | .iter() 207 | .zip(buffers.iter_mut()) 208 | .filter_map(|(config, buf)| config.as_ref().map(|config| (config, buf))) 209 | { 210 | match config { 211 | BufferConfig::Maximum(max) => { 212 | let (head, tail) = data.split_at_mut(size.min(*max)); 213 | data = tail; 214 | buffer.replace(head); 215 | } 216 | BufferConfig::Minimum(min) => { 217 | let (head, tail) = data.split_at_mut(size.max(*min)); 218 | data = tail; 219 | buffer.replace(head); 220 | } 221 | _ => {} 222 | } 223 | } 224 | 225 | // Any remaining buffers have no outstanding restrictions and the remaining memory will be 226 | // evenly split amongst them. 227 | let size = { 228 | let remainder = buffers.iter().filter(|x| x.is_none()).count(); 229 | data.len().checked_div(remainder).unwrap_or(0) 230 | }; 231 | 232 | for buffer in buffers.iter_mut().filter(|buf| buf.is_none()) { 233 | let (head, tail) = data.split_at_mut(size); 234 | data = tail; 235 | buffer.replace(head); 236 | } 237 | 238 | Config { 239 | broker: self.broker, 240 | client_id: self.client_id, 241 | will: self.will, 242 | downgrade_qos: self.downgrade_qos, 243 | keepalive_interval: self.keepalive_interval, 244 | rx_buffer: buffers[0].take().unwrap(), 245 | tx_buffer: buffers[1].take().unwrap(), 246 | state_buffer: buffers[2].take().unwrap(), 247 | auth: self.auth, 248 | } 249 | } 250 | } 251 | 252 | #[cfg(test)] 253 | mod tests { 254 | use super::*; 255 | use crate::broker::IpBroker; 256 | use core::net::IpAddr; 257 | 258 | #[test] 259 | pub fn basic_config() { 260 | let mut buffer = [0; 30]; 261 | let localhost: IpAddr = "127.0.0.1".parse().unwrap(); 262 | let builder: ConfigBuilder = ConfigBuilder::new(localhost.into(), &mut buffer); 263 | 264 | let config = builder.build(); 265 | assert!(config.tx_buffer.len() == 10); 266 | assert!(config.rx_buffer.len() == 10); 267 | assert!(config.state_buffer.len() == 10); 268 | } 269 | 270 | #[test] 271 | pub fn without_session_state() { 272 | let mut buffer = [0; 30]; 273 | let localhost: IpAddr = "127.0.0.1".parse().unwrap(); 274 | let builder: ConfigBuilder = ConfigBuilder::new(localhost.into(), &mut buffer) 275 | .session_state(BufferConfig::Exactly(0)); 276 | 277 | let config = builder.build(); 278 | assert!(config.tx_buffer.len() == 15); 279 | assert!(config.rx_buffer.len() == 15); 280 | assert!(config.state_buffer.is_empty()); 281 | } 282 | 283 | #[test] 284 | pub fn with_exact_sizes() { 285 | let mut buffer = [0; 30]; 286 | let localhost: IpAddr = "127.0.0.1".parse().unwrap(); 287 | let builder: ConfigBuilder = ConfigBuilder::new(localhost.into(), &mut buffer) 288 | .session_state(BufferConfig::Exactly(8)) 289 | .rx_buffer(BufferConfig::Exactly(4)) 290 | .tx_buffer(BufferConfig::Exactly(3)); 291 | 292 | let config = builder.build(); 293 | assert!(config.tx_buffer.len() == 3); 294 | assert!(config.rx_buffer.len() == 4); 295 | assert!(config.state_buffer.len() == 8); 296 | } 297 | 298 | #[test] 299 | pub fn with_max() { 300 | let mut buffer = [0; 30]; 301 | let localhost: IpAddr = "127.0.0.1".parse().unwrap(); 302 | let builder: ConfigBuilder = ConfigBuilder::new(localhost.into(), &mut buffer) 303 | .session_state(BufferConfig::Maximum(8)); 304 | 305 | let config = builder.build(); 306 | assert!(config.state_buffer.len() == 8); 307 | assert!(config.tx_buffer.len() == 11); 308 | assert!(config.rx_buffer.len() == 11); 309 | } 310 | 311 | #[test] 312 | pub fn with_min() { 313 | let mut buffer = [0; 30]; 314 | let localhost: IpAddr = "127.0.0.1".parse().unwrap(); 315 | let builder: ConfigBuilder = ConfigBuilder::new(localhost.into(), &mut buffer) 316 | .session_state(BufferConfig::Minimum(20)); 317 | 318 | let config = builder.build(); 319 | assert!(config.state_buffer.len() == 20); 320 | assert!(config.tx_buffer.len() == 5); 321 | assert!(config.rx_buffer.len() == 5); 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /src/de/deserializer.rs: -------------------------------------------------------------------------------- 1 | //! Custom MQTT message deserializer 2 | //! 3 | //! # Design 4 | //! This deserializer handles deserializing MQTT packets. It assumes the following: 5 | //! 6 | //! ### Integers 7 | //! All unsigned integers are transmitted in a fixed-width, big-endian notation. 8 | //! 9 | //! ### Binary data 10 | //! Binary data blocks (e.g. &[u8]) are always prefixed with a 16-bit integer denoting 11 | //! their size. 12 | //! 13 | //! ### Strings 14 | //! Strings are always prefixed with a 16-bit integer denoting their length. Strings are assumed to 15 | //! be utf-8 encoded. 16 | //! 17 | //! ### Options 18 | //! Options are assumed to be `Some` if there is any remaining data to be deserialized. If there is 19 | //! no remaining data, an option is assumed to be `None` 20 | //! 21 | //! ### Sequences 22 | //! Sequences are assumed to be prefixed by the number of bytes that the entire sequence 23 | //! represents, stored as a variable integer. The length of the sequence is not known until the 24 | //! sequence has been deserialized because elements may have variable sizes. 25 | //! 26 | //! ### Tuples 27 | //! Tuples are used as a special case of `sequence` where the number of elements, as opposed to the 28 | //! size of the binary data, is known at deserialization time. These are used to deserialize 29 | //! at-most N elements. 30 | //! 31 | //! ### Other Types 32 | //! Structs, enums, and other variants will be mapped to either a `tuple` or a `sequence` as 33 | //! appropriate. 34 | //! 35 | //! Other types are explicitly not implemented and there is no plan to implement them. 36 | use core::convert::TryInto; 37 | use serde::de::{DeserializeSeed, IntoDeserializer, Visitor}; 38 | use varint_rs::VarintReader; 39 | 40 | #[derive(Debug, Copy, Clone, PartialEq)] 41 | #[non_exhaustive] 42 | pub enum Error { 43 | /// A custom deserialization error occurred. 44 | Custom, 45 | 46 | /// An invalid string was encountered, where UTF-8 decoding failed. 47 | BadString, 48 | 49 | /// An invalid boolean was encountered, which did not use "0" or "1" to encode its value. 50 | BadBool, 51 | 52 | /// There was not sufficient data to deserialize the required datatype. 53 | InsufficientData, 54 | } 55 | 56 | impl serde::ser::StdError for Error {} 57 | 58 | impl serde::de::Error for Error { 59 | fn custom(_msg: T) -> Self { 60 | crate::error!("{}", _msg); 61 | Error::Custom 62 | } 63 | } 64 | 65 | impl core::fmt::Display for Error { 66 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 67 | write!( 68 | f, 69 | "{}", 70 | match self { 71 | Error::Custom => "Custom deserialization error", 72 | Error::BadString => "Improper UTF-8 string encountered", 73 | Error::BadBool => "Bad boolean encountered", 74 | Error::InsufficientData => "Not enough data in the packet", 75 | } 76 | ) 77 | } 78 | } 79 | 80 | /// Deserializes a byte buffer into an MQTT control packet. 81 | pub struct MqttDeserializer<'a> { 82 | buf: &'a [u8], 83 | index: usize, 84 | next_pending_length: Option, 85 | } 86 | 87 | impl<'a> MqttDeserializer<'a> { 88 | /// Construct a deserializer from a provided data buffer. 89 | pub fn new(buf: &'a [u8]) -> Self { 90 | Self { 91 | buf, 92 | index: 0, 93 | next_pending_length: None, 94 | } 95 | } 96 | 97 | /// Override the next binary bytes read with some pre-determined size. 98 | /// 99 | /// # Args 100 | /// * `len` - The known length of the next binary data blob. 101 | pub fn set_next_pending_length(&mut self, len: usize) { 102 | self.next_pending_length.replace(len); 103 | } 104 | 105 | /// Attempt to take N bytes from the buffer. 106 | pub fn try_take_n(&mut self, n: usize) -> Result<&'a [u8], Error> { 107 | if self.len() < n { 108 | return Err(Error::InsufficientData); 109 | } 110 | 111 | let data = &self.buf[self.index..self.index + n]; 112 | self.index += n; 113 | Ok(data) 114 | } 115 | 116 | /// Pop a single byte from the data buffer. 117 | pub fn pop(&mut self) -> Result { 118 | if self.len() == 0 { 119 | return Err(Error::InsufficientData); 120 | } 121 | 122 | let byte = self.buf[self.index]; 123 | self.index += 1; 124 | Ok(byte) 125 | } 126 | 127 | /// Read a 16-bit integer from the data buffer. 128 | pub fn read_u16(&mut self) -> Result { 129 | Ok(u16::from_be_bytes([self.pop()?, self.pop()?])) 130 | } 131 | 132 | /// Read the number of remaining bytes in the data buffer. 133 | pub fn len(&self) -> usize { 134 | self.buf.len() - self.index 135 | } 136 | 137 | /// Read a variable-length integer from the data buffer. 138 | pub fn read_varint(&mut self) -> Result { 139 | self.read_u32_varint() 140 | } 141 | 142 | /// Determine the number of bytes that were deserialized. 143 | pub fn deserialized_bytes(&self) -> usize { 144 | self.index 145 | } 146 | 147 | /// Extract any remaining data from the buffer. 148 | /// 149 | /// # Note 150 | /// This is intended to be used after deserialization has completed. 151 | pub fn remainder(&self) -> &'a [u8] { 152 | &self.buf[self.index..] 153 | } 154 | } 155 | 156 | impl varint_rs::VarintReader for MqttDeserializer<'_> { 157 | type Error = Error; 158 | 159 | fn read(&mut self) -> Result { 160 | self.pop() 161 | } 162 | } 163 | 164 | impl<'de> serde::de::Deserializer<'de> for &'_ mut MqttDeserializer<'de> { 165 | type Error = Error; 166 | 167 | fn deserialize_bool>(self, visitor: V) -> Result { 168 | let val = match self.pop()? { 169 | 0 => false, 170 | 1 => true, 171 | _ => return Err(Error::BadBool), 172 | }; 173 | visitor.visit_bool(val) 174 | } 175 | 176 | fn deserialize_i8>(self, visitor: V) -> Result { 177 | visitor.visit_i8(self.pop()? as i8) 178 | } 179 | 180 | fn deserialize_i16>(self, visitor: V) -> Result { 181 | visitor.visit_i16(i16::from_be_bytes(self.try_take_n(2)?.try_into().unwrap())) 182 | } 183 | 184 | fn deserialize_i32>(self, visitor: V) -> Result { 185 | visitor.visit_i32(i32::from_be_bytes(self.try_take_n(4)?.try_into().unwrap())) 186 | } 187 | 188 | fn deserialize_u8>(self, visitor: V) -> Result { 189 | visitor.visit_u8(self.pop()?) 190 | } 191 | 192 | fn deserialize_u16>(self, visitor: V) -> Result { 193 | visitor.visit_u16(self.read_u16()?) 194 | } 195 | 196 | fn deserialize_u32>(self, visitor: V) -> Result { 197 | visitor.visit_u32(u32::from_be_bytes(self.try_take_n(4)?.try_into().unwrap())) 198 | } 199 | 200 | fn deserialize_str>(self, visitor: V) -> Result { 201 | let length = self.read_u16()?; 202 | let bytes: &'de [u8] = self.try_take_n(length as usize)?; 203 | let string = core::str::from_utf8(bytes).map_err(|_| Error::BadString)?; 204 | visitor.visit_borrowed_str(string) 205 | } 206 | 207 | fn deserialize_string>(self, visitor: V) -> Result { 208 | self.deserialize_str(visitor) 209 | } 210 | 211 | fn deserialize_bytes>(self, visitor: V) -> Result { 212 | let length = match self.next_pending_length.take() { 213 | Some(length) => length, 214 | None => self.read_u16()? as usize, 215 | }; 216 | let bytes: &'de [u8] = self.try_take_n(length)?; 217 | visitor.visit_borrowed_bytes(bytes) 218 | } 219 | 220 | fn deserialize_byte_buf>(self, visitor: V) -> Result { 221 | self.deserialize_bytes(visitor) 222 | } 223 | 224 | fn deserialize_option>(self, visitor: V) -> Result { 225 | // Assume it is None there if there is remaining data. 226 | if self.len() == 0 { 227 | visitor.visit_none() 228 | } else { 229 | visitor.visit_some(self) 230 | } 231 | } 232 | 233 | fn deserialize_seq>(self, visitor: V) -> Result { 234 | // Sequences, which are properties, are always prefixed with the number of bytes contained 235 | // within them encoded as a variable-length integer. 236 | let length = self.read_varint()? as usize; 237 | 238 | // If the properties are read as a binary blob, we already know the size and we don't 239 | // want to read a u16-prefixed size. 240 | self.set_next_pending_length(length); 241 | 242 | visitor.visit_seq(SeqAccess { 243 | deserializer: self, 244 | length, 245 | }) 246 | } 247 | 248 | fn deserialize_tuple>( 249 | self, 250 | len: usize, 251 | visitor: V, 252 | ) -> Result { 253 | // Tuples are used to sequentially access the deserialization tool for at most the number 254 | // of provided elements. 255 | visitor.visit_seq(ElementAccess { 256 | deserializer: self, 257 | count: len, 258 | }) 259 | } 260 | 261 | fn deserialize_tuple_struct>( 262 | self, 263 | _name: &'static str, 264 | len: usize, 265 | visitor: V, 266 | ) -> Result { 267 | self.deserialize_tuple(len, visitor) 268 | } 269 | 270 | fn deserialize_struct>( 271 | self, 272 | _name: &'static str, 273 | fields: &'static [&'static str], 274 | visitor: V, 275 | ) -> Result { 276 | self.deserialize_tuple(fields.len(), visitor) 277 | } 278 | 279 | fn deserialize_enum>( 280 | self, 281 | _name: &'static str, 282 | _variants: &'static [&'static str], 283 | visitor: V, 284 | ) -> Result { 285 | visitor.visit_enum(self) 286 | } 287 | 288 | fn deserialize_unit_struct>( 289 | self, 290 | _name: &'static str, 291 | _visitor: V, 292 | ) -> Result { 293 | unimplemented!() 294 | } 295 | 296 | fn deserialize_newtype_struct>( 297 | self, 298 | _name: &'static str, 299 | _visitor: V, 300 | ) -> Result { 301 | unimplemented!() 302 | } 303 | 304 | fn deserialize_map>(self, _visitor: V) -> Result { 305 | unimplemented!() 306 | } 307 | 308 | fn deserialize_identifier>(self, _visitor: V) -> Result { 309 | unimplemented!() 310 | } 311 | 312 | fn deserialize_unit>(self, _visitor: V) -> Result { 313 | unimplemented!() 314 | } 315 | 316 | fn deserialize_ignored_any>( 317 | self, 318 | _visitor: V, 319 | ) -> Result { 320 | unimplemented!() 321 | } 322 | 323 | fn deserialize_f32>(self, _visitor: V) -> Result { 324 | unimplemented!() 325 | } 326 | 327 | fn deserialize_f64>(self, _visitor: V) -> Result { 328 | unimplemented!() 329 | } 330 | 331 | fn deserialize_char>(self, _visitor: V) -> Result { 332 | unimplemented!() 333 | } 334 | 335 | fn deserialize_i64>(self, _visitor: V) -> Result { 336 | unimplemented!() 337 | } 338 | 339 | fn deserialize_u64>(self, _visitor: V) -> Result { 340 | unimplemented!() 341 | } 342 | fn deserialize_any>(self, _visitor: V) -> Result { 343 | unimplemented!() 344 | } 345 | } 346 | 347 | /// Structure used to access a specified number of elements. 348 | struct ElementAccess<'a, 'de: 'a> { 349 | deserializer: &'a mut MqttDeserializer<'de>, 350 | count: usize, 351 | } 352 | 353 | impl<'a, 'de: 'a> serde::de::SeqAccess<'de> for ElementAccess<'a, 'de> { 354 | type Error = Error; 355 | 356 | fn next_element_seed>( 357 | &mut self, 358 | seed: V, 359 | ) -> Result, Error> { 360 | if self.count > 0 { 361 | self.count -= 1; 362 | let data = DeserializeSeed::deserialize(seed, &mut *self.deserializer)?; 363 | Ok(Some(data)) 364 | } else { 365 | Ok(None) 366 | } 367 | } 368 | 369 | fn size_hint(&self) -> Option { 370 | Some(self.count) 371 | } 372 | } 373 | 374 | /// Structure used to access a specified number of bytes. 375 | struct SeqAccess<'a, 'de: 'a> { 376 | deserializer: &'a mut MqttDeserializer<'de>, 377 | length: usize, 378 | } 379 | 380 | impl<'a, 'de: 'a> serde::de::SeqAccess<'de> for SeqAccess<'a, 'de> { 381 | type Error = Error; 382 | 383 | fn next_element_seed>( 384 | &mut self, 385 | seed: V, 386 | ) -> Result, Error> { 387 | if self.length > 0 { 388 | // We are deserializing a specified number of bytes in this case, so we need to track 389 | // how many bytes each serialization request uses. 390 | let original_remaining = self.deserializer.len(); 391 | let data = DeserializeSeed::deserialize(seed, &mut *self.deserializer)?; 392 | self.length = self 393 | .length 394 | .checked_sub(original_remaining - self.deserializer.len()) 395 | .ok_or(Error::InsufficientData)?; 396 | 397 | // Since some data was just read, we no longer know the size of the binary data block. 398 | self.deserializer.next_pending_length.take(); 399 | 400 | Ok(Some(data)) 401 | } else { 402 | Ok(None) 403 | } 404 | } 405 | 406 | fn size_hint(&self) -> Option { 407 | None 408 | } 409 | } 410 | 411 | impl<'de> serde::de::VariantAccess<'de> for &'_ mut MqttDeserializer<'de> { 412 | type Error = Error; 413 | 414 | fn unit_variant(self) -> Result<(), Error> { 415 | unimplemented!() 416 | } 417 | 418 | fn newtype_variant_seed>(self, seed: V) -> Result { 419 | DeserializeSeed::deserialize(seed, self) 420 | } 421 | 422 | fn struct_variant>( 423 | self, 424 | fields: &'static [&'static str], 425 | visitor: V, 426 | ) -> Result { 427 | serde::de::Deserializer::deserialize_tuple(self, fields.len(), visitor) 428 | } 429 | 430 | fn tuple_variant>( 431 | self, 432 | len: usize, 433 | visitor: V, 434 | ) -> Result { 435 | serde::de::Deserializer::deserialize_tuple(self, len, visitor) 436 | } 437 | } 438 | 439 | impl<'de> serde::de::EnumAccess<'de> for &'_ mut MqttDeserializer<'de> { 440 | type Error = Error; 441 | type Variant = Self; 442 | 443 | fn variant_seed>(self, seed: V) -> Result<(V::Value, Self), Error> { 444 | let varint = self.read_varint()?; 445 | crate::trace!("Read Varint: 0x{:2X}", varint); 446 | let v = DeserializeSeed::deserialize(seed, varint.into_deserializer())?; 447 | Ok((v, self)) 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /src/de/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod deserializer; 2 | mod packet_reader; 3 | pub mod received_packet; 4 | pub use deserializer::Error; 5 | pub(crate) use packet_reader::PacketReader; 6 | -------------------------------------------------------------------------------- /src/de/packet_reader.rs: -------------------------------------------------------------------------------- 1 | use super::received_packet::ReceivedPacket; 2 | use crate::ProtocolError as Error; 3 | 4 | pub(crate) struct PacketReader<'a> { 5 | pub buffer: &'a mut [u8], 6 | read_bytes: usize, 7 | packet_length: Option, 8 | } 9 | 10 | impl<'a> PacketReader<'a> { 11 | pub fn new(buffer: &'a mut [u8]) -> PacketReader<'a> { 12 | PacketReader { 13 | buffer, 14 | read_bytes: 0, 15 | packet_length: None, 16 | } 17 | } 18 | 19 | pub fn receive_buffer(&mut self) -> Result<&mut [u8], Error> { 20 | if self.packet_length.is_none() { 21 | self.probe_fixed_header()?; 22 | } 23 | 24 | let end = if let Some(packet_length) = &self.packet_length { 25 | *packet_length 26 | } else { 27 | self.read_bytes + 1 28 | }; 29 | 30 | if end <= self.buffer.len() { 31 | Ok(&mut self.buffer[self.read_bytes..end]) 32 | } else { 33 | Err(Error::MalformedPacket) 34 | } 35 | } 36 | 37 | pub fn commit(&mut self, count: usize) { 38 | self.read_bytes += count; 39 | } 40 | 41 | fn probe_fixed_header(&mut self) -> Result<(), Error> { 42 | if self.read_bytes <= 1 { 43 | return Ok(()); 44 | } 45 | 46 | self.packet_length = None; 47 | 48 | let mut packet_length = 0; 49 | for (index, value) in self.buffer[1..self.read_bytes].iter().take(4).enumerate() { 50 | packet_length += ((value & 0x7F) as usize) << (index * 7); 51 | if (value & 0x80) == 0 { 52 | let length_size_bytes = 1 + index; 53 | 54 | // MQTT headers encode the packet type in the first byte followed by the packet 55 | // length as a varint 56 | let header_size_bytes = 1 + length_size_bytes; 57 | self.packet_length = Some(header_size_bytes + packet_length); 58 | break; 59 | } 60 | } 61 | 62 | // We should have found the packet length by now. 63 | if self.read_bytes >= 5 && self.packet_length.is_none() { 64 | return Err(Error::MalformedPacket); 65 | } 66 | 67 | Ok(()) 68 | } 69 | 70 | pub fn packet_available(&self) -> bool { 71 | match self.packet_length { 72 | Some(length) => self.read_bytes >= length, 73 | None => false, 74 | } 75 | } 76 | 77 | pub fn reset(&mut self) { 78 | self.read_bytes = 0; 79 | self.packet_length = None; 80 | } 81 | 82 | pub fn received_packet(&mut self) -> Result, Error> { 83 | let packet_length = *self.packet_length.as_ref().ok_or(Error::MalformedPacket)?; 84 | 85 | // Reset the buffer now. Once the user drops the `ReceivedPacket`, this reader will then be 86 | // immediately ready to begin receiving a new packet. 87 | self.reset(); 88 | 89 | ReceivedPacket::from_buffer(&self.buffer[..packet_length]) 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod test { 95 | use super::PacketReader; 96 | #[test] 97 | fn dont_panic_on_bad_data() { 98 | let mut buffer: [u8; 4] = [0x20, 0x99, 0x00, 0x00]; 99 | let mut packet_reader = PacketReader::new(&mut buffer); 100 | packet_reader.commit(4); 101 | packet_reader 102 | .receive_buffer() 103 | .expect_err("parsed packet with invalid length"); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/de/received_packet.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | message_types::MessageType, 3 | packets::{ConnAck, Disconnect, Pub, PubAck, PubComp, PubRec, PubRel, SubAck}, 4 | varint::Varint, 5 | ProtocolError, QoS, Retain, 6 | }; 7 | 8 | use super::deserializer::MqttDeserializer; 9 | 10 | use bit_field::BitField; 11 | use core::convert::TryFrom; 12 | use serde::Deserialize; 13 | 14 | #[derive(Debug)] 15 | pub enum ReceivedPacket<'a> { 16 | ConnAck(ConnAck<'a>), 17 | Publish(Pub<'a, &'a [u8]>), 18 | PubAck(PubAck<'a>), 19 | SubAck(SubAck<'a>), 20 | PubRel(PubRel<'a>), 21 | PubRec(PubRec<'a>), 22 | PubComp(PubComp<'a>), 23 | Disconnect(Disconnect<'a>), 24 | PingResp, 25 | } 26 | 27 | impl<'a> ReceivedPacket<'a> { 28 | pub fn from_buffer(buf: &'a [u8]) -> Result { 29 | let mut deserializer = MqttDeserializer::new(buf); 30 | let mut packet = ReceivedPacket::deserialize(&mut deserializer)?; 31 | 32 | let remaining_payload = deserializer.remainder(); 33 | 34 | // We should only have remaining payload for publish messages. 35 | if !remaining_payload.is_empty() { 36 | match &mut packet { 37 | ReceivedPacket::Publish(publish) => { 38 | publish.payload = remaining_payload; 39 | } 40 | ReceivedPacket::SubAck(suback) => { 41 | suback.codes = remaining_payload; 42 | } 43 | _ => return Err(ProtocolError::MalformedPacket), 44 | } 45 | } 46 | 47 | Ok(packet) 48 | } 49 | } 50 | 51 | struct ControlPacketVisitor; 52 | 53 | impl<'de> serde::de::Visitor<'de> for ControlPacketVisitor { 54 | type Value = ReceivedPacket<'de>; 55 | 56 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 57 | write!(formatter, "MQTT Control Packet") 58 | } 59 | 60 | fn visit_seq>(self, mut seq: A) -> Result { 61 | use serde::de::Error; 62 | 63 | // Note(unwraps): These unwraps should never fail - the next_element() function should be 64 | // always providing us some new element or an error that we return based on our 65 | // deserialization implementation. 66 | let fixed_header: u8 = seq.next_element()?.unwrap(); 67 | let _length: Varint = seq.next_element()?.unwrap(); 68 | let packet_type = MessageType::try_from(fixed_header.get_bits(4..=7)) 69 | .map_err(|_| A::Error::custom("Invalid MQTT control packet type"))?; 70 | 71 | let packet = match packet_type { 72 | MessageType::ConnAck => ReceivedPacket::ConnAck(seq.next_element()?.unwrap()), 73 | MessageType::Publish => { 74 | let qos = QoS::try_from(fixed_header.get_bits(1..=2)) 75 | .map_err(|_| A::Error::custom("Bad QoS field"))?; 76 | 77 | let topic = seq.next_element()?.unwrap(); 78 | let packet_id = if qos > QoS::AtMostOnce { 79 | Some(seq.next_element()?.unwrap()) 80 | } else { 81 | None 82 | }; 83 | 84 | let properties = seq.next_element()?.unwrap(); 85 | 86 | let publish: Pub<'_, &[u8]> = Pub { 87 | topic, 88 | packet_id, 89 | properties, 90 | payload: &[], 91 | retain: if fixed_header.get_bit(0) { 92 | Retain::Retained 93 | } else { 94 | Retain::NotRetained 95 | }, 96 | dup: fixed_header.get_bit(3), 97 | qos, 98 | }; 99 | 100 | ReceivedPacket::Publish(publish) 101 | } 102 | MessageType::PubAck => ReceivedPacket::PubAck(seq.next_element()?.unwrap()), 103 | MessageType::SubAck => ReceivedPacket::SubAck(seq.next_element()?.unwrap()), 104 | MessageType::PingResp => ReceivedPacket::PingResp, 105 | MessageType::PubRec => ReceivedPacket::PubRec(seq.next_element()?.unwrap()), 106 | MessageType::PubRel => ReceivedPacket::PubRel(seq.next_element()?.unwrap()), 107 | MessageType::PubComp => ReceivedPacket::PubComp(seq.next_element()?.unwrap()), 108 | MessageType::Disconnect => ReceivedPacket::Disconnect(seq.next_element()?.unwrap()), 109 | _ => return Err(A::Error::custom("Unsupported message type")), 110 | }; 111 | 112 | Ok(packet) 113 | } 114 | } 115 | 116 | impl<'de> Deserialize<'de> for ReceivedPacket<'de> { 117 | fn deserialize>(deserializer: D) -> Result { 118 | // Deserialize the (fixed_header, length, control_packet | (topic, packet_id, properties)), 119 | // which corresponds to a maximum of 5 elements. 120 | deserializer.deserialize_tuple(5, ControlPacketVisitor) 121 | } 122 | } 123 | 124 | #[cfg(test)] 125 | mod test { 126 | use super::ReceivedPacket; 127 | use crate::reason_codes::ReasonCode; 128 | 129 | #[test] 130 | fn deserialize_good_connack() { 131 | env_logger::init(); 132 | let serialized_connack: [u8; 5] = [ 133 | 0x20, 0x03, // Remaining length = 3 bytes 134 | 0x00, // Connect acknowledge flags - bit 0 clear. 135 | 0x00, // Connect reason code - 0 (Success) 136 | 0x00, // Property length = 0 137 | // No payload. 138 | ]; 139 | 140 | let packet = ReceivedPacket::from_buffer(&serialized_connack).unwrap(); 141 | match packet { 142 | ReceivedPacket::ConnAck(conn_ack) => { 143 | assert_eq!(conn_ack.reason_code, ReasonCode::Success); 144 | } 145 | _ => panic!("Invalid message"), 146 | } 147 | } 148 | 149 | #[test] 150 | fn deserialize_good_publish() { 151 | let serialized_publish: [u8; 7] = [ 152 | 0x30, // Publish, no QoS 153 | 0x05, // Remaining length 154 | 0x00, 0x01, // Topic length (1) 155 | 0x41, // Topic name: 'A' 156 | 0x00, // Properties length 157 | 0x05, // Payload 158 | ]; 159 | 160 | let packet = ReceivedPacket::from_buffer(&serialized_publish).unwrap(); 161 | match packet { 162 | ReceivedPacket::Publish(pub_info) => { 163 | assert_eq!(pub_info.topic.0, "A"); 164 | } 165 | _ => panic!("Invalid message"), 166 | } 167 | } 168 | 169 | #[test] 170 | fn deserialize_good_puback() { 171 | let serialized_puback: [u8; 6] = [ 172 | 0x40, // PubAck 173 | 0x04, // Remaining length 174 | 0x00, 0x05, // Identifier 175 | 0x10, // Response Code 176 | 0x00, // Properties length 177 | ]; 178 | 179 | let packet = ReceivedPacket::from_buffer(&serialized_puback).unwrap(); 180 | match packet { 181 | ReceivedPacket::PubAck(pub_ack) => { 182 | assert_eq!(pub_ack.reason.code(), ReasonCode::NoMatchingSubscribers); 183 | assert_eq!(pub_ack.packet_identifier, 5); 184 | } 185 | _ => panic!("Invalid message"), 186 | } 187 | } 188 | 189 | #[test] 190 | fn deserialize_good_puback_without_reason() { 191 | let serialized_puback: [u8; 4] = [ 192 | 0x40, // PubAck 193 | 0x02, // Remaining length 194 | 0x00, 0x06, // Identifier 195 | ]; 196 | 197 | let packet = ReceivedPacket::from_buffer(&serialized_puback).unwrap(); 198 | match packet { 199 | ReceivedPacket::PubAck(pub_ack) => { 200 | assert_eq!(pub_ack.packet_identifier, 6); 201 | assert_eq!(pub_ack.reason.code(), ReasonCode::Success); 202 | } 203 | _ => panic!("Invalid message"), 204 | } 205 | } 206 | 207 | #[test] 208 | fn deserialize_good_puback_without_properties() { 209 | let serialized_puback: [u8; 5] = [ 210 | 0x40, // PubAck 211 | 0x03, // Remaining length 212 | 0x00, 0x06, // Identifier 213 | 0x10, // ReasonCode 214 | ]; 215 | 216 | let packet = ReceivedPacket::from_buffer(&serialized_puback).unwrap(); 217 | match packet { 218 | ReceivedPacket::PubAck(pub_ack) => { 219 | assert_eq!(pub_ack.packet_identifier, 6); 220 | assert_eq!(pub_ack.reason.code(), ReasonCode::NoMatchingSubscribers); 221 | } 222 | _ => panic!("Invalid message"), 223 | } 224 | } 225 | 226 | #[test] 227 | fn deserialize_good_suback() { 228 | let serialized_suback: [u8; 6] = [ 229 | 0x90, // SubAck 230 | 0x04, // Remaining length 231 | 0x00, 0x05, // Identifier 232 | 0x00, // Properties length 233 | 0x02, // Response Code 234 | ]; 235 | 236 | let packet = ReceivedPacket::from_buffer(&serialized_suback).unwrap(); 237 | match packet { 238 | ReceivedPacket::SubAck(sub_ack) => { 239 | assert_eq!(sub_ack.codes.len(), 1); 240 | assert_eq!(ReasonCode::from(sub_ack.codes[0]), ReasonCode::GrantedQos2); 241 | assert_eq!(sub_ack.packet_identifier, 5); 242 | } 243 | _ => panic!("Invalid message"), 244 | } 245 | } 246 | 247 | #[test] 248 | fn deserialize_good_ping_resp() { 249 | let serialized_ping_req: [u8; 2] = [ 250 | 0xd0, // Ping resp 251 | 0x00, // Remaining length (0) 252 | ]; 253 | 254 | let packet = ReceivedPacket::from_buffer(&serialized_ping_req).unwrap(); 255 | match packet { 256 | ReceivedPacket::PingResp => {} 257 | _ => panic!("Invalid message"), 258 | } 259 | } 260 | 261 | #[test] 262 | fn deserialize_good_pubcomp() { 263 | let serialized_pubcomp: [u8; 6] = [ 264 | 7 << 4, // PubComp 265 | 0x04, // Remaining length 266 | 0x00, 267 | 0x05, // Identifier 268 | 0x92, // Response Code 269 | 0x00, // Properties length 270 | ]; 271 | let packet = ReceivedPacket::from_buffer(&serialized_pubcomp).unwrap(); 272 | match packet { 273 | ReceivedPacket::PubComp(comp) => { 274 | assert_eq!(comp.packet_id, 5); 275 | assert_eq!(comp.reason.code(), ReasonCode::PacketIdNotFound); 276 | } 277 | _ => panic!("Invalid message"), 278 | } 279 | } 280 | 281 | #[test] 282 | fn deserialize_short_pubcomp() { 283 | let serialized_pubcomp: [u8; 4] = [ 284 | 7 << 4, // PubComp 285 | 0x02, // Remaining length 286 | 0x00, 287 | 0x05, // Identifier 288 | ]; 289 | let packet = ReceivedPacket::from_buffer(&serialized_pubcomp).unwrap(); 290 | match packet { 291 | ReceivedPacket::PubComp(comp) => { 292 | assert_eq!(comp.packet_id, 5); 293 | assert_eq!(comp.reason.code(), ReasonCode::Success); 294 | } 295 | _ => panic!("Invalid message"), 296 | } 297 | } 298 | 299 | #[test] 300 | fn deserialize_good_pubrec() { 301 | let serialized_pubrec: [u8; 6] = [ 302 | 5 << 4, // PubRec 303 | 0x04, // Remaining length 304 | 0x00, 305 | 0x05, // Identifier 306 | 0x10, // Response Code 307 | 0x00, // Properties length 308 | ]; 309 | let packet = ReceivedPacket::from_buffer(&serialized_pubrec).unwrap(); 310 | match packet { 311 | ReceivedPacket::PubRec(rec) => { 312 | assert_eq!(rec.packet_id, 5); 313 | assert_eq!(rec.reason.code(), ReasonCode::NoMatchingSubscribers); 314 | } 315 | _ => panic!("Invalid message"), 316 | } 317 | } 318 | 319 | #[test] 320 | fn deserialize_short_pubrec() { 321 | let serialized_pubrec: [u8; 4] = [ 322 | 5 << 4, // PubRec 323 | 0x02, // Remaining length 324 | 0x00, 325 | 0x05, // Identifier 326 | ]; 327 | let packet = ReceivedPacket::from_buffer(&serialized_pubrec).unwrap(); 328 | match packet { 329 | ReceivedPacket::PubRec(rec) => { 330 | assert_eq!(rec.packet_id, 5); 331 | assert_eq!(rec.reason.code(), ReasonCode::Success); 332 | } 333 | _ => panic!("Invalid message"), 334 | } 335 | } 336 | 337 | #[test] 338 | fn deserialize_good_pubrel() { 339 | let serialized_pubrel: [u8; 6] = [ 340 | 6 << 4 | 0b10, // PubRec 341 | 0x04, // Remaining length 342 | 0x00, 343 | 0x05, // Identifier 344 | 0x10, // Response Code 345 | 0x00, // Properties length 346 | ]; 347 | let packet = ReceivedPacket::from_buffer(&serialized_pubrel).unwrap(); 348 | match packet { 349 | ReceivedPacket::PubRel(rec) => { 350 | assert_eq!(rec.packet_id, 5); 351 | assert_eq!(rec.reason.code(), ReasonCode::NoMatchingSubscribers); 352 | } 353 | _ => panic!("Invalid message"), 354 | } 355 | } 356 | 357 | #[test] 358 | fn deserialize_short_pubrel() { 359 | let serialized_pubrel: [u8; 4] = [ 360 | 6 << 4 | 0b10, // PubRec 361 | 0x02, // Remaining length 362 | 0x00, 363 | 0x05, // Identifier 364 | ]; 365 | let packet = ReceivedPacket::from_buffer(&serialized_pubrel).unwrap(); 366 | match packet { 367 | ReceivedPacket::PubRel(rec) => { 368 | assert_eq!(rec.packet_id, 5); 369 | assert_eq!(rec.reason.code(), ReasonCode::Success); 370 | } 371 | _ => panic!("Invalid message"), 372 | } 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | //! # MiniMQ 3 | //! Provides a minimal MQTTv5 client and message parsing for the MQTT version 5 protocol. 4 | //! 5 | //! This crate provides a minimalistic MQTT 5 client that can be used to publish topics to an MQTT 6 | //! broker and subscribe to receive messages on specific topics. 7 | //! 8 | //! # Limitations 9 | //! This library does not currently support the following elements: 10 | //! * Subscribing above Quality-of-service `AtMostOnce` 11 | //! * Server Authentication 12 | //! * Topic aliases 13 | //! 14 | //! # Requirements 15 | //! This library requires that the user provide it an object that implements a basic TcpStack that 16 | //! can be used as the transport layer for MQTT communications. 17 | //! 18 | //! The maximum message size is configured through generic parameters. This allows the maximum 19 | //! message size to be configured by the user. Note that buffers will be allocated on the stack, so it 20 | //! is important to select a size such that the stack does not overflow. 21 | //! 22 | //! # Example 23 | //! Below is a sample snippet showing how this library is used. 24 | //! 25 | //! ```no_run 26 | //! use minimq::{ConfigBuilder, Minimq, Publication}; 27 | //! 28 | //! // Construct an MQTT client with a maximum packet size of 256 bytes 29 | //! // and a maximum of 16 messages that are allowed to be "in flight". 30 | //! // Messages are "in flight" if QoS::AtLeastOnce has not yet been acknowledged (PUBACK) 31 | //! // or QoS::ExactlyOnce has not been completed (PUBCOMP). 32 | //! // Connect to a broker at localhost - Use a client ID of "test". 33 | //! let mut buffer = [0; 256]; 34 | //! let localhost: std::net::IpAddr = "127.0.0.1".parse().unwrap(); 35 | //! let mut mqtt: Minimq<'_, _, _, minimq::broker::IpBroker> = Minimq::new( 36 | //! std_embedded_nal::Stack::default(), 37 | //! std_embedded_time::StandardClock::default(), 38 | //! ConfigBuilder::new(localhost.into(), &mut buffer) 39 | //! .client_id("test").unwrap(), 40 | //! ); 41 | //! 42 | //! let mut subscribed = false; 43 | //! 44 | //! loop { 45 | //! if mqtt.client().is_connected() && !subscribed { 46 | //! mqtt.client().subscribe(&["topic".into()], &[]).unwrap(); 47 | //! subscribed = true; 48 | //! } 49 | //! 50 | //! // The client must be continually polled to update the MQTT state machine. 51 | //! mqtt.poll(|client, topic, message, properties| { 52 | //! match topic { 53 | //! "topic" => { 54 | //! println!("{:?}", message); 55 | //! client.publish(Publication::new("echo", message)).unwrap(); 56 | //! }, 57 | //! topic => println!("Unknown topic: {}", topic), 58 | //! }; 59 | //! }).unwrap(); 60 | //! } 61 | //! ``` 62 | 63 | pub mod broker; 64 | pub mod config; 65 | mod de; 66 | mod message_types; 67 | pub mod mqtt_client; 68 | mod network_manager; 69 | mod packets; 70 | mod properties; 71 | pub mod publication; 72 | mod reason_codes; 73 | mod republication; 74 | mod ring_buffer; 75 | mod ser; 76 | mod session_state; 77 | pub mod types; 78 | mod varint; 79 | mod will; 80 | 81 | pub use broker::Broker; 82 | pub use config::ConfigBuilder; 83 | pub use properties::Property; 84 | pub use publication::Publication; 85 | pub use reason_codes::ReasonCode; 86 | pub use will::Will; 87 | 88 | pub use embedded_nal; 89 | pub use embedded_time; 90 | pub use mqtt_client::Minimq; 91 | use num_enum::TryFromPrimitive; 92 | 93 | pub use de::Error as DeError; 94 | pub use ser::Error as SerError; 95 | 96 | #[cfg(feature = "logging")] 97 | pub(crate) use log::{debug, error, info, trace, warn}; 98 | 99 | /// Default port number for unencrypted MQTT traffic 100 | /// 101 | /// # Note: 102 | /// See [IANA Port Numbers](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt) 103 | pub const MQTT_INSECURE_DEFAULT_PORT: u16 = 1883; 104 | 105 | /// Default port number for encrypted MQTT traffic 106 | /// 107 | /// # Note: 108 | /// See [IANA Port Numbers](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt) 109 | pub const MQTT_SECURE_DEFAULT_PORT: u16 = 8883; 110 | 111 | /// The quality-of-service for an MQTT message. 112 | #[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive, PartialOrd)] 113 | #[repr(u8)] 114 | pub enum QoS { 115 | /// A packet will be delivered at most once, but may not be delivered at all. 116 | AtMostOnce = 0, 117 | 118 | /// A packet will be delivered at least one time, but possibly more than once. 119 | AtLeastOnce = 1, 120 | 121 | /// A packet will be delivered exactly one time. 122 | ExactlyOnce = 2, 123 | } 124 | 125 | /// The retained status for an MQTT message. 126 | #[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)] 127 | #[repr(u8)] 128 | pub enum Retain { 129 | /// The message shall not be retained by the broker. 130 | NotRetained = 0, 131 | 132 | /// The message shall be marked for retention by the broker. 133 | Retained = 1, 134 | } 135 | 136 | /// Errors that are specific to the MQTT protocol implementation. 137 | #[non_exhaustive] 138 | #[derive(Debug, Copy, Clone, PartialEq)] 139 | pub enum ProtocolError { 140 | ProvidedClientIdTooLong, 141 | UnexpectedPacket, 142 | InvalidProperty, 143 | MalformedPacket, 144 | BufferSize, 145 | BadIdentifier, 146 | Unacknowledged, 147 | WrongQos, 148 | UnsupportedPacket, 149 | NoTopic, 150 | AuthAlreadySpecified, 151 | WillAlreadySpecified, 152 | Failed(ReasonCode), 153 | Serialization(SerError), 154 | Deserialization(DeError), 155 | } 156 | 157 | #[derive(Debug, PartialEq)] 158 | pub enum PubError { 159 | Error(Error), 160 | Serialization(E), 161 | } 162 | 163 | impl From> for PubError { 164 | fn from(e: crate::ser::PubError) -> Self { 165 | match e { 166 | crate::ser::PubError::Other(e) => crate::PubError::Serialization(e), 167 | crate::ser::PubError::Error(e) => crate::PubError::Error(crate::Error::Minimq( 168 | crate::MinimqError::Protocol(ProtocolError::from(e)), 169 | )), 170 | } 171 | } 172 | } 173 | 174 | impl From> for PubError { 175 | fn from(e: Error) -> Self { 176 | Self::Error(e) 177 | } 178 | } 179 | 180 | impl From for ProtocolError { 181 | fn from(err: crate::ser::Error) -> Self { 182 | ProtocolError::Serialization(err) 183 | } 184 | } 185 | 186 | impl From for ProtocolError { 187 | fn from(err: crate::de::Error) -> Self { 188 | ProtocolError::Deserialization(err) 189 | } 190 | } 191 | 192 | impl From for ProtocolError { 193 | fn from(code: ReasonCode) -> Self { 194 | ProtocolError::Failed(code) 195 | } 196 | } 197 | 198 | #[derive(Debug, PartialEq)] 199 | #[non_exhaustive] 200 | pub enum MinimqError { 201 | Protocol(ProtocolError), 202 | Clock(embedded_time::clock::Error), 203 | } 204 | 205 | /// Possible errors encountered during an MQTT connection. 206 | #[derive(Debug, PartialEq)] 207 | #[non_exhaustive] 208 | pub enum Error { 209 | WriteFail, 210 | NotReady, 211 | Unsupported, 212 | NoResponseTopic, 213 | SessionReset, 214 | Network(E), 215 | Minimq(MinimqError), 216 | } 217 | 218 | impl From for Error { 219 | fn from(minimq: MinimqError) -> Self { 220 | Error::Minimq(minimq) 221 | } 222 | } 223 | 224 | impl From for MinimqError { 225 | fn from(clock: embedded_time::clock::Error) -> Self { 226 | MinimqError::Clock(clock) 227 | } 228 | } 229 | 230 | impl From for Error { 231 | fn from(p: ProtocolError) -> Self { 232 | Error::Minimq(p.into()) 233 | } 234 | } 235 | 236 | impl From for Error { 237 | fn from(clock: embedded_time::clock::Error) -> Self { 238 | Error::Minimq(clock.into()) 239 | } 240 | } 241 | 242 | impl From for MinimqError { 243 | fn from(error: ProtocolError) -> Self { 244 | MinimqError::Protocol(error) 245 | } 246 | } 247 | 248 | #[doc(hidden)] 249 | #[cfg(not(feature = "logging"))] 250 | mod mqtt_log { 251 | #[doc(hidden)] 252 | #[macro_export] 253 | macro_rules! trace { 254 | ($($arg:tt)+) => { 255 | () 256 | }; 257 | } 258 | 259 | #[doc(hidden)] 260 | #[macro_export] 261 | macro_rules! debug { 262 | ($($arg:tt)+) => { 263 | () 264 | }; 265 | } 266 | 267 | #[doc(hidden)] 268 | #[macro_export] 269 | macro_rules! info { 270 | ($($arg:tt)+) => { 271 | () 272 | }; 273 | } 274 | 275 | #[doc(hidden)] 276 | #[macro_export] 277 | macro_rules! warn { 278 | ($($arg:tt)+) => { 279 | () 280 | }; 281 | } 282 | 283 | #[doc(hidden)] 284 | #[macro_export] 285 | macro_rules! error { 286 | ($($arg:tt)+) => { 287 | () 288 | }; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/message_types.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | packets::{ 3 | ConnAck, Connect, Disconnect, PingReq, PingResp, Pub, PubAck, PubComp, PubRec, PubRel, 4 | SubAck, Subscribe, 5 | }, 6 | Retain, 7 | }; 8 | use bit_field::BitField; 9 | use num_enum::TryFromPrimitive; 10 | 11 | #[derive(Copy, Clone, Debug, TryFromPrimitive)] 12 | #[repr(u8)] 13 | pub enum MessageType { 14 | Connect = 1, 15 | ConnAck = 2, 16 | Publish = 3, 17 | PubAck = 4, 18 | PubRec = 5, 19 | PubRel = 6, 20 | PubComp = 7, 21 | Subscribe = 8, 22 | SubAck = 9, 23 | Unsubscribe = 10, 24 | UnsubAck = 11, 25 | PingReq = 12, 26 | PingResp = 13, 27 | Disconnect = 14, 28 | Auth = 15, 29 | } 30 | 31 | pub trait ControlPacket { 32 | const MESSAGE_TYPE: MessageType; 33 | fn fixed_header_flags(&self) -> u8 { 34 | 0u8 35 | } 36 | } 37 | 38 | impl ControlPacket for Connect<'_> { 39 | const MESSAGE_TYPE: MessageType = MessageType::Connect; 40 | } 41 | 42 | impl ControlPacket for ConnAck<'_> { 43 | const MESSAGE_TYPE: MessageType = MessageType::ConnAck; 44 | } 45 | 46 | impl

Pub<'_, P> { 47 | pub fn fixed_header_flags(&self) -> u8 { 48 | *0u8.set_bits(1..=2, self.qos as u8) 49 | .set_bit(0, self.retain == Retain::Retained) 50 | } 51 | } 52 | 53 | impl ControlPacket for PubAck<'_> { 54 | const MESSAGE_TYPE: MessageType = MessageType::PubAck; 55 | } 56 | 57 | impl ControlPacket for PubRec<'_> { 58 | const MESSAGE_TYPE: MessageType = MessageType::PubRec; 59 | } 60 | 61 | impl ControlPacket for PubRel<'_> { 62 | const MESSAGE_TYPE: MessageType = MessageType::PubRel; 63 | fn fixed_header_flags(&self) -> u8 { 64 | 0b0010 65 | } 66 | } 67 | 68 | impl ControlPacket for PubComp<'_> { 69 | const MESSAGE_TYPE: MessageType = MessageType::PubComp; 70 | } 71 | 72 | impl ControlPacket for Subscribe<'_> { 73 | const MESSAGE_TYPE: MessageType = MessageType::Subscribe; 74 | fn fixed_header_flags(&self) -> u8 { 75 | 0b0010 76 | } 77 | } 78 | 79 | impl ControlPacket for SubAck<'_> { 80 | const MESSAGE_TYPE: MessageType = MessageType::SubAck; 81 | } 82 | 83 | impl ControlPacket for PingReq { 84 | const MESSAGE_TYPE: MessageType = MessageType::PingReq; 85 | } 86 | 87 | impl ControlPacket for PingResp { 88 | const MESSAGE_TYPE: MessageType = MessageType::PingResp; 89 | } 90 | 91 | impl ControlPacket for Disconnect<'_> { 92 | const MESSAGE_TYPE: MessageType = MessageType::Disconnect; 93 | } 94 | -------------------------------------------------------------------------------- /src/network_manager.rs: -------------------------------------------------------------------------------- 1 | //! Network Interface Holder 2 | //! 3 | //! # Design 4 | //! The embedded-nal network interface is abstracted away into a separate struct to facilitate 5 | //! simple ownership semantics of reading and writing to the network stack. This allows the network 6 | //! stack to be used to transmit buffers that may be stored internally in other structs without 7 | //! violating Rust's borrow rules. 8 | use crate::{message_types::ControlPacket, packets::Pub}; 9 | use core::net::SocketAddr; 10 | use embedded_nal::{nb, TcpClientStack, TcpError}; 11 | use serde::Serialize; 12 | 13 | use crate::{Error, ProtocolError}; 14 | 15 | /// Simple structure for maintaining state of the network connection. 16 | pub(crate) struct InterfaceHolder<'a, TcpStack: TcpClientStack> { 17 | socket: Option, 18 | network_stack: TcpStack, 19 | tx_buffer: &'a mut [u8], 20 | pending_write: Option<(usize, usize)>, 21 | connection_died: bool, 22 | } 23 | 24 | impl<'a, TcpStack> InterfaceHolder<'a, TcpStack> 25 | where 26 | TcpStack: TcpClientStack, 27 | { 28 | /// Construct a new network holder utility. 29 | pub fn new(stack: TcpStack, tx_buffer: &'a mut [u8]) -> Self { 30 | Self { 31 | socket: None, 32 | network_stack: stack, 33 | pending_write: None, 34 | connection_died: false, 35 | tx_buffer, 36 | } 37 | } 38 | 39 | pub fn socket_was_closed(&mut self) -> bool { 40 | let was_closed = self.connection_died; 41 | if was_closed { 42 | self.pending_write.take(); 43 | } 44 | self.connection_died = false; 45 | was_closed 46 | } 47 | 48 | /// Determine if there is a pending packet write that needs to be completed. 49 | pub fn has_pending_write(&self) -> bool { 50 | self.pending_write.is_some() 51 | } 52 | 53 | /// Allocate a new TCP socket. 54 | /// 55 | /// # Note 56 | /// If a TCP socket was previously open, it will be closed and a new socket will be allocated. 57 | pub fn allocate_socket(&mut self) -> Result<(), Error> { 58 | if let Some(socket) = self.socket.take() { 59 | self.network_stack.close(socket).map_err(Error::Network)?; 60 | } 61 | 62 | // Allocate a new socket to use and begin connecting it. 63 | self.socket 64 | .replace(self.network_stack.socket().map_err(Error::Network)?); 65 | 66 | Ok(()) 67 | } 68 | 69 | /// Connect the TCP socket to a remote address. 70 | /// 71 | /// # Args 72 | /// * `remote` - The address of the remote to connect to. 73 | pub fn connect(&mut self, remote: SocketAddr) -> Result> { 74 | if self.socket.is_none() { 75 | self.allocate_socket()?; 76 | } 77 | 78 | let socket = self.socket.as_mut().unwrap(); 79 | 80 | // Drop any pending unfinished packets, as we're establishing a new connection. 81 | self.pending_write.take(); 82 | 83 | match self.network_stack.connect(socket, remote) { 84 | Ok(_) => Ok(true), 85 | Err(nb::Error::WouldBlock) => Ok(false), 86 | Err(nb::Error::Other(err)) => Err(Error::Network(err)), 87 | } 88 | } 89 | 90 | /// Write data to the interface. 91 | /// 92 | /// # Args 93 | /// * `packet` - The data to write. 94 | fn commit_write(&mut self, start: usize, len: usize) -> Result<(), Error> { 95 | // If there's an unfinished write pending, it's invalid to try to write new data. The 96 | // previous write must first be completed. 97 | assert!(self.pending_write.is_none()); 98 | 99 | let socket = self.socket.as_mut().ok_or(Error::NotReady)?; 100 | self.network_stack 101 | .send(socket, &self.tx_buffer[start..][..len]) 102 | .or_else(|err| match err { 103 | nb::Error::WouldBlock => Ok(0), 104 | nb::Error::Other(err) if err.kind() == embedded_nal::TcpErrorKind::PipeClosed => { 105 | self.connection_died = true; 106 | Ok(0) 107 | } 108 | nb::Error::Other(err) => Err(Error::Network(err)), 109 | }) 110 | .map(|written| { 111 | crate::trace!("Wrote: {:0x?}", &self.tx_buffer[..written]); 112 | if written != len { 113 | crate::warn!("Saving pending data. Wrote {written} of {len}"); 114 | self.pending_write.replace((start + written, len - written)); 115 | } 116 | }) 117 | } 118 | 119 | pub fn write_multipart( 120 | &mut self, 121 | head: &[u8], 122 | tail: &[u8], 123 | ) -> Result<(), Error> { 124 | self.tx_buffer[..head.len()].copy_from_slice(head); 125 | self.tx_buffer[head.len()..][..tail.len()].copy_from_slice(tail); 126 | self.commit_write(0, head.len() + tail.len()) 127 | } 128 | 129 | /// Send an MQTT control packet over the interface. 130 | /// 131 | /// # Args 132 | /// * `packet` - The packet to transmit. 133 | pub fn send_packet( 134 | &mut self, 135 | packet: &T, 136 | ) -> Result<&[u8], Error> { 137 | // If there's an unfinished write pending, it's invalid to try to write new data. The 138 | // previous write must first be completed. 139 | assert!(self.pending_write.is_none()); 140 | 141 | crate::info!("Sending: {:?}", packet); 142 | let (offset, packet) = crate::ser::MqttSerializer::to_buffer_meta(self.tx_buffer, packet) 143 | .map_err(ProtocolError::from)?; 144 | let len = packet.len(); 145 | 146 | self.commit_write(offset, len)?; 147 | Ok(&self.tx_buffer[offset..][..len]) 148 | } 149 | 150 | pub fn send_pub( 151 | &mut self, 152 | pub_packet: Pub<'_, P>, 153 | ) -> Result<&[u8], crate::PubError> { 154 | // If there's an unfinished write pending, it's invalid to try to write new data. The 155 | // previous write must first be completed. 156 | assert!(self.pending_write.is_none()); 157 | 158 | crate::info!("Sending: {:?}", pub_packet); 159 | let (offset, packet) = 160 | crate::ser::MqttSerializer::pub_to_buffer_meta(self.tx_buffer, pub_packet)?; 161 | 162 | let len = packet.len(); 163 | 164 | self.commit_write(offset, len)?; 165 | Ok(&self.tx_buffer[offset..][..len]) 166 | } 167 | 168 | /// Finish writing an MQTT control packet to the interface if one exists. 169 | pub fn finish_write(&mut self) -> Result<(), Error> { 170 | if let Some((head, tail)) = self.pending_write.take() { 171 | self.commit_write(head, tail)?; 172 | } 173 | 174 | Ok(()) 175 | } 176 | 177 | /// Read data from the TCP interface. 178 | /// 179 | /// # Args 180 | /// * `buf` - A location to store read data into. 181 | /// 182 | /// # Returns 183 | /// The number of bytes successfully read. 184 | pub fn read(&mut self, buf: &mut [u8]) -> Result> { 185 | // Atomically access the socket. 186 | let socket = self.socket.as_mut().ok_or(Error::NotReady)?; 187 | let result = self.network_stack.receive(socket, buf); 188 | 189 | #[cfg(feature = "logging")] 190 | if let Ok(len) = result { 191 | if len > 0 { 192 | let data = &buf[..len]; 193 | crate::trace!("Read: {:0x?}", data); 194 | } 195 | } 196 | 197 | result.or_else(|err| match err { 198 | nb::Error::WouldBlock => Ok(0), 199 | nb::Error::Other(err) if err.kind() == embedded_nal::TcpErrorKind::PipeClosed => { 200 | self.connection_died = true; 201 | Ok(0) 202 | } 203 | nb::Error::Other(err) => Err(Error::Network(err)), 204 | }) 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/packets.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | publication::Publication, 3 | reason_codes::ReasonCode, 4 | types::{Auth, Properties, TopicFilter, Utf8String}, 5 | will::SerializedWill, 6 | QoS, Retain, 7 | }; 8 | use bit_field::BitField; 9 | use serde::{Deserialize, Serialize}; 10 | 11 | use serde::ser::SerializeStruct; 12 | 13 | /// An MQTT CONNECT packet. 14 | #[derive(Debug)] 15 | pub struct Connect<'a> { 16 | /// Specifies the keep-alive interval of the connection in seconds. 17 | pub keep_alive: u16, 18 | 19 | /// Any properties associated with the CONNECT request. 20 | pub properties: Properties<'a>, 21 | 22 | /// The ID of the client that is connecting. May be an empty string to automatically allocate 23 | /// an ID from the broker. 24 | pub client_id: Utf8String<'a>, 25 | 26 | /// An optional authentication message used by the server. 27 | pub auth: Option>, 28 | 29 | /// An optional will message to be transmitted whenever the connection is lost. 30 | pub(crate) will: Option>, 31 | 32 | /// Specified true there is no session state being taken in to the MQTT connection. 33 | pub clean_start: bool, 34 | } 35 | 36 | impl serde::Serialize for Connect<'_> { 37 | fn serialize(&self, serializer: S) -> Result { 38 | let mut flags: u8 = 0; 39 | flags.set_bit(1, self.clean_start); 40 | 41 | if let Some(will) = &self.will { 42 | // Update the flags for the will parameters. Indicate that the will is present, the QoS of 43 | // the will message, and whether or not the will message should be retained. 44 | flags.set_bit(2, true); 45 | flags.set_bits(3..=4, will.qos as u8); 46 | flags.set_bit(5, will.retained == Retain::Retained); 47 | } 48 | 49 | #[cfg(feature = "unsecure")] 50 | if self.auth.is_some() { 51 | flags.set_bit(6, true); 52 | flags.set_bit(7, true); 53 | } 54 | 55 | let mut item = serializer.serialize_struct("Connect", 0)?; 56 | item.serialize_field("protocol_name", &Utf8String("MQTT"))?; 57 | item.serialize_field("protocol_version", &5u8)?; 58 | item.serialize_field("flags", &flags)?; 59 | item.serialize_field("keep_alive", &self.keep_alive)?; 60 | item.serialize_field("properties", &self.properties)?; 61 | item.serialize_field("client_id", &self.client_id)?; 62 | if let Some(will) = &self.will { 63 | item.serialize_field("will", will.contents)?; 64 | } 65 | 66 | #[cfg(feature = "unsecure")] 67 | if let Some(auth) = &self.auth { 68 | item.serialize_field("user_name", &Utf8String(auth.user_name))?; 69 | item.serialize_field("password", &Utf8String(auth.password))?; 70 | } 71 | 72 | item.end() 73 | } 74 | } 75 | 76 | /// An MQTT CONNACK packet, representing a connection acknowledgement from a broker. 77 | #[derive(Debug, Deserialize)] 78 | pub struct ConnAck<'a> { 79 | /// Indicates true if session state is being maintained by the broker. 80 | pub session_present: bool, 81 | 82 | /// A status code indicating the success status of the connection. 83 | pub reason_code: ReasonCode, 84 | 85 | /// A list of properties associated with the connection. 86 | #[serde(borrow)] 87 | pub properties: Properties<'a>, 88 | } 89 | 90 | /// An MQTT PUBLISH packet, containing data to be sent or received. 91 | #[derive(Serialize)] 92 | pub struct Pub<'a, P> { 93 | /// The topic that the message was received on. 94 | pub topic: Utf8String<'a>, 95 | 96 | /// The ID of the internal message. 97 | pub packet_id: Option, 98 | 99 | /// The properties transmitted with the publish data. 100 | pub properties: Properties<'a>, 101 | 102 | /// The message to be transmitted. 103 | #[serde(skip)] 104 | pub payload: P, 105 | 106 | /// Specifies whether or not the message should be retained on the broker. 107 | #[serde(skip)] 108 | pub retain: Retain, 109 | 110 | /// Specifies the quality-of-service of the transmission. 111 | #[serde(skip)] 112 | pub qos: QoS, 113 | 114 | /// Specified true if this message is a duplicate (e.g. it has already been transmitted). 115 | #[serde(skip)] 116 | pub dup: bool, 117 | } 118 | 119 | impl

core::fmt::Debug for Pub<'_, P> { 120 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 121 | f.debug_struct("Pub") 122 | .field("topic", &self.topic) 123 | .field("packet_id", &self.packet_id) 124 | .field("properties", &self.properties) 125 | .field("retain", &self.retain) 126 | .field("qos", &self.qos) 127 | .field("dup", &self.dup) 128 | .field("payload", &"") 129 | .finish() 130 | } 131 | } 132 | 133 | impl<'a, P> From> for Pub<'a, P> { 134 | fn from(publication: Publication<'a, P>) -> Self { 135 | Self { 136 | topic: Utf8String(publication.topic), 137 | properties: publication.properties, 138 | packet_id: None, 139 | payload: publication.payload, 140 | retain: publication.retain, 141 | qos: publication.qos, 142 | dup: false, 143 | } 144 | } 145 | } 146 | 147 | /// An MQTT SUBSCRIBE control packet 148 | #[derive(Debug, Serialize)] 149 | pub struct Subscribe<'a> { 150 | /// Specifies the ID of this subscription request. 151 | pub packet_id: u16, 152 | 153 | /// A list of properties associated with the subscription. 154 | pub properties: Properties<'a>, 155 | 156 | /// A list of topic filters and associated subscription options for the subscription request. 157 | pub topics: &'a [TopicFilter<'a>], 158 | } 159 | 160 | /// An MQTT PINGREQ control packet 161 | #[derive(Debug, Serialize)] 162 | pub struct PingReq; 163 | 164 | /// An MQTT PINGRESP control packet 165 | #[derive(Debug, Deserialize)] 166 | pub struct PingResp; 167 | 168 | /// An MQTT PUBACK control packet 169 | #[derive(Debug, Deserialize, Serialize)] 170 | pub struct PubAck<'a> { 171 | /// The ID of the packet being acknowledged. 172 | pub packet_identifier: u16, 173 | 174 | /// The properties and reason code associated with the packet. 175 | #[serde(borrow)] 176 | pub reason: Reason<'a>, 177 | } 178 | 179 | /// An MQTT SUBACK control packet. 180 | #[derive(Debug, Deserialize)] 181 | pub struct SubAck<'a> { 182 | /// The identifier that the acknowledge is assocaited with. 183 | pub packet_identifier: u16, 184 | 185 | /// The optional properties associated with the acknowledgement. 186 | #[serde(borrow)] 187 | pub properties: Properties<'a>, 188 | 189 | /// The response status code of the subscription request. 190 | #[serde(skip)] 191 | pub codes: &'a [u8], 192 | } 193 | 194 | /// An MQTT PUBREC control packet 195 | #[derive(Debug, Serialize, Deserialize)] 196 | pub struct PubRec<'a> { 197 | /// The ID of the packet that publication reception occurred on. 198 | pub packet_id: u16, 199 | 200 | /// The properties and success status of associated with the publication. 201 | #[serde(borrow)] 202 | pub reason: Reason<'a>, 203 | } 204 | 205 | /// An MQTT PUBREL control packet 206 | #[derive(Debug, Deserialize, Serialize)] 207 | pub struct PubRel<'a> { 208 | /// The ID of the publication that this packet is associated with. 209 | pub packet_id: u16, 210 | 211 | /// The properties and success status of associated with the publication. 212 | #[serde(borrow)] 213 | pub reason: Reason<'a>, 214 | } 215 | 216 | /// An MQTT PUBCOMP control packet 217 | #[derive(Debug, Serialize, Deserialize)] 218 | pub struct PubComp<'a> { 219 | /// Packet identifier of the publication that this packet is associated with. 220 | pub packet_id: u16, 221 | 222 | /// The properties and reason code associated with this packet. 223 | #[serde(borrow)] 224 | pub reason: Reason<'a>, 225 | } 226 | 227 | /// An MQTT DISCONNECT control packet 228 | #[derive(Debug, Deserialize)] 229 | pub struct Disconnect<'a> { 230 | /// The success status of the disconnection. 231 | pub reason_code: ReasonCode, 232 | 233 | /// Properties associated with the disconnection. 234 | #[serde(borrow)] 235 | pub properties: Properties<'a>, 236 | } 237 | 238 | /// Success information for a control packet with optional data. 239 | #[derive(Debug, Deserialize, Serialize)] 240 | pub struct Reason<'a> { 241 | #[serde(borrow)] 242 | reason: Option>, 243 | } 244 | 245 | impl From for Reason<'_> { 246 | fn from(code: ReasonCode) -> Self { 247 | Self { 248 | reason: Some(ReasonData { 249 | code, 250 | _properties: Some(Properties::Slice(&[])), 251 | }), 252 | } 253 | } 254 | } 255 | 256 | impl Reason<'_> { 257 | /// Get the reason code of the packet. 258 | pub fn code(&self) -> ReasonCode { 259 | self.reason 260 | .as_ref() 261 | .map(|data| data.code) 262 | .unwrap_or(ReasonCode::Success) 263 | } 264 | } 265 | 266 | #[derive(Debug, Deserialize, Serialize)] 267 | struct ReasonData<'a> { 268 | /// Reason code 269 | pub code: ReasonCode, 270 | 271 | /// The properties transmitted with the publish data. 272 | #[serde(borrow)] 273 | pub _properties: Option>, 274 | } 275 | 276 | #[cfg(test)] 277 | mod tests { 278 | use crate::reason_codes::ReasonCode; 279 | use crate::ser::MqttSerializer; 280 | 281 | #[test] 282 | pub fn serialize_publish() { 283 | let good_publish: [u8; 10] = [ 284 | 0x30, // Publish message 285 | 0x08, // Remaining length (8) 286 | 0x00, 0x03, 0x41, 0x42, 0x43, // Topic: ABC 287 | 0x00, // Properties length 288 | 0xAB, 0xCD, // Payload 289 | ]; 290 | 291 | let publish = crate::packets::Pub { 292 | qos: crate::QoS::AtMostOnce, 293 | packet_id: None, 294 | dup: false, 295 | properties: crate::types::Properties::Slice(&[]), 296 | retain: crate::Retain::NotRetained, 297 | topic: crate::types::Utf8String("ABC"), 298 | payload: &[0xAB, 0xCD], 299 | }; 300 | 301 | let mut buffer: [u8; 900] = [0; 900]; 302 | let message = MqttSerializer::pub_to_buffer(&mut buffer, publish).unwrap(); 303 | 304 | assert_eq!(message, good_publish); 305 | } 306 | 307 | #[test] 308 | pub fn serialize_publish_qos1() { 309 | let good_publish: [u8; 12] = [ 310 | 0x32, // Publish message 311 | 0x0a, // Remaining length (10) 312 | 0x00, 0x03, 0x41, 0x42, 0x43, // Topic: ABC 313 | 0xBE, 0xEF, // Packet identifier 314 | 0x00, // Properties length 315 | 0xAB, 0xCD, // Payload 316 | ]; 317 | 318 | let publish = crate::packets::Pub { 319 | qos: crate::QoS::AtLeastOnce, 320 | topic: crate::types::Utf8String("ABC"), 321 | packet_id: Some(0xBEEF), 322 | dup: false, 323 | properties: crate::types::Properties::Slice(&[]), 324 | retain: crate::Retain::NotRetained, 325 | payload: &[0xAB, 0xCD], 326 | }; 327 | 328 | let mut buffer: [u8; 900] = [0; 900]; 329 | let message = MqttSerializer::pub_to_buffer(&mut buffer, publish).unwrap(); 330 | 331 | assert_eq!(message, good_publish); 332 | } 333 | 334 | #[test] 335 | fn serialize_subscribe() { 336 | let good_subscribe: [u8; 11] = [ 337 | 0x82, // Subscribe request 338 | 0x09, // Remaining length (11) 339 | 0x00, 0x10, // Packet identifier (16) 340 | 0x00, // Property length 341 | 0x00, 0x03, 0x41, 0x42, 0x43, // Topic: ABC 342 | 0x00, // Options byte = 0 343 | ]; 344 | 345 | let subscribe = crate::packets::Subscribe { 346 | packet_id: 16, 347 | properties: crate::types::Properties::Slice(&[]), 348 | topics: &["ABC".into()], 349 | }; 350 | 351 | let mut buffer: [u8; 900] = [0; 900]; 352 | let message = MqttSerializer::to_buffer(&mut buffer, &subscribe).unwrap(); 353 | 354 | assert_eq!(message, good_subscribe); 355 | } 356 | 357 | #[test] 358 | pub fn serialize_publish_with_properties() { 359 | let good_publish: [u8; 14] = [ 360 | 0x30, // Publish message 361 | 0x0c, // Remaining length (14) 362 | 0x00, 0x03, 0x41, 0x42, 0x43, // Topic: ABC 363 | 0x04, // Properties length - 1 property encoding a string of length 4 364 | 0x08, 0x00, 0x01, 0x41, // Response topic "A" 365 | 0xAB, 0xCD, // Payload 366 | ]; 367 | 368 | let publish = crate::packets::Pub { 369 | qos: crate::QoS::AtMostOnce, 370 | topic: crate::types::Utf8String("ABC"), 371 | packet_id: None, 372 | dup: false, 373 | properties: crate::types::Properties::Slice(&[ 374 | crate::properties::Property::ResponseTopic(crate::types::Utf8String("A")), 375 | ]), 376 | retain: crate::Retain::NotRetained, 377 | payload: &[0xAB, 0xCD], 378 | }; 379 | 380 | let mut buffer: [u8; 900] = [0; 900]; 381 | let message = MqttSerializer::pub_to_buffer(&mut buffer, publish).unwrap(); 382 | 383 | assert_eq!(message, good_publish); 384 | } 385 | 386 | #[test] 387 | pub fn serialize_publish_with_user_prop() { 388 | let good_publish: [u8; 17] = [ 389 | 0x30, // Publish message 390 | 0x0f, // Remaining length (15) 391 | 0x00, 0x03, 0x41, 0x42, 0x43, // Topic: ABC 392 | 0x07, // Properties length - 1 property encoding a string pair, each of length 1 393 | 0x26, 0x00, 0x01, 0x41, 0x00, 0x01, 0x42, // UserProperty("A", "B") 394 | 0xAB, 0xCD, // Payload 395 | ]; 396 | 397 | let publish = crate::packets::Pub { 398 | qos: crate::QoS::AtMostOnce, 399 | topic: crate::types::Utf8String("ABC"), 400 | packet_id: None, 401 | dup: false, 402 | properties: crate::types::Properties::Slice(&[ 403 | crate::properties::Property::UserProperty( 404 | crate::types::Utf8String("A"), 405 | crate::types::Utf8String("B"), 406 | ), 407 | ]), 408 | retain: crate::Retain::NotRetained, 409 | payload: &[0xAB, 0xCD], 410 | }; 411 | 412 | let mut buffer: [u8; 900] = [0; 900]; 413 | let message = MqttSerializer::pub_to_buffer(&mut buffer, publish).unwrap(); 414 | 415 | assert_eq!(message, good_publish); 416 | } 417 | 418 | #[test] 419 | fn serialize_connect() { 420 | let good_serialized_connect: [u8; 18] = [ 421 | 0x10, // Connect 422 | 0x10, // Remaining length (16) 423 | 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, // MQTT5 header 424 | 0x02, // Flags (Clean start) 425 | 0x00, 0x0a, // Keep-alive (10) 426 | 0x00, // Properties length 427 | 0x00, 0x03, 0x41, 0x42, 0x43, // ABC client ID 428 | ]; 429 | 430 | let mut buffer: [u8; 900] = [0; 900]; 431 | let connect: crate::packets::Connect<'_> = crate::packets::Connect { 432 | client_id: crate::types::Utf8String("ABC"), 433 | auth: None, 434 | will: None, 435 | keep_alive: 10, 436 | properties: crate::types::Properties::Slice(&[]), 437 | clean_start: true, 438 | }; 439 | 440 | let message = MqttSerializer::to_buffer(&mut buffer, &connect).unwrap(); 441 | 442 | assert_eq!(message, good_serialized_connect) 443 | } 444 | 445 | #[test] 446 | fn serialize_connect_with_will() { 447 | #[rustfmt::skip] 448 | let good_serialized_connect: [u8; 28] = [ 449 | 0x10, // Connect 450 | 26, // Remaining length 451 | 452 | // Header: "MQTT5" 453 | 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, 454 | 455 | // Flags: Clean start, will present, will not retained, will QoS = 0, 456 | 0b0000_0110, 457 | 458 | // Keep-alive: 10 seconds 459 | 0x00, 0x0a, 460 | 461 | // Connected Properties: None 462 | 0x00, 463 | // Client ID: "ABC" 464 | 0x00, 0x03, 0x41, 0x42, 0x43, 465 | // Will properties: None 466 | 0x00, 467 | // Will topic: "EFG" 468 | 0x00, 0x03, 0x45, 0x46, 0x47, 469 | // Will payload: [0xAB, 0xCD] 470 | 0x00, 0x02, 0xAB, 0xCD, 471 | ]; 472 | 473 | let mut buffer: [u8; 900] = [0; 900]; 474 | let mut will_buff = [0; 64]; 475 | let will = crate::will::Will::new("EFG", &[0xAB, 0xCD], &[]) 476 | .unwrap() 477 | .qos(crate::QoS::AtMostOnce); 478 | 479 | let connect = crate::packets::Connect { 480 | clean_start: true, 481 | keep_alive: 10, 482 | properties: crate::types::Properties::Slice(&[]), 483 | client_id: crate::types::Utf8String("ABC"), 484 | auth: None, 485 | will: Some(will.serialize(&mut will_buff).unwrap()), 486 | }; 487 | 488 | let message = MqttSerializer::to_buffer(&mut buffer, &connect).unwrap(); 489 | 490 | assert_eq!(message, good_serialized_connect) 491 | } 492 | 493 | #[test] 494 | fn serialize_ping_req() { 495 | let good_ping_req: [u8; 2] = [ 496 | 0xc0, // Ping 497 | 0x00, // Remaining length (0) 498 | ]; 499 | 500 | let mut buffer: [u8; 1024] = [0; 1024]; 501 | let message = MqttSerializer::to_buffer(&mut buffer, &crate::packets::PingReq {}).unwrap(); 502 | assert_eq!(message, good_ping_req); 503 | } 504 | 505 | #[test] 506 | fn serialize_pubrel() { 507 | let good_pubrel: [u8; 6] = [ 508 | 6 << 4 | 0b10, // PubRel 509 | 0x04, // Remaining length 510 | 0x00, 511 | 0x05, // Identifier 512 | 0x10, // Response Code 513 | 0x00, // Properties length 514 | ]; 515 | 516 | let pubrel = crate::packets::PubRel { 517 | packet_id: 5, 518 | reason: ReasonCode::NoMatchingSubscribers.into(), 519 | }; 520 | 521 | let mut buffer: [u8; 1024] = [0; 1024]; 522 | assert_eq!( 523 | MqttSerializer::to_buffer(&mut buffer, &pubrel).unwrap(), 524 | good_pubrel 525 | ); 526 | } 527 | 528 | #[test] 529 | fn serialize_puback() { 530 | let good_puback: [u8; 5] = [ 531 | 4 << 4, // PubAck 532 | 0x03, // Remaining length 533 | 0x00, 534 | 0x15, // Identifier 535 | 0x00, // Response Code 536 | ]; 537 | 538 | let pubrel = crate::packets::PubAck { 539 | packet_identifier: 0x15, 540 | reason: crate::packets::Reason { 541 | reason: Some(crate::packets::ReasonData { 542 | code: ReasonCode::Success, 543 | _properties: None, 544 | }), 545 | }, 546 | }; 547 | 548 | let mut buffer: [u8; 1024] = [0; 1024]; 549 | assert_eq!( 550 | MqttSerializer::to_buffer(&mut buffer, &pubrel).unwrap(), 551 | good_puback 552 | ); 553 | } 554 | 555 | #[test] 556 | fn serialize_pubcomp() { 557 | let good_pubcomp: [u8; 5] = [ 558 | 7 << 4, // PubComp 559 | 0x03, // Remaining length 560 | 0x00, 561 | 0x15, // Identifier 562 | 0x00, // Response Code 563 | ]; 564 | 565 | let pubrel = crate::packets::PubComp { 566 | packet_id: 0x15, 567 | reason: crate::packets::Reason { 568 | reason: Some(crate::packets::ReasonData { 569 | code: ReasonCode::Success, 570 | _properties: None, 571 | }), 572 | }, 573 | }; 574 | 575 | let mut buffer: [u8; 1024] = [0; 1024]; 576 | assert_eq!( 577 | MqttSerializer::to_buffer(&mut buffer, &pubrel).unwrap(), 578 | good_pubcomp 579 | ); 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /src/properties.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | types::{BinaryData, Utf8String}, 3 | varint::Varint, 4 | }; 5 | 6 | use core::convert::TryFrom; 7 | use num_enum::TryFromPrimitive; 8 | use serde::ser::SerializeSeq; 9 | 10 | #[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)] 11 | #[repr(u32)] 12 | pub(crate) enum PropertyIdentifier { 13 | Invalid = u32::MAX, 14 | 15 | PayloadFormatIndicator = 0x01, 16 | MessageExpiryInterval = 0x02, 17 | ContentType = 0x03, 18 | 19 | ResponseTopic = 0x08, 20 | CorrelationData = 0x09, 21 | 22 | SubscriptionIdentifier = 0x0B, 23 | 24 | SessionExpiryInterval = 0x11, 25 | AssignedClientIdentifier = 0x12, 26 | ServerKeepAlive = 0x13, 27 | AuthenticationMethod = 0x15, 28 | AuthenticationData = 0x16, 29 | RequestProblemInformation = 0x17, 30 | WillDelayInterval = 0x18, 31 | RequestResponseInformation = 0x19, 32 | 33 | ResponseInformation = 0x1A, 34 | 35 | ServerReference = 0x1C, 36 | 37 | ReasonString = 0x1F, 38 | 39 | ReceiveMaximum = 0x21, 40 | TopicAliasMaximum = 0x22, 41 | TopicAlias = 0x23, 42 | MaximumQoS = 0x24, 43 | RetainAvailable = 0x25, 44 | UserProperty = 0x26, 45 | MaximumPacketSize = 0x27, 46 | WildcardSubscriptionAvailable = 0x28, 47 | SubscriptionIdentifierAvailable = 0x29, 48 | SharedSubscriptionAvailable = 0x2A, 49 | } 50 | 51 | struct PropertyIdVisitor; 52 | 53 | impl serde::de::Visitor<'_> for PropertyIdVisitor { 54 | type Value = PropertyIdentifier; 55 | 56 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 57 | write!(formatter, "PropertyIdentifier") 58 | } 59 | 60 | fn visit_u32(self, v: u32) -> Result { 61 | PropertyIdentifier::try_from(v).map_err(|_| E::custom("Invalid PropertyIdentifier")) 62 | } 63 | } 64 | 65 | impl<'de> serde::de::Deserialize<'de> for PropertyIdentifier { 66 | fn deserialize>(deserializer: D) -> Result { 67 | let result = deserializer.deserialize_u32(PropertyIdVisitor)?; 68 | Ok(result) 69 | } 70 | } 71 | 72 | /// All of the possible properties that MQTT version 5 supports. 73 | #[derive(Debug, Copy, Clone, PartialEq)] 74 | pub enum Property<'a> { 75 | PayloadFormatIndicator(u8), 76 | MessageExpiryInterval(u32), 77 | ContentType(Utf8String<'a>), 78 | ResponseTopic(Utf8String<'a>), 79 | CorrelationData(BinaryData<'a>), 80 | SubscriptionIdentifier(Varint), 81 | SessionExpiryInterval(u32), 82 | AssignedClientIdentifier(Utf8String<'a>), 83 | ServerKeepAlive(u16), 84 | AuthenticationMethod(Utf8String<'a>), 85 | AuthenticationData(BinaryData<'a>), 86 | RequestProblemInformation(u8), 87 | WillDelayInterval(u32), 88 | RequestResponseInformation(u8), 89 | ResponseInformation(Utf8String<'a>), 90 | ServerReference(Utf8String<'a>), 91 | ReasonString(Utf8String<'a>), 92 | ReceiveMaximum(u16), 93 | TopicAliasMaximum(u16), 94 | TopicAlias(u16), 95 | MaximumQoS(u8), 96 | RetainAvailable(u8), 97 | UserProperty(Utf8String<'a>, Utf8String<'a>), 98 | MaximumPacketSize(u32), 99 | WildcardSubscriptionAvailable(u8), 100 | SubscriptionIdentifierAvailable(u8), 101 | SharedSubscriptionAvailable(u8), 102 | } 103 | 104 | struct UserPropertyVisitor<'a> { 105 | _data: core::marker::PhantomData<&'a ()>, 106 | } 107 | 108 | impl<'a, 'de: 'a> serde::de::Visitor<'de> for UserPropertyVisitor<'a> { 109 | type Value = (Utf8String<'a>, Utf8String<'a>); 110 | 111 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 112 | write!(formatter, "UserProperty") 113 | } 114 | 115 | fn visit_seq>(self, mut seq: A) -> Result { 116 | use serde::de::Error; 117 | let key = seq 118 | .next_element()? 119 | .ok_or_else(|| A::Error::custom("No key present"))?; 120 | let value = seq 121 | .next_element()? 122 | .ok_or_else(|| A::Error::custom("No value present"))?; 123 | Ok((key, value)) 124 | } 125 | } 126 | 127 | struct PropertyVisitor<'a> { 128 | _data: core::marker::PhantomData<&'a ()>, 129 | } 130 | 131 | impl<'a, 'de: 'a> serde::de::Visitor<'de> for PropertyVisitor<'a> { 132 | type Value = Property<'a>; 133 | 134 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 135 | write!(formatter, "enum Property") 136 | } 137 | 138 | fn visit_enum>(self, data: A) -> Result { 139 | use serde::de::{Error, VariantAccess}; 140 | 141 | let (field, variant) = data.variant::()?; 142 | crate::trace!("Deserializing {:?}", field); 143 | 144 | let property = match field { 145 | PropertyIdentifier::ResponseTopic => { 146 | Property::ResponseTopic(variant.newtype_variant()?) 147 | } 148 | PropertyIdentifier::PayloadFormatIndicator => { 149 | Property::PayloadFormatIndicator(variant.newtype_variant()?) 150 | } 151 | PropertyIdentifier::MessageExpiryInterval => { 152 | Property::MessageExpiryInterval(variant.newtype_variant()?) 153 | } 154 | PropertyIdentifier::ContentType => Property::ContentType(variant.newtype_variant()?), 155 | PropertyIdentifier::CorrelationData => { 156 | Property::CorrelationData(variant.newtype_variant()?) 157 | } 158 | PropertyIdentifier::SubscriptionIdentifier => { 159 | Property::SubscriptionIdentifier(variant.newtype_variant()?) 160 | } 161 | PropertyIdentifier::SessionExpiryInterval => { 162 | Property::SessionExpiryInterval(variant.newtype_variant()?) 163 | } 164 | PropertyIdentifier::AssignedClientIdentifier => { 165 | Property::AssignedClientIdentifier(variant.newtype_variant()?) 166 | } 167 | PropertyIdentifier::ServerKeepAlive => { 168 | Property::ServerKeepAlive(variant.newtype_variant()?) 169 | } 170 | PropertyIdentifier::AuthenticationMethod => { 171 | Property::AuthenticationMethod(variant.newtype_variant()?) 172 | } 173 | PropertyIdentifier::AuthenticationData => { 174 | Property::AuthenticationData(variant.newtype_variant()?) 175 | } 176 | PropertyIdentifier::RequestProblemInformation => { 177 | Property::RequestProblemInformation(variant.newtype_variant()?) 178 | } 179 | PropertyIdentifier::WillDelayInterval => { 180 | Property::WillDelayInterval(variant.newtype_variant()?) 181 | } 182 | PropertyIdentifier::RequestResponseInformation => { 183 | Property::RequestResponseInformation(variant.newtype_variant()?) 184 | } 185 | PropertyIdentifier::ResponseInformation => { 186 | Property::ResponseInformation(variant.newtype_variant()?) 187 | } 188 | PropertyIdentifier::ServerReference => { 189 | Property::ServerReference(variant.newtype_variant()?) 190 | } 191 | PropertyIdentifier::ReasonString => Property::ReasonString(variant.newtype_variant()?), 192 | PropertyIdentifier::ReceiveMaximum => { 193 | Property::ReceiveMaximum(variant.newtype_variant()?) 194 | } 195 | PropertyIdentifier::TopicAliasMaximum => { 196 | Property::TopicAliasMaximum(variant.newtype_variant()?) 197 | } 198 | PropertyIdentifier::TopicAlias => Property::TopicAlias(variant.newtype_variant()?), 199 | PropertyIdentifier::MaximumQoS => Property::MaximumQoS(variant.newtype_variant()?), 200 | PropertyIdentifier::RetainAvailable => { 201 | Property::RetainAvailable(variant.newtype_variant()?) 202 | } 203 | PropertyIdentifier::UserProperty => { 204 | let (key, value) = variant.tuple_variant( 205 | 2, 206 | UserPropertyVisitor { 207 | _data: core::marker::PhantomData, 208 | }, 209 | )?; 210 | Property::UserProperty(key, value) 211 | } 212 | PropertyIdentifier::MaximumPacketSize => { 213 | Property::MaximumPacketSize(variant.newtype_variant()?) 214 | } 215 | PropertyIdentifier::WildcardSubscriptionAvailable => { 216 | Property::WildcardSubscriptionAvailable(variant.newtype_variant()?) 217 | } 218 | PropertyIdentifier::SubscriptionIdentifierAvailable => { 219 | Property::SubscriptionIdentifierAvailable(variant.newtype_variant()?) 220 | } 221 | PropertyIdentifier::SharedSubscriptionAvailable => { 222 | Property::SharedSubscriptionAvailable(variant.newtype_variant()?) 223 | } 224 | 225 | _ => return Err(A::Error::custom("Invalid property identifier")), 226 | }; 227 | 228 | Ok(property) 229 | } 230 | } 231 | 232 | impl<'a, 'de: 'a> serde::de::Deserialize<'de> for Property<'a> { 233 | fn deserialize>(deserializer: D) -> Result { 234 | let prop = deserializer.deserialize_enum( 235 | "Property", 236 | &[], 237 | PropertyVisitor { 238 | _data: core::marker::PhantomData, 239 | }, 240 | )?; 241 | crate::debug!("Deserialized {:?}", prop); 242 | Ok(prop) 243 | } 244 | } 245 | 246 | impl serde::Serialize for Property<'_> { 247 | fn serialize(&self, serializer: S) -> Result { 248 | let mut serializer = serializer.serialize_seq(None)?; 249 | 250 | let id: PropertyIdentifier = self.into(); 251 | serializer.serialize_element(&Varint(id as u32))?; 252 | 253 | match self { 254 | Property::PayloadFormatIndicator(value) => serializer.serialize_element(value)?, 255 | Property::MessageExpiryInterval(value) => serializer.serialize_element(value)?, 256 | Property::ContentType(content_type) => serializer.serialize_element(content_type)?, 257 | Property::ResponseTopic(topic) => serializer.serialize_element(topic)?, 258 | Property::CorrelationData(data) => serializer.serialize_element(data)?, 259 | Property::SubscriptionIdentifier(data) => serializer.serialize_element(data)?, 260 | Property::SessionExpiryInterval(data) => serializer.serialize_element(data)?, 261 | Property::AssignedClientIdentifier(data) => serializer.serialize_element(data)?, 262 | Property::ServerKeepAlive(data) => serializer.serialize_element(data)?, 263 | Property::AuthenticationMethod(data) => serializer.serialize_element(data)?, 264 | Property::AuthenticationData(data) => serializer.serialize_element(data)?, 265 | Property::RequestProblemInformation(data) => serializer.serialize_element(data)?, 266 | Property::WillDelayInterval(data) => serializer.serialize_element(data)?, 267 | Property::RequestResponseInformation(data) => serializer.serialize_element(data)?, 268 | Property::ResponseInformation(data) => serializer.serialize_element(data)?, 269 | Property::ServerReference(data) => serializer.serialize_element(data)?, 270 | Property::ReasonString(data) => serializer.serialize_element(data)?, 271 | Property::ReceiveMaximum(data) => serializer.serialize_element(data)?, 272 | Property::TopicAliasMaximum(data) => serializer.serialize_element(data)?, 273 | Property::TopicAlias(data) => serializer.serialize_element(data)?, 274 | Property::MaximumQoS(data) => serializer.serialize_element(data)?, 275 | Property::RetainAvailable(data) => serializer.serialize_element(data)?, 276 | Property::UserProperty(key, value) => { 277 | serializer.serialize_element(key)?; 278 | serializer.serialize_element(value)?; 279 | } 280 | Property::MaximumPacketSize(data) => serializer.serialize_element(data)?, 281 | Property::WildcardSubscriptionAvailable(data) => serializer.serialize_element(data)?, 282 | Property::SubscriptionIdentifierAvailable(data) => { 283 | serializer.serialize_element(data)? 284 | } 285 | Property::SharedSubscriptionAvailable(data) => serializer.serialize_element(data)?, 286 | } 287 | 288 | serializer.end() 289 | } 290 | } 291 | 292 | impl<'a> From<&Property<'a>> for PropertyIdentifier { 293 | fn from(prop: &Property<'a>) -> PropertyIdentifier { 294 | match prop { 295 | Property::PayloadFormatIndicator(_) => PropertyIdentifier::PayloadFormatIndicator, 296 | Property::MessageExpiryInterval(_) => PropertyIdentifier::MessageExpiryInterval, 297 | Property::ContentType(_) => PropertyIdentifier::ContentType, 298 | Property::ResponseTopic(_) => PropertyIdentifier::ResponseTopic, 299 | Property::CorrelationData(_) => PropertyIdentifier::CorrelationData, 300 | Property::SubscriptionIdentifier(_) => PropertyIdentifier::SubscriptionIdentifier, 301 | Property::SessionExpiryInterval(_) => PropertyIdentifier::SessionExpiryInterval, 302 | Property::AssignedClientIdentifier(_) => PropertyIdentifier::AssignedClientIdentifier, 303 | Property::ServerKeepAlive(_) => PropertyIdentifier::ServerKeepAlive, 304 | Property::AuthenticationMethod(_) => PropertyIdentifier::AuthenticationMethod, 305 | Property::AuthenticationData(_) => PropertyIdentifier::AuthenticationData, 306 | Property::RequestProblemInformation(_) => PropertyIdentifier::RequestProblemInformation, 307 | Property::WillDelayInterval(_) => PropertyIdentifier::WillDelayInterval, 308 | Property::RequestResponseInformation(_) => { 309 | PropertyIdentifier::RequestResponseInformation 310 | } 311 | Property::ResponseInformation(_) => PropertyIdentifier::ResponseInformation, 312 | Property::ServerReference(_) => PropertyIdentifier::ServerReference, 313 | Property::ReasonString(_) => PropertyIdentifier::ReasonString, 314 | Property::ReceiveMaximum(_) => PropertyIdentifier::ReceiveMaximum, 315 | Property::TopicAliasMaximum(_) => PropertyIdentifier::TopicAliasMaximum, 316 | Property::TopicAlias(_) => PropertyIdentifier::TopicAlias, 317 | Property::MaximumQoS(_) => PropertyIdentifier::MaximumQoS, 318 | Property::RetainAvailable(_) => PropertyIdentifier::RetainAvailable, 319 | Property::UserProperty(_, _) => PropertyIdentifier::UserProperty, 320 | Property::MaximumPacketSize(_) => PropertyIdentifier::MaximumPacketSize, 321 | Property::WildcardSubscriptionAvailable(_) => { 322 | PropertyIdentifier::WildcardSubscriptionAvailable 323 | } 324 | Property::SubscriptionIdentifierAvailable(_) => { 325 | PropertyIdentifier::SubscriptionIdentifierAvailable 326 | } 327 | Property::SharedSubscriptionAvailable(_) => { 328 | PropertyIdentifier::SharedSubscriptionAvailable 329 | } 330 | } 331 | } 332 | } 333 | 334 | impl Property<'_> { 335 | pub(crate) fn size(&self) -> usize { 336 | let identifier: PropertyIdentifier = self.into(); 337 | let identifier_length = Varint(identifier as u32).len(); 338 | 339 | match self { 340 | Property::ContentType(data) 341 | | Property::ResponseTopic(data) 342 | | Property::AuthenticationMethod(data) 343 | | Property::ResponseInformation(data) 344 | | Property::ServerReference(data) 345 | | Property::ReasonString(data) 346 | | Property::AssignedClientIdentifier(data) => data.0.len() + 2 + identifier_length, 347 | Property::UserProperty(key, value) => { 348 | (value.0.len() + 2) + (key.0.len() + 2) + identifier_length 349 | } 350 | Property::CorrelationData(data) | Property::AuthenticationData(data) => { 351 | data.0.len() + 2 + identifier_length 352 | } 353 | Property::SubscriptionIdentifier(id) => id.len() + identifier_length, 354 | 355 | Property::MessageExpiryInterval(_) 356 | | Property::SessionExpiryInterval(_) 357 | | Property::WillDelayInterval(_) 358 | | Property::MaximumPacketSize(_) => 4 + identifier_length, 359 | Property::ServerKeepAlive(_) 360 | | Property::ReceiveMaximum(_) 361 | | Property::TopicAliasMaximum(_) 362 | | Property::TopicAlias(_) => 2 + identifier_length, 363 | Property::PayloadFormatIndicator(_) 364 | | Property::RequestProblemInformation(_) 365 | | Property::RequestResponseInformation(_) 366 | | Property::MaximumQoS(_) 367 | | Property::RetainAvailable(_) 368 | | Property::WildcardSubscriptionAvailable(_) 369 | | Property::SubscriptionIdentifierAvailable(_) 370 | | Property::SharedSubscriptionAvailable(_) => 1 + identifier_length, 371 | } 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /src/publication.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | properties::Property, 3 | types::{BinaryData, Properties}, 4 | ProtocolError, QoS, Retain, 5 | }; 6 | 7 | pub trait ToPayload { 8 | type Error; 9 | fn serialize(self, buffer: &mut [u8]) -> Result; 10 | } 11 | 12 | impl ToPayload for &[u8] { 13 | type Error = (); 14 | 15 | fn serialize(self, buffer: &mut [u8]) -> Result { 16 | if buffer.len() < self.len() { 17 | return Err(()); 18 | } 19 | buffer[..self.len()].copy_from_slice(self); 20 | Ok(self.len()) 21 | } 22 | } 23 | 24 | impl ToPayload for &str { 25 | type Error = (); 26 | 27 | fn serialize(self, buffer: &mut [u8]) -> Result { 28 | self.as_bytes().serialize(buffer) 29 | } 30 | } 31 | 32 | impl ToPayload for &[u8; N] { 33 | type Error = (); 34 | 35 | fn serialize(self, buffer: &mut [u8]) -> Result { 36 | (&self[..]).serialize(buffer) 37 | } 38 | } 39 | 40 | impl Result> ToPayload for F { 41 | type Error = E; 42 | fn serialize(self, buffer: &mut [u8]) -> Result { 43 | self(buffer) 44 | } 45 | } 46 | 47 | /// Builder pattern for generating MQTT publications. 48 | /// 49 | /// # Note 50 | /// By default, messages are constructed with: 51 | /// * A QoS setting of [QoS::AtMostOnce] 52 | /// * No properties 53 | /// * No destination topic 54 | /// * Retention set to [Retain::NotRetained] 55 | /// 56 | /// It is expected that the user provide a topic either by directly specifying a publication topic 57 | /// in [Publication::topic], or by parsing a topic from the [Property::ResponseTopic] property 58 | /// contained within received properties by using the [Publication::reply] API. 59 | pub struct Publication<'a, P> { 60 | pub(crate) topic: &'a str, 61 | pub(crate) properties: Properties<'a>, 62 | pub(crate) qos: QoS, 63 | pub(crate) payload: P, 64 | pub(crate) retain: Retain, 65 | } 66 | 67 | impl<'a, P> Publication<'a, P> { 68 | /// Generate the publication as a reply to some other received message. 69 | /// 70 | /// # Note 71 | /// The received message properties are parsed for both [Property::CorrelationData] and 72 | /// [Property::ResponseTopic]. 73 | /// 74 | /// * If correlation data is found, it is automatically appended to the 75 | /// publication properties. 76 | /// 77 | /// * If a response topic is identified, the message topic will be 78 | /// configured for it, which will override the default topic. 79 | pub fn respond( 80 | default_topic: Option<&'a str>, 81 | received_properties: &'a Properties<'a>, 82 | payload: P, 83 | ) -> Result { 84 | let response_topic = received_properties 85 | .into_iter() 86 | .flatten() 87 | .find_map(|p| { 88 | if let Property::ResponseTopic(topic) = p { 89 | Some(topic.0) 90 | } else { 91 | None 92 | } 93 | }) 94 | .or(default_topic) 95 | .ok_or(ProtocolError::NoTopic)?; 96 | 97 | let publication = Self::new(response_topic, payload); 98 | 99 | for p in received_properties.into_iter().flatten() { 100 | if let Property::CorrelationData(data) = p { 101 | return Ok(publication.correlate(data.0)); 102 | } 103 | } 104 | Ok(publication) 105 | } 106 | 107 | /// Construct a new publication with a payload. 108 | pub fn new(topic: &'a str, payload: P) -> Self { 109 | Self { 110 | payload, 111 | qos: QoS::AtMostOnce, 112 | topic, 113 | properties: Properties::Slice(&[]), 114 | retain: Retain::NotRetained, 115 | } 116 | } 117 | 118 | /// Specify the [QoS] of the publication. By default, the QoS is set to [QoS::AtMostOnce]. 119 | pub fn qos(mut self, qos: QoS) -> Self { 120 | self.qos = qos; 121 | self 122 | } 123 | 124 | /// Specify that this message should be [Retain::Retained]. 125 | pub fn retain(mut self) -> Self { 126 | self.retain = Retain::Retained; 127 | self 128 | } 129 | 130 | /// Specify properties associated with this publication. 131 | pub fn properties(mut self, properties: &'a [Property<'a>]) -> Self { 132 | self.properties = match self.properties { 133 | Properties::Slice(_) => Properties::Slice(properties), 134 | Properties::CorrelatedSlice { correlation, .. } => Properties::CorrelatedSlice { 135 | correlation, 136 | properties, 137 | }, 138 | _ => unimplemented!(), 139 | }; 140 | self 141 | } 142 | 143 | /// Include correlation data to the message 144 | /// 145 | /// # Note 146 | /// This will override any existing correlation data in the message. 147 | /// 148 | /// # Args 149 | /// * `data` - The data composing the correlation data. 150 | pub fn correlate(mut self, data: &'a [u8]) -> Self { 151 | self.properties = match self.properties { 152 | Properties::Slice(properties) | Properties::CorrelatedSlice { properties, .. } => { 153 | Properties::CorrelatedSlice { 154 | properties, 155 | correlation: Property::CorrelationData(BinaryData(data)), 156 | } 157 | } 158 | _ => unimplemented!(), 159 | }; 160 | 161 | self 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/reason_codes.rs: -------------------------------------------------------------------------------- 1 | use crate::ProtocolError; 2 | use num_enum::{FromPrimitive, IntoPrimitive}; 3 | 4 | /// MQTTv5-defined codes that may be returned in response to control packets. 5 | #[derive(PartialEq, PartialOrd, Copy, Clone, Debug, FromPrimitive, IntoPrimitive)] 6 | #[repr(u8)] 7 | pub enum ReasonCode { 8 | Success = 0x00, 9 | GrantedQos1 = 0x01, 10 | GrantedQos2 = 0x02, 11 | DisconnectWithWill = 0x04, 12 | NoMatchingSubscribers = 0x10, 13 | NoSubscriptionExisted = 0x11, 14 | ContinueAuthentication = 0x18, 15 | Reuathenticate = 0x19, 16 | 17 | UnspecifiedError = 0x80, 18 | MalformedPacket = 0x81, 19 | ProtocolError = 0x82, 20 | ImplementationError = 0x83, 21 | UnsupportedProtocol = 0x84, 22 | ClientIdentifierInvalid = 0x85, 23 | BadUsernameOrPassword = 0x86, 24 | NotAuthorized = 0x87, 25 | ServerUnavailable = 0x88, 26 | ServerBusy = 0x89, 27 | BadAuthMethod = 0x8c, 28 | KeepAliveTimeout = 0x8d, 29 | SessionTakenOver = 0x8e, 30 | TopicFilterInvalid = 0x8f, 31 | TopicNameInvalid = 0x90, 32 | PacketIdInUse = 0x91, 33 | PacketIdNotFound = 0x92, 34 | ReceiveMaxExceeded = 0x93, 35 | TopicAliasINvalid = 0x94, 36 | PacketTooLarge = 0x95, 37 | MessageRateTooHigh = 0x96, 38 | QuotaExceeded = 0x97, 39 | AdministrativeAction = 0x98, 40 | PayloadFormatInvalid = 0x99, 41 | RetainNotSupported = 0x9A, 42 | QosNotSupported = 0x9b, 43 | UseAnotherServer = 0x9c, 44 | ServerMoved = 0x9d, 45 | SharedSubscriptionsNotSupported = 0x9e, 46 | ConnectionRateExceeded = 0x9f, 47 | MaximumConnectTime = 0xa0, 48 | SubscriptionIdentifiersNotSupported = 0xa1, 49 | WildcardSubscriptionsNotSupported = 0xa2, 50 | 51 | /// The reason code is not one of the documented MQTT reason codes. 52 | #[num_enum(default)] 53 | Unknown = 0xFF, 54 | } 55 | 56 | impl serde::Serialize for ReasonCode { 57 | fn serialize(&self, serializer: S) -> Result { 58 | serializer.serialize_u8(self.into()) 59 | } 60 | } 61 | 62 | struct ReasonCodeVisitor; 63 | 64 | impl serde::de::Visitor<'_> for ReasonCodeVisitor { 65 | type Value = ReasonCode; 66 | 67 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 68 | write!(formatter, "ReasonCode") 69 | } 70 | 71 | fn visit_u8(self, data: u8) -> Result { 72 | Ok(ReasonCode::from(data)) 73 | } 74 | } 75 | 76 | impl<'de> serde::de::Deserialize<'de> for ReasonCode { 77 | fn deserialize>(deserializer: D) -> Result { 78 | deserializer.deserialize_u8(ReasonCodeVisitor) 79 | } 80 | } 81 | 82 | impl From<&ReasonCode> for u8 { 83 | fn from(code: &ReasonCode) -> u8 { 84 | (*code).into() 85 | } 86 | } 87 | 88 | impl> From> for ReasonCode { 89 | fn from(result: Result) -> ReasonCode { 90 | match result { 91 | Ok(_) => ReasonCode::Success, 92 | Err(code) => code.into(), 93 | } 94 | } 95 | } 96 | 97 | impl From for Result<(), ReasonCode> { 98 | fn from(code: ReasonCode) -> Result<(), ReasonCode> { 99 | if code.success() { 100 | Ok(()) 101 | } else { 102 | Err(code) 103 | } 104 | } 105 | } 106 | 107 | impl ReasonCode { 108 | pub fn as_result(&self) -> Result<(), ProtocolError> { 109 | let result: Result<(), ReasonCode> = (*self).into(); 110 | result?; 111 | Ok(()) 112 | } 113 | 114 | pub fn success(&self) -> bool { 115 | let value: u8 = self.into(); 116 | value < 0x80 117 | } 118 | 119 | pub fn failed(&self) -> bool { 120 | !self.success() 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/republication.rs: -------------------------------------------------------------------------------- 1 | use crate::packets::PubRel; 2 | use crate::Error; 3 | use crate::{ 4 | network_manager::InterfaceHolder, reason_codes::ReasonCode, ring_buffer::RingBuffer, 5 | ProtocolError, 6 | }; 7 | use core::convert::TryInto; 8 | use heapless::Deque; 9 | 10 | use embedded_nal::TcpClientStack; 11 | 12 | pub(crate) struct MqttHeader { 13 | pub len: usize, 14 | pub packet_id: u16, 15 | } 16 | 17 | pub(crate) struct RepublicationBuffer<'a> { 18 | publish_buffer: RingBuffer<'a>, 19 | pending_pub: Deque<(u16, usize), 10>, 20 | pending_pubrel: Deque<(u16, ReasonCode), 10>, 21 | republish_index: Option, 22 | pubrel_republish_index: Option, 23 | max_tx_size: usize, 24 | } 25 | 26 | impl<'a> RepublicationBuffer<'a> { 27 | pub fn new(buf: &'a mut [u8], max_tx_size: usize) -> Self { 28 | Self { 29 | publish_buffer: RingBuffer::new(buf), 30 | pending_pubrel: Deque::new(), 31 | pending_pub: Deque::new(), 32 | republish_index: None, 33 | pubrel_republish_index: None, 34 | max_tx_size, 35 | } 36 | } 37 | 38 | pub fn clear(&mut self) { 39 | self.republish_index.take(); 40 | self.pubrel_republish_index.take(); 41 | self.publish_buffer.clear(); 42 | self.pending_pubrel.clear(); 43 | self.pending_pub.clear(); 44 | } 45 | 46 | pub fn reset(&mut self) { 47 | self.republish_index.replace(0); 48 | self.pubrel_republish_index.replace(0); 49 | 50 | // If there's nothing to republish, clear out our states. 51 | if self.pending_pubrel.is_empty() { 52 | self.pubrel_republish_index.take(); 53 | } 54 | 55 | if self.publish_buffer.len() == 0 { 56 | self.republish_index.take(); 57 | } 58 | } 59 | 60 | pub fn pop_publish(&mut self, id: u16) -> Result<(), ProtocolError> { 61 | let (_, header) = self.probe_header(0).ok_or(ProtocolError::BadIdentifier)?; 62 | if header.packet_id != id { 63 | return Err(ProtocolError::BadIdentifier); 64 | } 65 | 66 | if let Some(index) = self.republish_index.take() { 67 | if index > 1 { 68 | self.republish_index.replace(index - 1); 69 | } 70 | } 71 | 72 | self.publish_buffer.pop(header.len); 73 | self.pending_pub.pop_front(); 74 | self.republish_index = self.republish_index.map(|index| index - 1); 75 | Ok(()) 76 | } 77 | 78 | pub fn push_publish(&mut self, id: u16, packet: &[u8]) -> Result<(), ProtocolError> { 79 | if self.publish_buffer.push_slice(packet).is_some() { 80 | return Err(ProtocolError::BufferSize); 81 | } 82 | 83 | self.pending_pub 84 | .push_back((id, packet.len())) 85 | .map_err(|_| ProtocolError::BufferSize)?; 86 | 87 | Ok(()) 88 | } 89 | 90 | pub fn pop_pubrel(&mut self, id: u16) -> Result<(), ProtocolError> { 91 | // We always have to pop from the front of the vector to enforce FIFO characteristics. 92 | let Some((pending_id, _)) = self.pending_pubrel.front() else { 93 | return Err(ProtocolError::UnexpectedPacket); 94 | }; 95 | 96 | if *pending_id != id { 97 | return Err(ProtocolError::BadIdentifier); 98 | } 99 | 100 | // Now that we received the PubComp for this PubRel, we can remove it from our session 101 | // state. We will not need to retransmit this upon reconnection. 102 | self.pending_pubrel.pop_front(); 103 | 104 | if let Some(index) = self.pubrel_republish_index.take() { 105 | if index > 1 { 106 | self.pubrel_republish_index.replace(index - 1); 107 | } 108 | } 109 | 110 | Ok(()) 111 | } 112 | 113 | fn probe_header(&mut self, index: usize) -> Option<(usize, MqttHeader)> { 114 | let offset = self 115 | .pending_pub 116 | .iter() 117 | .take(index) 118 | .map(|(_, len)| len) 119 | .sum(); 120 | let (id, len) = self.pending_pub.iter().nth(index).unwrap(); 121 | 122 | Some(( 123 | offset, 124 | MqttHeader { 125 | len: *len, 126 | packet_id: *id, 127 | }, 128 | )) 129 | } 130 | 131 | pub fn push_pubrel(&mut self, pubrel: &PubRel) -> Result<(), ProtocolError> { 132 | self.pending_pubrel 133 | .push_back((pubrel.packet_id, pubrel.reason.code())) 134 | .map_err(|_| ProtocolError::BufferSize)?; 135 | 136 | Ok(()) 137 | } 138 | 139 | pub fn pending_transactions(&self) -> bool { 140 | // If we have publications or pubrels pending, there's message transactions 141 | // underway 142 | self.publish_buffer.len() > 0 || !self.pending_pubrel.is_empty() 143 | } 144 | 145 | pub fn can_publish(&self) -> bool { 146 | self.publish_buffer.remainder() >= self.max_tx_size 147 | } 148 | 149 | pub fn next_republication( 150 | &mut self, 151 | net: &mut InterfaceHolder<'_, T>, 152 | ) -> Result> { 153 | // Finish off any pending pubrels 154 | if let Some(index) = self.pubrel_republish_index { 155 | let (packet_id, code) = self.pending_pubrel.iter().nth(index).unwrap(); 156 | let pubrel = PubRel { 157 | packet_id: *packet_id, 158 | reason: (*code).into(), 159 | }; 160 | 161 | net.send_packet(&pubrel)?; 162 | 163 | if index + 1 < self.pending_pubrel.len() { 164 | self.pubrel_republish_index.replace(index + 1); 165 | } else { 166 | self.pubrel_republish_index.take(); 167 | } 168 | 169 | return Ok(true); 170 | } 171 | 172 | if let Some(index) = self.republish_index { 173 | let (offset, header) = self.probe_header(index).unwrap(); 174 | 175 | let (head, tail) = self.publish_buffer.slices_mut(offset, header.len); 176 | 177 | // Set the dup flag on the republication 178 | head[0] |= 1 << 3; 179 | 180 | net.write_multipart(head, tail)?; 181 | 182 | if offset + header.len < self.publish_buffer.len() { 183 | self.republish_index.replace(index + 1); 184 | } else { 185 | self.republish_index.take(); 186 | } 187 | 188 | return Ok(true); 189 | } 190 | 191 | Ok(false) 192 | } 193 | 194 | pub fn is_republishing(&self) -> bool { 195 | self.republish_index.is_some() || self.pubrel_republish_index.is_some() 196 | } 197 | 198 | pub fn max_send_quota(&self) -> u16 { 199 | let pubrel_capacity = self.pending_pubrel.capacity(); 200 | let publish_length_capacity = self.pending_pub.capacity(); 201 | 202 | pubrel_capacity 203 | .min(publish_length_capacity) 204 | .try_into() 205 | .unwrap_or(u16::MAX) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/ring_buffer.rs: -------------------------------------------------------------------------------- 1 | pub(crate) struct RingBuffer<'a> { 2 | // Underlying storage data. 3 | data: &'a mut [u8], 4 | 5 | // Pointer to the first index of data, inclusive 6 | head: usize, 7 | 8 | // The number of elements stored. 9 | count: usize, 10 | } 11 | 12 | impl<'a> RingBuffer<'a> { 13 | pub fn new(buffer: &'a mut [u8]) -> Self { 14 | Self { 15 | data: buffer, 16 | head: 0, 17 | count: 0, 18 | } 19 | } 20 | 21 | pub fn clear(&mut self) { 22 | self.head = 0; 23 | self.count = 0; 24 | } 25 | 26 | pub fn push_slice<'b>(&mut self, data: &'b [u8]) -> Option<&'b [u8]> { 27 | if self.data.len() - self.count < data.len() { 28 | return Some(data); 29 | } 30 | 31 | let tail = (self.head + self.count).rem_euclid(self.data.len()); 32 | 33 | if tail + data.len() <= self.data.len() { 34 | // Easy mode: Copy directly into tail 35 | self.data[tail..][..data.len()].copy_from_slice(data); 36 | } else { 37 | // Split the buffer, writing the first N bytes to the tail, remainder to start of buffer 38 | // (wrap) 39 | let tail_len = self.data.len() - tail; 40 | self.data[tail..][..tail_len].copy_from_slice(&data[..tail_len]); 41 | 42 | let remainder = data.len() - tail_len; 43 | self.data[..remainder].copy_from_slice(&data[tail_len..]) 44 | } 45 | 46 | self.count += data.len(); 47 | 48 | None 49 | } 50 | 51 | pub fn slices_mut(&mut self, offset: usize, max_len: usize) -> (&mut [u8], &mut [u8]) { 52 | let start = (self.head + offset).rem_euclid(self.data.len()); 53 | let len = max_len.min(self.count); 54 | 55 | let end = (start + len).rem_euclid(self.data.len()); 56 | 57 | // When length is zero, the start and end indices are indentical because of wrap. As 58 | // such, we have to check for this condition. 59 | if len == 0 { 60 | (&mut [], &mut []) 61 | } else if start < end { 62 | (&mut self.data[start..end], &mut []) 63 | } else { 64 | let (front, back) = self.data.split_at_mut(end); 65 | (&mut back[(start - end)..], &mut front[..end]) 66 | } 67 | } 68 | 69 | pub fn pop(&mut self, len: usize) { 70 | let len = len.min(self.count); 71 | 72 | self.head = (self.head + len).rem_euclid(self.data.len()); 73 | self.count -= len; 74 | } 75 | 76 | /// Determine the number of remaining bytes in the buffer 77 | pub fn remainder(&self) -> usize { 78 | self.data.len() - self.count 79 | } 80 | 81 | pub fn len(&self) -> usize { 82 | self.count 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use super::RingBuffer; 89 | 90 | #[test] 91 | fn empty() { 92 | let mut data = [0; 256]; 93 | let mut buffer = RingBuffer::new(&mut data); 94 | 95 | assert!(buffer.len() == 0); 96 | assert!(buffer.remainder() == 256); 97 | 98 | // No data in the head or tail 99 | let (head, tail) = buffer.slices_mut(0, buffer.len()); 100 | assert!(head.is_empty()); 101 | assert!(tail.is_empty()); 102 | } 103 | 104 | #[test] 105 | fn push() { 106 | let mut backing = [0; 256]; 107 | let mut buffer = RingBuffer::new(&mut backing); 108 | 109 | let data = [1, 2, 3]; 110 | assert!(buffer.push_slice(&data).is_none()); 111 | 112 | assert!(buffer.len() == 3); 113 | assert!(buffer.remainder() == 253); 114 | 115 | let (head, tail) = buffer.slices_mut(0, buffer.len()); 116 | assert!(head == [1, 2, 3]); 117 | assert!(tail.is_empty()); 118 | 119 | assert!(buffer.push_slice(&data).is_none()); 120 | let (head, tail) = buffer.slices_mut(0, buffer.len()); 121 | assert!(head == [1, 2, 3, 1, 2, 3]); 122 | assert!(tail.is_empty()); 123 | } 124 | 125 | #[test] 126 | fn push_overflow() { 127 | let mut backing = [0; 256]; 128 | let mut buffer = RingBuffer::new(&mut backing); 129 | 130 | let data = [0; 256]; 131 | assert!(buffer.push_slice(&data).is_none()); 132 | 133 | assert!(buffer.len() == 256); 134 | assert!(buffer.remainder() == 0); 135 | 136 | assert!(buffer.push_slice(&[1]).is_some()); 137 | assert!(buffer.len() == 256); 138 | assert!(buffer.remainder() == 0); 139 | 140 | let (head, tail) = buffer.slices_mut(0, buffer.len()); 141 | assert!(head == [0; 256]); 142 | assert!(tail.is_empty()); 143 | } 144 | 145 | #[test] 146 | fn pop() { 147 | let mut backing = [0; 256]; 148 | let mut buffer = RingBuffer::new(&mut backing); 149 | 150 | let data = [1, 2, 3]; 151 | assert!(buffer.push_slice(&data).is_none()); 152 | 153 | assert!(buffer.len() == 3); 154 | buffer.pop(1); 155 | assert!(buffer.len() == 2); 156 | buffer.pop(1); 157 | assert!(buffer.len() == 1); 158 | 159 | buffer.pop(1); 160 | assert!(buffer.len() == 0); 161 | } 162 | 163 | #[test] 164 | fn pop_underflow() { 165 | let mut backing = [0; 256]; 166 | let mut buffer = RingBuffer::new(&mut backing); 167 | 168 | let data = [1, 2, 3]; 169 | assert!(buffer.push_slice(&data).is_none()); 170 | 171 | buffer.pop(1); 172 | assert!(buffer.len() == 2); 173 | 174 | buffer.pop(10); 175 | assert!(buffer.len() == 0); 176 | } 177 | 178 | #[test] 179 | fn pop_wrap() { 180 | let mut backing = [0; 256]; 181 | let mut buffer = RingBuffer::new(&mut backing); 182 | 183 | // Fill the buffer 184 | let data = [0; 256]; 185 | assert!(buffer.push_slice(&data).is_none()); 186 | 187 | // Pop a few bytes to cause the buffer to wrap. 188 | buffer.pop(10); 189 | 190 | assert!(buffer.push_slice(&[1, 2, 3]).is_none()); 191 | 192 | let (head, tail) = buffer.slices_mut(0, buffer.len()); 193 | assert!(head == [0; 246]); 194 | assert!(tail == [1, 2, 3]); 195 | 196 | // Pop over the boundary 197 | buffer.pop(247); 198 | let (head, tail) = buffer.slices_mut(0, buffer.len()); 199 | assert!(head == [2, 3]); 200 | assert!(tail.is_empty()); 201 | } 202 | 203 | #[test] 204 | fn push_wrap() { 205 | let mut backing = [0; 256]; 206 | let mut buffer = RingBuffer::new(&mut backing); 207 | 208 | // Fill the buffer 209 | let data = [0; 255]; 210 | assert!(buffer.push_slice(&data).is_none()); 211 | 212 | // Pop a few bytes from the front to free up space 213 | buffer.pop(10); 214 | 215 | // Push across the boundary 216 | assert!(buffer.push_slice(&[1, 2, 3]).is_none()); 217 | let (head, tail) = buffer.slices_mut(0, buffer.len()); 218 | assert!(*head.last().unwrap() == 1); 219 | assert!(tail == [2, 3]); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/ser/mod.rs: -------------------------------------------------------------------------------- 1 | //! Custom MQTT message serializer 2 | //! 3 | //! # Design 4 | //! This serializer handles serializing MQTT packets. 5 | //! 6 | //! This serializer does _not_ assume MQTT packet format. It will encode data linearly into a 7 | //! buffer and converts all data types to big-endian byte notation. 8 | //! 9 | //! The serializer reserves the first number of bytes for the MQTT fixed header, which is filled 10 | //! out after the rest of the packet has been serialized. 11 | //! 12 | //! # Limitations 13 | //! It is the responsibility of the user to handle prefixing necessary lengths and types on any 14 | //! MQTT-specified datatypes, such as "Properties", "Binary Data", and "UTF-8 Encoded Strings". 15 | //! 16 | //! # Supported Data Types 17 | //! 18 | //! Basic data types are supported, including: 19 | //! * Signed & Unsigned integers 20 | //! * Booleans 21 | //! * Strings 22 | //! * Bytes 23 | //! * Options (Some is encoded as the contained contents, None is not encoded as any data) 24 | //! * Sequences 25 | //! * Tuples 26 | //! * Structs 27 | //! 28 | //! Other types are explicitly not implemented and there is no plan to implement them. 29 | use crate::varint::VarintBuffer; 30 | use crate::{ 31 | message_types::{ControlPacket, MessageType}, 32 | packets::Pub, 33 | }; 34 | use bit_field::BitField; 35 | use serde::Serialize; 36 | use varint_rs::VarintWriter; 37 | 38 | /// The maximum size of the MQTT fixed header in bytes. This accounts for the header byte and the 39 | /// maximum variable integer length. 40 | const MAX_FIXED_HEADER_SIZE: usize = 5; 41 | 42 | /// Errors that result from the serialization process 43 | #[derive(Debug, Copy, Clone, PartialEq)] 44 | #[non_exhaustive] 45 | pub enum Error { 46 | /// The provided memory buffer did not have enough space to serialize into. 47 | InsufficientMemory, 48 | 49 | /// A custom serialization error occurred. 50 | Custom, 51 | } 52 | 53 | #[derive(Debug, Copy, Clone, PartialEq)] 54 | pub enum PubError { 55 | Error(Error), 56 | Other(E), 57 | } 58 | 59 | impl serde::ser::StdError for Error {} 60 | 61 | impl serde::ser::Error for Error { 62 | fn custom(_msg: T) -> Self { 63 | crate::error!("{}", _msg); 64 | Error::Custom 65 | } 66 | } 67 | 68 | impl core::fmt::Display for Error { 69 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 70 | write!( 71 | f, 72 | "{}", 73 | match self { 74 | Error::Custom => "Custom deserialization error", 75 | Error::InsufficientMemory => "Not enough data to encode the packet", 76 | } 77 | ) 78 | } 79 | } 80 | 81 | /// A structure to serialize MQTT data into a buffer. 82 | pub struct MqttSerializer<'a> { 83 | buf: &'a mut [u8], 84 | index: usize, 85 | with_header: bool, 86 | } 87 | 88 | impl<'a> MqttSerializer<'a> { 89 | /// Construct a new serializer. 90 | /// 91 | /// # Args 92 | /// * `buf` - The location to serialize data into. 93 | pub fn new(buf: &'a mut [u8]) -> Self { 94 | Self { 95 | buf, 96 | index: MAX_FIXED_HEADER_SIZE, 97 | with_header: true, 98 | } 99 | } 100 | 101 | pub fn new_without_header(buf: &'a mut [u8]) -> Self { 102 | Self { 103 | buf, 104 | index: 0, 105 | with_header: false, 106 | } 107 | } 108 | 109 | /// Immediately finish the packet and return the contents. 110 | /// 111 | /// # Note 112 | /// This does not append any MQTT headers. 113 | pub fn finish(self) -> &'a mut [u8] { 114 | assert!(!self.with_header); 115 | 116 | &mut self.buf[..self.index] 117 | } 118 | 119 | /// Encode an MQTT control packet into a buffer. 120 | /// 121 | /// # Args 122 | /// * `buf` - The buffer to encode data into. 123 | /// * `packet` - The packet to encode. 124 | pub fn to_buffer_meta( 125 | buf: &'a mut [u8], 126 | packet: &T, 127 | ) -> Result<(usize, &'a [u8]), Error> { 128 | let mut serializer = Self::new(buf); 129 | packet.serialize(&mut serializer)?; 130 | let (offset, packet) = serializer.finalize(T::MESSAGE_TYPE, packet.fixed_header_flags())?; 131 | Ok((offset, packet)) 132 | } 133 | 134 | pub fn pub_to_buffer_meta( 135 | buf: &'a mut [u8], 136 | pub_packet: Pub<'_, P>, 137 | ) -> Result<(usize, &'a [u8]), PubError> { 138 | let mut serializer = crate::ser::MqttSerializer::new(buf); 139 | pub_packet 140 | .serialize(&mut serializer) 141 | .map_err(PubError::Error)?; 142 | 143 | // Next, serialize the payload into the remaining buffer 144 | let flags = pub_packet.fixed_header_flags(); 145 | let len = pub_packet 146 | .payload 147 | .serialize(serializer.remainder()) 148 | .map_err(PubError::Other)?; 149 | serializer.commit(len).map_err(PubError::Error)?; 150 | 151 | // Finally, finish the packet and send it. 152 | let (offset, packet) = serializer 153 | .finalize(MessageType::Publish, flags) 154 | .map_err(PubError::Error)?; 155 | Ok((offset, packet)) 156 | } 157 | 158 | pub fn pub_to_buffer( 159 | buf: &'a mut [u8], 160 | pub_packet: Pub<'_, P>, 161 | ) -> Result<&'a [u8], PubError> { 162 | let (_, packet) = Self::pub_to_buffer_meta(buf, pub_packet)?; 163 | Ok(packet) 164 | } 165 | 166 | /// Encode an MQTT control packet into a buffer. 167 | /// 168 | /// # Args 169 | /// * `buf` - The buffer to encode data into. 170 | /// * `packet` - The packet to encode. 171 | pub fn to_buffer( 172 | buf: &'a mut [u8], 173 | packet: &T, 174 | ) -> Result<&'a [u8], Error> { 175 | let (_, packet) = Self::to_buffer_meta(buf, packet)?; 176 | Ok(packet) 177 | } 178 | 179 | /// Finalize the packet, prepending the MQTT fixed header. 180 | /// 181 | /// # Args 182 | /// * `typ` - The MQTT message type of the encoded packet. 183 | /// * `flags` - The MQTT flags associated with the packet. 184 | /// 185 | /// # Returns 186 | /// A slice representing the serialized packet. 187 | pub fn finalize(self, typ: MessageType, flags: u8) -> Result<(usize, &'a [u8]), Error> { 188 | let len = self.index - MAX_FIXED_HEADER_SIZE; 189 | 190 | let mut buffer = VarintBuffer::new(); 191 | buffer 192 | .write_u32_varint(len as u32) 193 | .map_err(|_| Error::InsufficientMemory)?; 194 | 195 | // Write the remaining packet length. 196 | self.buf[MAX_FIXED_HEADER_SIZE - buffer.data.len()..MAX_FIXED_HEADER_SIZE] 197 | .copy_from_slice(&buffer.data); 198 | 199 | // Write the header 200 | let header: u8 = *0u8.set_bits(4..8, typ as u8).set_bits(0..4, flags); 201 | self.buf[MAX_FIXED_HEADER_SIZE - buffer.data.len() - 1] = header; 202 | 203 | let offset = MAX_FIXED_HEADER_SIZE - buffer.data.len() - 1; 204 | Ok((offset, &self.buf[offset..self.index])) 205 | } 206 | 207 | /// Write data into the packet. 208 | /// 209 | /// # Args 210 | /// * `data` - The data to push to the current head of the packet. 211 | pub fn push_bytes(&mut self, data: &[u8]) -> Result<(), Error> { 212 | crate::trace!("Pushing {:?}", data); 213 | if self.buf.len() - self.index < data.len() { 214 | return Err(Error::InsufficientMemory); 215 | } 216 | 217 | self.buf[self.index..][..data.len()].copy_from_slice(data); 218 | self.index += data.len(); 219 | 220 | Ok(()) 221 | } 222 | 223 | /// Push a byte to the tail of the packet. 224 | /// 225 | /// # Args 226 | /// * `byte` - The byte to write to the tail. 227 | pub fn push(&mut self, byte: u8) -> Result<(), Error> { 228 | if self.buf.len() - self.index < 1 { 229 | return Err(Error::InsufficientMemory); 230 | } 231 | self.buf[self.index] = byte; 232 | self.index += 1; 233 | 234 | Ok(()) 235 | } 236 | 237 | /// Get the remaining buffer to serialize into directly. 238 | /// 239 | /// # Note 240 | /// You must call `commit` after serializing. 241 | pub fn remainder(&mut self) -> &mut [u8] { 242 | &mut self.buf[self.index..] 243 | } 244 | 245 | /// Commit previously-serialized data into the buffer. 246 | /// 247 | /// # Args 248 | /// * `len` - The number of bytes serialized. 249 | pub fn commit(&mut self, len: usize) -> Result<(), Error> { 250 | if self.buf.len() < (self.index + len) { 251 | return Err(Error::InsufficientMemory); 252 | } 253 | 254 | self.index += len; 255 | Ok(()) 256 | } 257 | } 258 | 259 | impl serde::Serializer for &mut MqttSerializer<'_> { 260 | type Ok = (); 261 | type Error = Error; 262 | 263 | type SerializeSeq = Self; 264 | type SerializeTuple = Self; 265 | type SerializeTupleStruct = Self; 266 | type SerializeTupleVariant = Self; 267 | type SerializeMap = Self; 268 | type SerializeStruct = Self; 269 | type SerializeStructVariant = Self; 270 | 271 | fn serialize_bool(self, v: bool) -> Result { 272 | self.push(v as u8) 273 | } 274 | 275 | fn serialize_i8(self, v: i8) -> Result { 276 | self.push(v as u8) 277 | } 278 | 279 | fn serialize_i16(self, v: i16) -> Result { 280 | self.push_bytes(&v.to_be_bytes()) 281 | } 282 | 283 | fn serialize_i32(self, v: i32) -> Result { 284 | self.push_bytes(&v.to_be_bytes()) 285 | } 286 | 287 | fn serialize_i64(self, v: i64) -> Result { 288 | self.push_bytes(&v.to_be_bytes()) 289 | } 290 | 291 | fn serialize_u8(self, v: u8) -> Result { 292 | self.push(v) 293 | } 294 | 295 | fn serialize_u16(self, v: u16) -> Result { 296 | self.push_bytes(&v.to_be_bytes()) 297 | } 298 | 299 | fn serialize_u32(self, v: u32) -> Result { 300 | self.push_bytes(&v.to_be_bytes()) 301 | } 302 | 303 | fn serialize_u64(self, v: u64) -> Result { 304 | self.push_bytes(&v.to_be_bytes()) 305 | } 306 | 307 | fn serialize_str(self, v: &str) -> Result { 308 | self.serialize_bytes(v.as_bytes()) 309 | } 310 | 311 | fn serialize_bytes(self, v: &[u8]) -> Result { 312 | self.push_bytes(v) 313 | } 314 | 315 | fn serialize_none(self) -> Result<(), Error> { 316 | Ok(()) 317 | } 318 | 319 | fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> { 320 | Ok(()) 321 | } 322 | 323 | fn serialize_newtype_struct( 324 | self, 325 | _name: &'static str, 326 | value: &T, 327 | ) -> Result<(), Error> { 328 | value.serialize(self) 329 | } 330 | 331 | fn serialize_some(self, value: &T) -> Result<(), Error> { 332 | value.serialize(self) 333 | } 334 | 335 | fn serialize_seq(self, _len: Option) -> Result { 336 | Ok(self) 337 | } 338 | 339 | fn serialize_tuple(self, _len: usize) -> Result { 340 | Ok(self) 341 | } 342 | 343 | fn serialize_struct( 344 | self, 345 | _name: &'static str, 346 | _len: usize, 347 | ) -> Result { 348 | Ok(self) 349 | } 350 | 351 | fn serialize_char(self, _v: char) -> Result { 352 | unimplemented!() 353 | } 354 | 355 | fn serialize_unit(self) -> Result<(), Error> { 356 | unimplemented!() 357 | } 358 | 359 | fn serialize_unit_variant( 360 | self, 361 | _name: &'static str, 362 | _variant_index: u32, 363 | _variant: &'static str, 364 | ) -> Result<(), Error> { 365 | unimplemented!() 366 | } 367 | 368 | fn serialize_newtype_variant( 369 | self, 370 | _name: &'static str, 371 | _variant_index: u32, 372 | _variant: &'static str, 373 | _value: &T, 374 | ) -> Result<(), Error> { 375 | unimplemented!() 376 | } 377 | 378 | fn serialize_tuple_struct( 379 | self, 380 | _name: &'static str, 381 | _len: usize, 382 | ) -> Result { 383 | unimplemented!() 384 | } 385 | 386 | fn serialize_tuple_variant( 387 | self, 388 | _name: &'static str, 389 | _variant_index: u32, 390 | _variant: &'static str, 391 | _len: usize, 392 | ) -> Result { 393 | unimplemented!() 394 | } 395 | 396 | fn serialize_map(self, _len: Option) -> Result { 397 | unimplemented!() 398 | } 399 | 400 | fn serialize_struct_variant( 401 | self, 402 | _name: &'static str, 403 | _variant_index: u32, 404 | _variant: &'static str, 405 | _len: usize, 406 | ) -> Result { 407 | unimplemented!() 408 | } 409 | 410 | fn collect_str(self, _value: &T) -> Result { 411 | unimplemented!() 412 | } 413 | 414 | fn serialize_f32(self, _v: f32) -> Result { 415 | unimplemented!() 416 | } 417 | 418 | fn serialize_f64(self, _v: f64) -> Result { 419 | unimplemented!() 420 | } 421 | } 422 | 423 | impl serde::ser::SerializeStruct for &mut MqttSerializer<'_> { 424 | type Ok = (); 425 | type Error = Error; 426 | 427 | fn serialize_field( 428 | &mut self, 429 | _key: &'static str, 430 | value: &T, 431 | ) -> Result<(), Error> { 432 | value.serialize(&mut **self) 433 | } 434 | 435 | fn end(self) -> Result<(), Error> { 436 | Ok(()) 437 | } 438 | } 439 | 440 | impl serde::ser::SerializeSeq for &mut MqttSerializer<'_> { 441 | type Ok = (); 442 | type Error = Error; 443 | 444 | fn serialize_element(&mut self, value: &T) -> Result<(), Error> { 445 | value.serialize(&mut **self) 446 | } 447 | 448 | fn end(self) -> Result<(), Error> { 449 | Ok(()) 450 | } 451 | } 452 | 453 | impl serde::ser::SerializeTuple for &mut MqttSerializer<'_> { 454 | type Ok = (); 455 | type Error = Error; 456 | 457 | fn serialize_element(&mut self, value: &T) -> Result<(), Error> { 458 | value.serialize(&mut **self) 459 | } 460 | 461 | fn end(self) -> Result<(), Error> { 462 | Ok(()) 463 | } 464 | } 465 | 466 | impl serde::ser::SerializeTupleStruct for &mut MqttSerializer<'_> { 467 | type Ok = (); 468 | type Error = Error; 469 | 470 | fn serialize_field(&mut self, _value: &T) -> Result<(), Error> { 471 | unimplemented!() 472 | } 473 | 474 | fn end(self) -> Result<(), Error> { 475 | unimplemented!() 476 | } 477 | } 478 | 479 | impl serde::ser::SerializeTupleVariant for &mut MqttSerializer<'_> { 480 | type Ok = (); 481 | type Error = Error; 482 | 483 | fn serialize_field(&mut self, _value: &T) -> Result<(), Error> { 484 | unimplemented!() 485 | } 486 | 487 | fn end(self) -> Result<(), Error> { 488 | unimplemented!() 489 | } 490 | } 491 | 492 | impl serde::ser::SerializeMap for &mut MqttSerializer<'_> { 493 | type Ok = (); 494 | type Error = Error; 495 | 496 | fn serialize_key(&mut self, _key: &T) -> Result<(), Error> { 497 | unimplemented!() 498 | } 499 | 500 | fn serialize_value(&mut self, _value: &T) -> Result<(), Error> { 501 | unimplemented!() 502 | } 503 | 504 | fn end(self) -> Result<(), Error> { 505 | unimplemented!() 506 | } 507 | } 508 | 509 | impl serde::ser::SerializeStructVariant for &mut MqttSerializer<'_> { 510 | type Ok = (); 511 | type Error = Error; 512 | 513 | fn serialize_field( 514 | &mut self, 515 | _key: &'static str, 516 | _value: &T, 517 | ) -> Result<(), Error> { 518 | unimplemented!() 519 | } 520 | 521 | fn end(self) -> Result<(), Error> { 522 | unimplemented!() 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /src/session_state.rs: -------------------------------------------------------------------------------- 1 | use crate::packets::PubRel; 2 | /// This module represents the session state of an MQTT communication session. 3 | use crate::{ 4 | network_manager::InterfaceHolder, reason_codes::ReasonCode, republication::RepublicationBuffer, 5 | Error, ProtocolError, QoS, 6 | }; 7 | use embedded_nal::TcpClientStack; 8 | use heapless::{String, Vec}; 9 | 10 | pub(crate) struct SessionState<'a> { 11 | pub(crate) client_id: String<64>, 12 | pub(crate) repub: RepublicationBuffer<'a>, 13 | 14 | /// Represents a list of packet_ids current in use by the server for Publish packets with 15 | /// QoS::ExactlyOnce 16 | pub pending_server_packet_ids: Vec, 17 | 18 | packet_id: u16, 19 | active: bool, 20 | was_reset: bool, 21 | } 22 | 23 | impl<'a> SessionState<'a> { 24 | pub fn new(id: String<64>, buffer: &'a mut [u8], max_tx_size: usize) -> SessionState<'a> { 25 | SessionState { 26 | active: false, 27 | client_id: id, 28 | packet_id: 1, 29 | repub: RepublicationBuffer::new(buffer, max_tx_size), 30 | pending_server_packet_ids: Vec::new(), 31 | was_reset: false, 32 | } 33 | } 34 | 35 | pub fn register_connected(&mut self) { 36 | self.active = true; 37 | 38 | // Reset the republish indices 39 | self.repub.reset(); 40 | } 41 | 42 | pub fn reset(&mut self) { 43 | // Only register a reset if we previously had an active session state. 44 | self.was_reset = self.active; 45 | self.active = false; 46 | self.packet_id = 1; 47 | self.repub.clear(); 48 | self.pending_server_packet_ids.clear(); 49 | } 50 | 51 | /// Check if the session state has been reset. 52 | pub fn was_reset(&mut self) -> bool { 53 | let reset = self.was_reset; 54 | self.was_reset = false; 55 | reset 56 | } 57 | 58 | /// Called when publish with QoS > 0 is called so that we can keep track of acknowledgement. 59 | pub fn handle_publish( 60 | &mut self, 61 | qos: QoS, 62 | id: u16, 63 | packet: &[u8], 64 | ) -> Result<(), ProtocolError> { 65 | // QoS::AtMostOnce requires no additional state tracking. 66 | if qos == QoS::AtMostOnce { 67 | return Ok(()); 68 | } 69 | 70 | self.repub.push_publish(id, packet)?; 71 | 72 | Ok(()) 73 | } 74 | 75 | pub fn remove_packet(&mut self, packet_id: u16) -> Result<(), ProtocolError> { 76 | self.repub.pop_publish(packet_id) 77 | } 78 | 79 | pub fn handle_pubrec(&mut self, pubrel: &PubRel) -> Result<(), ProtocolError> { 80 | self.repub.push_pubrel(pubrel) 81 | } 82 | 83 | pub fn handle_pubcomp(&mut self, id: u16) -> Result<(), ProtocolError> { 84 | self.repub.pop_pubrel(id) 85 | } 86 | 87 | /// Indicates if publish with QoS 1 is possible. 88 | pub fn can_publish(&self, qos: QoS) -> bool { 89 | match qos { 90 | QoS::AtMostOnce => true, 91 | QoS::AtLeastOnce | QoS::ExactlyOnce => self.repub.can_publish(), 92 | } 93 | } 94 | 95 | pub fn handshakes_pending(&self) -> bool { 96 | // If we're exchanging messages with the server still, there's messages pending. 97 | if !self.pending_server_packet_ids.is_empty() { 98 | return false; 99 | } 100 | 101 | // Otherwise, there's handshakes pending if our republication state has something pending. 102 | self.repub.pending_transactions() 103 | } 104 | 105 | /// Indicates if there is present session state available. 106 | pub fn is_present(&self) -> bool { 107 | self.active 108 | } 109 | 110 | pub fn get_packet_identifier(&mut self) -> u16 { 111 | let packet_id = self.packet_id; 112 | 113 | let (result, overflow) = self.packet_id.overflowing_add(1); 114 | 115 | // Packet identifiers must always be non-zero. 116 | if overflow { 117 | self.packet_id = 1; 118 | } else { 119 | self.packet_id = result; 120 | } 121 | 122 | packet_id 123 | } 124 | 125 | /// Get the next message that needs to be republished. 126 | /// 127 | /// # Note 128 | /// Messages provided by this function must be properly transmitted to maintain proper state. 129 | /// 130 | /// # Returns 131 | /// The next message in the sequence that must be republished. 132 | pub fn next_pending_republication( 133 | &mut self, 134 | net: &mut InterfaceHolder<'_, T>, 135 | ) -> Result> { 136 | self.repub.next_republication(net) 137 | } 138 | 139 | /// Check if a server packet ID is in use. 140 | pub fn server_packet_id_in_use(&self, id: u16) -> bool { 141 | self.pending_server_packet_ids 142 | .iter() 143 | .any(|&inflight_id| inflight_id == id) 144 | } 145 | 146 | /// Register a server packet ID as being in use. 147 | pub fn push_server_packet_id(&mut self, id: u16) -> ReasonCode { 148 | if self.pending_server_packet_ids.push(id).is_err() { 149 | ReasonCode::ReceiveMaxExceeded 150 | } else { 151 | ReasonCode::Success 152 | } 153 | } 154 | 155 | /// Mark a server packet ID as no longer in use. 156 | pub fn remove_server_packet_id(&mut self, id: u16) -> ReasonCode { 157 | if let Some(position) = self 158 | .pending_server_packet_ids 159 | .iter() 160 | .position(|&inflight_id| inflight_id == id) 161 | { 162 | self.pending_server_packet_ids.swap_remove(position); 163 | ReasonCode::Success 164 | } else { 165 | ReasonCode::PacketIdNotFound 166 | } 167 | } 168 | 169 | /// The number of packets that we support receiving simultanenously. 170 | pub fn receive_maximum(&self) -> usize { 171 | self.pending_server_packet_ids.capacity() 172 | } 173 | 174 | pub fn max_send_quota(&self) -> u16 { 175 | self.repub.max_send_quota() 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | //! MQTT-Specific Data Types 2 | //! 3 | //! This module provides wrapper methods and serde functionality for MQTT-specified data types. 4 | use crate::{ 5 | de::deserializer::MqttDeserializer, properties::Property, varint::Varint, ProtocolError, QoS, 6 | }; 7 | use bit_field::BitField; 8 | use serde::ser::SerializeStruct; 9 | use serde::{Deserialize, Serialize}; 10 | 11 | #[derive(Debug, PartialEq)] 12 | pub enum Properties<'a> { 13 | /// Properties ready for transmission are provided as a list of properties that will be later 14 | /// encoded into a packet. 15 | Slice(&'a [Property<'a>]), 16 | 17 | /// Properties have an unknown size when being received. As such, we store them as a binary 18 | /// blob that we iterate across. 19 | DataBlock(&'a [u8]), 20 | 21 | /// Properties that are correlated to a previous message. 22 | CorrelatedSlice { 23 | correlation: Property<'a>, 24 | properties: &'a [Property<'a>], 25 | }, 26 | } 27 | 28 | impl Properties<'_> { 29 | /// The length in bytes of the serialized properties. 30 | pub fn size(&self) -> usize { 31 | // Properties in MQTTv5 must be prefixed with a variable-length integer denoting the size 32 | // of the all of the properties in bytes. 33 | match self { 34 | Properties::Slice(props) => props.iter().map(|prop| prop.size()).sum(), 35 | Properties::CorrelatedSlice { 36 | correlation, 37 | properties, 38 | } => properties 39 | .iter() 40 | .chain([*correlation].iter()) 41 | .map(|prop| prop.size()) 42 | .sum(), 43 | Properties::DataBlock(block) => block.len(), 44 | } 45 | } 46 | } 47 | 48 | /// Used to progressively iterate across binary property blocks, deserializing them along the way. 49 | pub struct PropertiesIter<'a> { 50 | props: &'a [u8], 51 | index: usize, 52 | } 53 | 54 | impl<'a> PropertiesIter<'a> { 55 | pub fn response_topic(&mut self) -> Option<&'a str> { 56 | self.find_map(|prop| { 57 | if let Ok(crate::Property::ResponseTopic(topic)) = prop { 58 | Some(topic.0) 59 | } else { 60 | None 61 | } 62 | }) 63 | } 64 | } 65 | impl<'a> core::iter::Iterator for PropertiesIter<'a> { 66 | type Item = Result, ProtocolError>; 67 | 68 | fn next(&mut self) -> Option { 69 | if self.index >= self.props.len() { 70 | return None; 71 | } 72 | 73 | // Progressively deserialize properties and yield them. 74 | let mut deserializer = MqttDeserializer::new(&self.props[self.index..]); 75 | let property = 76 | Property::deserialize(&mut deserializer).map_err(ProtocolError::Deserialization); 77 | self.index += deserializer.deserialized_bytes(); 78 | Some(property) 79 | } 80 | } 81 | 82 | impl<'a> core::iter::IntoIterator for &'a Properties<'a> { 83 | type Item = Result, ProtocolError>; 84 | type IntoIter = PropertiesIter<'a>; 85 | 86 | fn into_iter(self) -> PropertiesIter<'a> { 87 | if let Properties::DataBlock(data) = self { 88 | PropertiesIter { 89 | props: data, 90 | index: 0, 91 | } 92 | } else { 93 | // Iterating over other property types is not implemented. The user may instead iterate 94 | // through slices directly. 95 | unimplemented!() 96 | } 97 | } 98 | } 99 | 100 | impl serde::Serialize for Properties<'_> { 101 | fn serialize(&self, serializer: S) -> Result { 102 | let mut item = serializer.serialize_struct("Properties", 0)?; 103 | item.serialize_field("_len", &Varint(self.size() as u32))?; 104 | 105 | // Properties in MQTTv5 must be prefixed with a variable-length integer denoting the size 106 | // of the all of the properties in bytes. 107 | match self { 108 | Properties::Slice(props) => { 109 | item.serialize_field("_props", props)?; 110 | } 111 | Properties::CorrelatedSlice { 112 | correlation, 113 | properties, 114 | } => { 115 | item.serialize_field("_correlation", &correlation)?; 116 | item.serialize_field("_props", properties)?; 117 | } 118 | Properties::DataBlock(block) => { 119 | item.serialize_field("_data", block)?; 120 | } 121 | } 122 | 123 | item.end() 124 | } 125 | } 126 | 127 | struct PropertiesVisitor; 128 | 129 | impl<'de> serde::de::Visitor<'de> for PropertiesVisitor { 130 | type Value = Properties<'de>; 131 | 132 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 133 | write!(formatter, "Properties") 134 | } 135 | 136 | fn visit_seq>(self, mut seq: S) -> Result { 137 | let data = seq.next_element()?; 138 | Ok(Properties::DataBlock(data.unwrap_or(&[]))) 139 | } 140 | } 141 | 142 | impl<'a, 'de: 'a> serde::de::Deserialize<'de> for Properties<'a> { 143 | fn deserialize>(deserializer: D) -> Result { 144 | deserializer.deserialize_seq(PropertiesVisitor) 145 | } 146 | } 147 | 148 | /// A wrapper type for "Binary Data" as defined in the MQTT v5 specification. 149 | /// 150 | /// # Note 151 | /// This wrapper type is primarily used to support custom serde functionality. 152 | #[derive(Copy, Clone, Debug, PartialEq)] 153 | pub struct BinaryData<'a>(pub &'a [u8]); 154 | 155 | impl serde::Serialize for BinaryData<'_> { 156 | fn serialize(&self, serializer: S) -> Result { 157 | use serde::ser::Error; 158 | 159 | if self.0.len() > u16::MAX as usize { 160 | return Err(S::Error::custom("Provided string is too long")); 161 | } 162 | 163 | let len = self.0.len() as u16; 164 | let mut item = serializer.serialize_struct("_BinaryData", 0)?; 165 | 166 | // Binary data in MQTTv5 must be transmitted with a prefix of its length in bytes as a u16. 167 | item.serialize_field("_len", &len)?; 168 | item.serialize_field("_data", self.0)?; 169 | item.end() 170 | } 171 | } 172 | 173 | struct BinaryDataVisitor; 174 | 175 | impl<'de> serde::de::Visitor<'de> for BinaryDataVisitor { 176 | type Value = BinaryData<'de>; 177 | 178 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 179 | write!(formatter, "BinaryData") 180 | } 181 | 182 | fn visit_borrowed_bytes(self, data: &'de [u8]) -> Result { 183 | Ok(BinaryData(data)) 184 | } 185 | } 186 | 187 | impl<'de> serde::de::Deserialize<'de> for BinaryData<'de> { 188 | fn deserialize>(deserializer: D) -> Result { 189 | deserializer.deserialize_bytes(BinaryDataVisitor) 190 | } 191 | } 192 | 193 | #[derive(Debug, Copy, Clone)] 194 | pub struct Auth<'a> { 195 | pub user_name: &'a str, 196 | pub password: &'a str, 197 | } 198 | 199 | /// A wrapper type for "UTF-8 Encoded Strings" as defined in the MQTT v5 specification. 200 | /// 201 | /// # Note 202 | /// This wrapper type is primarily used to support custom serde functionality. 203 | #[derive(Copy, Clone, Debug, PartialEq)] 204 | pub struct Utf8String<'a>(pub &'a str); 205 | 206 | impl serde::Serialize for Utf8String<'_> { 207 | fn serialize(&self, serializer: S) -> Result { 208 | use serde::ser::Error; 209 | 210 | if self.0.len() > u16::MAX as usize { 211 | return Err(S::Error::custom("Provided string is too long")); 212 | } 213 | 214 | let len = self.0.len() as u16; 215 | let mut item = serializer.serialize_struct("_Utf8String", 0)?; 216 | 217 | // UTF-8 encoded strings in MQTT require a u16 length prefix to indicate their length. 218 | item.serialize_field("_len", &len)?; 219 | item.serialize_field("_string", self.0)?; 220 | item.end() 221 | } 222 | } 223 | 224 | struct Utf8StringVisitor<'a> { 225 | _data: core::marker::PhantomData<&'a ()>, 226 | } 227 | 228 | impl<'a, 'de: 'a> serde::de::Visitor<'de> for Utf8StringVisitor<'a> { 229 | type Value = Utf8String<'a>; 230 | 231 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 232 | write!(formatter, "Utf8String") 233 | } 234 | 235 | fn visit_borrowed_str(self, data: &'de str) -> Result { 236 | Ok(Utf8String(data)) 237 | } 238 | } 239 | 240 | impl<'a, 'de: 'a> serde::de::Deserialize<'de> for Utf8String<'a> { 241 | fn deserialize>(deserializer: D) -> Result { 242 | // The UTF-8 string in MQTTv5 is semantically equivalent to a rust &str. 243 | deserializer.deserialize_str(Utf8StringVisitor { 244 | _data: core::marker::PhantomData, 245 | }) 246 | } 247 | } 248 | 249 | /// Used to specify how currently-retained messages should be handled after the topic is subscribed to. 250 | #[derive(Copy, Clone, Debug)] 251 | #[repr(u8)] 252 | pub enum RetainHandling { 253 | /// All retained messages should immediately be transmitted if they are present. 254 | Immediately = 0b00, 255 | 256 | /// Retained messages should only be published if the subscription does not already exist. 257 | IfSubscriptionDoesNotExist = 0b01, 258 | 259 | /// Do not provide any retained messages on this topic. 260 | Never = 0b10, 261 | } 262 | 263 | /// A wrapper type for "Subscription Options" as defined in the MQTT v5 specification. 264 | /// 265 | /// # Note 266 | /// This wrapper type is primarily used to support custom serde functionality. 267 | #[derive(Copy, Clone, Debug)] 268 | pub struct SubscriptionOptions { 269 | maximum_qos: QoS, 270 | no_local: bool, 271 | retain_as_published: bool, 272 | retain_behavior: RetainHandling, 273 | } 274 | 275 | impl Default for SubscriptionOptions { 276 | fn default() -> Self { 277 | Self { 278 | maximum_qos: QoS::AtMostOnce, 279 | no_local: false, 280 | retain_as_published: false, 281 | retain_behavior: RetainHandling::Immediately, 282 | } 283 | } 284 | } 285 | 286 | impl SubscriptionOptions { 287 | /// Specify the maximum QoS supported on this subscription. 288 | pub fn maximum_qos(mut self, qos: QoS) -> Self { 289 | self.maximum_qos = qos; 290 | self 291 | } 292 | 293 | /// Specify the retain behavior of the topic subscription. 294 | pub fn retain_behavior(mut self, handling: RetainHandling) -> Self { 295 | self.retain_behavior = handling; 296 | self 297 | } 298 | 299 | /// Ignore locally-published messages on this subscription. 300 | pub fn ignore_local_messages(mut self) -> Self { 301 | self.no_local = true; 302 | self 303 | } 304 | 305 | /// Keep the retain bits unchanged for this subscription. 306 | pub fn retain_as_published(mut self) -> Self { 307 | self.retain_as_published = true; 308 | self 309 | } 310 | } 311 | 312 | impl serde::Serialize for SubscriptionOptions { 313 | fn serialize(&self, serializer: S) -> Result { 314 | let value = *0u8 315 | .set_bits(0..2, self.maximum_qos as u8) 316 | .set_bit(2, self.no_local) 317 | .set_bit(3, self.retain_as_published) 318 | .set_bits(4..6, self.retain_behavior as u8); 319 | serializer.serialize_u8(value) 320 | } 321 | } 322 | 323 | impl<'a> From<&'a str> for TopicFilter<'a> { 324 | fn from(topic: &'a str) -> Self { 325 | Self { 326 | topic: Utf8String(topic), 327 | options: SubscriptionOptions::default(), 328 | } 329 | } 330 | } 331 | 332 | /// A single topic subscription. 333 | /// 334 | /// # Note 335 | /// Many topic filters may be requested in a single subscription request. 336 | #[derive(Serialize, Copy, Clone, Debug)] 337 | pub struct TopicFilter<'a> { 338 | topic: Utf8String<'a>, 339 | options: SubscriptionOptions, 340 | } 341 | 342 | impl<'a> TopicFilter<'a> { 343 | /// Create a new topic filter for subscription. 344 | pub fn new(topic: &'a str) -> Self { 345 | topic.into() 346 | } 347 | 348 | /// Specify custom options for the subscription. 349 | pub fn options(mut self, options: SubscriptionOptions) -> Self { 350 | self.options = options; 351 | self 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/varint.rs: -------------------------------------------------------------------------------- 1 | use heapless::Vec; 2 | use serde::ser::SerializeSeq; 3 | use varint_rs::{VarintReader, VarintWriter}; 4 | 5 | #[derive(Copy, Clone, Debug, PartialEq)] 6 | pub struct Varint(pub u32); 7 | 8 | impl Varint { 9 | pub fn len(&self) -> usize { 10 | let mut buffer = VarintBuffer::new(); 11 | buffer.write_u32_varint(self.0).unwrap(); 12 | buffer.data.len() 13 | } 14 | } 15 | 16 | impl From for Varint { 17 | fn from(val: u32) -> Varint { 18 | Varint(val) 19 | } 20 | } 21 | 22 | pub struct VarintBuffer { 23 | pub data: Vec, 24 | } 25 | 26 | impl VarintBuffer { 27 | pub fn new() -> Self { 28 | Self { data: Vec::new() } 29 | } 30 | } 31 | 32 | impl VarintWriter for VarintBuffer { 33 | type Error = (); 34 | fn write(&mut self, byte: u8) -> Result<(), ()> { 35 | self.data.push(byte).map_err(|_| ()) 36 | } 37 | } 38 | 39 | struct VarintVisitor; 40 | 41 | struct VarintParser<'de, T> { 42 | seq: T, 43 | _data: core::marker::PhantomData<&'de ()>, 44 | } 45 | 46 | impl<'de, T: serde::de::SeqAccess<'de>> VarintReader for VarintParser<'de, T> { 47 | type Error = T::Error; 48 | 49 | fn read(&mut self) -> Result { 50 | use serde::de::Error; 51 | let next = self.seq.next_element()?; 52 | next.ok_or_else(|| T::Error::custom("Invalid varint")) 53 | } 54 | } 55 | 56 | impl<'de> serde::de::Visitor<'de> for VarintVisitor { 57 | type Value = Varint; 58 | 59 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 60 | write!(formatter, "Varint") 61 | } 62 | 63 | fn visit_seq>(self, seq: A) -> Result { 64 | let mut reader = VarintParser { 65 | seq, 66 | _data: core::marker::PhantomData, 67 | }; 68 | Ok(Varint(reader.read_u32_varint()?)) 69 | } 70 | } 71 | 72 | impl<'de> serde::de::Deserialize<'de> for Varint { 73 | fn deserialize>(deserializer: D) -> Result { 74 | deserializer.deserialize_tuple(4, VarintVisitor) 75 | } 76 | } 77 | 78 | impl serde::Serialize for Varint { 79 | fn serialize(&self, serializer: S) -> Result { 80 | use serde::ser::Error; 81 | 82 | let mut buffer = VarintBuffer::new(); 83 | buffer 84 | .write_u32_varint(self.0) 85 | .map_err(|_| S::Error::custom("Failed to encode varint"))?; 86 | 87 | let mut seq = serializer.serialize_seq(Some(buffer.data.len()))?; 88 | for byte in buffer.data.iter() { 89 | seq.serialize_element(byte)?; 90 | } 91 | seq.end() 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/will.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | properties::{Property, PropertyIdentifier}, 3 | types::{BinaryData, Properties, Utf8String}, 4 | varint::Varint, 5 | ProtocolError, QoS, Retain, 6 | }; 7 | 8 | use serde::Serialize; 9 | 10 | #[derive(Debug)] 11 | pub struct Will<'a> { 12 | topic: &'a str, 13 | data: &'a [u8], 14 | qos: QoS, 15 | retained: Retain, 16 | properties: &'a [Property<'a>], 17 | } 18 | 19 | #[derive(Serialize)] 20 | struct WillMessage<'a> { 21 | properties: Properties<'a>, 22 | topic: Utf8String<'a>, 23 | data: BinaryData<'a>, 24 | } 25 | 26 | impl<'a> Will<'a> { 27 | /// Construct a new will message. 28 | /// 29 | /// # Args 30 | /// * `topic` - The topic to send the message on 31 | /// * `data` - The message to transmit 32 | /// * `properties` - Any properties to send with the will message. 33 | pub fn new( 34 | topic: &'a str, 35 | data: &'a [u8], 36 | properties: &'a [Property<'a>], 37 | ) -> Result { 38 | // Check that the input properties are valid for a will. 39 | for property in properties { 40 | match property.into() { 41 | PropertyIdentifier::WillDelayInterval 42 | | PropertyIdentifier::PayloadFormatIndicator 43 | | PropertyIdentifier::MessageExpiryInterval 44 | | PropertyIdentifier::ContentType 45 | | PropertyIdentifier::ResponseTopic 46 | | PropertyIdentifier::CorrelationData 47 | | PropertyIdentifier::UserProperty => {} 48 | _ => return Err(ProtocolError::InvalidProperty), 49 | } 50 | } 51 | 52 | Ok(Self { 53 | topic, 54 | data, 55 | properties, 56 | qos: QoS::AtMostOnce, 57 | retained: Retain::NotRetained, 58 | }) 59 | } 60 | 61 | /// Serialize the will contents into a flattened, borrowed buffer. 62 | pub(crate) fn serialize<'b>( 63 | &self, 64 | buf: &'b mut [u8], 65 | ) -> Result, crate::ser::Error> { 66 | let message = WillMessage { 67 | topic: Utf8String(self.topic), 68 | properties: Properties::Slice(self.properties), 69 | data: BinaryData(self.data), 70 | }; 71 | 72 | let mut serializer = crate::ser::MqttSerializer::new_without_header(buf); 73 | message.serialize(&mut serializer)?; 74 | Ok(SerializedWill { 75 | qos: self.qos, 76 | retained: self.retained, 77 | contents: serializer.finish(), 78 | }) 79 | } 80 | 81 | /// Precalculate the length of the serialized will. 82 | pub(crate) fn serialized_len(&self) -> usize { 83 | let prop_len = { 84 | let prop_size = Properties::Slice(self.properties).size(); 85 | Varint(prop_size as u32).len() + prop_size 86 | }; 87 | let topic_len = self.topic.len() + core::mem::size_of::(); 88 | let payload_len = self.data.len() + core::mem::size_of::(); 89 | topic_len + payload_len + prop_len 90 | } 91 | 92 | /// Specify the will as a retained message. 93 | pub fn retained(mut self) -> Self { 94 | self.retained = Retain::Retained; 95 | self 96 | } 97 | 98 | /// Set the quality of service at which the will message is sent. 99 | /// 100 | /// # Args 101 | /// * `qos` - The desired quality-of-service level to send the message at. 102 | pub fn qos(mut self, qos: QoS) -> Self { 103 | self.qos = qos; 104 | self 105 | } 106 | } 107 | 108 | /// A will where the topic, properties, and contents have already been serialized. 109 | #[derive(Debug, Copy, Clone, PartialEq)] 110 | pub(crate) struct SerializedWill<'a> { 111 | pub(crate) qos: QoS, 112 | pub(crate) retained: Retain, 113 | pub(crate) contents: &'a [u8], 114 | } 115 | -------------------------------------------------------------------------------- /tests/at_least_once_subscription.rs: -------------------------------------------------------------------------------- 1 | use minimq::{ 2 | types::{SubscriptionOptions, TopicFilter}, 3 | Minimq, Publication, QoS, 4 | }; 5 | 6 | use core::net::{IpAddr, Ipv4Addr}; 7 | use std_embedded_time::StandardClock; 8 | 9 | #[test] 10 | fn main() -> std::io::Result<()> { 11 | env_logger::init(); 12 | 13 | let mut buffer = [0u8; 1024]; 14 | let stack = std_embedded_nal::Stack; 15 | let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 16 | let mut mqtt: Minimq<'_, _, _, minimq::broker::IpBroker> = Minimq::new( 17 | stack, 18 | StandardClock::default(), 19 | minimq::ConfigBuilder::new(localhost.into(), &mut buffer).keepalive_interval(60), 20 | ); 21 | 22 | let mut published = false; 23 | let mut subscribed = false; 24 | let mut received_messages = 0; 25 | 26 | loop { 27 | // Continually process the client until no more data is received. 28 | while let Some(count) = mqtt 29 | .poll(|_client, _topic, _payload, _properties| 1) 30 | .unwrap() 31 | { 32 | received_messages += count; 33 | } 34 | 35 | if !mqtt.client().is_connected() { 36 | continue; 37 | } 38 | 39 | if !subscribed { 40 | let topic_filter = TopicFilter::new("data") 41 | .options(SubscriptionOptions::default().maximum_qos(QoS::AtLeastOnce)); 42 | mqtt.client().subscribe(&[topic_filter], &[]).unwrap(); 43 | subscribed = true; 44 | } 45 | 46 | if mqtt.client().subscriptions_pending() { 47 | continue; 48 | } 49 | 50 | if !published && mqtt.client().can_publish(QoS::AtLeastOnce) { 51 | mqtt.client() 52 | .publish(Publication::new("data", "Ping").qos(QoS::AtLeastOnce)) 53 | .unwrap(); 54 | log::info!("Publishing message"); 55 | published = true; 56 | } 57 | 58 | if received_messages > 0 { 59 | assert!(published); 60 | assert!(!mqtt.client().pending_messages()); 61 | log::info!("Reception Complete"); 62 | std::process::exit(0); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/deferred_payload.rs: -------------------------------------------------------------------------------- 1 | use minimq::{Minimq, Publication, QoS}; 2 | 3 | use core::net::{IpAddr, Ipv4Addr}; 4 | use std_embedded_time::StandardClock; 5 | 6 | #[test] 7 | fn main() -> std::io::Result<()> { 8 | env_logger::init(); 9 | 10 | let mut buffer = [0u8; 1024]; 11 | let stack = std_embedded_nal::Stack; 12 | let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 13 | let mut mqtt: Minimq<'_, _, _, minimq::broker::IpBroker> = Minimq::new( 14 | stack, 15 | StandardClock::default(), 16 | minimq::ConfigBuilder::new(localhost.into(), &mut buffer).keepalive_interval(60), 17 | ); 18 | 19 | while !mqtt.client().is_connected() { 20 | mqtt.poll(|_client, _topic, _payload, _properties| {}) 21 | .unwrap(); 22 | } 23 | 24 | assert!(matches!( 25 | mqtt.client().publish( 26 | Publication::new("data", |_buf: &mut [u8]| Err("Oops!")).qos(QoS::ExactlyOnce) 27 | ), 28 | Err(minimq::PubError::Serialization("Oops!")) 29 | )); 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /tests/exactly_once_subscription.rs: -------------------------------------------------------------------------------- 1 | use minimq::{ 2 | types::{SubscriptionOptions, TopicFilter}, 3 | Minimq, Publication, QoS, 4 | }; 5 | 6 | use core::net::{IpAddr, Ipv4Addr}; 7 | use std_embedded_time::StandardClock; 8 | 9 | #[test] 10 | fn main() -> std::io::Result<()> { 11 | env_logger::init(); 12 | 13 | let mut buffer = [0u8; 1024]; 14 | let stack = std_embedded_nal::Stack; 15 | let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 16 | let mut mqtt: Minimq<'_, _, _, minimq::broker::IpBroker> = Minimq::new( 17 | stack, 18 | StandardClock::default(), 19 | minimq::ConfigBuilder::new(localhost.into(), &mut buffer).keepalive_interval(60), 20 | ); 21 | 22 | let mut published = false; 23 | let mut subscribed = false; 24 | let mut received_messages = 0; 25 | 26 | loop { 27 | // Continually process the client until no more data is received. 28 | while let Some(count) = mqtt 29 | .poll(|_client, _topic, _payload, _properties| 1) 30 | .unwrap() 31 | { 32 | log::info!("Received a message"); 33 | received_messages += count; 34 | } 35 | 36 | if !mqtt.client().is_connected() { 37 | continue; 38 | } 39 | 40 | if !subscribed { 41 | let topic_filter = TopicFilter::new("data") 42 | .options(SubscriptionOptions::default().maximum_qos(QoS::ExactlyOnce)); 43 | mqtt.client().subscribe(&[topic_filter], &[]).unwrap(); 44 | subscribed = true; 45 | } 46 | 47 | if mqtt.client().subscriptions_pending() { 48 | continue; 49 | } 50 | 51 | if !published && mqtt.client().can_publish(QoS::ExactlyOnce) { 52 | mqtt.client() 53 | .publish(Publication::new("data", b"Ping").qos(QoS::ExactlyOnce)) 54 | .unwrap(); 55 | log::info!("Publishing message"); 56 | published = true; 57 | } 58 | 59 | if received_messages > 0 && !mqtt.client().pending_messages() { 60 | assert!(published); 61 | log::info!("Reception Complete"); 62 | std::process::exit(0); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/exactly_once_transmission.rs: -------------------------------------------------------------------------------- 1 | use minimq::{Minimq, Publication, QoS}; 2 | 3 | use core::net::{IpAddr, Ipv4Addr}; 4 | use std_embedded_time::StandardClock; 5 | 6 | #[test] 7 | fn main() -> std::io::Result<()> { 8 | env_logger::init(); 9 | 10 | let mut buffer = [0u8; 1024]; 11 | let stack = std_embedded_nal::Stack; 12 | let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 13 | let mut mqtt: Minimq<'_, _, _, minimq::broker::IpBroker> = Minimq::new( 14 | stack, 15 | StandardClock::default(), 16 | minimq::ConfigBuilder::new(localhost.into(), &mut buffer).keepalive_interval(60), 17 | ); 18 | 19 | let mut published = false; 20 | 21 | loop { 22 | // Continually process the client until no more data is received. 23 | while mqtt 24 | .poll(|_client, _topic, _payload, _properties| {}) 25 | .unwrap() 26 | .is_some() 27 | {} 28 | 29 | if mqtt.client().is_connected() && !published && mqtt.client().can_publish(QoS::ExactlyOnce) 30 | { 31 | mqtt.client() 32 | .publish(Publication::new("data", b"Ping").qos(QoS::ExactlyOnce)) 33 | .unwrap(); 34 | log::info!("Publishing message"); 35 | published = true; 36 | } 37 | 38 | if published && !mqtt.client().pending_messages() { 39 | log::info!("Transmission complete"); 40 | std::process::exit(0); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | use minimq::{types::Utf8String, Minimq, Property, Publication, QoS, Will}; 2 | 3 | use core::net::{IpAddr, Ipv4Addr}; 4 | use std_embedded_time::StandardClock; 5 | 6 | #[test] 7 | fn main() -> std::io::Result<()> { 8 | env_logger::init(); 9 | 10 | let will = Will::new("exit", "Test complete".as_bytes(), &[]).unwrap(); 11 | 12 | let mut buffer = [0u8; 1024]; 13 | let stack = std_embedded_nal::Stack; 14 | let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 15 | let mut mqtt: Minimq<'_, _, _, minimq::broker::IpBroker> = Minimq::new( 16 | stack, 17 | StandardClock::default(), 18 | minimq::ConfigBuilder::new(localhost.into(), &mut buffer) 19 | .will(will) 20 | .unwrap() 21 | .keepalive_interval(60), 22 | ); 23 | 24 | let mut published = false; 25 | let mut subscribed = false; 26 | let mut responses = 0; 27 | 28 | loop { 29 | // Continually poll the client until there is no more data. 30 | while let Some(was_response) = mqtt 31 | .poll(|client, topic, payload, properties| { 32 | log::info!("{} < {}", topic, core::str::from_utf8(payload).unwrap()); 33 | 34 | if let Ok(response) = Publication::respond(None, properties, b"Pong") { 35 | client.publish(response).unwrap(); 36 | } 37 | 38 | topic == "response" 39 | }) 40 | .unwrap() 41 | { 42 | if was_response { 43 | responses += 1; 44 | if responses == 2 { 45 | assert!(!mqtt.client().pending_messages()); 46 | std::process::exit(0); 47 | } 48 | } 49 | } 50 | 51 | let client = mqtt.client(); 52 | 53 | if !subscribed { 54 | if client.is_connected() { 55 | client 56 | .subscribe(&["response".into(), "request".into()], &[]) 57 | .unwrap(); 58 | subscribed = true; 59 | } 60 | } else if !client.subscriptions_pending() && !published { 61 | println!("PUBLISH request"); 62 | let properties = [Property::ResponseTopic(Utf8String("response"))]; 63 | let publication = Publication::new("request", b"Ping").properties(&properties); 64 | 65 | client.publish(publication).unwrap(); 66 | 67 | let publication = Publication::new("request", b"Ping") 68 | .properties(&properties) 69 | .qos(QoS::AtLeastOnce); 70 | 71 | client.publish(publication).unwrap(); 72 | 73 | // The message cannot be ack'd until the next poll call 74 | assert!(client.pending_messages()); 75 | 76 | published = true; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/poll_result.rs: -------------------------------------------------------------------------------- 1 | use minimq::{Minimq, Publication, QoS}; 2 | 3 | use core::net::{IpAddr, Ipv4Addr}; 4 | use std_embedded_time::StandardClock; 5 | 6 | #[test] 7 | fn main() -> std::io::Result<()> { 8 | env_logger::init(); 9 | 10 | let mut buffer = [0u8; 1024]; 11 | let stack = std_embedded_nal::Stack; 12 | let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 13 | let mut mqtt: Minimq<'_, _, _, minimq::broker::IpBroker> = Minimq::new( 14 | stack, 15 | StandardClock::default(), 16 | minimq::ConfigBuilder::new(localhost.into(), &mut buffer).keepalive_interval(60), 17 | ); 18 | 19 | let mut published = false; 20 | let mut subscribed = false; 21 | 22 | loop { 23 | // Service the MQTT client until there's no more data to process. 24 | while let Some(complete) = mqtt 25 | .poll(|_client, topic, _payload, _properties| topic == "data") 26 | .unwrap() 27 | { 28 | if complete { 29 | log::info!("Transmission complete"); 30 | std::process::exit(0); 31 | } 32 | } 33 | 34 | if !mqtt.client().is_connected() { 35 | continue; 36 | } 37 | 38 | if !subscribed { 39 | mqtt.client().subscribe(&["data".into()], &[]).unwrap(); 40 | subscribed = true; 41 | } 42 | 43 | if !mqtt.client().subscriptions_pending() 44 | && !published 45 | && mqtt.client().can_publish(QoS::ExactlyOnce) 46 | { 47 | mqtt.client() 48 | .publish(Publication::new("data", b"Ping").qos(QoS::ExactlyOnce)) 49 | .unwrap(); 50 | log::info!("Publishing message"); 51 | published = true; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/reconnection.rs: -------------------------------------------------------------------------------- 1 | use minimq::{ 2 | types::{SubscriptionOptions, TopicFilter}, 3 | Minimq, Publication, QoS, 4 | }; 5 | 6 | use core::net::{IpAddr, Ipv4Addr}; 7 | use std_embedded_time::StandardClock; 8 | 9 | mod stack; 10 | 11 | #[test] 12 | fn main() -> std::io::Result<()> { 13 | env_logger::init(); 14 | 15 | let sockets = std::cell::RefCell::new(Vec::new()); 16 | let stack = stack::MitmStack::new(&sockets); 17 | 18 | let mut buffer = [0u8; 1024]; 19 | let localhost = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 20 | let mut mqtt: Minimq<'_, _, _, minimq::broker::IpBroker> = Minimq::new( 21 | stack, 22 | StandardClock::default(), 23 | minimq::ConfigBuilder::new(localhost.into(), &mut buffer).keepalive_interval(1), 24 | ); 25 | 26 | // 1. Poll until we're connected and subscribed to a test topic 27 | while !mqtt.client().is_connected() { 28 | mqtt.poll(|_client, _topic, _payload, _properties| {}) 29 | .unwrap(); 30 | } 31 | 32 | let topic_filter = TopicFilter::new("test") 33 | .options(SubscriptionOptions::default().maximum_qos(QoS::ExactlyOnce)); 34 | mqtt.client().subscribe(&[topic_filter], &[]).unwrap(); 35 | 36 | while mqtt.client().subscriptions_pending() { 37 | mqtt.poll(|_client, _topic, _payload, _properties| {}) 38 | .unwrap(); 39 | } 40 | 41 | // 2. Send a QoS::AtLeastOnce message 42 | mqtt.client() 43 | .publish(Publication::new("test", b"Ping").qos(QoS::ExactlyOnce)) 44 | .unwrap(); 45 | 46 | // Force a disconnect from the broker. 47 | for socket in sockets.borrow_mut().iter_mut() { 48 | socket.1.close(); 49 | } 50 | 51 | // 3. Wait until the keepalive timeout lapses and we disconnect from the broker. 52 | while mqtt.client().is_connected() { 53 | mqtt.poll(|_client, _topic, _payload, _properties| {}) 54 | .unwrap(); 55 | } 56 | 57 | assert!(mqtt.client().pending_messages()); 58 | 59 | // 4. Poll until we're reconnected 60 | while !mqtt.client().is_connected() { 61 | mqtt.poll(|_client, _topic, _payload, _properties| {}) 62 | .unwrap(); 63 | } 64 | 65 | // 5. Verify that we finish transmission of our pending message. 66 | let mut rx_messages = 0; 67 | while mqtt.client().pending_messages() || rx_messages == 0 { 68 | mqtt.poll(|_client, _topic, _payload, _properties| { 69 | rx_messages += 1; 70 | }) 71 | .unwrap(); 72 | } 73 | 74 | // 5. Verify that we receive the message after reconnection 75 | assert!(rx_messages == 1); 76 | 77 | Ok(()) 78 | } 79 | -------------------------------------------------------------------------------- /tests/stack/mod.rs: -------------------------------------------------------------------------------- 1 | use embedded_nal::{nb, TcpClientStack}; 2 | use std::cell::RefCell; 3 | use std::io::{Error, ErrorKind, Read, Write}; 4 | use std::net::{IpAddr, SocketAddr, TcpStream}; 5 | 6 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 7 | pub struct TcpHandle { 8 | id: usize, 9 | } 10 | 11 | pub struct MitmStack<'a> { 12 | pub sockets: &'a RefCell>, 13 | handle: usize, 14 | } 15 | 16 | impl<'a> MitmStack<'a> { 17 | pub fn new(sockets: &'a RefCell>) -> Self { 18 | Self { sockets, handle: 0 } 19 | } 20 | 21 | pub fn add_socket(&mut self) -> TcpHandle { 22 | let handle = TcpHandle { id: self.handle }; 23 | self.handle += 1; 24 | self.sockets.borrow_mut().push((handle, TcpSocket::new())); 25 | handle 26 | } 27 | 28 | pub fn close(&mut self, handle: TcpHandle) { 29 | let index = self 30 | .sockets 31 | .borrow_mut() 32 | .iter() 33 | .position(|(h, _sock)| h == &handle) 34 | .unwrap(); 35 | self.sockets.borrow_mut().swap_remove(index); 36 | } 37 | } 38 | 39 | #[derive(Debug)] 40 | pub struct TcpError(pub Error); 41 | 42 | impl TcpError { 43 | fn broken_pipe() -> TcpError { 44 | TcpError(Error::new(ErrorKind::BrokenPipe, "Connection interrupted")) 45 | } 46 | } 47 | 48 | fn to_nb(e: std::io::Error) -> nb::Error { 49 | match e.kind() { 50 | ErrorKind::WouldBlock | ErrorKind::TimedOut => nb::Error::WouldBlock, 51 | _ => nb::Error::Other(TcpError(e)), 52 | } 53 | } 54 | 55 | impl From for TcpError { 56 | fn from(e: Error) -> Self { 57 | Self(e) 58 | } 59 | } 60 | 61 | impl embedded_nal::TcpError for TcpError { 62 | fn kind(&self) -> embedded_nal::TcpErrorKind { 63 | match self.0.kind() { 64 | std::io::ErrorKind::BrokenPipe => embedded_nal::TcpErrorKind::PipeClosed, 65 | _ => embedded_nal::TcpErrorKind::Other, 66 | } 67 | } 68 | } 69 | 70 | pub struct TcpSocket { 71 | stream: Option, 72 | } 73 | 74 | impl TcpSocket { 75 | fn new() -> Self { 76 | Self { stream: None } 77 | } 78 | 79 | fn connect(&mut self, remote: SocketAddr) -> Result<(), Error> { 80 | let IpAddr::V4(addr) = remote.ip() else { 81 | return Err(Error::new(ErrorKind::Other, "Only IPv4 supported")); 82 | }; 83 | let remote = SocketAddr::new(addr.octets().into(), remote.port()); 84 | let soc = TcpStream::connect(remote)?; 85 | soc.set_nonblocking(true)?; 86 | self.stream.replace(soc); 87 | Ok(()) 88 | } 89 | 90 | fn stream_mut(&mut self) -> Result<&mut TcpStream, nb::Error> { 91 | self.stream 92 | .as_mut() 93 | .ok_or_else(|| nb::Error::Other(TcpError::broken_pipe())) 94 | } 95 | 96 | pub fn close(&mut self) { 97 | self.stream.take(); 98 | } 99 | } 100 | 101 | impl<'a> TcpClientStack for MitmStack<'a> { 102 | type TcpSocket = TcpHandle; 103 | type Error = TcpError; 104 | 105 | fn socket(&mut self) -> Result { 106 | Ok(self.add_socket()) 107 | } 108 | 109 | fn connect( 110 | &mut self, 111 | socket: &mut Self::TcpSocket, 112 | remote: SocketAddr, 113 | ) -> nb::Result<(), Self::Error> { 114 | let index = self 115 | .sockets 116 | .borrow_mut() 117 | .iter() 118 | .position(|(h, _sock)| h == socket) 119 | .unwrap(); 120 | let socket = &mut self.sockets.borrow_mut()[index].1; 121 | socket.connect(remote).map_err(to_nb)?; 122 | Ok(()) 123 | } 124 | 125 | fn send( 126 | &mut self, 127 | socket: &mut Self::TcpSocket, 128 | buffer: &[u8], 129 | ) -> nb::Result { 130 | let index = self 131 | .sockets 132 | .borrow_mut() 133 | .iter() 134 | .position(|(h, _sock)| h == socket) 135 | .unwrap(); 136 | let socket = &mut self.sockets.borrow_mut()[index].1; 137 | 138 | let socket = socket.stream_mut()?; 139 | socket.write(buffer).map_err(to_nb) 140 | } 141 | 142 | fn receive( 143 | &mut self, 144 | socket: &mut Self::TcpSocket, 145 | buffer: &mut [u8], 146 | ) -> nb::Result { 147 | let index = self 148 | .sockets 149 | .borrow_mut() 150 | .iter() 151 | .position(|(h, _sock)| h == socket) 152 | .unwrap(); 153 | let socket = &mut self.sockets.borrow_mut()[index].1; 154 | let socket = socket.stream_mut()?; 155 | socket.read(buffer).map_err(to_nb) 156 | } 157 | 158 | fn close(&mut self, socket: Self::TcpSocket) -> Result<(), Self::Error> { 159 | self.close(socket); 160 | Ok(()) 161 | } 162 | } 163 | --------------------------------------------------------------------------------