Rust MQTT

This commit is contained in:
Ondrej Babec
2022-03-12 15:56:37 +01:00
parent c8ee05821a
commit d993457add
77 changed files with 1010 additions and 1661 deletions

26
mqtt/Cargo.toml Normal file
View File

@@ -0,0 +1,26 @@
[package]
name = "rust-mqtt"
version = "0.0.1"
authors = ["Ondrej Babec <ond.babec@gmail.com>"]
edition = "2021"
resolver = "2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
env_logger = "0.9.0"
log = "0.4.14"
heapless = "0.7.10"
rand_core = "0.6.0"
tokio = { version = "1", features = ["full"], optional = true }
tokio-test = { version = "0.4.2", optional = true}
[dev-dependencies]
tokio = { version = "1", features = ["full"] }
tokio-test = "0.4.2"
[features]
default = ["tokio", "std", "tokio-test"]
std = []
no_std = []

View File

@@ -0,0 +1,91 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::property::Property;
use crate::packet::v5::publish_packet::QualityOfService;
use crate::utils::types::{BinaryData, EncodedString};
use heapless::Vec;
pub struct ClientConfig<'a, const MAX_PROPERTIES: usize> {
pub qos: QualityOfService,
pub keep_alive: u16,
pub client_id: EncodedString<'a>,
pub username_flag: bool,
pub username: EncodedString<'a>,
pub password_flag: bool,
pub password: BinaryData<'a>,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
pub max_packet_size: u32,
}
impl<'a, const MAX_PROPERTIES: usize> ClientConfig<'a, MAX_PROPERTIES> {
pub fn new() -> Self {
Self {
qos: QualityOfService::QoS0,
keep_alive: 60,
client_id: EncodedString::new(),
username_flag: false,
username: EncodedString::new(),
password_flag: false,
password: BinaryData::new(),
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
max_packet_size: 265_000,
}
}
pub fn add_qos(&mut self, qos: QualityOfService) {
self.qos = qos;
}
pub fn add_username(&mut self, username: &'a str) {
let mut username_s: EncodedString = EncodedString::new();
username_s.string = username;
username_s.len = username.len() as u16;
self.username_flag = true;
self.username = username_s;
}
pub fn add_password(&mut self, password: &'a str) {
let mut password_s: BinaryData = BinaryData::new();
password_s.bin = password.as_bytes();
password_s.len = password_s.bin.len() as u16;
self.password = password_s;
self.password_flag = true;
}
pub fn add_property(&mut self, prop: Property<'a>) {
if self.properties.len() < MAX_PROPERTIES {
self.properties.push(prop);
}
}
pub fn add_max_packet_size_as_prop(&mut self) -> u32 {
if self.properties.len() < MAX_PROPERTIES {
let prop = Property::MaximumPacketSize(self.max_packet_size);
self.properties.push(prop);
return 5;
}
return 0;
}
}

View File

