diff --git a/src/client/client_config.rs b/src/client/client_config.rs index 22b6d34..a1d1fd9 100644 --- a/src/client/client_config.rs +++ b/src/client/client_config.rs @@ -22,25 +22,33 @@ * SOFTWARE. */ +use heapless::Vec; +use crate::packet::v5::property::Property; use crate::packet::v5::publish_packet::QualityOfService; use crate::utils::types::{BinaryData, EncodedString}; -pub struct ClientConfig<'a> { +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, MAX_PROPERTIES>, } -impl ClientConfig<'a> { +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::, MAX_PROPERTIES>::new() } } @@ -63,4 +71,10 @@ impl ClientConfig<'a> { 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); + } + } } diff --git a/src/client/client_v5.rs b/src/client/client_v5.rs index 141d0e5..58fb2fa 100644 --- a/src/client/client_v5.rs +++ b/src/client/client_v5.rs @@ -25,7 +25,7 @@ pub struct MqttClientV5<'a, T, const MAX_PROPERTIES: usize> { recv_buffer: &'a mut [u8], recv_buffer_len: usize, rng: CountingRng, - config: ClientConfig<'a>, + config: ClientConfig<'a, MAX_PROPERTIES>, } impl<'a, T, const MAX_PROPERTIES: usize> MqttClientV5<'a, T, MAX_PROPERTIES> @@ -38,7 +38,7 @@ where buffer_len: usize, recv_buffer: &'a mut [u8], recv_buffer_len: usize, - config: ClientConfig<'a>, + config: ClientConfig<'a, MAX_PROPERTIES>, ) -> Self { Self { network_driver, @@ -54,6 +54,7 @@ where pub async fn connect_to_broker<'b>(&'b mut self) -> Result<(), ReasonCode> { let len = { let mut connect = ConnectPacket::<'b, 3, 0>::clean(); + connect.keep_alive = self.config.keep_alive; if self.config.username_flag { connect.add_username(&self.config.username); } diff --git a/src/lib.rs b/src/lib.rs index e18bc7e..e1d08c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,3 +37,4 @@ pub mod network; pub mod packet; pub mod tokio_network; pub mod utils; +pub mod tests; diff --git a/src/network/network_trait.rs b/src/network/network_trait.rs index 07f1469..d3b3d56 100644 --- a/src/network/network_trait.rs +++ b/src/network/network_trait.rs @@ -24,6 +24,10 @@ pub trait Network { where Self: 'm; + type TimerFuture<'m>: Future + where + Self: 'm; + fn new(ip: [u8; 4], port: u16) -> Self; fn create_connection(&'m mut self) -> Self::ConnectionFuture<'m>; @@ -31,4 +35,6 @@ pub trait Network { 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 count_down(&'m mut self, time_in_secs: u64) -> Self::TimerFuture<'m>; } diff --git a/src/packet/v5/auth_packet.rs b/src/packet/v5/auth_packet.rs index ee43549..d4077e6 100644 --- a/src/packet/v5/auth_packet.rs +++ b/src/packet/v5/auth_packet.rs @@ -85,7 +85,7 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for AuthPacket<'a, MAX_PROPERTI 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.encode_properties::(&self.properties)?; + buff_writer.write_properties::(&self.properties)?; Ok(buff_writer.position) } @@ -107,6 +107,10 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for AuthPacket<'a, MAX_PROPERTI 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; } diff --git a/src/packet/v5/connack_packet.rs b/src/packet/v5/connack_packet.rs index f4cd75f..966ba93 100644 --- a/src/packet/v5/connack_packet.rs +++ b/src/packet/v5/connack_packet.rs @@ -43,18 +43,6 @@ pub struct ConnackPacket<'a, const MAX_PROPERTIES: usize> { } impl<'a, const MAX_PROPERTIES: usize> ConnackPacket<'a, MAX_PROPERTIES> { - pub fn decode_connack_packet( - &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) - } } impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for ConnackPacket<'a, MAX_PROPERTIES> { @@ -80,12 +68,18 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for ConnackPacket<'a, MAX_PROPE 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.encode_properties(&self.properties)?; + buff_writer.write_properties(&self.properties)?; Ok(buff_writer.position) } fn decode(&mut self, buff_reader: &mut BuffReader<'a>) -> Result<(), BufferError> { - self.decode_connack_packet(buff_reader) + 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) { @@ -100,6 +94,10 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for ConnackPacket<'a, MAX_PROPE 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; } diff --git a/src/packet/v5/connect_packet.rs b/src/packet/v5/connect_packet.rs index 09ea8b5..555c629 100644 --- a/src/packet/v5/connect_packet.rs +++ b/src/packet/v5/connect_packet.rs @@ -68,11 +68,13 @@ impl<'a, const MAX_PROPERTIES: usize, const MAX_WILL_PROPERTIES: usize> property_len: 3, properties: Vec::, 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::, MAX_WILL_PROPERTIES>::new(), will_topic: EncodedString::new(), will_payload: BinaryData::new(), username: EncodedString::new(), + password: BinaryData::new(), }; @@ -96,6 +98,10 @@ impl<'a, const MAX_PROPERTIES: usize, const MAX_WILL_PROPERTIES: usize> 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> @@ -159,12 +165,12 @@ impl<'a, const MAX_PROPERTIES: usize, const MAX_WILL_PROPERTIES: usize> Packet<' 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.encode_properties::(&self.properties)?; + buff_writer.write_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.encode_properties(&self.will_properties)?; + buff_writer.write_properties(&self.will_properties)?; buff_writer.write_string_ref(&self.will_topic)?; buff_writer.write_binary_ref(&self.will_payload)?; } @@ -197,6 +203,10 @@ impl<'a, const MAX_PROPERTIES: usize, const MAX_WILL_PROPERTIES: usize> Packet<' 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; } diff --git a/src/packet/v5/disconnect_packet.rs b/src/packet/v5/disconnect_packet.rs index fc5149e..ebaf8b7 100644 --- a/src/packet/v5/disconnect_packet.rs +++ b/src/packet/v5/disconnect_packet.rs @@ -73,13 +73,13 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for DisconnectPacket<'a, MAX_PR 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.encode_properties(&self.properties)?; + 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::Pingresp).into() { - log::error!("Packet you are trying to decode is not PUBACK packet!"); + 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()?; @@ -98,6 +98,10 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for DisconnectPacket<'a, MAX_PR 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; } diff --git a/src/packet/v5/mqtt_packet.rs b/src/packet/v5/mqtt_packet.rs index 4b36063..af64fa8 100644 --- a/src/packet/v5/mqtt_packet.rs +++ b/src/packet/v5/mqtt_packet.rs @@ -22,6 +22,7 @@ * SOFTWARE. */ +use heapless::Vec; use crate::packet::v5::packet_type::PacketType; use crate::utils::buffer_reader::BuffReader; use crate::utils::types::BufferError; @@ -43,6 +44,26 @@ pub trait Packet<'a> { 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(& mut self, properties: &Vec, 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); diff --git a/src/packet/v5/pingreq_packet.rs b/src/packet/v5/pingreq_packet.rs index 12c9bb0..0702305 100644 --- a/src/packet/v5/pingreq_packet.rs +++ b/src/packet/v5/pingreq_packet.rs @@ -70,6 +70,10 @@ impl<'a> Packet<'a> for PingreqPacket { 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; } diff --git a/src/packet/v5/pingresp_packet.rs b/src/packet/v5/pingresp_packet.rs index 705360c..e03aa28 100644 --- a/src/packet/v5/pingresp_packet.rs +++ b/src/packet/v5/pingresp_packet.rs @@ -48,7 +48,7 @@ impl<'a> Packet<'a> for PingrespPacket { fn encode(&mut self, buffer: &mut [u8], buffer_len: usize) -> Result { 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)?; + buff_writer.write_variable_byte_int(self.remain_len)?; Ok(buff_writer.position) } @@ -78,6 +78,10 @@ impl<'a> Packet<'a> for PingrespPacket { 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; } diff --git a/src/packet/v5/property.rs b/src/packet/v5/property.rs index e729e66..1a0ada7 100644 --- a/src/packet/v5/property.rs +++ b/src/packet/v5/property.rs @@ -22,11 +22,13 @@ * 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)] +#[derive(Clone)] pub enum Property<'a> { PayloadFormat(u8), MessageExpiryInterval(u32), @@ -59,6 +61,142 @@ pub enum Property<'a> { } 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, @@ -76,7 +214,7 @@ impl<'a> Property<'a> { Property::ContentType(u) => u.len(), Property::ResponseTopic(u) => u.len(), Property::CorrelationData(u) => u.len(), - Property::SubscriptionIdentifier(_u) => 4, + Property::SubscriptionIdentifier(u) => VariableByteIntegerEncoder::len(VariableByteIntegerEncoder::encode(*u).unwrap()) as u16, Property::SessionExpiryInterval(_u) => 4, Property::AssignedClientIdentifier(u) => u.len(), Property::ServerKeepAlive(_u) => 2, diff --git a/src/packet/v5/puback_packet.rs b/src/packet/v5/puback_packet.rs index d15e4a6..bc379ac 100644 --- a/src/packet/v5/puback_packet.rs +++ b/src/packet/v5/puback_packet.rs @@ -69,7 +69,7 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubackPacket<'a, MAX_PROPER 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.encode_properties::(&self.properties)?; + buff_writer.write_properties::(&self.properties)?; Ok(buff_writer.position) } @@ -102,6 +102,10 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubackPacket<'a, MAX_PROPER 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; } diff --git a/src/packet/v5/pubcomp_packet.rs b/src/packet/v5/pubcomp_packet.rs index 7e44227..6eb8a81 100644 --- a/src/packet/v5/pubcomp_packet.rs +++ b/src/packet/v5/pubcomp_packet.rs @@ -69,7 +69,7 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubcompPacket<'a, MAX_PROPE 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.encode_properties::(&self.properties)?; + buff_writer.write_properties::(&self.properties)?; Ok(buff_writer.position) } @@ -96,6 +96,10 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubcompPacket<'a, MAX_PROPE 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; } diff --git a/src/packet/v5/publish_packet.rs b/src/packet/v5/publish_packet.rs index 73c0838..9f36bbe 100644 --- a/src/packet/v5/publish_packet.rs +++ b/src/packet/v5/publish_packet.rs @@ -129,7 +129,7 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PublishPacket<'a, MAX_PROPE } buff_writer.write_variable_byte_int(self.property_len)?; - buff_writer.encode_properties::(&self.properties)?; + buff_writer.write_properties::(&self.properties)?; buff_writer.insert_ref(msg_len as usize, self.message.unwrap())?; Ok(buff_writer.position) } @@ -165,6 +165,10 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PublishPacket<'a, MAX_PROPE 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; } diff --git a/src/packet/v5/pubrec_packet.rs b/src/packet/v5/pubrec_packet.rs index cd7a692..49060b5 100644 --- a/src/packet/v5/pubrec_packet.rs +++ b/src/packet/v5/pubrec_packet.rs @@ -69,7 +69,7 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubrecPacket<'a, MAX_PROPER 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.encode_properties::(&self.properties)?; + buff_writer.write_properties::(&self.properties)?; Ok(buff_writer.position) } @@ -94,6 +94,11 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubrecPacket<'a, MAX_PROPER 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; } diff --git a/src/packet/v5/pubrel_packet.rs b/src/packet/v5/pubrel_packet.rs index d15b270..675e701 100644 --- a/src/packet/v5/pubrel_packet.rs +++ b/src/packet/v5/pubrel_packet.rs @@ -69,7 +69,7 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubrelPacket<'a, MAX_PROPER 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.encode_properties::(&self.properties)?; + buff_writer.write_properties::(&self.properties)?; Ok(buff_writer.position) } @@ -95,6 +95,10 @@ impl<'a, const MAX_PROPERTIES: usize> Packet<'a> for PubrelPacket<'a, MAX_PROPER 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; } diff --git a/src/packet/v5/suback_packet.rs b/src/packet/v5/suback_packet.rs index e1cadfe..16bb9ec 100644 --- a/src/packet/v5/suback_packet.rs +++ b/src/packet/v5/suback_packet.rs @@ -23,6 +23,7 @@ */ use heapless::Vec; +use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder; use crate::packet::v5::mqtt_packet::Packet; use crate::utils::buffer_reader::BuffReader; @@ -47,11 +48,14 @@ impl<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize> &mut self, buff_reader: &mut BuffReader<'a>, ) -> Result<(), BufferError> { - let mut i = 0; + 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()?); - i = i + 1; - if i == MAX_REASONS { + if buff_reader.position == max { break; } } @@ -100,6 +104,10 @@ impl<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize> Packet<'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; } diff --git a/src/packet/v5/subscription_packet.rs b/src/packet/v5/subscription_packet.rs index cd115a8..86834bb 100644 --- a/src/packet/v5/subscription_packet.rs +++ b/src/packet/v5/subscription_packet.rs @@ -96,8 +96,8 @@ impl<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize> Packet<'a> 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.encode_properties::(&self.properties)?; - buff_writer.encode_topic_filters_ref( + buff_writer.write_properties::(&self.properties)?; + buff_writer.write_topic_filters_ref( true, self.topic_filter_len as usize, &self.topic_filters, @@ -121,6 +121,10 @@ impl<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize> Packet<'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; } diff --git a/src/packet/v5/unsuback_packet.rs b/src/packet/v5/unsuback_packet.rs index 142941f..98f03b7 100644 --- a/src/packet/v5/unsuback_packet.rs +++ b/src/packet/v5/unsuback_packet.rs @@ -100,6 +100,10 @@ impl<'a, const MAX_REASONS: usize, const MAX_PROPERTIES: usize> Packet<'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; } diff --git a/src/packet/v5/unsubscription_packet.rs b/src/packet/v5/unsubscription_packet.rs index fd35791..a1932c3 100644 --- a/src/packet/v5/unsubscription_packet.rs +++ b/src/packet/v5/unsubscription_packet.rs @@ -26,6 +26,7 @@ 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}; @@ -45,6 +46,16 @@ pub struct UnsubscriptionPacket<'a, const MAX_FILTERS: usize, const MAX_PROPERTI 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 | (>::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> @@ -68,15 +79,24 @@ impl<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize> Packet<'a> 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 + 4 + self.topic_filter_len as u32; + + 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.encode_properties::(&self.properties)?; - buff_writer.write_u16(self.topic_filter_len)?; - buff_writer.encode_topic_filters_ref( + buff_writer.write_properties::(&self.properties)?; + buff_writer.write_topic_filters_ref( false, self.topic_filter_len as usize, &self.topic_filters, @@ -101,6 +121,10 @@ impl<'a, const MAX_FILTERS: usize, const MAX_PROPERTIES: usize> Packet<'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; } diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..0b21c1a --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,26 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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)] +pub mod unit; \ No newline at end of file diff --git a/src/tests/unit/client/client_v5_unit.rs b/src/tests/unit/client/client_v5_unit.rs new file mode 100644 index 0000000..e518c59 --- /dev/null +++ b/src/tests/unit/client/client_v5_unit.rs @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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. + */ + +#[test] +fn it_works() { + assert_eq!(2 + 2, 4); +} \ No newline at end of file diff --git a/src/tests/unit/client/mod.rs b/src/tests/unit/client/mod.rs new file mode 100644 index 0000000..0c9e02d --- /dev/null +++ b/src/tests/unit/client/mod.rs @@ -0,0 +1,25 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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 client_v5_unit; \ No newline at end of file diff --git a/src/tests/unit/encoding/mod.rs b/src/tests/unit/encoding/mod.rs new file mode 100644 index 0000000..3dc3a6b --- /dev/null +++ b/src/tests/unit/encoding/mod.rs @@ -0,0 +1,25 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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; \ No newline at end of file diff --git a/src/tests/unit/encoding/variable_byte_integer_unit.rs b/src/tests/unit/encoding/variable_byte_integer_unit.rs new file mode 100644 index 0000000..d812b5e --- /dev/null +++ b/src/tests/unit/encoding/variable_byte_integer_unit.rs @@ -0,0 +1,78 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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); +} \ No newline at end of file diff --git a/src/tests/unit/mod.rs b/src/tests/unit/mod.rs new file mode 100644 index 0000000..baded47 --- /dev/null +++ b/src/tests/unit/mod.rs @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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 utils; +pub mod encoding; +pub mod packet; +pub mod client; \ No newline at end of file diff --git a/src/tests/unit/packet/mod.rs b/src/tests/unit/packet/mod.rs new file mode 100644 index 0000000..b28ef3c --- /dev/null +++ b/src/tests/unit/packet/mod.rs @@ -0,0 +1,25 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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; \ No newline at end of file diff --git a/src/tests/unit/packet/v5/connack_packet_unit.rs b/src/tests/unit/packet/v5/connack_packet_unit.rs new file mode 100644 index 0000000..49efb53 --- /dev/null +++ b/src/tests/unit/packet/v5/connack_packet_unit.rs @@ -0,0 +1,63 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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>::into(prop), 0x21); + if let Property::ReceiveMaximum(u) = *prop { + assert_eq!(u, 21); + } + +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/connect_packet_unit.rs b/src/tests/unit/packet/v5/connect_packet_unit.rs new file mode 100644 index 0000000..829093d --- /dev/null +++ b/src/tests/unit/packet/v5/connect_packet_unit.rs @@ -0,0 +1,38 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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]) +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/disconnect_packet_unit.rs b/src/tests/unit/packet/v5/disconnect_packet_unit.rs new file mode 100644 index 0000000..dd5478e --- /dev/null +++ b/src/tests/unit/packet/v5/disconnect_packet_unit.rs @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::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; + +#[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::::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 mut 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>::into(prop.unwrap()), 0x11); + if let Property::SessionExpiryInterval(u) = *prop.unwrap() { + assert_eq!(u, 1024); + } +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/mod.rs b/src/tests/unit/packet/v5/mod.rs new file mode 100644 index 0000000..b4959b9 --- /dev/null +++ b/src/tests/unit/packet/v5/mod.rs @@ -0,0 +1,38 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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; \ No newline at end of file diff --git a/src/tests/unit/packet/v5/pingreq_packet_unit.rs b/src/tests/unit/packet/v5/pingreq_packet_unit.rs new file mode 100644 index 0000000..9a1e49d --- /dev/null +++ b/src/tests/unit/packet/v5/pingreq_packet_unit.rs @@ -0,0 +1,38 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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]) +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/pingresp_packet_unit.rs b/src/tests/unit/packet/v5/pingresp_packet_unit.rs new file mode 100644 index 0000000..02cadc3 --- /dev/null +++ b/src/tests/unit/packet/v5/pingresp_packet_unit.rs @@ -0,0 +1,49 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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 mut 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); +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/puback_packet_unit.rs b/src/tests/unit/packet/v5/puback_packet_unit.rs new file mode 100644 index 0000000..9d9bd0a --- /dev/null +++ b/src/tests/unit/packet/v5/puback_packet_unit.rs @@ -0,0 +1,68 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::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; + +#[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::::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 mut 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>::into(prop.unwrap()), 0x1F); + if let Property::ReasonString(u) = (*prop.unwrap()).clone() { + assert_eq!(u.string, "Hello"); + } +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/pubcomp_packet_unit.rs b/src/tests/unit/packet/v5/pubcomp_packet_unit.rs new file mode 100644 index 0000000..8e7fe8b --- /dev/null +++ b/src/tests/unit/packet/v5/pubcomp_packet_unit.rs @@ -0,0 +1,68 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::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; + +#[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::::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 mut 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>::into(prop.unwrap()), 0x1F); + if let Property::ReasonString(u) = (*prop.unwrap()).clone() { + assert_eq!(u.string, "Wheel"); + } +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/publish_packet_unit.rs b/src/tests/unit/packet/v5/publish_packet_unit.rs new file mode 100644 index 0000000..ab95f2f --- /dev/null +++ b/src/tests/unit/packet/v5/publish_packet_unit.rs @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::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; + +#[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::::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 mut 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>::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>::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]); + } +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/pubrec_packet_unit.rs b/src/tests/unit/packet/v5/pubrec_packet_unit.rs new file mode 100644 index 0000000..fca6dd7 --- /dev/null +++ b/src/tests/unit/packet/v5/pubrec_packet_unit.rs @@ -0,0 +1,78 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::packet::v5::packet_type::PacketType; +use crate::packet::v5::property::Property; +use crate::packet::v5::puback_packet::PubackPacket; +use crate::packet::v5::pubrec_packet::PubrecPacket; +use crate::utils::buffer_reader::BuffReader; +use crate::utils::types::{EncodedString, StringPair}; + +#[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::::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 mut 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>::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"); + } +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/pubrel_packet_unit.rs b/src/tests/unit/packet/v5/pubrel_packet_unit.rs new file mode 100644 index 0000000..036d48d --- /dev/null +++ b/src/tests/unit/packet/v5/pubrel_packet_unit.rs @@ -0,0 +1,82 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::packet::v5::packet_type::PacketType; +use crate::packet::v5::property::Property; +use crate::packet::v5::puback_packet::PubackPacket; +use crate::packet::v5::pubrec_packet::PubrecPacket; +use crate::packet::v5::pubrel_packet::PubrelPacket; +use crate::utils::buffer_reader::BuffReader; +use crate::utils::types::{EncodedString, StringPair}; + +#[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::::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 mut 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>::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"); + } +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/suback_packet_unit.rs b/src/tests/unit/packet/v5/suback_packet_unit.rs new file mode 100644 index 0000000..db314f4 --- /dev/null +++ b/src/tests/unit/packet/v5/suback_packet_unit.rs @@ -0,0 +1,71 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::packet::v5::packet_type::PacketType; +use crate::packet::v5::property::Property; +use crate::packet::v5::puback_packet::PubackPacket; +use crate::packet::v5::pubrec_packet::PubrecPacket; +use crate::packet::v5::pubrel_packet::PubrelPacket; +use crate::packet::v5::suback_packet::SubackPacket; +use crate::utils::buffer_reader::BuffReader; +use crate::utils::types::{EncodedString, StringPair}; + +#[test] +fn test_decode() { + let mut 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>::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); + } +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/subscription_packet_unit.rs b/src/tests/unit/packet/v5/subscription_packet_unit.rs new file mode 100644 index 0000000..99b13f8 --- /dev/null +++ b/src/tests/unit/packet/v5/subscription_packet_unit.rs @@ -0,0 +1,49 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::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; + +#[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::::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]); +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/unsuback_packet_unit.rs b/src/tests/unit/packet/v5/unsuback_packet_unit.rs new file mode 100644 index 0000000..9b99a55 --- /dev/null +++ b/src/tests/unit/packet/v5/unsuback_packet_unit.rs @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::packet::v5::packet_type::PacketType; +use crate::packet::v5::property::Property; +use crate::packet::v5::puback_packet::PubackPacket; +use crate::packet::v5::pubrec_packet::PubrecPacket; +use crate::packet::v5::pubrel_packet::PubrelPacket; +use crate::packet::v5::suback_packet::SubackPacket; +use crate::packet::v5::unsuback_packet::UnsubackPacket; +use crate::utils::buffer_reader::BuffReader; +use crate::utils::types::{EncodedString, StringPair}; + +#[test] +fn test_decode() { + let mut 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>::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); + } +} \ No newline at end of file diff --git a/src/tests/unit/packet/v5/unsubscription_packet_unit.rs b/src/tests/unit/packet/v5/unsubscription_packet_unit.rs new file mode 100644 index 0000000..cd22a26 --- /dev/null +++ b/src/tests/unit/packet/v5/unsubscription_packet_unit.rs @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::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 crate::packet::v5::unsubscription_packet::UnsubscriptionPacket; +use crate::utils::types::{EncodedString, StringPair}; + +#[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::::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]); +} \ No newline at end of file diff --git a/src/tests/unit/utils/buffer_reader_unit.rs b/src/tests/unit/utils/buffer_reader_unit.rs new file mode 100644 index 0000000..6f6943b --- /dev/null +++ b/src/tests/unit/utils/buffer_reader_unit.rs @@ -0,0 +1,213 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::str; + +use log::info; +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); +} \ No newline at end of file diff --git a/src/tests/unit/utils/buffer_writer_unit.rs b/src/tests/unit/utils/buffer_writer_unit.rs new file mode 100644 index 0000000..ec7e9ac --- /dev/null +++ b/src/tests/unit/utils/buffer_writer_unit.rs @@ -0,0 +1,360 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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::property::Property; +use crate::utils::buffer_writer::BuffWriter; +use crate::utils::types::{BinaryData, BufferError, EncodedString, StringPair, TopicFilter}; + +#[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::::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::::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::::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::::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) +} \ No newline at end of file diff --git a/src/tests/unit/utils/mod.rs b/src/tests/unit/utils/mod.rs new file mode 100644 index 0000000..8516624 --- /dev/null +++ b/src/tests/unit/utils/mod.rs @@ -0,0 +1,26 @@ +/* + * MIT License + * + * Copyright (c) [2022] [Ondrej Babec ] + * + * 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; \ No newline at end of file diff --git a/src/tokio_network.rs b/src/tokio_network.rs index 78fa121..3791479 100644 --- a/src/tokio_network.rs +++ b/src/tokio_network.rs @@ -1,9 +1,11 @@ use alloc::format; use alloc::string::String; use core::future::Future; +use core::time::Duration; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; +use tokio::time::sleep; use crate::network::network_trait::Network; use crate::packet::v5::reason_codes::ReasonCode; @@ -34,11 +36,17 @@ impl Network for TokioNetwork { where Self: 'm, = impl Future> + 'm; + type ReadFuture<'m> where Self: 'm, = impl Future> + 'm; + type TimerFuture<'m> + where + Self: 'm, + = impl Future; + fn new(ip: [u8; 4], port: u16) -> Self { return Self { ip, @@ -82,4 +90,11 @@ impl Network for TokioNetwork { }; } } + + fn count_down(&'m mut self, time_in_secs: u64) -> Self::TimerFuture<'m> { + async move { + return sleep(Duration::from_secs(time_in_secs)) + .await + } + } } diff --git a/src/utils/buffer_reader.rs b/src/utils/buffer_reader.rs index eaf600f..2cb1ecf 100644 --- a/src/utils/buffer_reader.rs +++ b/src/utils/buffer_reader.rs @@ -52,22 +52,36 @@ impl<'a> BuffReader<'a> { /// 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 { - let variable_byte_integer: [u8; 4] = [ - self.buffer[self.position], - self.buffer[self.position + 1], - self.buffer[self.position + 2], - self.buffer[self.position + 3], - ]; + 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 continous Byte - if variable_byte_integer[0] & 0x80 == 1 { - len = len + 1; - if variable_byte_integer[1] & 0x80 == 1 { - len = len + 1; - if variable_byte_integer[2] & 0x80 == 1 { - len = len + 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); @@ -75,6 +89,9 @@ impl<'a> BuffReader<'a> { /// Reading u32 from buffer as `Big endian` pub fn read_u32(&mut self) -> Result { + if self.position + 4 > self.len { + return Err(BufferError::InsufficientBufferSize); + } let (int_bytes, _rest) = self.buffer[self.position..].split_at(mem::size_of::()); let ret: u32 = u32::from_be_bytes(int_bytes.try_into().unwrap()); self.increment_position(4); @@ -83,6 +100,9 @@ impl<'a> BuffReader<'a> { /// Reading u16 from buffer as `Big endinan` pub fn read_u16(&mut self) -> Result { + if self.position + 2 > self.len { + return Err(BufferError::InsufficientBufferSize); + } let (int_bytes, _rest) = self.buffer[self.position..].split_at(mem::size_of::()); let ret: u16 = u16::from_be_bytes(int_bytes.try_into().unwrap()); self.increment_position(2); @@ -91,6 +111,9 @@ impl<'a> BuffReader<'a> { /// Reading one byte from buffer as `Big endian` pub fn read_u8(&mut self) -> Result { + if self.position >= self.len { + return Err(BufferError::InsufficientBufferSize); + } let ret: u8 = self.buffer[self.position]; self.increment_position(1); return Ok(ret); @@ -98,50 +121,47 @@ impl<'a> BuffReader<'a> { /// Reading UTF-8 encoded string from buffer pub fn read_string(&mut self) -> Result, BufferError> { - let len = self.read_u16(); - match len { - Err(err) => return Err(err), - _ => {} + let len = self.read_u16() ? as usize; + + if self.position + len - 1 >= self.len { + return Err(BufferError::InsufficientBufferSize); } - let len_res = len.unwrap(); + let res_str = - str::from_utf8(&(self.buffer[self.position..(self.position + len_res as usize)])); + 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_res as usize); + self.increment_position(len); return Ok(EncodedString { string: res_str.unwrap(), - len: len_res, + len: len as u16, }); } /// Read Binary data from buffer pub fn read_binary(&mut self) -> Result, BufferError> { - let len = self.read_u16()?; + 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: len, + len, }); } /// Read string pair from buffer pub fn read_string_pair(&mut self) -> Result, BufferError> { - let name = self.read_string(); - match name { - Err(err) => return Err(err), - _ => log::debug!("[String pair] name not parsed"), - } - let value = self.read_string(); - match value { - Err(err) => return Err(err), - _ => log::debug!("[String pair] value not parsed"), - } + let name = self.read_string() ?; + let value = self.read_string() ?; return Ok(StringPair { - name: name.unwrap(), - value: value.unwrap(), + name, + value, }); } diff --git a/src/utils/buffer_writer.rs b/src/utils/buffer_writer.rs index eabef8e..df8f16c 100644 --- a/src/utils/buffer_writer.rs +++ b/src/utils/buffer_writer.rs @@ -49,7 +49,7 @@ impl<'a> BuffWriter<'a> { pub fn insert_ref(&mut self, len: usize, array: &[u8]) -> Result<(), BufferError> { let mut x: usize = 0; - if self.position + 3 >= self.len { + if self.position + len > self.len { return Err(BufferError::InsufficientBufferSize); } if len != 0 { @@ -91,45 +91,29 @@ impl<'a> BuffWriter<'a> { return self.insert_ref(str.len as usize, bytes); } - pub fn write_string(&mut self, str: EncodedString<'a>) -> Result<(), BufferError> { - self.write_u16(str.len)?; - let bytes = str.string.as_bytes(); - return self.insert_ref(str.len as usize, bytes); - } - 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_binary(&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_string_pair(&mut self, str_pair: StringPair<'a>) -> Result<(), BufferError> { - self.write_string(str_pair.name)?; - return self.write_string(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); } - pub fn encode_property(&mut self, property: &Property<'a>) -> Result<(), BufferError> { + 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 encode_properties( + pub fn write_properties( &mut self, properties: &Vec, LEN>, ) -> Result<(), BufferError> { @@ -138,7 +122,7 @@ impl<'a> BuffWriter<'a> { if len != 0 { loop { let prop: &Property = properties.get(i).unwrap_or(&Property::Reserved()); - self.encode_property(prop)?; + self.write_property(prop)?; i = i + 1; if i == len { break; @@ -148,7 +132,7 @@ impl<'a> BuffWriter<'a> { Ok(()) } - fn encode_topic_filter_ref( + fn write_topic_filter_ref( &mut self, sub: bool, topic_filter: &TopicFilter<'a>, @@ -160,7 +144,7 @@ impl<'a> BuffWriter<'a> { return Ok(()); } - pub fn encode_topic_filters_ref( + pub fn write_topic_filters_ref( &mut self, sub: bool, len: usize, @@ -169,7 +153,7 @@ impl<'a> BuffWriter<'a> { let mut i = 0; loop { let topic_filter: &TopicFilter<'a> = filters.get(i).unwrap(); - self.encode_topic_filter_ref(sub, topic_filter)?; + self.write_topic_filter_ref(sub, topic_filter)?; i = i + 1; if i == len { break; diff --git a/src/utils/types.rs b/src/utils/types.rs index a4e9342..b35e962 100644 --- a/src/utils/types.rs +++ b/src/utils/types.rs @@ -25,6 +25,7 @@ use core::fmt::{Display, Formatter}; #[derive(core::fmt::Debug, Clone)] +#[derive(PartialEq)] pub enum BufferError { Utf8Error, InsufficientBufferSize, @@ -65,6 +66,7 @@ impl EncodedString<'_> { pub fn new() -> Self { Self { string: "", len: 0 } } + /// Return length of string pub fn len(&self) -> u16 { return self.len + 2; @@ -89,13 +91,19 @@ impl BinaryData<'_> { } /// String pair struct represents `String pair` in MQTTv5 (2 UTF-8 encoded strings name-value) -#[derive(Debug)] +#[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();