@@ -0,0 +1,353 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::client::client_config::ClientConfig;
use crate::network::network_trait::NetworkConnection;
use crate::packet::v5::connack_packet::ConnackPacket;
use crate::packet::v5::connect_packet::ConnectPacket;
use crate::packet::v5::disconnect_packet::DisconnectPacket;
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::pingreq_packet::PingreqPacket;
use crate::packet::v5::pingresp_packet::PingrespPacket;
use crate::packet::v5::puback_packet::PubackPacket;
use crate::packet::v5::publish_packet::QualityOfService::QoS1;
use crate::packet::v5::publish_packet::{PublishPacket, QualityOfService};
use crate::packet::v5::reason_codes::ReasonCode;
use crate::packet::v5::suback_packet::SubackPacket;
use crate::packet::v5::subscription_packet::SubscriptionPacket;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::rng_generator::CountingRng;
use crate::utils::types::BufferError;
use heapless::Vec;
use rand_core::RngCore;
pub struct MqttClientV5<'a, T, const MAX_PROPERTIES: usize> {
network_driver: &'a mut T,
buffer: &'a mut [u8],
buffer_len: usize,
recv_buffer: &'a mut [u8],
recv_buffer_len: usize,
rng: CountingRng,
config: ClientConfig<'a, MAX_PROPERTIES>,
}
impl<'a, T, const MAX_PROPERTIES: usize> MqttClientV5<'a, T, MAX_PROPERTIES>
where
T: NetworkConnection,
{
pub fn new(
network_driver: &'a mut T,
buffer: &'a mut [u8],
buffer_len: usize,
recv_buffer: &'a mut [u8],
recv_buffer_len: usize,
config: ClientConfig<'a, MAX_PROPERTIES>,
) -> Self {
Self {
network_driver,
buffer,
buffer_len,
recv_buffer,
recv_buffer_len,
rng: CountingRng(50),
config,
}
}
// Muze prijit disconnect kvuli male velikosti packetu
pub async fn connect_to_broker<'b>(&'b mut self) -> Result<(), ReasonCode> {
let len = {
let mut connect = ConnectPacket::<'b, MAX_PROPERTIES, 0>::new();
connect.keep_alive = self.config.keep_alive;
self.config.add_max_packet_size_as_prop();
connect.property_len = connect.add_properties(&self.config.properties);
if self.config.username_flag {
connect.add_username(&self.config.username);
}
if self.config.password_flag {
connect.add_password(&self.config.password)
}
connect.encode(self.buffer, self.buffer_len)
};
if let Err(err) = len {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
self.network_driver.send(self.buffer, len.unwrap()).await?;
//connack
let reason: Result<u8, BufferError> = {
self.network_driver.receive(self.buffer).await?;
let mut packet = ConnackPacket::<'b, 5>::new();
if let Err(err) = packet.decode(&mut BuffReader::new(self.buffer, self.buffer_len)) {
if err == BufferError::PacketTypeMismatch {
let mut disc = DisconnectPacket::<'b, 5>::new();
if disc
.decode(&mut BuffReader::new(self.buffer, self.buffer_len))
.is_ok()
{
log::error!("Client was disconnected with reason: ");
return Err(ReasonCode::from(disc.disconnect_reason));
}
}
Err(err)
} else {
Ok(packet.connect_reason_code)
}
};
if let Err(err) = reason {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
let res = reason.unwrap();
if res != 0x00 {
return Err(ReasonCode::from(res));
} else {
Ok(())
}
}
pub async fn disconnect<'b>(&'b mut self) -> Result<(), ReasonCode> {
let mut disconnect = DisconnectPacket::<'b, 5>::new();
let len = disconnect.encode(self.buffer, self.buffer_len);
if let Err(err) = len {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
self.network_driver.send(self.buffer, len.unwrap()).await?;
Ok(())
}
pub async fn send_message<'b>(
&'b mut self,
topic_name: &'b str,
message: &'b str,
) -> Result<(), ReasonCode> {
let identifier: u16 = self.rng.next_u32() as u16;
let len = {
let mut packet = PublishPacket::<'b, 5>::new();
packet.add_topic_name(topic_name);
packet.add_qos(self.config.qos);
packet.add_identifier(identifier);
packet.add_message(message.as_bytes());
packet.encode(self.buffer, self.buffer_len)
};
if let Err(err) = len {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
self.network_driver.send(self.buffer, len.unwrap()).await?;
//QoS1
if <QualityOfService as Into<u8>>::into(self.config.qos)
== <QualityOfService as Into<u8>>::into(QoS1)
{
let reason: Result<[u16; 2], BufferError> = {
self.network_driver.receive(self.buffer).await?;
let mut packet = PubackPacket::<'b, 5>::new();
if let Err(err) = packet.decode(&mut BuffReader::new(self.buffer, self.buffer_len))
{
Err(err)
} else {
Ok([packet.packet_identifier, packet.reason_code as u16])
}
};
if let Err(err) = reason {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
let res = reason.unwrap();
if identifier != res[0] {
return Err(ReasonCode::PacketIdentifierNotFound);
}
if res[1] != 0 {
return Err(ReasonCode::from(res[1] as u8));
}
}
Ok(())
}
pub async fn subscribe_to_topics<'b, const TOPICS: usize>(
&'b mut self,
topic_names: &'b Vec<&'b str, TOPICS>,
) -> Result<(), ReasonCode> {
let len = {
let mut subs = SubscriptionPacket::<'b, TOPICS, 1>::new();
let mut i = 0;
loop {
if i == TOPICS {
break;
}
subs.add_new_filter(topic_names.get(i).unwrap(), self.config.qos);
i = i + 1;
}
subs.encode(self.buffer, self.buffer_len)
};
if let Err(err) = len {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
self.network_driver.send(self.buffer, len.unwrap()).await?;
let reason: Result<Vec<u8, TOPICS>, BufferError> = {
self.network_driver.receive(self.buffer).await?;
let mut packet = SubackPacket::<'b, TOPICS, 5>::new();
if let Err(err) = packet.decode(&mut BuffReader::new(self.buffer, self.buffer_len)) {
Err(err)
} else {
Ok(packet.reason_codes)
}
};
if let Err(err) = reason {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
let reasons = reason.unwrap();
let mut i = 0;
loop {
if i == TOPICS {
break;
}
if *reasons.get(i).unwrap() != QualityOfService::into(self.config.qos) {
return Err(ReasonCode::from(*reasons.get(i).unwrap()));
}
i = i + 1;
}
Ok(())
}
pub async fn subscribe_to_topic<'b>(
&'b mut self,
topic_name: &'b str,
) -> Result<(), ReasonCode> {
let len = {
let mut subs = SubscriptionPacket::<'b, 1, 1>::new();
subs.add_new_filter(topic_name, self.config.qos);
subs.encode(self.buffer, self.buffer_len)
};
if let Err(err) = len {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
self.network_driver.send(self.buffer, len.unwrap()).await?;
let reason: Result<u8, BufferError> = {
self.network_driver.receive(self.buffer).await?;
let mut packet = SubackPacket::<'b, 5, 5>::new();
if let Err(err) = packet.decode(&mut BuffReader::new(self.buffer, self.buffer_len)) {
Err(err)
} else {
Ok(*packet.reason_codes.get(0).unwrap())
}
};
if let Err(err) = reason {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
let res = reason.unwrap();
if res != (<QualityOfService as Into<u8>>::into(self.config.qos) >> 1) {
Err(ReasonCode::from(res))
} else {
Ok(())
}
}
pub async fn receive_message<'b>(&'b mut self) -> Result<&'b [u8], ReasonCode> {
self.network_driver.receive(self.recv_buffer).await?;
let mut packet = PublishPacket::<'b, 5>::new();
if let Err(err) =
packet.decode(&mut BuffReader::new(self.recv_buffer, self.recv_buffer_len))
{
if err == BufferError::PacketTypeMismatch {
let mut disc = DisconnectPacket::<'b, 5>::new();
if disc
.decode(&mut BuffReader::new(self.buffer, self.buffer_len))
.is_ok()
{
log::error!("Client was disconnected with reason: ");
return Err(ReasonCode::from(disc.disconnect_reason));
}
}
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
if (packet.fixed_header & 0x06)
== <QualityOfService as Into<u8>>::into(QualityOfService::QoS1)
{
let mut puback = PubackPacket::<'b, 5>::new();
puback.packet_identifier = packet.packet_identifier;
puback.reason_code = 0x00;
{
let len = puback.encode(self.buffer, self.buffer_len);
if let Err(err) = len {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
self.network_driver.send(self.buffer, len.unwrap()).await?;
}
}
return Ok(packet.message.unwrap());
}
pub async fn send_ping<'b>(&'b mut self) -> Result<(), ReasonCode> {
let len = {
let mut packet = PingreqPacket::new();
packet.encode(self.buffer, self.buffer_len)
};
if let Err(err) = len {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
}
self.network_driver.send(self.buffer, len.unwrap()).await?;
self.network_driver.receive(self.buffer).await?;
let mut packet = PingrespPacket::new();
if let Err(err) = packet.decode(&mut BuffReader::new(self.buffer, self.buffer_len)) {
log::error!("[DECODE ERR]: {}", err);
return Err(ReasonCode::BuffError);
} else {
Ok(())
}
}
}

27
mqtt/src/client/mod.rs Normal file
View File

@@ -0,0 +1,27 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#[allow(unused_must_use)]
pub mod client_config;
pub mod client_v5;

25
mqtt/src/encoding/mod.rs Normal file
View File

@@ -0,0 +1,25 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod variable_byte_integer;

View File

@@ -0,0 +1,118 @@
/*
MIT License
Copyright (c) [2022] [Ondrej Babec <ond.babec@gmailc.com>]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
use crate::utils::types::BufferError;
/// VariableByteIntegerEncoder and VariableByteIntegerDecoder are implemented based on
/// pseudo code which is introduced in MQTT version 5.0 OASIS standard accesible from
/// https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901107
/// Variable byte integer encoder structure is help structure which implements function used to
/// encode integer into MQTT variable byte integer format. This format is mainly used to encode
/// lenghts stored in a packet.
pub struct VariableByteIntegerEncoder;
/// Variable byte integers error enumeration is used by both encoder and decoder for
/// error notification.
pub type VariableByteInteger = [u8; 4];
impl VariableByteIntegerEncoder {
/// Encode function takes as parameter integer as u32 type and encodes
/// this integer into maximal 4 Bytes. MSb of each Byte is controll bit.
/// This bit is saying if there is continuing Byte in stream or not, this way
/// we can effectively use 1 to 4 Bytes based in integer len.
pub fn encode(mut target: u32) -> Result<VariableByteInteger, BufferError> {
// General known informations from OASIS
const MAX_ENCODABLE: u32 = 268435455;
const MOD: u32 = 128;
if target > MAX_ENCODABLE {
log::error!("Maximal value of integer for encoding was exceeded");
return Err(BufferError::EncodingError);
}
let mut res: [u8; 4] = [0; 4];
let mut encoded_byte: u8;
let mut i: usize = 0;
loop {
encoded_byte = (target % MOD) as u8;
target = target / 128;
if target > 0 {
encoded_byte = encoded_byte | 128;
}
res[i] = encoded_byte;
i = i + 1;
if target <= 0 {
break;
}
}
return Ok(res);
}
pub fn len(var_int: VariableByteInteger) -> usize {
let mut i: usize = 0;
loop {
let encoded_byte: u8;
encoded_byte = var_int[i];
i = i + 1;
if (encoded_byte & 128) == 0 {
break;
}
}
return i;
}
}
/// Variable byte integer decoder structure is help structure which implements function used to
/// decode message lenghts in MQTT packet and other parts encoded into variable byte integer.
pub struct VariableByteIntegerDecoder;
impl VariableByteIntegerDecoder {
/// Decode function takes as paramater encoded integer represented
/// as array of 4 unsigned numbers of exactly 1 Byte each -> 4 Bytes maximal
/// same as maximal amount of bytes for variable byte encoding in MQTT.
pub fn decode(encoded: VariableByteInteger) -> Result<u32, BufferError> {
let mut multiplier: u32 = 1;
let mut ret: u32 = 0;
let mut encoded_byte: u8;
let mut i: usize = 0;
loop {
encoded_byte = encoded[i];
i = i + 1;
ret = ret + ((encoded_byte & 127) as u32 * multiplier) as u32;
if multiplier > 128 * 128 * 128 {
return Err(BufferError::DecodingError);
}
multiplier = multiplier * 128;
if (encoded_byte & 128) == 0 {
break;
}
}
return Ok(ret);
}
}

38
mqtt/src/lib.rs Normal file
View File

@@ -0,0 +1,38 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#![feature(in_band_lifetimes)]
#![macro_use]
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(dead_code)]
#![feature(type_alias_impl_trait)]
#![feature(generic_associated_types)]
pub mod client;
pub mod encoding;
pub mod network;
pub mod packet;
pub mod tests;
pub mod tokio_net;
pub mod utils;

25
mqtt/src/network/mod.rs Normal file
View File

@@ -0,0 +1,25 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod network_trait;

View File

@@ -0,0 +1,66 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use core::future::Future;
use crate::packet::v5::reason_codes::ReasonCode;
#[derive(Debug)]
pub enum NetworkError {
Connection,
Unknown,
QoSAck,
IDNotMatchedOnAck,
NoMatchingSubs,
}
pub trait NetworkConnectionFactory: Sized {
type Connection: NetworkConnection;
type ConnectionFuture<'m>: Future<Output = Result<Self::Connection, ReasonCode>>
where
Self: 'm;
fn connect<'m>(&'m mut self, ip: [u8; 4], port: u16) -> Self::ConnectionFuture<'m>;
}
pub trait NetworkConnection {
type WriteFuture<'m>: Future<Output = Result<(), ReasonCode>>
where
Self: 'm;
type ReadFuture<'m>: Future<Output = Result<usize, ReasonCode>>
where
Self: 'm;
type CloseFuture<'m>: Future<Output = Result<(), ReasonCode>>
where
Self: 'm;
fn send(&'m mut self, buffer: &'m mut [u8], len: usize) -> Self::WriteFuture<'m>;
fn receive(&'m mut self, buffer: &'m mut [u8]) -> Self::ReadFuture<'m>;
fn close(self) -> Self::CloseFuture<'m>;
}

26
mqtt/src/packet/mod.rs Normal file
View File

@@ -0,0 +1,26 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#[allow(unused_must_use)]
pub mod v5;

View File

@@ -0,0 +1,121 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
/// Auth packets serves MQTTv5 extended authentication. This packet is not currently supported
/// by rust-mqtt client but decoding and encoding of packet is prepared for future development.
pub struct AuthPacket<'a, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub auth_reason: u8,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
}
impl<'a, const MAX_PROPERTIES: usize> AuthPacket<'a, MAX_PROPERTIES> {
pub fn add_reason_code(&mut self, code: u8) {
if code != 0 && code != 24 && code != 25 {
log::error!("Provided reason code is not supported!");
return;
}
self.auth_reason = code;
}
pub fn add_property(&mut self, p: Property<'a>) {
if p.auth_property() {
self.push_to_properties(p);
} else {
log::error!("Provided property is not correct AUTH packet property!");
}
}
}
impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for AuthPacket<'a, MAX_PROPERTIES> {
fn new() -> Self {
Self {
fixed_header: PacketType::Auth.into(),
remain_len: 0,
auth_reason: 0x00,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buff_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buff_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
rm_ln = rm_ln + property_len_len as u32;
rm_ln = rm_ln + 1;
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_u8(self.auth_reason)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
self.decode_fixed_header(buff_reader)?;
self.auth_reason = buff_reader.read_u8()?;
return self.decode_properties(buff_reader);
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.auth_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,107 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct ConnackPacket<'a, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub ack_flags: u8,
pub connect_reason_code: u8,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
}
impl<'a, const MAX_PROPERTIES: usize> ConnackPacket<'a, MAX_PROPERTIES> {}
impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for ConnackPacket<'a, MAX_PROPERTIES> {
fn new() -> Self {
Self {
fixed_header: PacketType::Connack.into(),
remain_len: 0,
ack_flags: 0,
connect_reason_code: 0,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
buff_writer.write_u8(self.fixed_header)?;
let property_len_enc = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
let rm_len: u32 = 2 + self.property_len + property_len_len as u32;
buff_writer.write_variable_byte_int(rm_len)?;
buff_writer.write_u8(self.ack_flags)?;
buff_writer.write_u8(self.connect_reason_code)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties(&self.properties)?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Connack).into() {
log::error!("Packet you are trying to decode is not CONNACK packet!");
return Err(BufferError::PacketTypeMismatch);
}
self.ack_flags = buff_reader.read_u8()?;
self.connect_reason_code = buff_reader.read_u8()?;
self.decode_properties(buff_reader)
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.connack_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,217 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::{BinaryData, BufferError, EncodedString};
use super::packet_type::PacketType;
use super::property::Property;
pub struct ConnectPacket<'a, const MAX_PROPERTIES: usize, const MAX_WILL_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub protocol_name_len: u16,
pub protocol_name: u32,
pub protocol_version: u8,
pub connect_flags: u8,
pub keep_alive: u16,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
pub client_id: EncodedString<'a>,
pub will_property_len: u32,
pub will_properties: Vec<Property<'a>, MAX_WILL_PROPERTIES>,
pub will_topic: EncodedString<'a>,
pub will_payload: BinaryData<'a>,
pub username: EncodedString<'a>,
pub password: BinaryData<'a>,
}
impl<'a, const MAX_PROPERTIES: usize, const MAX_WILL_PROPERTIES: usize>
ConnectPacket<'a, MAX_PROPERTIES, MAX_WILL_PROPERTIES>
{
pub fn clean() -> Self {
let mut x = Self {
fixed_header: PacketType::Connect.into(),
remain_len: 0,
protocol_name_len: 4,
protocol_name: 0x4d515454,
protocol_version: 5,
connect_flags: 0x02,
keep_alive: 60,
property_len: 3,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
client_id: EncodedString::new(),
/// Will is not supported as it is un-necessary load for embedded
will_property_len: 0,
will_properties: Vec::<Property<'a>, MAX_WILL_PROPERTIES>::new(),
will_topic: EncodedString::new(),
will_payload: BinaryData::new(),
username: EncodedString::new(),
password: BinaryData::new(),
};
let y = Property::ReceiveMaximum(20);
x.properties.push(y);
x.client_id.len = 0;
return x;
}
pub fn add_packet_type(&mut self, new_packet_type: PacketType) {
self.fixed_header = self.fixed_header & 0x0F;
self.fixed_header = self.fixed_header | <PacketType as Into<u8>>::into(new_packet_type);
}
pub fn add_username(&mut self, username: &EncodedString<'a>) {
self.username = (*username).clone();
self.connect_flags = self.connect_flags | 0x80;
}
pub fn add_password(&mut self, password: &BinaryData<'a>) {
self.password = (*password).clone();
self.connect_flags = self.connect_flags | 0x40;
}
pub fn add_client_id(&mut self, id: &EncodedString<'a>) {
self.client_id = (*id).clone();
}
}
impl<'a, const MAX_PROPERTIES: usize, const MAX_WILL_PROPERTIES: usize> Packet<'a>
for ConnectPacket<'a, MAX_PROPERTIES, MAX_WILL_PROPERTIES>
{
fn new() -> Self {
Self {
fixed_header: PacketType::Connect.into(),
remain_len: 0,
protocol_name_len: 4,
// MQTT
protocol_name: 0x4d515454,
protocol_version: 5,
// Clean start flag
connect_flags: 0x02,
keep_alive: 180,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
client_id: EncodedString::new(),
will_property_len: 0,
will_properties: Vec::<Property<'a>, MAX_WILL_PROPERTIES>::new(),
will_topic: EncodedString::new(),
will_payload: BinaryData::new(),
username: EncodedString::new(),
password: BinaryData::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
// Number 12 => protocol_name_len + protocol_name (6) + protocol_version (1)+ connect_flags (1) + keep_alive (2) + client_id_len (2)
rm_ln = rm_ln + property_len_len as u32 + 10 + self.client_id.len as u32 + 2;
if self.connect_flags & 0x04 != 0 {
let wil_prop_len_enc = VariableByteIntegerEncoder::encode(self.will_property_len)?;
let wil_prop_len_len = VariableByteIntegerEncoder::len(wil_prop_len_enc);
rm_ln = rm_ln
+ wil_prop_len_len as u32
+ self.will_property_len as u32
+ self.will_topic.len as u32
+ self.will_payload.len as u32;
}
if (self.connect_flags & 0x80) != 0 {
rm_ln = rm_ln + self.username.len as u32 + 2;
}
if self.connect_flags & 0x40 != 0 {
rm_ln = rm_ln + self.password.len as u32 + 2;
}
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_u16(self.protocol_name_len)?;
buff_writer.write_u32(self.protocol_name)?;
buff_writer.write_u8(self.protocol_version)?;
buff_writer.write_u8(self.connect_flags)?;
buff_writer.write_u16(self.keep_alive)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
buff_writer.write_string_ref(&self.client_id)?;
if self.connect_flags & 0x04 != 0 {
buff_writer.write_variable_byte_int(self.will_property_len)?;
buff_writer.write_properties(&self.will_properties)?;
buff_writer.write_string_ref(&self.will_topic)?;
buff_writer.write_binary_ref(&self.will_payload)?;
}
if self.connect_flags & 0x80 != 0 {
buff_writer.write_string_ref(&self.username)?;
}
if self.connect_flags & 0x40 != 0 {
buff_writer.write_binary_ref(&self.password)?;
}
Ok(buff_writer.position)
}
fn decode(&mut self, _buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
log::error!("Decode function is not available for control packet!");
Err(BufferError::WrongPacketToDecode)
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.connect_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,112 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct DisconnectPacket<'a, const MAX_PROPERTIES: usize> {
// 7 - 4 mqtt control packet type, 3-0 flagy
pub fixed_header: u8,
// 1 - 4 B lenght of variable header + len of payload
pub remain_len: u32,
pub disconnect_reason: u8,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
}
impl<'a, const MAX_PROPERTIES: usize> DisconnectPacket<'a, MAX_PROPERTIES> {
fn add_reason(&mut self, reason: u8) {
self.disconnect_reason = reason;
}
}
impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for DisconnectPacket<'a, MAX_PROPERTIES> {
fn new() -> Self {
Self {
fixed_header: PacketType::Disconnect.into(),
remain_len: 5,
disconnect_reason: 0x00,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
buff_writer.write_u8(self.fixed_header)?;
let property_len_enc = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
let rm_len: u32 = 1 + self.property_len + property_len_len as u32;
buff_writer.write_variable_byte_int(rm_len)?;
buff_writer.write_u8(self.disconnect_reason)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties(&self.properties)?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Disconnect).into() {
log::error!("Packet you are trying to decode is not DISCONNECT packet!");
return Err(BufferError::WrongPacketToDecode);
}
self.disconnect_reason = buff_reader.read_u8()?;
return self.decode_properties(buff_reader);
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.disconnect_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

44
mqtt/src/packet/v5/mod.rs Normal file
View File

@@ -0,0 +1,44 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod auth_packet;
pub mod connack_packet;
pub mod mqtt_packet;
pub mod packet_type;
pub mod property;
pub mod puback_packet;
pub mod pubcomp_packet;
pub mod publish_packet;
pub mod pubrec_packet;
pub mod pubrel_packet;
pub mod subscription_packet;
pub mod unsubscription_packet;
pub mod connect_packet;
pub mod disconnect_packet;
pub mod pingreq_packet;
pub mod pingresp_packet;
pub mod reason_codes;
pub mod suback_packet;
pub mod unsuback_packet;

View File

@@ -0,0 +1,107 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::packet_type::PacketType;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::BufferError;
use heapless::Vec;
use super::property::Property;
/// This trait provide interface for mapping MQTTv5 packets to human readable structures
/// which can be later modified and used for communication purposes.
pub trait Packet<'a> {
fn new() -> Self;
/// Method encode provide way how to transfer Packet struct into Byte array (buffer)
fn encode(&mut self, buffer: &mut [u8], buff_len: usize) -> Result<usize, BufferError>;
/// Decode method is opposite of encode - decoding Byte array and mapping it into corresponding Packet struct
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError>;
/// Setter method for packet properties len - not all Packet types support this
fn set_property_len(&mut self, value: u32);
/// Setter method for packet properties len - not all Packet types support this
fn get_property_len(&mut self) -> u32;
/// Method enables pushing new property into packet properties
fn push_to_properties(&mut self, property: Property<'a>);
/// Returns if property is allowed for packet
fn property_allowed(&mut self, property: &Property<'a>) -> bool;
/// Method enables adding properties from client config - each packet decides if property can be used with that or not
fn add_properties<const MAX_PROPERTIES: usize>(
&mut self,
properties: &Vec<Property<'a>, MAX_PROPERTIES>,
) -> u32 {
let mut i = 0;
let max = properties.len();
let mut res: u32 = 0;
loop {
let prop = properties.get(i).unwrap();
if self.property_allowed(prop) {
self.push_to_properties((*prop).clone());
res = res + prop.len() as u32 + 1;
}
i = i + 1;
if i == max {
break;
}
}
return res;
}
/// Setter for packet fixed header
fn set_fixed_header(&mut self, header: u8);
/// Setter for remaining len
fn set_remaining_len(&mut self, remaining_len: u32);
/// Method is decoding Byte array pointing to properties into heapless Vec
/// in packet. If decoding goes wrong method is returning Error
fn decode_properties(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
self.set_property_len(buff_reader.read_variable_byte_int().unwrap());
let mut x: u32 = 0;
let mut prop: Property;
if self.get_property_len() != 0 {
loop {
prop = Property::decode(buff_reader)?;
log::debug!("Parsed property {:?}", prop);
x = x + prop.len() as u32 + 1;
self.push_to_properties(prop);
if x == self.get_property_len() {
break;
}
}
}
Ok(())
}
/// Method is decoding packet header into fixed header part and remaining length
fn decode_fixed_header(
&mut self,
buff_reader: &mut BuffReader,
) -> Result<PacketType, BufferError> {
let first_byte: u8 = buff_reader.read_u8()?;
self.set_fixed_header(first_byte);
self.set_remaining_len(buff_reader.read_variable_byte_int()?);
return Ok(PacketType::from(first_byte));
}
}

View File

@@ -0,0 +1,93 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// x x x x - - - -
#[derive(PartialEq)]
pub enum PacketType {
Reserved,
Connect,
Connack,
Publish,
Puback,
Pubrec,
Pubrel,
Pubcomp,
Subscribe,
Suback,
Unsubscribe,
Unsuback,
Pingreq,
Pingresp,
Disconnect,
Auth,
}
impl From<u8> for PacketType {
fn from(orig: u8) -> Self {
let packet_type: u8 = orig & 0xF0;
return match packet_type {
0x10 => PacketType::Connect,
0x20 => PacketType::Connack,
0x00 => PacketType::Reserved,
0x30 => PacketType::Publish,
0x40 => PacketType::Puback,
0x50 => PacketType::Pubrec,
0x60 => PacketType::Pubrel,
0x70 => PacketType::Pubcomp,
0x80 => PacketType::Subscribe,
0x90 => PacketType::Suback,
0xA0 => PacketType::Unsubscribe,
0xB0 => PacketType::Unsuback,
0xC0 => PacketType::Pingreq,
0xD0 => PacketType::Pingresp,
0xE0 => PacketType::Disconnect,
0xF0 => PacketType::Auth,
_ => PacketType::Reserved,
};
}
}
impl Into<u8> for PacketType {
fn into(self) -> u8 {
return match self {
PacketType::Connect => 0x10,
PacketType::Connack => 0x20,
PacketType::Publish => 0x30,
PacketType::Puback => 0x40,
PacketType::Pubrec => 0x50,
PacketType::Pubrel => 0x60,
PacketType::Pubcomp => 0x70,
PacketType::Subscribe => 0x82,
PacketType::Suback => 0x90,
PacketType::Unsubscribe => 0xA0,
PacketType::Unsuback => 0xB0,
PacketType::Pingreq => 0xC0,
PacketType::Pingresp => 0xD0,
PacketType::Disconnect => 0xE0,
PacketType::Auth => 0xF0,
PacketType::Reserved => 0x00,
};
}
}

View File

@@ -0,0 +1,84 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct PingreqPacket {
pub fixed_header: u8,
pub remain_len: u32,
}
impl PingreqPacket {}
impl<'a> Packet<'a> for PingreqPacket {
fn new() -> Self {
Self {
fixed_header: PacketType::Pingreq.into(),
remain_len: 0,
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(0 as u32)?;
Ok(buff_writer.position)
}
fn decode(&mut self, _buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
log::error!("Pingreq Packet packet does not support decode funtion on client!");
Err(BufferError::WrongPacketToDecode)
}
fn set_property_len(&mut self, _value: u32) {
log::error!("PINGREQ packet does not contain any properties!");
}
fn get_property_len(&mut self) -> u32 {
log::error!("PINGREQ packet does not contain any properties!");
return 0;
}
fn push_to_properties(&mut self, _property: Property<'a>) {
log::error!("PINGREQ packet does not contain any properties!");
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.pingreq_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,92 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct PingrespPacket {
pub fixed_header: u8,
pub remain_len: u32,
}
impl<'a> PingrespPacket {}
impl<'a> Packet<'a> for PingrespPacket {
fn new() -> Self {
Self {
fixed_header: PacketType::Pingresp.into(),
remain_len: 0,
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(self.remain_len)?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
let x = self.decode_fixed_header(buff_reader)?;
if x != (PacketType::Pingresp).into() {
log::error!("Packet you are trying to decode is not PINGRESP packet!");
return Err(BufferError::PacketTypeMismatch);
}
if self.remain_len != 0 {
log::error!("PINGRESP packet does not have 0 lenght!");
return Err(BufferError::PacketTypeMismatch);
}
Ok(())
}
fn set_property_len(&mut self, _value: u32) {
log::error!("PINGRESP packet does not contain any properties!");
}
fn get_property_len(&mut self) -> u32 {
log::error!("PINGRESP packet does not contain any properties!");
return 0;
}
fn push_to_properties(&mut self, _property: Property<'a>) {
log::error!("PINGRESP packet does not contain any properties!");
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.pingresp_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,364 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::{BinaryData, BufferError, EncodedString, StringPair};
#[derive(Debug, Clone)]
pub enum Property<'a> {
PayloadFormat(u8),
MessageExpiryInterval(u32),
ContentType(EncodedString<'a>),
ResponseTopic(EncodedString<'a>),
CorrelationData(BinaryData<'a>),
SubscriptionIdentifier(u32),
SessionExpiryInterval(u32),
AssignedClientIdentifier(EncodedString<'a>),
ServerKeepAlive(u16),
AuthenticationMethod(EncodedString<'a>),
AuthenticationData(BinaryData<'a>),
RequestProblemInformation(u8),
WillDelayInterval(u32),
RequestResponseInformation(u8),
ResponseInformation(EncodedString<'a>),
ServerReference(EncodedString<'a>),
ReasonString(EncodedString<'a>),
ReceiveMaximum(u16),
TopicAliasMaximum(u16),
TopicAlias(u16),
MaximumQoS(u8),
RetainAvailable(u8),
UserProperty(StringPair<'a>),
MaximumPacketSize(u32),
WildcardSubscriptionAvailable(u8),
SubscriptionIdentifierAvailable(u8),
SharedSubscriptionAvailable(u8),
Reserved(),
}
impl<'a> Property<'a> {
pub fn connect_property(&self) -> bool {
return match self {
Property::SessionExpiryInterval(_u) => true,
Property::ReceiveMaximum(_u) => true,
Property::MaximumPacketSize(_u) => true,
Property::TopicAliasMaximum(_u) => true,
Property::RequestResponseInformation(_u) => true,
Property::RequestProblemInformation(_u) => true,
Property::UserProperty(_u) => true,
Property::AuthenticationMethod(_u) => true,
Property::AuthenticationData(_u) => true,
_ => false,
};
}
pub fn connack_property(&self) -> bool {
return match self {
Property::SessionExpiryInterval(_u) => true,
Property::ReceiveMaximum(_u) => true,
Property::MaximumQoS(_u) => true,
Property::MaximumPacketSize(_u) => true,
Property::AssignedClientIdentifier(_u) => true,
Property::TopicAliasMaximum(_u) => true,
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
Property::WildcardSubscriptionAvailable(_u) => true,
Property::SubscriptionIdentifierAvailable(_u) => true,
Property::SharedSubscriptionAvailable(_u) => true,
Property::ServerKeepAlive(_u) => true,
Property::ResponseInformation(_u) => true,
Property::ServerReference(_u) => true,
Property::AuthenticationMethod(_u) => true,
Property::AuthenticationData(_u) => true,
_ => false,
};
}
pub fn publish_property(&self) -> bool {
return match self {
Property::PayloadFormat(_u) => true,
Property::MessageExpiryInterval(_u) => true,
Property::TopicAlias(_u) => true,
Property::ResponseTopic(_u) => true,
Property::CorrelationData(_u) => true,
Property::UserProperty(_u) => true,
Property::SubscriptionIdentifier(_u) => true,
Property::ContentType(_u) => true,
_ => false,
};
}
pub fn puback_property(&self) -> bool {
return match self {
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn pubrec_property(&self) -> bool {
return match self {
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn pubrel_property(&self) -> bool {
return match self {
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn pubcomp_property(&self) -> bool {
return match self {
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn subscribe_property(&self) -> bool {
return match self {
Property::SubscriptionIdentifier(_u) => true,
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn suback_property(&self) -> bool {
return match self {
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn unsubscribe_property(&self) -> bool {
return match self {
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn unsuback_property(&self) -> bool {
return match self {
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn pingreq_property(&self) -> bool {
return match self {
_ => false,
};
}
pub fn pingresp_property(&self) -> bool {
return match self {
_ => false,
};
}
pub fn disconnect_property(&self) -> bool {
return match self {
Property::SessionExpiryInterval(_u) => true,
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
Property::ServerReference(_u) => true,
_ => false,
};
}
pub fn auth_property(&self) -> bool {
return match self {
Property::AuthenticationMethod(_u) => true,
Property::AuthenticationData(_u) => true,
Property::ReasonString(_u) => true,
Property::UserProperty(_u) => true,
_ => false,
};
}
pub fn len(&self) -> u16 {
return match self {
Property::PayloadFormat(_u) => 1,
Property::MessageExpiryInterval(_u) => 4,
Property::ContentType(u) => u.len(),
Property::ResponseTopic(u) => u.len(),
Property::CorrelationData(u) => u.len(),
Property::SubscriptionIdentifier(u) => {
VariableByteIntegerEncoder::len(VariableByteIntegerEncoder::encode(*u).unwrap())
as u16
}
Property::SessionExpiryInterval(_u) => 4,
Property::AssignedClientIdentifier(u) => u.len(),
Property::ServerKeepAlive(_u) => 2,
Property::AuthenticationMethod(u) => u.len(),
Property::AuthenticationData(u) => u.len(),
Property::RequestProblemInformation(_u) => 1,
Property::WillDelayInterval(_u) => 4,
Property::RequestResponseInformation(_u) => 1,
Property::ResponseInformation(u) => u.len(),
Property::ServerReference(u) => u.len(),
Property::ReasonString(u) => u.len(),
Property::ReceiveMaximum(_u) => 2,
Property::TopicAliasMaximum(_u) => 2,
Property::TopicAlias(_u) => 2,
Property::MaximumQoS(_u) => 1,
Property::RetainAvailable(_u) => 1,
Property::UserProperty(u) => u.len(),
Property::MaximumPacketSize(_u) => 4,
Property::WildcardSubscriptionAvailable(_u) => 1,
Property::SubscriptionIdentifierAvailable(_u) => 1,
Property::SharedSubscriptionAvailable(_u) => 1,
_ => 0,
};
}
pub fn encode(&self, buff_writer: &mut BuffWriter<'a>) -> Result<(), BufferError> {
return match self {
Property::PayloadFormat(u) => buff_writer.write_u8(*u),
Property::MessageExpiryInterval(u) => buff_writer.write_u32(*u),
Property::ContentType(u) => buff_writer.write_string_ref(u),
Property::ResponseTopic(u) => buff_writer.write_string_ref(u),
Property::CorrelationData(u) => buff_writer.write_binary_ref(u),
Property::SubscriptionIdentifier(u) => buff_writer.write_variable_byte_int(*u),
Property::SessionExpiryInterval(u) => buff_writer.write_u32(*u),
Property::AssignedClientIdentifier(u) => buff_writer.write_string_ref(u),
Property::ServerKeepAlive(u) => buff_writer.write_u16(*u),
Property::AuthenticationMethod(u) => buff_writer.write_string_ref(u),
Property::AuthenticationData(u) => buff_writer.write_binary_ref(u),
Property::RequestProblemInformation(u) => buff_writer.write_u8(*u),
Property::WillDelayInterval(u) => buff_writer.write_u32(*u),
Property::RequestResponseInformation(u) => buff_writer.write_u8(*u),
Property::ResponseInformation(u) => buff_writer.write_string_ref(u),
Property::ServerReference(u) => buff_writer.write_string_ref(u),
Property::ReasonString(u) => buff_writer.write_string_ref(u),
Property::ReceiveMaximum(u) => buff_writer.write_u16(*u),
Property::TopicAliasMaximum(u) => buff_writer.write_u16(*u),
Property::TopicAlias(u) => buff_writer.write_u16(*u),
Property::MaximumQoS(u) => buff_writer.write_u8(*u),
Property::RetainAvailable(u) => buff_writer.write_u8(*u),
Property::UserProperty(u) => buff_writer.write_string_pair_ref(u),
Property::MaximumPacketSize(u) => buff_writer.write_u32(*u),
Property::WildcardSubscriptionAvailable(u) => buff_writer.write_u8(*u),
Property::SubscriptionIdentifierAvailable(u) => buff_writer.write_u8(*u),
Property::SharedSubscriptionAvailable(u) => buff_writer.write_u8(*u),
_ => Err(BufferError::PropertyNotFound),
};
}
pub fn decode(buff_reader: &mut BuffReader<'a>) -> Result<Property<'a>, BufferError> {
let property_identifier = buff_reader.read_u8();
return match property_identifier {
Ok(0x01) => Ok(Property::PayloadFormat(buff_reader.read_u8()?)),
Ok(0x02) => Ok(Property::MessageExpiryInterval(buff_reader.read_u32()?)),
Ok(0x03) => Ok(Property::ContentType(buff_reader.read_string()?)),
Ok(0x08) => Ok(Property::ResponseTopic(buff_reader.read_string()?)),
Ok(0x09) => Ok(Property::CorrelationData(buff_reader.read_binary()?)),
Ok(0x0B) => Ok(Property::SubscriptionIdentifier(
buff_reader.read_variable_byte_int()?,
)),
Ok(0x11) => Ok(Property::SessionExpiryInterval(buff_reader.read_u32()?)),
Ok(0x12) => Ok(Property::AssignedClientIdentifier(
buff_reader.read_string()?,
)),
Ok(0x13) => Ok(Property::ServerKeepAlive(buff_reader.read_u16()?)),
Ok(0x15) => Ok(Property::AuthenticationMethod(buff_reader.read_string()?)),
Ok(0x16) => Ok(Property::AuthenticationData(buff_reader.read_binary()?)),
Ok(0x17) => Ok(Property::RequestProblemInformation(buff_reader.read_u8()?)),
Ok(0x18) => Ok(Property::WillDelayInterval(buff_reader.read_u32()?)),
Ok(0x19) => Ok(Property::RequestResponseInformation(buff_reader.read_u8()?)),
Ok(0x1A) => Ok(Property::ResponseInformation(buff_reader.read_string()?)),
Ok(0x1C) => Ok(Property::ServerReference(buff_reader.read_string()?)),
Ok(0x1F) => Ok(Property::ReasonString(buff_reader.read_string()?)),
Ok(0x21) => Ok(Property::ReceiveMaximum(buff_reader.read_u16()?)),
Ok(0x22) => Ok(Property::TopicAliasMaximum(buff_reader.read_u16()?)),
Ok(0x23) => Ok(Property::TopicAlias(buff_reader.read_u16()?)),
Ok(0x24) => Ok(Property::MaximumQoS(buff_reader.read_u8()?)),
Ok(0x25) => Ok(Property::RetainAvailable(buff_reader.read_u8()?)),
Ok(0x26) => Ok(Property::UserProperty(buff_reader.read_string_pair()?)),
Ok(0x28) => Ok(Property::WildcardSubscriptionAvailable(
buff_reader.read_u8()?,
)),
Ok(0x29) => Ok(Property::SubscriptionIdentifierAvailable(
buff_reader.read_u8()?,
)),
Ok(0x2A) => Ok(Property::SharedSubscriptionAvailable(
buff_reader.read_u8()?,
)),
Err(err) => Err(err),
_ => Err(BufferError::IdNotFound),
};
}
}
impl Into<u8> for &Property<'a> {
fn into(self) -> u8 {
return match &*self {
Property::PayloadFormat(_u) => 0x01,
Property::MessageExpiryInterval(_u) => 0x02,
Property::ContentType(_u) => 0x03,
Property::ResponseTopic(_u) => 0x08,
Property::CorrelationData(_u) => 0x09,
Property::SubscriptionIdentifier(_u) => 0x0B,
Property::SessionExpiryInterval(_u) => 0x11,
Property::AssignedClientIdentifier(_u) => 0x12,
Property::ServerKeepAlive(_u) => 0x13,
Property::AuthenticationMethod(_u) => 0x15,
Property::AuthenticationData(_u) => 0x16,
Property::RequestProblemInformation(_u) => 0x17,
Property::WillDelayInterval(_u) => 0x18,
Property::RequestResponseInformation(_u) => 0x19,
Property::ResponseInformation(_u) => 0x1A,
Property::ServerReference(_u) => 0x1C,
Property::ReasonString(_u) => 0x1F,
Property::ReceiveMaximum(_u) => 0x21,
Property::TopicAliasMaximum(_u) => 0x22,
Property::TopicAlias(_u) => 0x23,
Property::MaximumQoS(_u) => 0x24,
Property::RetainAvailable(_u) => 0x25,
Property::UserProperty(_u) => 0x26,
Property::MaximumPacketSize(_u) => 0x27,
Property::WildcardSubscriptionAvailable(_u) => 0x28,
Property::SubscriptionIdentifierAvailable(_u) => 0x29,
Property::SharedSubscriptionAvailable(_u) => 0x2A,
_ => 0x00,
};
}
}
impl From<u8> for Property<'a> {
fn from(_orig: u8) -> Self {
return match _orig {
_ => Property::Reserved(),
};
}
}

View File

@@ -0,0 +1,116 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct PubackPacket<'a, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub packet_identifier: u16,
pub reason_code: u8,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
}
impl<'a, const MAX_PROPERTIES: usize> PubackPacket<'a, MAX_PROPERTIES> {}
impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubackPacket<'a, MAX_PROPERTIES> {
fn new() -> Self {
Self {
fixed_header: PacketType::Puback.into(),
remain_len: 0,
packet_identifier: 0,
reason_code: 0,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
rm_ln = rm_ln + property_len_len as u32 + 3;
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_u16(self.packet_identifier)?;
buff_writer.write_u8(self.reason_code)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Puback).into() {
log::error!("Packet you are trying to decode is not PUBACK packet!");
return Err(BufferError::PacketTypeMismatch);
}
self.packet_identifier = buff_reader.read_u16()?;
if self.remain_len != 2 {
self.reason_code = buff_reader.read_u8()?;
}
if self.remain_len < 4 {
self.property_len = 0;
} else {
self.decode_properties(buff_reader)?;
}
Ok(())
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.puback_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,110 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct PubcompPacket<'a, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub packet_identifier: u16,
pub reason_code: u8,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
}
impl<'a, const MAX_PROPERTIES: usize> PubcompPacket<'a, MAX_PROPERTIES> {}
impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubcompPacket<'a, MAX_PROPERTIES> {
fn new() -> Self {
Self {
fixed_header: PacketType::Pubcomp.into(),
remain_len: 0,
packet_identifier: 0,
reason_code: 0,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
rm_ln = rm_ln + property_len_len as u32 + 3;
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_u16(self.packet_identifier)?;
buff_writer.write_u8(self.reason_code)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Pubcomp).into() {
log::error!("Packet you are trying to decode is not PUBCOMP packet!");
return Err(BufferError::PacketTypeMismatch);
}
self.packet_identifier = buff_reader.read_u16()?;
self.reason_code = buff_reader.read_u8()?;
self.decode_properties(buff_reader)?;
Ok(())
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.pubcomp_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,179 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::publish_packet::QualityOfService::{QoS0, QoS1, QoS2, INVALID};
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::{BufferError, EncodedString};
use super::packet_type::PacketType;
use super::property::Property;
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum QualityOfService {
QoS0,
QoS1,
QoS2,
INVALID,
}
impl From<u8> for QualityOfService {
fn from(orig: u8) -> Self {
return match orig {
0 => QoS0,
2 => QoS1,
4 => QoS2,
_ => INVALID,
};
}
}
impl Into<u8> for QualityOfService {
fn into(self) -> u8 {
return match self {
QoS0 => 0,
QoS1 => 2,
QoS2 => 4,
INVALID => 3,
};
}
}
pub struct PublishPacket<'a, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub topic_name: EncodedString<'a>,
pub packet_identifier: u16,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
pub message: Option<&'a [u8]>,
}
impl<'a, const MAX_PROPERTIES: usize> PublishPacket<'a, MAX_PROPERTIES> {
pub fn add_topic_name(&mut self, topic_name: &'a str) {
self.topic_name.string = topic_name;
self.topic_name.len = topic_name.len() as u16;
}
pub fn add_message(&mut self, message: &'a [u8]) {
self.message = Some(message);
}
pub fn add_qos(&mut self, qos: QualityOfService) {
self.fixed_header = self.fixed_header | <QualityOfService as Into<u8>>::into(qos);
}
pub fn add_identifier(&mut self, identifier: u16) {
self.packet_identifier = identifier;
}
}
impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PublishPacket<'a, MAX_PROPERTIES> {
fn new() -> Self {
Self {
fixed_header: PacketType::Publish.into(),
remain_len: 0,
topic_name: EncodedString::new(),
packet_identifier: 1,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
message: None,
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
let msg_len = self.message.unwrap().len() as u32;
rm_ln = rm_ln + property_len_len as u32 + msg_len + self.topic_name.len as u32 + 2;
buff_writer.write_u8(self.fixed_header)?;
let qos = self.fixed_header & 0x03;
if qos != 0 {
rm_ln = rm_ln + 2;
}
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_string_ref(&self.topic_name)?;
if qos != 0 {
buff_writer.write_u16(self.packet_identifier)?;
}
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
buff_writer.insert_ref(msg_len as usize, self.message.unwrap())?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Publish).into() {
log::error!("Packet you are trying to decode is not PUBLISH packet!");
return Err(BufferError::PacketTypeMismatch);
}
self.topic_name = buff_reader.read_string()?;
let qos = self.fixed_header & 0x03;
if qos != 0 {
// Decode only for QoS 1 / 2
self.packet_identifier = buff_reader.read_u16()?;
}
self.decode_properties(buff_reader)?;
let mut total_len =
VariableByteIntegerEncoder::len(VariableByteIntegerEncoder::encode(self.remain_len)?);
total_len = total_len + 1 + self.remain_len as usize;
self.message = Some(buff_reader.read_message(total_len));
Ok(())
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.publish_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,109 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct PubrecPacket<'a, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub packet_identifier: u16,
pub reason_code: u8,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
}
impl<'a, const MAX_PROPERTIES: usize> PubrecPacket<'a, MAX_PROPERTIES> {}
impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubrecPacket<'a, MAX_PROPERTIES> {
fn new() -> Self {
Self {
fixed_header: PacketType::Pubrec.into(),
remain_len: 0,
packet_identifier: 0,
reason_code: 0,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
rm_ln = rm_ln + property_len_len as u32 + 3;
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_u16(self.packet_identifier)?;
buff_writer.write_u8(self.reason_code)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Pubrec).into() {
log::error!("Packet you are trying to decode is not PUBREC packet!");
return Err(BufferError::PacketTypeMismatch);
}
self.packet_identifier = buff_reader.read_u16()?;
self.reason_code = buff_reader.read_u8()?;
return self.decode_properties(buff_reader);
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.pubrec_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,109 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct PubrelPacket<'a, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub packet_identifier: u16,
pub reason_code: u8,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
}
impl<'a, const MAX_PROPERTIES: usize> PubrelPacket<'a, MAX_PROPERTIES> {}
impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubrelPacket<'a, MAX_PROPERTIES> {
fn new() -> Self {
Self {
fixed_header: 0,
remain_len: 0,
packet_identifier: 0,
reason_code: 0,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
rm_ln = rm_ln + property_len_len as u32 + 3;
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_u16(self.packet_identifier)?;
buff_writer.write_u8(self.reason_code)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
Ok(buff_writer.position)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Pubrel).into() {
log::error!("Packet you are trying to decode is not PUBREL packet!");
return Err(BufferError::PacketTypeMismatch);
}
self.packet_identifier = buff_reader.read_u16()?;
self.reason_code = buff_reader.read_u8()?;
return self.decode_properties(buff_reader);
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.pubrel_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,241 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use core::fmt::{Display, Formatter};
#[derive(Debug, PartialEq)]
pub enum ReasonCode {
Success,
GrantedQoS1,
GrantedQoS2,
DisconnectWithWillMessage,
NoMatchingSubscribers,
NoSubscriptionExisted,
ContinueAuth,
ReAuthenticate,
UnspecifiedError,
MalformedPacket,
ProtocolError,
ImplementationSpecificError,
UnsupportedProtocolVersion,
ClientIdNotValid,
BadUserNameOrPassword,
NotAuthorized,
ServerUnavailable,
ServerBusy,
Banned,
ServerShuttingDown,
BadAuthMethod,
KeepAliveTimeout,
SessionTakeOver,
TopicFilterInvalid,
TopicNameInvalid,
PacketIdentifierInUse,
PacketIdentifierNotFound,
ReceiveMaximumExceeded,
TopicAliasInvalid,
PacketTooLarge,
MessageRateTooHigh,
QuotaExceeded,
AdministrativeAction,
PayloadFormatInvalid,
RetainNotSupported,
QoSNotSupported,
UseAnotherServer,
ServerMoved,
SharedSubscriptionNotSupported,
ConnectionRateExceeded,
MaximumConnectTime,
SubscriptionIdentifiersNotSupported,
WildcardSubscriptionNotSupported,
TimerNotSupported,
BuffError,
NetworkError,
}
impl Into<u8> for ReasonCode {
fn into(self) -> u8 {
return match self {
ReasonCode::Success => 0x00,
ReasonCode::GrantedQoS1 => 0x01,
ReasonCode::GrantedQoS2 => 0x02,
ReasonCode::DisconnectWithWillMessage => 0x04,
ReasonCode::NoMatchingSubscribers => 0x10,
ReasonCode::NoSubscriptionExisted => 0x11,
ReasonCode::ContinueAuth => 0x18,
ReasonCode::ReAuthenticate => 0x19,
ReasonCode::UnspecifiedError => 0x80,
ReasonCode::MalformedPacket => 0x81,
ReasonCode::ProtocolError => 0x82,
ReasonCode::ImplementationSpecificError => 0x83,
ReasonCode::UnsupportedProtocolVersion => 0x84,
ReasonCode::ClientIdNotValid => 0x85,
ReasonCode::BadUserNameOrPassword => 0x86,
ReasonCode::NotAuthorized => 0x87,
ReasonCode::ServerUnavailable => 0x88,
ReasonCode::ServerBusy => 0x89,
ReasonCode::Banned => 0x8A,
ReasonCode::ServerShuttingDown => 0x8B,
ReasonCode::BadAuthMethod => 0x8C,
ReasonCode::KeepAliveTimeout => 0x8D,
ReasonCode::SessionTakeOver => 0x8E,
ReasonCode::TopicFilterInvalid => 0x8F,
ReasonCode::TopicNameInvalid => 0x90,
ReasonCode::PacketIdentifierInUse => 0x91,
ReasonCode::PacketIdentifierNotFound => 0x92,
ReasonCode::ReceiveMaximumExceeded => 0x93,
ReasonCode::TopicAliasInvalid => 0x94,
ReasonCode::PacketTooLarge => 0x95,
ReasonCode::MessageRateTooHigh => 0x96,
ReasonCode::QuotaExceeded => 0x97,
ReasonCode::AdministrativeAction => 0x98,
ReasonCode::PayloadFormatInvalid => 0x99,
ReasonCode::RetainNotSupported => 0x9A,
ReasonCode::QoSNotSupported => 0x9B,
ReasonCode::UseAnotherServer => 0x9C,
ReasonCode::ServerMoved => 0x9D,
ReasonCode::SharedSubscriptionNotSupported => 0x9E,
ReasonCode::ConnectionRateExceeded => 0x9F,
ReasonCode::MaximumConnectTime => 0xA0,
ReasonCode::SubscriptionIdentifiersNotSupported => 0xA1,
ReasonCode::WildcardSubscriptionNotSupported => 0xA2,
ReasonCode::TimerNotSupported => 0xFD,
ReasonCode::BuffError => 0xFE,
ReasonCode::NetworkError => 0xFF,
};
}
}
impl From<u8> for ReasonCode {
fn from(orig: u8) -> Self {
return match orig {
0x00 => ReasonCode::Success,
0x01 => ReasonCode::GrantedQoS1,
0x02 => ReasonCode::GrantedQoS2,
0x04 => ReasonCode::DisconnectWithWillMessage,
0x10 => ReasonCode::NoMatchingSubscribers,
0x11 => ReasonCode::NoSubscriptionExisted,
0x18 => ReasonCode::ContinueAuth,
0x19 => ReasonCode::ReAuthenticate,
0x80 => ReasonCode::UnspecifiedError,
0x81 => ReasonCode::MalformedPacket,
0x82 => ReasonCode::ProtocolError,
0x83 => ReasonCode::ImplementationSpecificError,
0x84 => ReasonCode::UnsupportedProtocolVersion,
0x85 => ReasonCode::ClientIdNotValid,
0x86 => ReasonCode::BadUserNameOrPassword,
0x87 => ReasonCode::NotAuthorized,
0x88 => ReasonCode::ServerUnavailable,
0x89 => ReasonCode::ServerBusy,
0x8A => ReasonCode::Banned,
0x8B => ReasonCode::ServerShuttingDown,
0x8C => ReasonCode::BadAuthMethod,
0x8D => ReasonCode::KeepAliveTimeout,
0x8E => ReasonCode::SessionTakeOver,
0x8F => ReasonCode::TopicFilterInvalid,
0x90 => ReasonCode::TopicNameInvalid,
0x91 => ReasonCode::PacketIdentifierInUse,
0x92 => ReasonCode::PacketIdentifierNotFound,
0x93 => ReasonCode::ReceiveMaximumExceeded,
0x94 => ReasonCode::TopicAliasInvalid,
0x95 => ReasonCode::PacketTooLarge,
0x96 => ReasonCode::MessageRateTooHigh,
0x97 => ReasonCode::QuotaExceeded,
0x98 => ReasonCode::AdministrativeAction,
0x99 => ReasonCode::PayloadFormatInvalid,
0x9A => ReasonCode::RetainNotSupported,
0x9B => ReasonCode::QoSNotSupported,
0x9C => ReasonCode::UseAnotherServer,
0x9D => ReasonCode::ServerMoved,
0x9E => ReasonCode::SharedSubscriptionNotSupported,
0xA0 => ReasonCode::MaximumConnectTime,
0xA1 => ReasonCode::SubscriptionIdentifiersNotSupported,
0xA2 => ReasonCode::WildcardSubscriptionNotSupported,
0xFD => ReasonCode::TimerNotSupported,
0xFE => ReasonCode::BuffError,
_ => ReasonCode::NetworkError,
};
}
}
impl Display for ReasonCode {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match *self {
ReasonCode::Success => write!(f, "Operation was successful!"),
ReasonCode::GrantedQoS1 => write!(f, "Granted QoS level 1!"),
ReasonCode::GrantedQoS2 => write!(f, "Granted QoS level 2!"),
ReasonCode::DisconnectWithWillMessage => write!(f, "Disconnected with Will message!"),
ReasonCode::NoMatchingSubscribers => write!(f, "No matching subscribers on broker!"),
ReasonCode::NoSubscriptionExisted => write!(f, "Subscription not exist!"),
ReasonCode::ContinueAuth => write!(f, "Broker asks for more AUTH packets!"),
ReasonCode::ReAuthenticate => write!(f, "Broker requires re-authentication!"),
ReasonCode::UnspecifiedError => write!(f, "Unspecified error!"),
ReasonCode::MalformedPacket => write!(f, "Malformed packet sent!"),
ReasonCode::ProtocolError => write!(f, "Protocol specific error!"),
ReasonCode::ImplementationSpecificError => write!(f, "Implementation specific error!"),
ReasonCode::UnsupportedProtocolVersion => write!(f, "Unsupported protocol version!"),
ReasonCode::ClientIdNotValid => write!(f, "Client sent not valid identification"),
ReasonCode::BadUserNameOrPassword => {
write!(f, "Authentication error, username of password not valid!")
}
ReasonCode::NotAuthorized => write!(f, "Client not authorized!"),
ReasonCode::ServerUnavailable => write!(f, "Server unavailable!"),
ReasonCode::ServerBusy => write!(f, "Server is busy!"),
ReasonCode::Banned => write!(f, "Client is banned on broker!"),
ReasonCode::ServerShuttingDown => write!(f, "Server is shutting down!"),
ReasonCode::BadAuthMethod => write!(f, "Provided bad authentication method!"),
ReasonCode::KeepAliveTimeout => write!(f, "Client reached timeout"),
ReasonCode::SessionTakeOver => write!(f, "Took over session!"),
ReasonCode::TopicFilterInvalid => write!(f, "Topic filter is not valid!"),
ReasonCode::TopicNameInvalid => write!(f, "Topic name is not valid!"),
ReasonCode::PacketIdentifierInUse => write!(f, "Packet identifier is already in use!"),
ReasonCode::PacketIdentifierNotFound => write!(f, "Packet identifier not found!"),
ReasonCode::ReceiveMaximumExceeded => write!(f, "Maximum receive amount exceeded!"),
ReasonCode::TopicAliasInvalid => write!(f, "Invalid topic alias!"),
ReasonCode::PacketTooLarge => write!(f, "Sent packet was too large!"),
ReasonCode::MessageRateTooHigh => write!(f, "Message rate is too high!"),
ReasonCode::QuotaExceeded => write!(f, "Quota exceeded!"),
ReasonCode::AdministrativeAction => write!(f, "Administrative action!"),
ReasonCode::PayloadFormatInvalid => write!(f, "Invalid payload format!"),
ReasonCode::RetainNotSupported => write!(f, "Message retain not supported!"),
ReasonCode::QoSNotSupported => write!(f, "Used QoS is not supported!"),
ReasonCode::UseAnotherServer => write!(f, "Use another server!"),
ReasonCode::ServerMoved => write!(f, "Server moved!"),
ReasonCode::SharedSubscriptionNotSupported => {
write!(f, "Shared subscription is not supported")
}
ReasonCode::ConnectionRateExceeded => write!(f, "Connection rate exceeded!"),
ReasonCode::MaximumConnectTime => write!(f, "Maximum connect time exceeded!"),
ReasonCode::SubscriptionIdentifiersNotSupported => {
write!(f, "Subscription identifier not supported!")
}
ReasonCode::WildcardSubscriptionNotSupported => {
write!(f, "Wildcard subscription not supported!")
}
ReasonCode::TimerNotSupported => write!(f, "Timer implementation is not provided"),
ReasonCode::BuffError => write!(f, "Error encountered during write / read from packet"),
ReasonCode::NetworkError => write!(f, "Unknown error!"),
}
}
}

View File

@@ -0,0 +1,120 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use heapless::Vec;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct SubackPacket<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub packet_identifier: u16,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
pub reason_codes: Vec<u8, MAX_REASONS>,
}
impl<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize>
SubackPacket<'a, MAX_REASONS, MAX_PROPERTIES>
{
pub fn read_reason_codes(
&mut self,
buff_reader: &mut BuffReader<'a>,
) -> Result<(), BufferError> {
let rm_ln_ln = VariableByteIntegerEncoder::len(
VariableByteIntegerEncoder::encode(self.remain_len).unwrap(),
);
let max = self.remain_len as usize + rm_ln_ln + 1;
if buff_reader.position >= max {
return Ok(());
}
loop {
self.reason_codes.push(buff_reader.read_u8()?);
if buff_reader.position == max {
break;
}
}
return Ok(());
}
}
impl<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize> Packet<'a>
for SubackPacket<'a, MAX_REASONS, MAX_PROPERTIES>
{
fn new() -> Self {
Self {
fixed_header: PacketType::Suback.into(),
remain_len: 0,
packet_identifier: 0,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
reason_codes: Vec::<u8, MAX_REASONS>::new(),
}
}
fn encode(&mut self, _buffer: &mut [u8], _buffer_len: usize) -> Result<usize, BufferError> {
log::error!("SUBACK packet does not support encoding!");
return Err(BufferError::WrongPacketToEncode);
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Suback).into() {
log::error!("Packet you are trying to decode is not SUBACK packet!");
return Err(BufferError::PacketTypeMismatch);
}
self.packet_identifier = buff_reader.read_u16()?;
self.decode_properties(buff_reader)?;
return self.read_reason_codes(buff_reader);
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.suback_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,135 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use super::packet_type::PacketType;
use super::property::Property;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::publish_packet::QualityOfService;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::{BufferError, TopicFilter};
pub struct SubscriptionPacket<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub packet_identifier: u16,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
pub topic_filter_len: u16,
pub topic_filters: Vec<TopicFilter<'a>, MAX_FILTERS>,
}
impl<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize>
SubscriptionPacket<'a, MAX_FILTERS, MAX_PROPERTIES>
{
pub fn add_new_filter(&mut self, topic_name: &'a str, qos: QualityOfService) {
let len = topic_name.len();
let mut new_filter = TopicFilter::new();
new_filter.filter.string = topic_name;
new_filter.filter.len = len as u16;
new_filter.sub_options =
new_filter.sub_options | (<QualityOfService as Into<u8>>::into(qos) >> 1);
self.topic_filters.push(new_filter);
self.topic_filter_len = self.topic_filter_len + 1;
}
}
impl<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize> Packet<'a>
for SubscriptionPacket<'a, MAX_FILTERS, MAX_PROPERTIES>
{
fn new() -> Self {
let x = Self {
fixed_header: PacketType::Subscribe.into(),
remain_len: 0,
packet_identifier: 1,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
topic_filter_len: 0,
topic_filters: Vec::<TopicFilter<'a>, MAX_FILTERS>::new(),
};
return x;
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
let mut lt = 0;
let mut filters_len = 0;
loop {
filters_len = filters_len + self.topic_filters.get(lt).unwrap().filter.len + 3;
lt = lt + 1;
if lt == self.topic_filter_len as usize {
break;
}
}
rm_ln = rm_ln + property_len_len as u32 + 2 + filters_len as u32;
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_u16(self.packet_identifier)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
buff_writer.write_topic_filters_ref(
true,
self.topic_filter_len as usize,
&self.topic_filters,
)?;
Ok(buff_writer.position)
}
fn decode(&mut self, _buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
log::error!("Subscribe packet does not support decode funtion on client!");
Err(BufferError::WrongPacketToDecode)
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.subscribe_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,114 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::packet::v5::mqtt_packet::Packet;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::BufferError;
use super::packet_type::PacketType;
use super::property::Property;
pub struct UnsubackPacket<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub packet_identifier: u16,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
pub reason_codes: Vec<u8, MAX_REASONS>,
}
impl<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize>
UnsubackPacket<'a, MAX_REASONS, MAX_PROPERTIES>
{
pub fn read_reason_codes(
&mut self,
buff_reader: &mut BuffReader<'a>,
) -> Result<(), BufferError> {
let mut i = 0;
loop {
self.reason_codes.push(buff_reader.read_u8()?);
i = i + 1;
if i == MAX_REASONS {
break;
}
}
Ok(())
}
}
impl<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize> Packet<'a>
for UnsubackPacket<'a, MAX_REASONS, MAX_PROPERTIES>
{
fn new() -> Self {
Self {
fixed_header: PacketType::Unsuback.into(),
remain_len: 0,
packet_identifier: 0,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
reason_codes: Vec::<u8, MAX_REASONS>::new(),
}
}
fn encode(&mut self, _buffer: &mut [u8], _buffer_len: usize) -> Result<usize, BufferError> {
log::error!("UNSUBACK packet does not support encoding!");
Err(BufferError::WrongPacketToEncode)
}
fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
if self.decode_fixed_header(buff_reader)? != (PacketType::Unsuback).into() {
log::error!("Packet you are trying to decode is not UNSUBACK packet!");
return Err(BufferError::PacketTypeMismatch);
}
self.packet_identifier = buff_reader.read_u16()?;
self.decode_properties(buff_reader)?;
return self.read_reason_codes(buff_reader);
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.unsuback_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,135 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::publish_packet::QualityOfService;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::{BufferError, TopicFilter};
use super::property::Property;
pub struct UnsubscriptionPacket<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize> {
pub fixed_header: u8,
pub remain_len: u32,
pub packet_identifier: u16,
pub property_len: u32,
pub properties: Vec<Property<'a>, MAX_PROPERTIES>,
pub topic_filter_len: u16,
pub topic_filters: Vec<TopicFilter<'a>, MAX_FILTERS>,
}
impl<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize>
UnsubscriptionPacket<'a, MAX_FILTERS, MAX_PROPERTIES>
{
pub fn add_new_filter(&mut self, topic_name: &'a str, qos: QualityOfService) {
let len = topic_name.len();
let mut new_filter = TopicFilter::new();
new_filter.filter.string = topic_name;
new_filter.filter.len = len as u16;
new_filter.sub_options =
new_filter.sub_options | (<QualityOfService as Into<u8>>::into(qos) >> 1);
self.topic_filters.push(new_filter);
self.topic_filter_len = self.topic_filter_len + 1;
}
}
impl<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize> Packet<'a>
for UnsubscriptionPacket<'a, MAX_FILTERS, MAX_PROPERTIES>
{
fn new() -> Self {
Self {
fixed_header: 0,
remain_len: 0,
packet_identifier: 0,
property_len: 0,
properties: Vec::<Property<'a>, MAX_PROPERTIES>::new(),
topic_filter_len: 0,
topic_filters: Vec::<TopicFilter<'a>, MAX_FILTERS>::new(),
}
}
fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result<usize, BufferError> {
let mut buff_writer = BuffWriter::new(buffer, buffer_len);
let mut rm_ln = self.property_len;
let property_len_enc: [u8; 4] = VariableByteIntegerEncoder::encode(self.property_len)?;
let property_len_len = VariableByteIntegerEncoder::len(property_len_enc);
let mut lt = 0;
let mut filters_len = 0;
loop {
filters_len = filters_len + self.topic_filters.get(lt).unwrap().filter.len + 2;
lt = lt + 1;
if lt == self.topic_filter_len as usize {
break;
}
}
rm_ln = rm_ln + property_len_len as u32 + 2 + filters_len as u32;
buff_writer.write_u8(self.fixed_header)?;
buff_writer.write_variable_byte_int(rm_ln)?;
buff_writer.write_u16(self.packet_identifier)?;
buff_writer.write_variable_byte_int(self.property_len)?;
buff_writer.write_properties::<MAX_PROPERTIES>(&self.properties)?;
buff_writer.write_topic_filters_ref(
false,
self.topic_filter_len as usize,
&self.topic_filters,
)?;
Ok(buff_writer.position)
}
fn decode(&mut self, _buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> {
log::error!("Unsubscribe packet does not support decode funtion on client!");
Err(BufferError::WrongPacketToDecode)
}
fn set_property_len(&mut self, value: u32) {
self.property_len = value;
}
fn get_property_len(&mut self) -> u32 {
return self.property_len;
}
fn push_to_properties(&mut self, property: Property<'a>) {
self.properties.push(property);
}
fn property_allowed(&mut self, property: &Property<'a>) -> bool {
property.unsubscribe_property()
}
fn set_fixed_header(&mut self, header: u8) {
self.fixed_header = header;
}
fn set_remaining_len(&mut self, remaining_len: u32) {
self.remain_len = remaining_len;
}
}

View File

@@ -0,0 +1,228 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
extern crate alloc;
use alloc::string::String;
use core::time::Duration;
use std::future::Future;
use tokio::time::sleep;
use tokio::{join, task};
use tokio_test::assert_ok;
use crate::client::client_config::ClientConfig;
use crate::client::client_v5::MqttClientV5;
use crate::network::network_trait::{NetworkConnection, NetworkConnectionFactory};
use crate::packet::v5::property::Property;
use crate::packet::v5::publish_packet::QualityOfService;
use crate::packet::v5::reason_codes::ReasonCode;
use crate::packet::v5::reason_codes::ReasonCode::NotAuthorized;
use crate::tokio_net::tokio_network::{TokioNetwork, TokioNetworkFactory};
use crate::utils::types::BufferError;
static IP: [u8; 4] = [127, 0, 0, 1];
static PORT: u16 = 1883;
static USERNAME: &str = "test";
static PASSWORD: &str = "testPass";
static MSG: &str = "testMessage";
fn init() {
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Info)
.format_timestamp_nanos()
.try_init();
}
async fn publish_core<'b>(
client: &mut MqttClientV5<'b, TokioNetwork, 5>,
topic: &str,
) -> Result<(), ReasonCode> {
log::info!(
"[Publisher] Connection to broker with username {} and password {}",
USERNAME,
PASSWORD
);
let mut result = { client.connect_to_broker().await };
assert_ok!(result);
log::info!("[Publisher] Waiting {} seconds before sending", 5);
sleep(Duration::from_secs(5)).await;
log::info!("[Publisher] Sending new message {} to topic {}", MSG, topic);
result = { client.send_message(topic, MSG).await };
assert_ok!(result);
log::info!("[Publisher] Disconnecting!");
result = { client.disconnect().await };
assert_ok!(result);
Ok(())
}
async fn publish(qos: QualityOfService, topic: &str) -> Result<(), ReasonCode> {
let mut tokio_factory: TokioNetworkFactory = TokioNetworkFactory::new();
let mut tokio_network: TokioNetwork = tokio_factory.connect(IP, PORT).await?;
let mut config = ClientConfig::new();
config.add_qos(qos);
config.add_username(USERNAME);
config.add_password(PASSWORD);
config.max_packet_size = 100;
let mut recv_buffer = [0; 80];
let mut write_buffer = [0; 80];
let mut client = MqttClientV5::<TokioNetwork, 5>::new(
&mut tokio_network,
&mut write_buffer,
80,
&mut recv_buffer,
80,
config,
);
publish_core(&mut client, topic).await
}
async fn receive_core<'b>(
client: &mut MqttClientV5<'b, TokioNetwork, 5>,
topic: &str,
) -> Result<(), ReasonCode> {
log::info!(
"[Receiver] Connection to broker with username {} and password {}",
USERNAME,
PASSWORD
);
let mut result = { client.connect_to_broker().await };
assert_ok!(result);
log::info!("[Receiver] Subscribing to topic {}", topic);
result = { client.subscribe_to_topic(topic).await };
assert_ok!(result);
log::info!("[Receiver] Waiting for new message!");
let msg = { client.receive_message().await };
assert_ok!(msg);
let act_message = String::from_utf8_lossy(msg?);
log::info!("[Receiver] Got new message: {}", act_message);
assert_eq!(act_message, MSG);
log::info!("[Receiver] Disconnecting");
result = { client.disconnect().await };
assert_ok!(result);
Ok(())
}
async fn receive(qos: QualityOfService, topic: &str) -> Result<(), ReasonCode> {
let mut tokio_factory: TokioNetworkFactory = TokioNetworkFactory::new();
let mut tokio_network: TokioNetwork = tokio_factory.connect(IP, PORT).await?;
let mut config = ClientConfig::new();
config.add_qos(qos);
config.add_username(USERNAME);
config.add_password(PASSWORD);
config.max_packet_size = 60;
config.properties.push(Property::ReceiveMaximum(20));
let mut recv_buffer = [0; 100];
let mut write_buffer = [0; 100];
let mut client = MqttClientV5::<TokioNetwork, 5>::new(
&mut tokio_network,
&mut write_buffer,
100,
&mut recv_buffer,
100,
config,
);
receive_core(&mut client, topic).await
}
async fn receive_with_wrong_cred(qos: QualityOfService) -> Result<(), ReasonCode> {
let mut tokio_factory: TokioNetworkFactory = TokioNetworkFactory::new();
let mut tokio_network: TokioNetwork = tokio_factory.connect(IP, PORT).await?;
let mut config = ClientConfig::new();
config.add_qos(qos);
config.add_username("xyz");
config.add_password(PASSWORD);
config.max_packet_size = 60;
config.properties.push(Property::ReceiveMaximum(20));
let mut recv_buffer = [0; 100];
let mut write_buffer = [0; 100];
let mut client = MqttClientV5::<TokioNetwork, 5>::new(
&mut tokio_network,
&mut write_buffer,
100,
&mut recv_buffer,
100,
config,
);
log::info!(
"[Receiver] Connection to broker with username {} and password {}",
"xyz",
PASSWORD
);
let result = { client.connect_to_broker().await };
assert!(result.is_err());
assert_eq!(result.unwrap_err(), NotAuthorized);
Ok(())
}
#[tokio::test]
async fn simple_publish_recv() {
init();
log::info!("Running simple integration test");
let recv =
task::spawn(async move { receive(QualityOfService::QoS0, "test/recv/simple").await });
let publ =
task::spawn(async move { publish(QualityOfService::QoS0, "test/recv/simple").await });
let (r, p) = join!(recv, publ);
assert_ok!(r.unwrap());
assert_ok!(p.unwrap());
}
#[tokio::test]
async fn simple_publish_recv_qos() {
init();
log::info!("Running simple integration test with Quality of Service 1");
let recv = task::spawn(async move { receive(QualityOfService::QoS1, "test/recv/qos").await });
let publ = task::spawn(async move { publish(QualityOfService::QoS1, "test/recv/qos").await });
let (r, p) = join!(recv, publ);
assert_ok!(r.unwrap());
assert_ok!(p.unwrap());
}
#[tokio::test]
async fn simple_publish_recv_wrong_cred() {
init();
log::info!("Running simple integration test wrong credentials");
let recv = task::spawn(async move { receive_with_wrong_cred(QualityOfService::QoS1).await });
let recv_right =
task::spawn(async move { receive(QualityOfService::QoS0, "test/recv/wrong").await });
let publ = task::spawn(async move { publish(QualityOfService::QoS1, "test/recv/wrong").await });
let (r, rv, p) = join!(recv, recv_right, publ);
assert_ok!(rv.unwrap());
assert_ok!(p.unwrap());
}

View File

@@ -0,0 +1,24 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod integration_test_single;

33
mqtt/src/tests/mod.rs Normal file
View File

@@ -0,0 +1,33 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#[cfg(test)]
#[allow(unused_must_use)]
pub mod unit;
#[allow(dead_code)]
#[allow(unused_must_use)]
#[allow(unused_imports)]
#[cfg(feature = "tokio")]
pub mod integration;

View File

@@ -0,0 +1,25 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod variable_byte_integer_unit;

View File

@@ -0,0 +1,80 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::encoding::variable_byte_integer::{
VariableByteInteger, VariableByteIntegerDecoder, VariableByteIntegerEncoder,
};
use crate::utils::types::BufferError;
#[test]
fn test_decode() {
static BUFFER: VariableByteInteger = [0x81, 0x81, 0x81, 0x01];
let decoded = VariableByteIntegerDecoder::decode(BUFFER);
assert!(decoded.is_ok());
assert_eq!(decoded.unwrap(), 2113665);
}
#[test]
fn test_decode_small() {
static BUFFER: VariableByteInteger = [0x81, 0x81, 0x01, 0x85];
let decoded = VariableByteIntegerDecoder::decode(BUFFER);
assert!(decoded.is_ok());
assert_eq!(decoded.unwrap(), 16_513);
}
#[test]
fn test_encode() {
let encoded = VariableByteIntegerEncoder::encode(211_366_5);
assert!(encoded.is_ok());
let res = encoded.unwrap();
assert_eq!(res, [0x81, 0x81, 0x81, 0x01]);
assert_eq!(VariableByteIntegerEncoder::len(res), 4);
}
#[test]
fn test_encode_small() {
let encoded = VariableByteIntegerEncoder::encode(16_513);
assert!(encoded.is_ok());
let res = encoded.unwrap();
assert_eq!(res, [0x81, 0x81, 0x01, 0x00]);
assert_eq!(VariableByteIntegerEncoder::len(res), 3);
}
#[test]
fn test_encode_extra_small() {
let encoded = VariableByteIntegerEncoder::encode(5);
assert!(encoded.is_ok());
let res = encoded.unwrap();
assert_eq!(res, [0x05, 0x00, 0x00, 0x00]);
assert_eq!(VariableByteIntegerEncoder::len(res), 1);
}
#[test]
fn test_encode_max() {
let encoded = VariableByteIntegerEncoder::encode(288_435_455);
assert!(encoded.is_err());
assert_eq!(encoded.unwrap_err(), BufferError::EncodingError);
}

View File

@@ -0,0 +1,27 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod encoding;
pub mod packet;
pub mod utils;

View File

@@ -0,0 +1,25 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod v5;

View File

@@ -0,0 +1,86 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::connack_packet::ConnackPacket;
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::property::Property;
use crate::packet::v5::reason_codes::ReasonCode;
use crate::utils::buffer_reader::BuffReader;
#[test]
fn test_encode() {
let mut buffer: [u8; 100] = [0; 100];
let mut connack = ConnackPacket::<2>::new();
connack.property_len = 3;
let prop = Property::ReceiveMaximum(21);
connack.properties.push(prop);
connack.connect_reason_code = ReasonCode::ServerMoved.into();
connack.ack_flags = 0x45;
let res = connack.encode(&mut buffer, 100);
assert!(res.is_ok());
assert_eq!(
buffer[0..res.unwrap()],
[
0x20,
0x06,
0x45,
ReasonCode::ServerMoved.into(),
0x03,
0x21,
0x00,
0x15
]
)
}
#[test]
fn test_decode() {
let mut buffer: [u8; 8] = [
0x20,
0x06,
0x45,
ReasonCode::ServerMoved.into(),
0x03,
0x21,
0x00,
0x15,
];
let mut connack_res = ConnackPacket::<2>::new();
let res = connack_res.decode(&mut BuffReader::new(&mut buffer, 8));
assert!(res.is_ok());
assert_eq!(connack_res.property_len, 3);
assert_eq!(connack_res.ack_flags, 0x45);
assert_eq!(
connack_res.connect_reason_code,
ReasonCode::ServerMoved.into()
);
assert_eq!(connack_res.property_len, 3);
let prop = connack_res.properties.get(0).unwrap();
assert_eq!(<&Property as Into<u8>>::into(prop), 0x21);
if let Property::ReceiveMaximum(u) = *prop {
assert_eq!(u, 21);
}
}

View File

@@ -0,0 +1,42 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::connect_packet::ConnectPacket;
use crate::packet::v5::mqtt_packet::Packet;
#[test]
fn test_encode() {
let mut buffer: [u8; 100] = [0; 100];
let mut connect = ConnectPacket::<1, 0>::clean();
let res = connect.encode(&mut buffer, 100);
assert!(res.is_ok());
assert_eq!(
buffer[0..res.unwrap()],
[
0x10, 0x10, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, 0x02, 0x00, 0x3c, 0x03, 0x21,
0x00, 0x14, 0x00, 0x00
]
)
}

View File

@@ -0,0 +1,64 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::disconnect_packet::DisconnectPacket;
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::utils::buffer_reader::BuffReader;
use heapless::Vec;
#[test]
fn test_encode() {
let mut buffer: [u8; 10] = [0; 10];
let mut packet = DisconnectPacket::<1>::new();
let prop: Property = Property::SessionExpiryInterval(512);
let mut props = Vec::<Property, 1>::new();
props.push(prop);
packet.property_len = packet.add_properties(&props);
let res = packet.encode(&mut buffer, 100);
assert!(res.is_ok());
assert_eq!(
buffer[0..res.unwrap()],
[0xE0, 0x07, 0x00, 0x05, 0x11, 0x00, 0x00, 0x02, 0x00]
)
}
#[test]
fn test_decode() {
let buffer: [u8; 10] = [0xE0, 0x07, 0x00, 0x05, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00];
let mut packet = DisconnectPacket::<1>::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 10));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, PacketType::Disconnect.into());
assert_eq!(packet.remain_len, 7);
assert_eq!(packet.disconnect_reason, 0x00);
assert_eq!(packet.property_len, 5);
let prop = packet.properties.get(0);
assert!(prop.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop.unwrap()), 0x11);
if let Property::SessionExpiryInterval(u) = *prop.unwrap() {
assert_eq!(u, 1024);
}
}

View File

@@ -0,0 +1,38 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod connack_packet_unit;
pub mod connect_packet_unit;
pub mod disconnect_packet_unit;
pub mod pingreq_packet_unit;
pub mod pingresp_packet_unit;
pub mod puback_packet_unit;
pub mod pubcomp_packet_unit;
pub mod publish_packet_unit;
pub mod pubrec_packet_unit;
pub mod pubrel_packet_unit;
pub mod suback_packet_unit;
pub mod subscription_packet_unit;
pub mod unsuback_packet_unit;
pub mod unsubscription_packet_unit;

View File

@@ -0,0 +1,38 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::pingreq_packet::PingreqPacket;
#[test]
fn test_encode() {
let mut buffer: [u8; 3] = [0x00, 0x98, 0x45];
let mut packet = PingreqPacket::new();
packet.fixed_header = PacketType::Pingreq.into();
packet.remain_len = 0;
let res = packet.encode(&mut buffer, 3);
assert!(res.is_ok());
assert_eq!(buffer, [0xC0, 0x00, 0x45])
}

View File

@@ -0,0 +1,49 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::pingresp_packet::PingrespPacket;
use crate::utils::buffer_reader::BuffReader;
#[test]
fn test_encode() {
let mut buffer: [u8; 3] = [0x00, 0x98, 0x45];
let mut packet = PingrespPacket::new();
packet.fixed_header = PacketType::Pingresp.into();
packet.remain_len = 0;
let res = packet.encode(&mut buffer, 3);
assert!(res.is_ok());
assert_eq!(buffer, [0xD0, 0x00, 0x45])
}
#[test]
fn test_decode() {
let buffer: [u8; 3] = [0xD0, 0x00, 0x51];
let mut packet = PingrespPacket::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 3));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, PacketType::Pingresp.into());
assert_eq!(packet.remain_len, 0);
}

View File

@@ -0,0 +1,73 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::puback_packet::PubackPacket;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::EncodedString;
use heapless::Vec;
#[test]
fn test_encode() {
let mut buffer: [u8; 14] = [0; 14];
let mut packet = PubackPacket::<1>::new();
packet.packet_identifier = 35420;
packet.reason_code = 0x00;
let mut str = EncodedString::new();
str.string = "Hello";
str.len = 5;
let mut props = Vec::<Property, 1>::new();
props.push(Property::ReasonString(str));
packet.property_len = packet.add_properties(&props);
let res = packet.encode(&mut buffer, 14);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 14);
assert_eq!(
buffer,
[0x40, 0x0C, 0x8A, 0x5C, 0x00, 0x08, 0x1F, 0x00, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
)
}
#[test]
fn test_decode() {
let buffer: [u8; 14] = [
0x40, 0x0C, 0x8A, 0x5E, 0x15, 0x08, 0x1F, 0x00, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
];
let mut packet = PubackPacket::<1>::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 14));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, PacketType::Puback.into());
assert_eq!(packet.remain_len, 12);
assert_eq!(packet.packet_identifier, 35422);
assert_eq!(packet.reason_code, 0x15);
assert_eq!(packet.property_len, 8);
let prop = packet.properties.get(0);
assert!(prop.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop.unwrap()), 0x1F);
if let Property::ReasonString(u) = (*prop.unwrap()).clone() {
assert_eq!(u.string, "Hello");
}
}

View File

@@ -0,0 +1,73 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::pubcomp_packet::PubcompPacket;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::EncodedString;
use heapless::Vec;
#[test]
fn test_encode() {
let mut buffer: [u8; 14] = [0; 14];
let mut packet = PubcompPacket::<1>::new();
packet.fixed_header = PacketType::Pubcomp.into();
packet.packet_identifier = 35420;
packet.reason_code = 0x00;
let mut str = EncodedString::new();
str.string = "Wheel";
str.len = 5;
let mut props = Vec::<Property, 1>::new();
props.push(Property::ReasonString(str));
packet.property_len = packet.add_properties(&props);
let res = packet.encode(&mut buffer, 14);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 14);
assert_eq!(
buffer,
[0x70, 0x0C, 0x8A, 0x5C, 0x00, 0x08, 0x1F, 0x00, 0x05, 0x57, 0x68, 0x65, 0x65, 0x6c]
)
}
#[test]
fn test_decode() {
let buffer: [u8; 14] = [
0x70, 0x0C, 0x8A, 0x5C, 0x00, 0x08, 0x1F, 0x00, 0x05, 0x57, 0x68, 0x65, 0x65, 0x6c,
];
let mut packet = PubcompPacket::<1>::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 14));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, PacketType::Pubcomp.into());
assert_eq!(packet.packet_identifier, 35420);
assert_eq!(packet.reason_code, 0x00);
assert_eq!(packet.property_len, 8);
let prop = packet.properties.get(0);
assert!(prop.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop.unwrap()), 0x1F);
if let Property::ReasonString(u) = (*prop.unwrap()).clone() {
assert_eq!(u.string, "Wheel");
}
}

View File

@@ -0,0 +1,97 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::publish_packet::{PublishPacket, QualityOfService};
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::EncodedString;
use heapless::Vec;
#[test]
fn test_encode() {
let mut buffer: [u8; 29] = [0; 29];
let mut packet = PublishPacket::<2>::new();
packet.fixed_header = PacketType::Publish.into();
packet.add_qos(QualityOfService::QoS1);
let mut topic = EncodedString::new();
topic.string = "test";
topic.len = 4;
packet.topic_name = topic;
packet.packet_identifier = 23432;
let mut props = Vec::<Property, 2>::new();
props.push(Property::PayloadFormat(0x01));
props.push(Property::MessageExpiryInterval(45678));
packet.property_len = packet.add_properties(&props);
static MESSAGE: [u8; 11] = [
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
];
packet.add_message(&MESSAGE);
let res = packet.encode(&mut buffer, 100);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 29);
assert_eq!(
buffer,
[
0x32, 0x1B, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x5B, 0x88, 0x07, 0x01, 0x01, 0x02,
0x00, 0x00, 0xB2, 0x6E, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c,
0x64
]
)
}
#[test]
fn test_decode() {
let buffer: [u8; 29] = [
0x32, 0x1B, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x5B, 0x88, 0x07, 0x01, 0x01, 0x02, 0x00,
0x00, 0xB2, 0x6E, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
];
let mut packet = PublishPacket::<2>::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 29));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, 0x32);
assert_eq!(packet.topic_name.len, 4);
assert_eq!(packet.topic_name.string, "test");
assert_eq!(packet.packet_identifier, 23432);
assert_eq!(packet.property_len, 7);
let prop = packet.properties.get(0);
assert!(prop.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop.unwrap()), 0x01);
if let Property::PayloadFormat(u) = (*prop.unwrap()).clone() {
assert_eq!(u, 0x01);
}
let prop2 = packet.properties.get(1);
assert!(prop2.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop2.unwrap()), 0x02);
if let Property::MessageExpiryInterval(u) = (*prop2.unwrap()).clone() {
assert_eq!(u, 45678);
}
if let Some(message) = packet.message {
assert_eq!(
*message,
[0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64]
);
}
}

View File

@@ -0,0 +1,86 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::pubrec_packet::PubrecPacket;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::{EncodedString, StringPair};
use heapless::Vec;
#[test]
fn test_encode() {
let mut buffer: [u8; 20] = [0; 20];
let mut packet = PubrecPacket::<2>::new();
packet.packet_identifier = 35420;
packet.reason_code = 0x12;
let mut name = EncodedString::new();
name.string = "name1";
name.len = 5;
let mut val = EncodedString::new();
val.string = "val1";
val.len = 4;
let mut pair = StringPair::new();
pair.name = name;
pair.value = val;
let mut props = Vec::<Property, 1>::new();
props.push(Property::UserProperty(pair));
packet.property_len = packet.add_properties(&props);
let res = packet.encode(&mut buffer, 20);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 20);
assert_eq!(
buffer,
[
0x50, 0x12, 0x8A, 0x5C, 0x12, 0x0E, 0x26, 0x00, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x31,
0x00, 0x04, 0x76, 0x61, 0x6c, 0x31
]
)
}
#[test]
fn test_decode() {
let buffer: [u8; 20] = [
0x50, 0x12, 0x8A, 0x5C, 0x12, 0x0E, 0x26, 0x00, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x31, 0x00,
0x04, 0x76, 0x61, 0x6c, 0x31,
];
let mut packet = PubrecPacket::<1>::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 20));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, PacketType::Pubrec.into());
assert_eq!(packet.remain_len, 18);
assert_eq!(packet.packet_identifier, 35420);
assert_eq!(packet.reason_code, 0x12);
assert_eq!(packet.property_len, 14);
let prop = packet.properties.get(0);
assert!(prop.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop.unwrap()), 0x26);
if let Property::UserProperty(u) = (*prop.unwrap()).clone() {
assert_eq!(u.name.len, 5);
assert_eq!(u.name.string, "name1");
assert_eq!(u.value.len, 4);
assert_eq!(u.value.string, "val1");
}
}

View File

@@ -0,0 +1,87 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::pubrel_packet::PubrelPacket;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::{EncodedString, StringPair};
use heapless::Vec;
#[test]
fn test_encode() {
let mut buffer: [u8; 21] = [0; 21];
let mut packet = PubrelPacket::<1>::new();
packet.fixed_header = PacketType::Pubrel.into();
packet.packet_identifier = 12345;
packet.reason_code = 0x86;
let mut name = EncodedString::new();
name.string = "haha";
name.len = 4;
let mut val = EncodedString::new();
val.string = "hehe89";
val.len = 6;
let mut pair = StringPair::new();
pair.name = name;
pair.value = val;
let mut props = Vec::<Property, 1>::new();
props.push(Property::UserProperty(pair));
packet.property_len = packet.add_properties(&props);
let res = packet.encode(&mut buffer, 21);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 21);
assert_eq!(
buffer,
[
0x60, 0x13, 0x30, 0x39, 0x86, 0x0F, 0x26, 0x00, 0x04, 0x68, 0x61, 0x68, 0x61, 0x00,
0x06, 0x68, 0x65, 0x68, 0x65, 0x38, 0x39
]
)
}
#[test]
fn test_decode() {
let buffer: [u8; 21] = [
0x60, 0x13, 0x30, 0x39, 0x86, 0x0F, 0x26, 0x00, 0x04, 0x68, 0x61, 0x68, 0x61, 0x00, 0x06,
0x68, 0x65, 0x68, 0x65, 0x38, 0x39,
];
let mut packet = PubrelPacket::<1>::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 21));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, PacketType::Pubrel.into());
assert_eq!(packet.remain_len, 19);
assert_eq!(packet.packet_identifier, 12345);
assert_eq!(packet.reason_code, 0x86);
assert_eq!(packet.property_len, 15);
let prop = packet.properties.get(0);
assert!(prop.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop.unwrap()), 0x26);
if let Property::UserProperty(u) = (*prop.unwrap()).clone() {
assert_eq!(u.name.len, 4);
assert_eq!(u.name.string, "haha");
assert_eq!(u.value.len, 6);
assert_eq!(u.value.string, "hehe89");
}
}

View File

@@ -0,0 +1,67 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::suback_packet::SubackPacket;
use crate::utils::buffer_reader::BuffReader;
#[test]
fn test_decode() {
let buffer: [u8; 23] = [
0x90, 0x15, 0xCC, 0x08, 0x0F, 0x1F, 0x00, 0x0C, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x53,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x56,
];
let mut packet = SubackPacket::<3, 1>::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 23));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, PacketType::Suback.into());
assert_eq!(packet.remain_len, 21);
assert_eq!(packet.packet_identifier, 52232);
assert_eq!(packet.property_len, 15);
let prop = packet.properties.get(0);
assert!(prop.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop.unwrap()), 0x1F);
if let Property::ReasonString(u) = (*prop.unwrap()).clone() {
assert_eq!(u.len, 12);
assert_eq!(u.string, "reasonString");
}
assert_eq!(packet.reason_codes.len(), 3);
let res1 = packet.reason_codes.get(0);
assert!(res1.is_some());
if let Some(r) = res1 {
assert_eq!(*r, 0x12);
}
let res2 = packet.reason_codes.get(1);
assert!(res2.is_some());
if let Some(r) = res2 {
assert_eq!(*r, 0x34);
}
let res3 = packet.reason_codes.get(2);
assert!(res3.is_some());
if let Some(r) = res3 {
assert_eq!(*r, 0x56);
}
}

View File

@@ -0,0 +1,54 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::publish_packet::QualityOfService::{QoS0, QoS1};
use crate::packet::v5::subscription_packet::SubscriptionPacket;
use heapless::Vec;
#[test]
fn test_encode() {
let mut buffer: [u8; 30] = [0; 30];
let mut packet = SubscriptionPacket::<2, 1>::new();
packet.fixed_header = PacketType::Subscribe.into();
packet.packet_identifier = 5432;
let mut props = Vec::<Property, 2>::new();
props.push(Property::SubscriptionIdentifier(2432));
packet.property_len = packet.add_properties(&props);
packet.add_new_filter("test/topic", QoS0);
packet.add_new_filter("hehe/#", QoS1);
let res = packet.encode(&mut buffer, 30);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 30);
assert_eq!(
buffer,
[
0x82, 0x1C, 0x15, 0x38, 0x03, 0x0B, 0x80, 0x13, 0x00, 0x0A, 0x74, 0x65, 0x73, 0x74,
0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x00, 0x00, 0x06, 0x68, 0x65, 0x68, 0x65, 0x2F,
0x23, 0x01
]
);
}

View File

@@ -0,0 +1,62 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::unsuback_packet::UnsubackPacket;
use crate::utils::buffer_reader::BuffReader;
#[test]
fn test_decode() {
let buffer: [u8; 22] = [
0xB0, 0x14, 0xCC, 0x08, 0x0F, 0x1F, 0x00, 0x0C, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x53,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x77, 0x55,
];
let mut packet = UnsubackPacket::<2, 1>::new();
let res = packet.decode(&mut BuffReader::new(&buffer, 22));
assert!(res.is_ok());
assert_eq!(packet.fixed_header, PacketType::Unsuback.into());
assert_eq!(packet.remain_len, 20);
assert_eq!(packet.packet_identifier, 52232);
assert_eq!(packet.property_len, 15);
let prop = packet.properties.get(0);
assert!(prop.is_some());
assert_eq!(<&Property as Into<u8>>::into(prop.unwrap()), 0x1F);
if let Property::ReasonString(u) = (*prop.unwrap()).clone() {
assert_eq!(u.len, 12);
assert_eq!(u.string, "reasonString");
}
assert_eq!(packet.reason_codes.len(), 2);
let res1 = packet.reason_codes.get(0);
assert!(res1.is_some());
if let Some(r) = res1 {
assert_eq!(*r, 0x77);
}
let res2 = packet.reason_codes.get(1);
assert!(res2.is_some());
if let Some(r) = res2 {
assert_eq!(*r, 0x55);
}
}

View File

@@ -0,0 +1,64 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::mqtt_packet::Packet;
use crate::packet::v5::packet_type::PacketType;
use crate::packet::v5::property::Property;
use crate::packet::v5::publish_packet::QualityOfService::{QoS0, QoS1};
use crate::packet::v5::unsubscription_packet::UnsubscriptionPacket;
use crate::utils::types::{EncodedString, StringPair};
use heapless::Vec;
#[test]
fn test_encode() {
let mut buffer: [u8; 40] = [0; 40];
let mut packet = UnsubscriptionPacket::<2, 1>::new();
packet.fixed_header = PacketType::Unsubscribe.into();
packet.packet_identifier = 5432;
let mut name = EncodedString::new();
name.string = "haha";
name.len = 4;
let mut val = EncodedString::new();
val.string = "hehe89";
val.len = 6;
let mut pair = StringPair::new();
pair.name = name;
pair.value = val;
let mut props = Vec::<Property, 1>::new();
props.push(Property::UserProperty(pair));
packet.property_len = packet.add_properties(&props);
packet.add_new_filter("test/topic", QoS0);
packet.add_new_filter("hehe/#", QoS1);
let res = packet.encode(&mut buffer, 40);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 40);
assert_eq!(
buffer,
[
0xA0, 0x26, 0x15, 0x38, 0x0F, 0x26, 0x00, 0x04, 0x68, 0x61, 0x68, 0x61, 0x00, 0x06,
0x68, 0x65, 0x68, 0x65, 0x38, 0x39, 0x00, 0x0A, 0x74, 0x65, 0x73, 0x74, 0x2F, 0x74,
0x6F, 0x70, 0x69, 0x63, 0x00, 0x06, 0x68, 0x65, 0x68, 0x65, 0x2F, 0x23
]
);
}

View File

@@ -0,0 +1,235 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::utils::buffer_reader::BuffReader;
use crate::utils::types::BufferError;
#[test]
fn buffer_read_variable_byte() {
static BUFFER: [u8; 5] = [0x82, 0x82, 0x03, 0x85, 0x84];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 5);
let test_number = reader.read_variable_byte_int();
assert!(test_number.is_ok());
assert_eq!(reader.position, 3);
assert_eq!(test_number.unwrap(), 49410);
}
#[test]
fn buffer_read_invalid_size() {
static BUFFER: [u8; 2] = [0x82, 0x82];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 2);
let test_number = reader.read_variable_byte_int();
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn test_smaller_var_int() {
static BUFFER: [u8; 2] = [0x82, 0x02];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 2);
let test_number = reader.read_variable_byte_int();
assert!(test_number.is_ok());
assert_eq!(reader.position, 2);
assert_eq!(test_number.unwrap(), 258);
}
#[test]
fn test_complete_var_int() {
static BUFFER: [u8; 4] = [0x81, 0x81, 0x81, 0x01];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 4);
let test_number = reader.read_variable_byte_int();
assert!(test_number.is_ok());
assert_eq!(reader.position, 4);
assert_eq!(test_number.unwrap(), 2113665);
}
#[test]
fn test_var_empty_buffer() {
static BUFFER: [u8; 0] = [];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 0);
let test_number = reader.read_variable_byte_int();
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn test_read_u32() {
static BUFFER: [u8; 4] = [0x00, 0x02, 0x5E, 0xC1];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 4);
let test_number = reader.read_u32();
assert!(test_number.is_ok());
assert_eq!(test_number.unwrap(), 155329);
}
#[test]
fn test_read_u32_oob() {
static BUFFER: [u8; 3] = [0x00, 0x02, 0x5E];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 3);
let test_number = reader.read_u32();
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn test_read_u16() {
static BUFFER: [u8; 2] = [0x48, 0x5F];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 2);
let test_number = reader.read_u16();
assert!(test_number.is_ok());
assert_eq!(test_number.unwrap(), 18527);
}
#[test]
fn test_read_u16_oob() {
static BUFFER: [u8; 1] = [0x5E];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 1);
let test_number = reader.read_u16();
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn test_read_u8() {
static BUFFER: [u8; 1] = [0xFD];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 1);
let test_number = reader.read_u8();
assert!(test_number.is_ok());
assert_eq!(test_number.unwrap(), 253);
}
#[test]
fn test_read_u8_oob() {
static BUFFER: [u8; 0] = [];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 0);
let test_number = reader.read_u8();
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn test_read_string() {
static BUFFER: [u8; 6] = [0x00, 0x04, 0xF0, 0x9F, 0x92, 0x96];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 6);
let test_string = reader.read_string();
assert!(test_string.is_ok());
let unw = test_string.unwrap();
assert_eq!(unw.string, "💖");
assert_eq!(unw.len, 4);
}
#[test]
fn test_read_string_utf8_wrong() {
static BUFFER: [u8; 5] = [0x00, 0x03, 0xF0, 0x9F, 0x92];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 5);
let test_string = reader.read_string();
assert!(test_string.is_err());
assert_eq!(test_string.unwrap_err(), BufferError::Utf8Error);
}
#[test]
fn test_read_string_oob() {
static BUFFER: [u8; 5] = [0x00, 0x04, 0xF0, 0x9F, 0x92];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 5);
let test_string = reader.read_string();
assert!(test_string.is_err());
assert_eq!(
test_string.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn test_read_binary() {
static BUFFER: [u8; 6] = [0x00, 0x04, 0xFF, 0xEE, 0xDD, 0xCC];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 6);
let test_bin = reader.read_binary();
assert!(test_bin.is_ok());
let unw = test_bin.unwrap();
assert_eq!(unw.bin, [0xFF, 0xEE, 0xDD, 0xCC]);
assert_eq!(unw.len, 4);
}
#[test]
fn test_read_binary_oob() {
static BUFFER: [u8; 5] = [0x00, 0x04, 0xFF, 0xEE, 0xDD];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 5);
let test_bin = reader.read_binary();
assert!(test_bin.is_err());
assert_eq!(test_bin.unwrap_err(), BufferError::InsufficientBufferSize);
}
#[test]
fn test_read_string_pair() {
static BUFFER: [u8; 11] = [
0x00, 0x04, 0xF0, 0x9F, 0x98, 0x8E, 0x00, 0x03, 0xE2, 0x93, 0x87,
];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 11);
let string_pair = reader.read_string_pair();
assert!(string_pair.is_ok());
let unw = string_pair.unwrap();
assert_eq!(unw.name.string, "😎");
assert_eq!(unw.name.len, 4);
assert_eq!(unw.value.string, "");
assert_eq!(unw.value.len, 3);
}
#[test]
fn test_read_string_pair_wrong_utf8() {
static BUFFER: [u8; 11] = [
0x00, 0x03, 0xF0, 0x9F, 0x92, 0x00, 0x04, 0xF0, 0x9F, 0x98, 0x8E,
];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 11);
let string_pair = reader.read_string_pair();
assert!(string_pair.is_err());
assert_eq!(string_pair.unwrap_err(), BufferError::Utf8Error)
}
#[test]
fn test_read_string_pair_oob() {
static BUFFER: [u8; 11] = [
0x00, 0x04, 0xF0, 0x9F, 0x98, 0x8E, 0x00, 0x04, 0xE2, 0x93, 0x87,
];
let mut reader: BuffReader = BuffReader::new(&BUFFER, 11);
let string_pair = reader.read_string_pair();
assert!(string_pair.is_err());
assert_eq!(
string_pair.unwrap_err(),
BufferError::InsufficientBufferSize
);
}

View File

@@ -0,0 +1,381 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::packet::v5::property::Property;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::{BinaryData, BufferError, EncodedString, StringPair, TopicFilter};
use heapless::Vec;
#[test]
fn buffer_write_ref() {
static BUFFER: [u8; 5] = [0x82, 0x82, 0x03, 0x85, 0x84];
let mut res_buffer: [u8; 5] = [0; 5];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 5);
let test_write = writer.insert_ref(5, &BUFFER);
assert!(test_write.is_ok());
assert_eq!(writer.position, 5);
assert_eq!(BUFFER, res_buffer);
}
#[test]
fn buffer_write_ref_oob() {
static BUFFER: [u8; 5] = [0x82, 0x82, 0x03, 0x85, 0x84];
let mut res_buffer: [u8; 4] = [0; 4];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 4);
let test_number = writer.insert_ref(5, &BUFFER);
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
assert_eq!(res_buffer, [0; 4])
}
#[test]
fn buffer_write_u8() {
let mut res_buffer: [u8; 1] = [0; 1];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 1);
let test_write = writer.write_u8(0xFA);
assert!(test_write.is_ok());
assert_eq!(writer.position, 1);
assert_eq!(res_buffer, [0xFA]);
}
#[test]
fn buffer_write_u8_oob() {
let mut res_buffer: [u8; 0] = [];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 0);
let test_number = writer.write_u8(0xFA);
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn buffer_write_u16() {
let mut res_buffer: [u8; 2] = [0; 2];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 2);
let test_write = writer.write_u16(0xFAED);
assert!(test_write.is_ok());
assert_eq!(writer.position, 2);
assert_eq!(res_buffer, [0xFA, 0xED]);
}
#[test]
fn buffer_write_u16_oob() {
let mut res_buffer: [u8; 1] = [0; 1];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 1);
let test_number = writer.write_u16(0xFAED);
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn buffer_write_u32() {
let mut res_buffer: [u8; 4] = [0; 4];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 4);
let test_write = writer.write_u32(0xFAEDCC09);
assert!(test_write.is_ok());
assert_eq!(writer.position, 4);
assert_eq!(res_buffer, [0xFA, 0xED, 0xCC, 0x09]);
}
#[test]
fn buffer_write_u32_oob() {
let mut res_buffer: [u8; 3] = [0; 3];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 3);
let test_number = writer.write_u32(0xFAEDCC08);
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
#[test]
fn buffer_write_string() {
let mut res_buffer: [u8; 6] = [0; 6];
let mut string = EncodedString::new();
string.string = "😎";
string.len = 4;
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 6);
let test_write = writer.write_string_ref(&string);
assert!(test_write.is_ok());
assert_eq!(writer.position, 6);
assert_eq!(res_buffer, [0x00, 0x04, 0xF0, 0x9F, 0x98, 0x8E]);
}
#[test]
fn buffer_write_string_oob() {
let mut res_buffer: [u8; 5] = [0; 5];
let mut string = EncodedString::new();
string.string = "😎";
string.len = 4;
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 5);
let test_write = writer.write_string_ref(&string);
assert!(test_write.is_err());
assert_eq!(test_write.unwrap_err(), BufferError::InsufficientBufferSize);
}
#[test]
fn buffer_write_bin() {
let mut res_buffer: [u8; 6] = [0; 6];
let mut bin = BinaryData::new();
bin.bin = &[0xAB, 0xEF, 0x88, 0x43];
bin.len = 4;
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 6);
let test_write = writer.write_binary_ref(&bin);
assert!(test_write.is_ok());
assert_eq!(writer.position, 6);
assert_eq!(res_buffer, [0x00, 0x04, 0xAB, 0xEF, 0x88, 0x43]);
}
#[test]
fn buffer_write_bin_oob() {
let mut res_buffer: [u8; 6] = [0; 6];
let mut bin = BinaryData::new();
bin.bin = &[0xAB, 0xEF, 0x88, 0x43];
bin.len = 4;
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 5);
let test_write = writer.write_binary_ref(&bin);
assert!(test_write.is_err());
assert_eq!(test_write.unwrap_err(), BufferError::InsufficientBufferSize);
}
#[test]
fn buffer_write_string_pair() {
let mut res_buffer: [u8; 12] = [0; 12];
let mut name = EncodedString::new();
name.string = "Name";
name.len = 4;
let mut value = EncodedString::new();
value.string = "😎";
value.len = 4;
let mut pair = StringPair::new();
pair.name = name;
pair.value = value;
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 12);
let test_write = writer.write_string_pair_ref(&pair);
assert!(test_write.is_ok());
assert_eq!(writer.position, 12);
assert_eq!(
res_buffer,
[0x00, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x04, 0xF0, 0x9F, 0x98, 0x8E]
);
}
#[test]
fn buffer_write_string_pair_oob() {
let mut res_buffer: [u8; 12] = [0; 12];
let mut name = EncodedString::new();
name.string = "Name";
name.len = 4;
let mut value = EncodedString::new();
value.string = "😎";
value.len = 4;
let mut pair = StringPair::new();
pair.name = name;
pair.value = value;
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 10);
let test_write = writer.write_string_pair_ref(&pair);
assert!(test_write.is_err());
assert_eq!(test_write.unwrap_err(), BufferError::InsufficientBufferSize)
}
#[test]
fn buffer_write_var_byte() {
let mut res_buffer: [u8; 2] = [0; 2];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 2);
let test_write = writer.write_variable_byte_int(512);
assert!(test_write.is_ok());
assert_eq!(writer.position, 2);
assert_eq!(res_buffer, [0x80, 0x04]);
}
#[test]
fn buffer_write_var_byte_oob() {
let mut res_buffer: [u8; 2] = [0; 2];
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 2);
let test_number = writer.write_variable_byte_int(453123);
assert!(test_number.is_err());
assert_eq!(
test_number.unwrap_err(),
BufferError::InsufficientBufferSize
);
}
/*#[test]
fn buffer_write_property() {
let mut res_buffer: [u8; 7] = [0; 7];
let mut topic = EncodedString::new();
topic.string = "Name";
topic.len = 4;
let prop = Property::ResponseTopic(topic);
let mut writer: BuffWriter = BuffWriter::new(& mut res_buffer, 7);
let test_write = writer.write_property(&prop);
assert!(test_write.is_ok());
assert_eq!(writer.position, 7);
assert_eq!(res_buffer, [0x08, 0x00, 0x04, 0x4e, 0x61, 0x6d, 0x65]);
}
#[test]
fn buffer_write_property_oob() {
let mut res_buffer: [u8; 7] = [0; 7];
let mut topic = EncodedString::new();
topic.string = "Name";
topic.len = 4;
let prop = Property::ResponseTopic(topic);
let mut writer: BuffWriter = BuffWriter::new(& mut res_buffer, 4);
let test_write = writer.write_property(&prop);
assert!(test_write.is_err());
assert_eq!(test_write.unwrap_err(), BufferError::InsufficientBufferSize);
}*/
#[test]
fn buffer_write_properties() {
let mut res_buffer: [u8; 13] = [0; 13];
let mut topic = EncodedString::new();
topic.string = "Name";
topic.len = 4;
let prop = Property::ResponseTopic(topic);
let mut corr = BinaryData::new();
corr.bin = &[0x12, 0x34, 0x56];
corr.len = 3;
let prop2 = Property::CorrelationData(corr);
let mut properties = Vec::<Property, 2>::new();
properties.push(prop);
properties.push(prop2);
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 13);
let test_write = writer.write_properties(&properties);
assert!(test_write.is_ok());
assert_eq!(writer.position, 13);
assert_eq!(
res_buffer,
[0x08, 0x00, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x09, 0x00, 0x03, 0x12, 0x34, 0x56]
);
}
#[test]
fn buffer_write_properties_oob() {
let mut res_buffer: [u8; 10] = [0; 10];
let mut topic = EncodedString::new();
topic.string = "Name";
topic.len = 4;
let prop = Property::ResponseTopic(topic);
let mut corr = BinaryData::new();
corr.bin = &[0x12, 0x34, 0x56];
corr.len = 3;
let prop2 = Property::CorrelationData(corr);
let mut properties = Vec::<Property, 2>::new();
properties.push(prop);
properties.push(prop2);
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 10);
let test_write = writer.write_properties(&properties);
assert!(test_write.is_err());
assert_eq!(test_write.unwrap_err(), BufferError::InsufficientBufferSize);
}
#[test]
fn buffer_write_filters() {
let mut res_buffer: [u8; 15] = [0; 15];
static STR1: &str = "test";
static STR2: &str = "topic";
let mut topic = EncodedString::new();
topic.string = STR1;
topic.len = 4;
let mut topic2 = EncodedString::new();
topic2.string = STR2;
topic2.len = 5;
let mut filter1 = TopicFilter::new();
filter1.filter = topic;
filter1.sub_options = 0xAE;
let mut filter2 = TopicFilter::new();
filter2.filter = topic2;
filter2.sub_options = 0x22;
let mut filters = Vec::<TopicFilter, 2>::new();
filters.push(filter1);
filters.push(filter2);
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 15);
let test_write = writer.write_topic_filters_ref(true, 2, &filters);
assert!(test_write.is_ok());
assert_eq!(writer.position, 15);
assert_eq!(
res_buffer,
[
0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0xAE, 0x00, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63,
0x22
]
);
}
#[test]
fn buffer_write_filters_oob() {
let mut res_buffer: [u8; 15] = [0; 15];
static STR1: &str = "test";
static STR2: &str = "topic";
let mut topic = EncodedString::new();
topic.string = STR1;
topic.len = 4;
let mut topic2 = EncodedString::new();
topic2.string = STR2;
topic2.len = 5;
let mut filter1 = TopicFilter::new();
filter1.filter = topic;
filter1.sub_options = 0xAE;
let mut filter2 = TopicFilter::new();
filter2.filter = topic2;
filter2.sub_options = 0x22;
let mut filters = Vec::<TopicFilter, 2>::new();
filters.push(filter1);
filters.push(filter2);
let mut writer: BuffWriter = BuffWriter::new(&mut res_buffer, 5);
let test_write = writer.write_topic_filters_ref(true, 2, &filters);
assert!(test_write.is_err());
assert_eq!(test_write.unwrap_err(), BufferError::InsufficientBufferSize)
}

View File

@@ -0,0 +1,26 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod buffer_reader_unit;
pub mod buffer_writer_unit;

31
mqtt/src/tokio_net/mod.rs Normal file
View File

@@ -0,0 +1,31 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#![feature(in_band_lifetimes)]
#![macro_use]
#![allow(dead_code)]
#![feature(type_alias_impl_trait)]
#![feature(generic_associated_types)]
#[cfg(feature = "tokio")]
pub mod tokio_network;

View File

@@ -0,0 +1,145 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
extern crate alloc;
use alloc::format;
use alloc::string::String;
use core::future::Future;
use core::time::Duration;
use crate::network::network_trait::{NetworkConnection, NetworkConnectionFactory};
use crate::packet::v5::reason_codes::ReasonCode;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio::time::sleep;
pub struct TokioNetwork {
stream: Option<TcpStream>,
}
impl TokioNetwork {
pub fn new(stream: TcpStream) -> Self {
Self {
stream: Some(stream),
}
}
pub fn convert_ip(ip: [u8; 4], port: u16) -> String {
String::from(format!("{}.{}.{}.{}:{}", ip[0], ip[1], ip[2], ip[3], port))
}
}
impl NetworkConnection for TokioNetwork {
type WriteFuture<'m>
where
Self: 'm,
= impl Future<Output = Result<(), ReasonCode>> + 'm;
type ReadFuture<'m>
where
Self: 'm,
= impl Future<Output = Result<usize, ReasonCode>> + 'm;
type CloseFuture<'m>
where
Self: 'm,
= impl Future<Output = Result<(), ReasonCode>> + 'm;
/*type TimerFuture<'m>
where
Self: 'm,
= impl Future<Output = ()>;*/
fn send<'m>(&'m mut self, buffer: &'m mut [u8], len: usize) -> Self::WriteFuture<'m> {
async move {
return if let Some(ref mut stream) = self.stream {
stream
.write_all(&buffer[0..len])
.await
.map_err(|_| ReasonCode::NetworkError)
} else {
Err(ReasonCode::NetworkError)
};
}
}
fn receive<'m>(&'m mut self, buffer: &'m mut [u8]) -> Self::ReadFuture<'m> {
async move {
return if let Some(ref mut stream) = self.stream {
stream
.read(buffer)
.await
.map_err(|_| ReasonCode::NetworkError)
} else {
Err(ReasonCode::NetworkError)
};
}
}
fn close<'m>(mut self) -> Self::CloseFuture<'m> {
async move {
return if let Some(ref mut stream) = self.stream {
stream
.shutdown()
.await
.map_err(|_| ReasonCode::NetworkError)
} else {
Err(ReasonCode::NetworkError)
};
}
}
/*fn count_down(&'m mut self, time_in_secs: u64) -> Self::TimerFuture<'m> {
async move {
return sleep(Duration::from_secs(time_in_secs))
.await
}
}*/
}
pub struct TokioNetworkFactory {}
impl TokioNetworkFactory {
pub fn new() -> Self {
Self {}
}
}
impl NetworkConnectionFactory for TokioNetworkFactory {
type Connection = TokioNetwork;
type ConnectionFuture<'m>
where
Self: 'm,
= impl Future<Output = Result<TokioNetwork, ReasonCode>> + 'm;
fn connect<'m>(&'m mut self, ip: [u8; 4], port: u16) -> Self::ConnectionFuture<'m> {
async move {
let stream = TcpStream::connect(TokioNetwork::convert_ip(ip, port))
.await
.map_err(|_| ReasonCode::NetworkError)?;
Ok(TokioNetwork::new(stream))
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use core::mem;
use core::str;
use crate::encoding::variable_byte_integer::VariableByteIntegerDecoder;
use crate::utils::types::{BinaryData, BufferError, EncodedString, StringPair};
/// Buff reader is reading corresponding types from buffer (Byte array) and stores current position
/// (later as cursor)
pub struct BuffReader<'a> {
buffer: &'a [u8],
pub position: usize,
len: usize,
}
impl<'a> BuffReader<'a> {
pub fn increment_position(&mut self, increment: usize) {
self.position = self.position + increment;
}
pub fn new(buffer: &'a [u8], buff_len: usize) -> Self {
return BuffReader {
buffer,
position: 0,
len: buff_len,
};
}
/// Variable byte integer can be 1-4 Bytes long. Buffer reader takes all 4 Bytes at first and
/// than check what is true length of varbyteint and increment cursor by that
pub fn read_variable_byte_int(&mut self) -> Result<u32, BufferError> {
let mut variable_byte_integer: [u8; 4] = [0; 4];
let mut len: usize = 1;
// Everytime checking first bit of Byte which determines whenever there is continuous Byte
let mut x = 0;
loop {
if x >= 4 {
break;
}
if self.position + x >= self.len {
return Err(BufferError::InsufficientBufferSize);
}
if self.buffer[self.position + x] & 0x80 != 0 {
variable_byte_integer[x] = self.buffer[self.position + x];
len = len + 1
} else {
variable_byte_integer[x] = self.buffer[self.position + x];
x = x + 1;
if x != 4 {
loop {
variable_byte_integer[x] = 0;
x = x + 1;
if x == 4 {
break;
}
}
break;
}
}
x = x + 1;
}
self.increment_position(len);
return VariableByteIntegerDecoder::decode(variable_byte_integer);
}
/// Reading u32 from buffer as `Big endian`
pub fn read_u32(&mut self) -> Result<u32, BufferError> {
if self.position + 4 > self.len {
return Err(BufferError::InsufficientBufferSize);
}
let (int_bytes, _rest) = self.buffer[self.position..].split_at(mem::size_of::<u32>());
let ret: u32 = u32::from_be_bytes(int_bytes.try_into().unwrap());
self.increment_position(4);
return Ok(ret);
}
/// Reading u16 from buffer as `Big endinan`
pub fn read_u16(&mut self) -> Result<u16, BufferError> {
if self.position + 2 > self.len {
return Err(BufferError::InsufficientBufferSize);
}
let (int_bytes, _rest) = self.buffer[self.position..].split_at(mem::size_of::<u16>());
let ret: u16 = u16::from_be_bytes(int_bytes.try_into().unwrap());
self.increment_position(2);
return Ok(ret);
}
/// Reading one byte from buffer as `Big endian`
pub fn read_u8(&mut self) -> Result<u8, BufferError> {
if self.position >= self.len {
return Err(BufferError::InsufficientBufferSize);
}
let ret: u8 = self.buffer[self.position];
self.increment_position(1);
return Ok(ret);
}
/// Reading UTF-8 encoded string from buffer
pub fn read_string(&mut self) -> Result<EncodedString<'a>, BufferError> {
let len = self.read_u16()? as usize;
if self.position + len - 1 >= self.len {
return Err(BufferError::InsufficientBufferSize);
}
let res_str = str::from_utf8(&(self.buffer[self.position..(self.position + len)]));
if res_str.is_err() {
log::error!("Could not parse utf-8 string");
return Err(BufferError::Utf8Error);
}
self.increment_position(len);
return Ok(EncodedString {
string: res_str.unwrap(),
len: len as u16,
});
}
/// Read Binary data from buffer
pub fn read_binary(&mut self) -> Result<BinaryData<'a>, BufferError> {
let len = self.read_u16()?;
if self.position + len as usize - 1 >= self.len {
return Err(BufferError::InsufficientBufferSize);
}
let res_bin = &(self.buffer[self.position..(self.position + len as usize)]);
return Ok(BinaryData { bin: res_bin, len });
}
/// Read string pair from buffer
pub fn read_string_pair(&mut self) -> Result<StringPair<'a>, BufferError> {
let name = self.read_string()?;
let value = self.read_string()?;
return Ok(StringPair { name, value });
}
/// Read payload message from buffer
pub fn read_message(&mut self, total_len: usize) -> &'a [u8] {
if total_len > self.len {
return &self.buffer[self.position..self.len];
}
return &self.buffer[self.position..total_len];
}
}

View File

@@ -0,0 +1,167 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use heapless::Vec;
use crate::encoding::variable_byte_integer::{VariableByteInteger, VariableByteIntegerEncoder};
use crate::packet::v5::property::Property;
use crate::utils::types::{BinaryData, BufferError, EncodedString, StringPair, TopicFilter};
pub struct BuffWriter<'a> {
buffer: &'a mut [u8],
pub position: usize,
len: usize,
}
impl<'a> BuffWriter<'a> {
pub fn new(buffer: &'a mut [u8], buff_len: usize) -> Self {
return BuffWriter {
buffer,
position: 0,
len: buff_len,
};
}
fn increment_position(&mut self, increment: usize) {
self.position = self.position + increment;
}
pub fn insert_ref(&mut self, len: usize, array: &[u8]) -> Result<(), BufferError> {
let mut x: usize = 0;
if self.position + len > self.len {
return Err(BufferError::InsufficientBufferSize);
}
if len != 0 {
loop {
self.buffer[self.position] = array[x];
self.increment_position(1);
x = x + 1;
if x == len {
break;
}
}
}
return Ok(());
}
pub fn write_u8(&mut self, byte: u8) -> Result<(), BufferError> {
return if self.position >= self.len {
Err(BufferError::InsufficientBufferSize)
} else {
self.buffer[self.position] = byte;
self.increment_position(1);
Ok(())
};
}
pub fn write_u16(&mut self, two_bytes: u16) -> Result<(), BufferError> {
let bytes: [u8; 2] = two_bytes.to_be_bytes();
return self.insert_ref(2, &bytes);
}
pub fn write_u32(&mut self, four_bytes: u32) -> Result<(), BufferError> {
let bytes: [u8; 4] = four_bytes.to_be_bytes();
return self.insert_ref(4, &bytes);
}
pub fn write_string_ref(&mut self, str: &EncodedString<'a>) -> Result<(), BufferError> {
self.write_u16(str.len)?;
if str.len != 0 {
let bytes = str.string.as_bytes();
return self.insert_ref(str.len as usize, bytes);
}
return Ok(());
}
pub fn write_binary_ref(&mut self, bin: &BinaryData<'a>) -> Result<(), BufferError> {
self.write_u16(bin.len)?;
return self.insert_ref(bin.len as usize, bin.bin);
}
pub fn write_string_pair_ref(&mut self, str_pair: &StringPair<'a>) -> Result<(), BufferError> {
self.write_string_ref(&str_pair.name)?;
return self.write_string_ref(&str_pair.value);
}
pub fn write_variable_byte_int(&mut self, int: u32) -> Result<(), BufferError> {
let x: VariableByteInteger = VariableByteIntegerEncoder::encode(int)?;
let len = VariableByteIntegerEncoder::len(x);
return self.insert_ref(len, &x);
}
fn write_property(&mut self, property: &Property<'a>) -> Result<(), BufferError> {
let x: u8 = property.into();
self.write_u8(x)?;
return property.encode(self);
}
pub fn write_properties<const LEN: usize>(
&mut self,
properties: &Vec<Property<'a>, LEN>,
) -> Result<(), BufferError> {
let mut i = 0;
let len = properties.len();
if len != 0 {
loop {
let prop: &Property = properties.get(i).unwrap_or(&Property::Reserved());
self.write_property(prop)?;
i = i + 1;
if i == len {
break;
}
}
}
Ok(())
}
fn write_topic_filter_ref(
&mut self,
sub: bool,
topic_filter: &TopicFilter<'a>,
) -> Result<(), BufferError> {
self.write_string_ref(&topic_filter.filter)?;
if sub {
self.write_u8(topic_filter.sub_options)?;
}
return Ok(());
}
pub fn write_topic_filters_ref<const MAX: usize>(
&mut self,
sub: bool,
len: usize,
filters: &Vec<TopicFilter<'a>, MAX>,
) -> Result<(), BufferError> {
let mut i = 0;
loop {
let topic_filter: &TopicFilter<'a> = filters.get(i).unwrap();
self.write_topic_filter_ref(sub, topic_filter)?;
i = i + 1;
if i == len {
break;
}
}
Ok(())
}
}

28
mqtt/src/utils/mod.rs Normal file
View File

@@ -0,0 +1,28 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub mod buffer_reader;
pub mod buffer_writer;
pub mod rng_generator;
pub mod types;

View File

@@ -0,0 +1,28 @@
// This code is handed from Embedded Rust documentation and
// is accessible from https://docs.rust-embedded.org/cortex-m-rt/0.6.0/rand/trait.RngCore.html
use rand_core::{impls, Error, RngCore};
pub struct CountingRng(pub u64);
impl RngCore for CountingRng {
fn next_u32(&mut self) -> u32 {
self.next_u64() as u32
}
fn next_u64(&mut self) -> u64 {
self.0 += 1;
if self.0 > u16::MAX as u64 {
self.0 = 1;
}
self.0
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
impls::fill_bytes_via_next(self, dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
Ok(self.fill_bytes(dest))
}
}

131
mqtt/src/utils/types.rs Normal file
View File

@@ -0,0 +1,131 @@
/*
* MIT License
*
* Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use core::fmt::{Display, Formatter};
#[derive(core::fmt::Debug, Clone, PartialEq)]
pub enum BufferError {
Utf8Error,
InsufficientBufferSize,
VariableByteIntegerError,
IdNotFound,
EncodingError,
DecodingError,
PacketTypeMismatch,
WrongPacketToDecode,
WrongPacketToEncode,
PropertyNotFound,
}
impl Display for BufferError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match *self {
BufferError::Utf8Error => write!(f, "Error encountered during UTF8 decoding!"),
BufferError::InsufficientBufferSize => write!(f, "Buffer size is not sufficient for packet!"),
BufferError::VariableByteIntegerError => write!(f, "Error encountered during variable byte integer decoding / encoding!"),
BufferError::IdNotFound => write!(f, "Packet identifier not found!"),
BufferError::EncodingError => write!(f, "Error encountered during packet encoding!"),
BufferError::DecodingError => write!(f, "Error encountered during packet decoding!"),
BufferError::PacketTypeMismatch => write!(f, "Packet type not matched during decoding (Received different packet type than encode type)!"),
BufferError::WrongPacketToDecode => write!(f, "Not able to decode packet, this packet is used just for sending to broker, not receiving by client!"),
BufferError::WrongPacketToEncode => write!(f, "Not able to encode packet, this packet is used only from server to client not the opposite way!"),
BufferError::PropertyNotFound => write!(f, "Property with ID not found!")
}
}
}
/// Encoded string provides structure representing UTF-8 encoded string in MQTTv5 packets
#[derive(Debug, Clone)]
pub struct EncodedString<'a> {
pub string: &'a str,
pub len: u16,
}
impl EncodedString<'_> {
pub fn new() -> Self {
Self { string: "", len: 0 }
}
/// Return length of string
pub fn len(&self) -> u16 {
return self.len + 2;
}
}
/// Binary data represents `Binary data` in MQTTv5 protocol
#[derive(Debug, Clone)]
pub struct BinaryData<'a> {
pub bin: &'a [u8],
pub len: u16,
}
impl BinaryData<'_> {
pub fn new() -> Self {
Self { bin: &[0], len: 0 }
}
/// Returns length of Byte array
pub fn len(&self) -> u16 {
return self.len + 2;
}
}
/// String pair struct represents `String pair` in MQTTv5 (2 UTF-8 encoded strings name-value)
#[derive(Debug, Clone)]
pub struct StringPair<'a> {
pub name: EncodedString<'a>,
pub value: EncodedString<'a>,
}
impl StringPair<'_> {
pub fn new() -> Self {
Self {
name: EncodedString::new(),
value: EncodedString::new(),
}
}
/// Returns length which is equal to sum of the lenghts of UTF-8 encoded strings in pair
pub fn len(&self) -> u16 {
let ln = self.name.len() + self.value.len();
return ln;
}
}
/// Topic filter serves as bound for topic selection and subscription options for `SUBSCRIPTION` packet
#[derive(Debug)]
pub struct TopicFilter<'a> {
pub filter: EncodedString<'a>,
pub sub_options: u8,
}
impl TopicFilter<'_> {
pub fn new() -> Self {
Self {
filter: EncodedString::new(),
sub_options: 0,
}
}
pub fn len(&self) -> u16 {
return self.filter.len + 3;
}
}