From 4485de404408b1eddfd8783d0130ccf90bcb80c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 10:21:42 +0100 Subject: [PATCH 01/67] Make packet module private MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5ee766e8..2752d076 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,4 +5,4 @@ // mod codecs; -pub mod packet; +mod packet; From 5f920b925f45a51ec4392af9fe8c25c540e31935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 12:31:10 +0100 Subject: [PATCH 02/67] Add compat feature to tokio-util MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 2b66ad47..df86a431 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -992,6 +992,7 @@ checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 09ef8ad2..b6a98e2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,6 @@ mqtt-format = { version = "0.5.0", path = "mqtt-format", features = [ stable_deref_trait = "1.2.0" thiserror = "1.0.58" tokio = { version = "1.36.0", features = ["macros", "full"] } -tokio-util = { version = "0.7.10", features = ["codec"] } +tokio-util = { version = "0.7.10", features = ["codec", "compat"] } winnow = "0.6.5" yoke = "0.7.3" From 9b68753f5e84f31fbaaf2919ddbe302d69931afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 12:31:35 +0100 Subject: [PATCH 03/67] Implement first draft of Client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 215 insertions(+) create mode 100644 src/client.rs diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 00000000..900dd487 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,214 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use std::num::NonZeroU16; +use std::time::Duration; + +use futures::AsyncRead; +use futures::AsyncWrite; +use tokio::io::DuplexStream; +use tokio::net::TcpStream; +use tokio_util::compat::Compat as TokioCompat; +use tokio_util::compat::TokioAsyncReadCompatExt; + +enum MqttConnection { + Tokio(TokioCompat), + Duplex(TokioCompat), +} + +impl AsyncRead for MqttConnection { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t).poll_read(cx, buf), + MqttConnection::Duplex(d) => std::pin::pin!(d).poll_read(cx, buf), + } + } +} + +impl AsyncWrite for MqttConnection { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t).poll_write(cx, buf), + MqttConnection::Duplex(d) => std::pin::pin!(d).poll_write(cx, buf), + } + } + + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t).poll_flush(cx), + MqttConnection::Duplex(d) => std::pin::pin!(d).poll_flush(cx), + } + } + + fn poll_close( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t).poll_close(cx), + MqttConnection::Duplex(d) => std::pin::pin!(d).poll_close(cx), + } + } +} + +pub enum MqttConnectTransport { + TokioTcp(TcpStream), + TokioDuplex(DuplexStream), +} + +impl From for MqttConnection { + fn from(value: MqttConnectTransport) -> Self { + match value { + MqttConnectTransport::TokioTcp(t) => MqttConnection::Tokio(t.compat()), + MqttConnectTransport::TokioDuplex(d) => MqttConnection::Duplex(d.compat()), + } + } +} + +pub struct MinimalRequiredClientIdentifier(String); +pub struct PotentiallyAcceptedClientIdentifier(String); + +pub enum ClientIdentifier { + MinimalRequired(MinimalRequiredClientIdentifier), + PotentiallyServerProvided, + PotentiallyAccepted(PotentiallyAcceptedClientIdentifier), +} + +impl ClientIdentifier { + pub fn new_minimal_required(s: impl Into) -> Result { + todo!() + } + + pub fn as_str(&self) -> &str { + todo!() + } +} + +pub enum CleanStart { + No, + Yes, +} + +impl CleanStart { + pub fn as_bool(&self) -> bool { + match self { + CleanStart::No => false, + CleanStart::Yes => true, + } + } +} + +pub enum KeepAlive { + Disabled, + Seconds(NonZeroU16), +} + +impl KeepAlive { + fn as_u16(&self) -> u16 { + match self { + KeepAlive::Disabled => 0, + KeepAlive::Seconds(s) => s.get(), + } + } +} +impl TryFrom for KeepAlive { + type Error = (); + fn try_from(value: Duration) -> Result { + todo!() + } +} + +pub struct ConnectProperties { + session_expiry_interval: Option, +} + +impl ConnectProperties { + fn new() -> Self { + ConnectProperties { + session_expiry_interval: None, + } + } +} + +impl ConnectProperties { + pub fn as_ref(&self) -> mqtt_format::v5::packets::connect::ConnectProperties<'_> { + mqtt_format::v5::packets::connect::ConnectProperties { + session_expiry_interval: self + .session_expiry_interval + .clone() + .map(mqtt_format::v5::variable_header::SessionExpiryInterval), + ..mqtt_format::v5::packets::connect::ConnectProperties::new() + } + } +} + +pub struct MqttClientConnector { + transport: MqttConnectTransport, + client_identifier: ClientIdentifier, + clean_start: CleanStart, + keep_alive: KeepAlive, + properties: ConnectProperties, +} + +impl MqttClientConnector { + pub fn set_session_expiry_inteveral(&mut self, interval: u32) { + self.properties.session_expiry_interval = Some(interval); + } +} + +impl MqttClientConnector { + pub fn new( + transport: MqttConnectTransport, + client_identifier: ClientIdentifier, + clean_start: CleanStart, + keep_alive: KeepAlive, + ) -> MqttClientConnector { + MqttClientConnector { + transport, + client_identifier, + clean_start, + keep_alive, + properties: ConnectProperties::new(), + } + } + + pub async fn connect(self) -> Result { + let conn: MqttConnection = self.transport.into(); + + let conn_packet = mqtt_format::v5::packets::connect::MConnect { + client_identifier: self.client_identifier.as_str(), + username: None, + password: None, + clean_start: self.clean_start.as_bool(), + will: None, + properties: self.properties.as_ref(), + keep_alive: self.keep_alive.as_u16(), + }; + + todo!() + } +} + +pub struct MqttClient { + conn: MqttConnection, +} + +impl MqttClient { + pub(crate) fn new_with_connection(conn: MqttConnection) -> Self { + MqttClient { conn } + } +} diff --git a/src/lib.rs b/src/lib.rs index 2752d076..9107965d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,4 @@ mod codecs; mod packet; +mod client; From 57dd58077dae00f720c19883773d024cf2f9bb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 12:31:44 +0100 Subject: [PATCH 04/67] Add barebones Error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/error.rs | 8 ++++++++ src/lib.rs | 1 + 2 files changed, 9 insertions(+) create mode 100644 src/error.rs diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..687a1ec2 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,8 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +#[derive(Debug, thiserror::Error)] +pub enum MqttError {} diff --git a/src/lib.rs b/src/lib.rs index 9107965d..24985088 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,3 +7,4 @@ mod codecs; mod packet; mod client; +mod error; From 3618f4438d45f1c62e4baa5b5d0dec9f1ac83d88 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Mar 2024 13:21:49 +0100 Subject: [PATCH 05/67] Move ClientIdentifier to own mod Signed-off-by: Matthias Beyer --- src/client.rs | 21 ++------------------- src/client_identifier.rs | 25 +++++++++++++++++++++++++ src/lib.rs | 1 + 3 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 src/client_identifier.rs diff --git a/src/client.rs b/src/client.rs index 900dd487..0a418537 100644 --- a/src/client.rs +++ b/src/client.rs @@ -14,6 +14,8 @@ use tokio::net::TcpStream; use tokio_util::compat::Compat as TokioCompat; use tokio_util::compat::TokioAsyncReadCompatExt; +use crate::client_identifier::ClientIdentifier; + enum MqttConnection { Tokio(TokioCompat), Duplex(TokioCompat), @@ -79,25 +81,6 @@ impl From for MqttConnection { } } -pub struct MinimalRequiredClientIdentifier(String); -pub struct PotentiallyAcceptedClientIdentifier(String); - -pub enum ClientIdentifier { - MinimalRequired(MinimalRequiredClientIdentifier), - PotentiallyServerProvided, - PotentiallyAccepted(PotentiallyAcceptedClientIdentifier), -} - -impl ClientIdentifier { - pub fn new_minimal_required(s: impl Into) -> Result { - todo!() - } - - pub fn as_str(&self) -> &str { - todo!() - } -} - pub enum CleanStart { No, Yes, diff --git a/src/client_identifier.rs b/src/client_identifier.rs new file mode 100644 index 00000000..1b66a1f6 --- /dev/null +++ b/src/client_identifier.rs @@ -0,0 +1,25 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +pub enum ClientIdentifier { + MinimalRequired(MinimalRequiredClientIdentifier), + PotentiallyServerProvided, + PotentiallyAccepted(PotentiallyAcceptedClientIdentifier), +} + +impl ClientIdentifier { + pub fn new_minimal_required(s: impl Into) -> Result { + todo!() + } + + pub fn as_str(&self) -> &str { + todo!() + } +} + +pub struct MinimalRequiredClientIdentifier(String); +pub struct PotentiallyAcceptedClientIdentifier(String); + diff --git a/src/lib.rs b/src/lib.rs index 24985088..a3c2110a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,4 +7,5 @@ mod codecs; mod packet; mod client; +mod client_identifier; mod error; From 633f0a0f389ae8970f3a65cb5b658055ce2d64f9 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Mar 2024 13:35:56 +0100 Subject: [PATCH 06/67] Impl ClientIdentifier::new_minimal_required() Signed-off-by: Matthias Beyer --- src/client_identifier.rs | 42 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/client_identifier.rs b/src/client_identifier.rs index 1b66a1f6..b4e8dc25 100644 --- a/src/client_identifier.rs +++ b/src/client_identifier.rs @@ -11,8 +11,31 @@ pub enum ClientIdentifier { } impl ClientIdentifier { - pub fn new_minimal_required(s: impl Into) -> Result { - todo!() + pub fn new_minimal_required( + s: impl Into, + ) -> Result { + const ALLOWED_CHARS: &str = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + let s = s.into(); + + let disallowed_chars = s + .chars() + .filter(|c| !ALLOWED_CHARS.chars().any(|allowed| allowed == *c)) + .collect::>(); + + if !disallowed_chars.is_empty() { + return Err(ClientIdentifierError::MinimalNotAllowedChar( + disallowed_chars, + )); + } + + if s.len() > 23 { + return Err(ClientIdentifierError::MinimalTooLong(s.len())); + } + + Ok(ClientIdentifier::MinimalRequired( + MinimalRequiredClientIdentifier(s), + )) } pub fn as_str(&self) -> &str { @@ -23,3 +46,18 @@ impl ClientIdentifier { pub struct MinimalRequiredClientIdentifier(String); pub struct PotentiallyAcceptedClientIdentifier(String); +#[derive(Debug, thiserror::Error)] +pub enum ClientIdentifierError { + // I am ugly + #[error("Minimal client identifier contains disallowed characters: {}", .0.iter().copied().map(String::from).collect::>().join(", "))] + MinimalNotAllowedChar(Vec), + + #[error("Minimal client identifier contains more characters than allowed: {}", .0)] + MinimalTooLong(usize), + + #[error("Client identifier is now allowed to be of length zero")] + ZeroLen, + + #[error("Client identifier is now allowed to be of length {}, maximum is {}", .0, u16::MAX)] + TooLong(usize), +} From f7a02c1693176a8f0cbf7b93367ced22cee6182c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Mar 2024 13:38:35 +0100 Subject: [PATCH 07/67] Implement ClientIdentifier::new_potentially_server_provided() Signed-off-by: Matthias Beyer --- src/client_identifier.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client_identifier.rs b/src/client_identifier.rs index b4e8dc25..9beaa382 100644 --- a/src/client_identifier.rs +++ b/src/client_identifier.rs @@ -38,6 +38,10 @@ impl ClientIdentifier { )) } + pub fn new_potentially_server_provided() -> ClientIdentifier { + ClientIdentifier::PotentiallyServerProvided + } + pub fn as_str(&self) -> &str { todo!() } From e02dd066484aad10e9e78c80cf5c498eeb70d1f3 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Mar 2024 13:38:42 +0100 Subject: [PATCH 08/67] Implement ClientIdentifier::new_potentially_accepted() Signed-off-by: Matthias Beyer --- src/client_identifier.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/client_identifier.rs b/src/client_identifier.rs index 9beaa382..f0f5356d 100644 --- a/src/client_identifier.rs +++ b/src/client_identifier.rs @@ -42,13 +42,26 @@ impl ClientIdentifier { ClientIdentifier::PotentiallyServerProvided } + pub fn new_potetially_accepted( + s: impl Into, + ) -> Result { + let s = s.into(); + if s.is_empty() { + return Err(ClientIdentifierError::Zero); + } + crate::string::MqttString::try_from(s) + .map(PotentiallyAcceptedClientIdentifier) + .map(ClientIdentifier::PotentiallyAccepted) + .map_err(ClientIdentifierError::from) + } + pub fn as_str(&self) -> &str { todo!() } } pub struct MinimalRequiredClientIdentifier(String); -pub struct PotentiallyAcceptedClientIdentifier(String); +pub struct PotentiallyAcceptedClientIdentifier(crate::string::MqttString); #[derive(Debug, thiserror::Error)] pub enum ClientIdentifierError { @@ -59,9 +72,9 @@ pub enum ClientIdentifierError { #[error("Minimal client identifier contains more characters than allowed: {}", .0)] MinimalTooLong(usize), - #[error("Client identifier is now allowed to be of length zero")] - ZeroLen, + #[error("Client identifier is not allowed to be empty")] + Zero, - #[error("Client identifier is now allowed to be of length {}, maximum is {}", .0, u16::MAX)] - TooLong(usize), + #[error(transparent)] + String(#[from] crate::string::MqttStringError), } From 0b035ecb54d1c5843e1e2458e2ba60f914c17721 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Mar 2024 13:40:21 +0100 Subject: [PATCH 09/67] Move KeepAlive to dedicated mod Signed-off-by: Matthias Beyer --- src/client.rs | 24 +----------------------- src/keep_alive.rs | 28 ++++++++++++++++++++++++++++ src/lib.rs | 1 + 3 files changed, 30 insertions(+), 23 deletions(-) create mode 100644 src/keep_alive.rs diff --git a/src/client.rs b/src/client.rs index 0a418537..2c2617d2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,9 +4,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // -use std::num::NonZeroU16; -use std::time::Duration; - use futures::AsyncRead; use futures::AsyncWrite; use tokio::io::DuplexStream; @@ -15,6 +12,7 @@ use tokio_util::compat::Compat as TokioCompat; use tokio_util::compat::TokioAsyncReadCompatExt; use crate::client_identifier::ClientIdentifier; +use crate::keep_alive::KeepAlive; enum MqttConnection { Tokio(TokioCompat), @@ -95,26 +93,6 @@ impl CleanStart { } } -pub enum KeepAlive { - Disabled, - Seconds(NonZeroU16), -} - -impl KeepAlive { - fn as_u16(&self) -> u16 { - match self { - KeepAlive::Disabled => 0, - KeepAlive::Seconds(s) => s.get(), - } - } -} -impl TryFrom for KeepAlive { - type Error = (); - fn try_from(value: Duration) -> Result { - todo!() - } -} - pub struct ConnectProperties { session_expiry_interval: Option, } diff --git a/src/keep_alive.rs b/src/keep_alive.rs new file mode 100644 index 00000000..654250d4 --- /dev/null +++ b/src/keep_alive.rs @@ -0,0 +1,28 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use std::num::NonZeroU16; +use std::time::Duration; + +pub enum KeepAlive { + Disabled, + Seconds(NonZeroU16), +} + +impl KeepAlive { + pub(crate) fn as_u16(&self) -> u16 { + match self { + KeepAlive::Disabled => 0, + KeepAlive::Seconds(s) => s.get(), + } + } +} +impl TryFrom for KeepAlive { + type Error = (); + fn try_from(value: Duration) -> Result { + todo!() + } +} diff --git a/src/lib.rs b/src/lib.rs index a3c2110a..e2bb8827 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,3 +9,4 @@ mod packet; mod client; mod client_identifier; mod error; +mod keep_alive; From 52bc016106123fc210c45a2ddf18d7d6aba10776 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Mar 2024 13:45:28 +0100 Subject: [PATCH 10/67] Impl TryFrom for KeepAlive Signed-off-by: Matthias Beyer --- src/keep_alive.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/keep_alive.rs b/src/keep_alive.rs index 654250d4..b185fa1f 100644 --- a/src/keep_alive.rs +++ b/src/keep_alive.rs @@ -20,9 +20,26 @@ impl KeepAlive { } } } + impl TryFrom for KeepAlive { - type Error = (); + type Error = KeepAliveError; + fn try_from(value: Duration) -> Result { - todo!() + let secs = value.as_secs(); + if secs > u16::MAX.into() { + return Err(KeepAliveError::OutOfBounds) + } + let secs = secs as u16; + + Ok(KeepAlive::Seconds(NonZeroU16::try_from(secs)?)) } } + +#[derive(Debug, thiserror::Error)] +pub enum KeepAliveError { + #[error("KeepAlive cannot be of zero duration")] + KeepAliveZero(#[from] std::num::TryFromIntError), + + #[error("KeepAlive out of bounds, maximum is {} seconds", u16::MAX)] + OutOfBounds, +} From 943180336598a558229eff98123fbb2a9e2681c7 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Mar 2024 14:11:00 +0100 Subject: [PATCH 11/67] Add MqttString helper type Signed-off-by: Matthias Beyer --- src/lib.rs | 1 + src/string.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/string.rs diff --git a/src/lib.rs b/src/lib.rs index e2bb8827..36549a70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,3 +10,4 @@ mod client; mod client_identifier; mod error; mod keep_alive; +mod string; diff --git a/src/string.rs b/src/string.rs new file mode 100644 index 00000000..67c4777f --- /dev/null +++ b/src/string.rs @@ -0,0 +1,54 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct MqttString(String); + +impl MqttString { + pub const MAX_LEN: usize = u16::MAX as usize; +} + +impl std::str::FromStr for MqttString { + type Err = MqttStringError; + + fn from_str(s: &str) -> Result { + if s.len() > Self::MAX_LEN { + Err(MqttStringError::TooLong(s.len())) + } else { + Ok(Self(s.to_string())) + } + } +} + +impl TryFrom for MqttString { + type Error = MqttStringError; + + fn try_from(s: String) -> Result { + if s.len() > Self::MAX_LEN { + Err(MqttStringError::TooLong(s.len())) + } else { + Ok(Self(s.to_string())) + } + } +} + +impl AsRef for MqttString { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl std::fmt::Debug for MqttString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum MqttStringError { + #[error("String of length {} is too long, max length is {}", .0, MqttString::MAX_LEN)] + TooLong(usize), +} From 41d21b6a240c08733916ada1520be06d4b91f6ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 16:50:03 +0100 Subject: [PATCH 12/67] Add paste for easier macro writing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + 2 files changed, 8 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index df86a431..e343b5d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,7 @@ version = "0.5.0" dependencies = [ "futures", "mqtt-format", + "paste", "stable_deref_trait", "thiserror", "tokio", @@ -646,6 +647,12 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pin-project-lite" version = "0.2.13" diff --git a/Cargo.toml b/Cargo.toml index b6a98e2f..fd8f1e83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ mqtt-format = { version = "0.5.0", path = "mqtt-format", features = [ "yoke", "mqttv5", ] } +paste = "1.0.14" stable_deref_trait = "1.2.0" thiserror = "1.0.58" tokio = { version = "1.36.0", features = ["macros", "full"] } From 4c28e4d21b2919e9e718db27a7cd7b1bedc95fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 16:50:26 +0100 Subject: [PATCH 13/67] Add properties for cloudmqtt proper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/lib.rs | 5 +- src/properties.rs | 256 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 src/properties.rs diff --git a/src/lib.rs b/src/lib.rs index 36549a70..9229b4df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,10 +4,11 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // -mod codecs; -mod packet; mod client; mod client_identifier; +mod codecs; mod error; mod keep_alive; +mod packet; +mod properties; mod string; diff --git a/src/properties.rs b/src/properties.rs new file mode 100644 index 00000000..e096ea54 --- /dev/null +++ b/src/properties.rs @@ -0,0 +1,256 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +pub type TypeOfProperty

=

::Inner; +pub type SetterTypeOfProperty

=

::Setter; + +macro_rules! define_properties { + (@no_lt $name:ident $pat:ident $lt:lifetime) => { + type $name<'a> = mqtt_format::v5::variable_header:: $pat <'a>; + }; + (@no_lt $name:ident $pat:ident) => { + type $name = mqtt_format::v5::variable_header:: $pat; + }; + + (@statify $pat:ident $lt:lifetime) => { + mqtt_format::v5::variable_header:: $pat <'static> + }; + (@statify $pat:ident) => { + mqtt_format::v5::variable_header:: $pat + }; + ( + properties_type: $packettypename:ty, + $( anker: $anker:literal $(,)?)? + pub struct $name:ident { + $( $((anker: $prop_anker:literal ))? $prop_name:ident : $prop:ident $(<$prop_lt:lifetime>)?),* $(,)? + } + ) => { + #[derive(Clone, Debug, PartialEq)] + pub struct $name { + $( + pub $prop_name: Option> + ),* + } + + paste::paste! { + impl $name { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + $name { + $($prop_name: None),* + } + } + + $( + pub fn [](&mut self, value: impl Into>) -> &mut Self { + ::apply(&mut self.$prop_name, value); + + self + } + )* + + pub fn as_ref(&self) -> $packettypename { + $packettypename { + $($prop_name: { + self.$prop_name.as_ref().map(|v| { + crate::properties::define_properties!(@no_lt Prop $prop $($prop_lt)?); + Prop { + 0: ::get(v) + } + }) + }),* + } + } + } + } + }; +} +pub(crate) use define_properties; + +use crate::packet::VecWriter; +use crate::string::MqttString; + +pub struct UserProperty { + key: MqttString, + value: MqttString, +} + +pub(crate) trait FormatProperty { + type Inner; + type Setter; + type Outer<'a>; + + fn apply(inner: &mut Option, value: impl Into); + + fn get(inner: &Self::Inner) -> Self::Outer<'_>; +} + +impl<'i> FormatProperty for mqtt_format::v5::variable_header::UserProperties<'i> { + type Inner = Vec; + type Setter = UserProperty; + type Outer<'a> = &'a [u8]; + + fn apply(inner: &mut Option, key_value: impl Into) { + let key_value = key_value.into(); + let user_prop = mqtt_format::v5::variable_header::UserProperty { + key: key_value.key.as_ref(), + value: key_value.value.as_ref(), + }; + let inner = inner.get_or_insert_with(Default::default); + if !inner.is_empty() { + mqtt_format::v5::integers::write_variable_u32( + &mut VecWriter(inner), + ::IDENTIFIER, + ) + .expect("Writing a u32 should not fail") + } + user_prop + .write(&mut VecWriter(inner)) + .expect("Writing MqttStrings should never be invalid"); + } + + fn get(inner: &Self::Inner) -> Self::Outer<'_> { + inner.as_ref() + } +} + +macro_rules! define_property_types { + (@access_pattern ref $value:ident) => { + $value.as_ref() + }; + (@access_pattern deref $value:ident) => { + *$value + }; + ([ $( $prop:ty => inner = $inner:ty; setter = $setter:ty; outer $mode:tt = $outer:ty ),* $(,)? ]) => { + $( + impl<'i> FormatProperty for $prop { + type Inner = $inner; + type Setter = $setter; + type Outer<'a> = $outer; + + fn apply(inner: &mut Option, value: impl Into) { + *inner = Some(value.into()); + } + + fn get(inner: &Self::Inner) -> Self::Outer<'_> { + define_property_types!(@access_pattern $mode inner) + } + } + )* + }; +} + +define_property_types! {[ + mqtt_format::v5::variable_header::PayloadFormatIndicator => inner = u8; setter = u8; outer deref = u8, + mqtt_format::v5::variable_header::MessageExpiryInterval => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::ContentType<'i> => inner = String; setter = String; outer ref = &'a str, + mqtt_format::v5::variable_header::ResponseTopic<'i> => inner = String; setter = String; outer ref = &'a str, + mqtt_format::v5::variable_header::CorrelationData<'i> => inner = Vec; setter = Vec; outer ref = &'a [u8], + mqtt_format::v5::variable_header::SubscriptionIdentifier => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::SessionExpiryInterval => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::AssignedClientIdentifier<'i> => inner = String; setter = String; outer ref = &'a str, + mqtt_format::v5::variable_header::ServerKeepAlive => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::AuthenticationMethod<'i> => inner = String; setter = String; outer ref = &'a str, + mqtt_format::v5::variable_header::AuthenticationData<'i> => inner = Vec; setter = Vec; outer ref = &'a [u8], + mqtt_format::v5::variable_header::RequestProblemInformation => inner = u8; setter = u8; outer deref = u8, + mqtt_format::v5::variable_header::WillDelayInterval => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::RequestResponseInformation => inner = u8; setter = u8; outer deref = u8, + mqtt_format::v5::variable_header::ResponseInformation<'i> => inner = String; setter = String; outer ref = &'a str, + mqtt_format::v5::variable_header::ServerReference<'i> => inner = String; setter = String; outer ref = &'a str, + mqtt_format::v5::variable_header::ReasonString<'i> => inner = String; setter = String; outer ref = &'a str, + mqtt_format::v5::variable_header::ReceiveMaximum => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::TopicAliasMaximum => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::TopicAlias => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::MaximumQoS => inner = u8; setter = u8; outer deref = u8, + mqtt_format::v5::variable_header::RetainAvailable => inner = u8; setter = u8; outer deref = u8, + mqtt_format::v5::variable_header::MaximumPacketSize => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::WildcardSubscriptionAvailable => inner = u8; setter = u8; outer deref = u8, + mqtt_format::v5::variable_header::SubscriptionIdentifiersAvailable => inner = u8; setter = u8; outer deref = u8, + mqtt_format::v5::variable_header::SharedSubscriptionAvailable => inner = u8; setter = u8; outer deref = u8, +]} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::UserProperty; + use crate::packet::VecWriter; + use crate::string::MqttString; + + crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::connect::ConnectProperties, + anker: "_Toc3901046", + pub struct ConnectProperties { + (anker: "_Toc3901048") + session_expiry_interval: SessionExpiryInterval, + + (anker: "_Toc3901049") + receive_maximum: ReceiveMaximum, + + (anker: "_Toc3901050") + maximum_packet_size: MaximumPacketSize, + + (anker: "_Toc3901051") + topic_alias_maximum: TopicAliasMaximum, + + (anker: "_Toc3901052") + request_response_information: RequestResponseInformation, + + (anker: "_Toc3901053") + request_problem_information: RequestProblemInformation, + + (anker: "_Toc3901054") + user_properties: UserProperties<'a>, + + (anker: "_Toc3901055") + authentication_method: AuthenticationMethod<'a>, + + (anker: "_Toc3901056") + authentication_data: AuthenticationData<'a>, + } + } + + #[test] + fn check_properties() { + let mut props = ConnectProperties::new(); + + props.set_session_expiry_interval(16u32); + props.set_user_properties(UserProperty { + key: MqttString::from_str("foo").unwrap(), + value: MqttString::from_str("bar").unwrap(), + }); + for _ in 0..5 { + props.set_user_properties(UserProperty { + key: MqttString::from_str("foo").unwrap(), + value: MqttString::from_str("bar").unwrap(), + }); + } + + let conn_props = props.as_ref(); + + assert_eq!( + conn_props + .user_properties() + .as_ref() + .unwrap() + .iter() + .count(), + 6 + ); + + let mut buffer = vec![]; + let mut buffer = VecWriter(&mut buffer); + + conn_props.write(&mut buffer).unwrap(); + + let new_props = mqtt_format::v5::packets::connect::ConnectProperties::parse( + &mut winnow::Bytes::new(&buffer.0), + ) + .unwrap(); + + assert_eq!(conn_props, new_props); + } +} From 562a99d2c5e14880dea3336ab6a23932a03673ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 16:50:43 +0100 Subject: [PATCH 14/67] Make MqttConnection pub(crate) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 2c2617d2..509e250b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -14,7 +14,7 @@ use tokio_util::compat::TokioAsyncReadCompatExt; use crate::client_identifier::ClientIdentifier; use crate::keep_alive::KeepAlive; -enum MqttConnection { +pub(crate) enum MqttConnection { Tokio(TokioCompat), Duplex(TokioCompat), } From 53dc2b2e23293950ab077a50a8b1115160c13dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 16:50:56 +0100 Subject: [PATCH 15/67] Use macro to define cloudmqtt ConnectProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/client.rs b/src/client.rs index 509e250b..a6040d4d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -93,27 +93,36 @@ impl CleanStart { } } -pub struct ConnectProperties { - session_expiry_interval: Option, -} +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::connect::ConnectProperties, + anker: "_Toc3901046", + pub struct ConnectProperties { + (anker: "_Toc3901048") + session_expiry_interval: SessionExpiryInterval, -impl ConnectProperties { - fn new() -> Self { - ConnectProperties { - session_expiry_interval: None, - } - } -} + (anker: "_Toc3901049") + receive_maximum: ReceiveMaximum, -impl ConnectProperties { - pub fn as_ref(&self) -> mqtt_format::v5::packets::connect::ConnectProperties<'_> { - mqtt_format::v5::packets::connect::ConnectProperties { - session_expiry_interval: self - .session_expiry_interval - .clone() - .map(mqtt_format::v5::variable_header::SessionExpiryInterval), - ..mqtt_format::v5::packets::connect::ConnectProperties::new() - } + (anker: "_Toc3901050") + maximum_packet_size: MaximumPacketSize, + + (anker: "_Toc3901051") + topic_alias_maximum: TopicAliasMaximum, + + (anker: "_Toc3901052") + request_response_information: RequestResponseInformation, + + (anker: "_Toc3901053") + request_problem_information: RequestProblemInformation, + + (anker: "_Toc3901054") + user_properties: UserProperties<'a>, + + (anker: "_Toc3901055") + authentication_method: AuthenticationMethod<'a>, + + (anker: "_Toc3901056") + authentication_data: AuthenticationData<'a>, } } @@ -126,8 +135,8 @@ pub struct MqttClientConnector { } impl MqttClientConnector { - pub fn set_session_expiry_inteveral(&mut self, interval: u32) { - self.properties.session_expiry_interval = Some(interval); + pub fn properties_mut(&mut self) -> &mut ConnectProperties { + &mut self.properties } } From 9a02b361a16b7bad92c41f1719791f18c181be1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 26 Mar 2024 16:51:32 +0100 Subject: [PATCH 16/67] Add a vec writer wrapper typer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packet.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/packet.rs b/src/packet.rs index 500baa9e..c4ec1bc2 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -7,6 +7,7 @@ use std::ops::Deref; use mqtt_format::v5::packets::MqttPacket as FormatMqttPacket; +use mqtt_format::v5::write::MqttWriteError; use mqtt_format::v5::write::WriteMqttPacket; use stable_deref_trait::StableDeref; use tokio_util::bytes::BufMut; @@ -61,6 +62,23 @@ impl From for MqttWriterError { } } +pub struct VecWriter<'a>(pub &'a mut Vec); + +impl<'a> WriteMqttPacket for VecWriter<'a> { + type Error = MqttWriteError; + + fn write_byte(&mut self, u: u8) -> mqtt_format::v5::write::WResult { + self.0.push(u); + + Ok(()) + } + + fn write_slice(&mut self, u: &[u8]) -> mqtt_format::v5::write::WResult { + self.0.extend_from_slice(u); + Ok(()) + } +} + pub struct MqttWriter<'a>(pub &'a mut BytesMut); impl<'a> WriteMqttPacket for MqttWriter<'a> { From f54125bf81e1ecec2b87ed06bc31b070221eb08e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:33:18 +0100 Subject: [PATCH 17/67] Explicitely specify the setter type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 18 +++++++++--------- src/properties.rs | 32 +++++++++++++++++--------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/client.rs b/src/client.rs index a6040d4d..f456805b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -98,31 +98,31 @@ crate::properties::define_properties! { anker: "_Toc3901046", pub struct ConnectProperties { (anker: "_Toc3901048") - session_expiry_interval: SessionExpiryInterval, + session_expiry_interval: SessionExpiryInterval with setter = u32, (anker: "_Toc3901049") - receive_maximum: ReceiveMaximum, + receive_maximum: ReceiveMaximum with setter = u32, (anker: "_Toc3901050") - maximum_packet_size: MaximumPacketSize, + maximum_packet_size: MaximumPacketSize with setter = u32, (anker: "_Toc3901051") - topic_alias_maximum: TopicAliasMaximum, + topic_alias_maximum: TopicAliasMaximum with setter = u32, (anker: "_Toc3901052") - request_response_information: RequestResponseInformation, + request_response_information: RequestResponseInformation with setter = u8, (anker: "_Toc3901053") - request_problem_information: RequestProblemInformation, + request_problem_information: RequestProblemInformation with setter = u8, (anker: "_Toc3901054") - user_properties: UserProperties<'a>, + user_properties: UserProperties<'a> with setter = crate::properties::UserProperty, (anker: "_Toc3901055") - authentication_method: AuthenticationMethod<'a>, + authentication_method: AuthenticationMethod<'a> with setter = String, (anker: "_Toc3901056") - authentication_data: AuthenticationData<'a>, + authentication_data: AuthenticationData<'a> with setter = Vec, } } diff --git a/src/properties.rs b/src/properties.rs index e096ea54..85103af8 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -25,17 +25,18 @@ macro_rules! define_properties { properties_type: $packettypename:ty, $( anker: $anker:literal $(,)?)? pub struct $name:ident { - $( $((anker: $prop_anker:literal ))? $prop_name:ident : $prop:ident $(<$prop_lt:lifetime>)?),* $(,)? + $( $((anker: $prop_anker:literal ))? $prop_name:ident : $prop:ident $(<$prop_lt:lifetime>)? with setter = $setter:ty),* $(,)? } ) => { #[derive(Clone, Debug, PartialEq)] pub struct $name { $( - pub $prop_name: Option> + $prop_name: Option> ),* } paste::paste! { + #[allow(dead_code)] impl $name { #[allow(clippy::new_without_default)] pub fn new() -> Self { @@ -45,7 +46,7 @@ macro_rules! define_properties { } $( - pub fn [](&mut self, value: impl Into>) -> &mut Self { + pub fn [](&mut self, value: $setter) -> &mut Self { ::apply(&mut self.$prop_name, value); self @@ -185,31 +186,31 @@ mod tests { anker: "_Toc3901046", pub struct ConnectProperties { (anker: "_Toc3901048") - session_expiry_interval: SessionExpiryInterval, + session_expiry_interval: SessionExpiryInterval with setter = u32, (anker: "_Toc3901049") - receive_maximum: ReceiveMaximum, + receive_maximum: ReceiveMaximum with setter = u32, (anker: "_Toc3901050") - maximum_packet_size: MaximumPacketSize, + maximum_packet_size: MaximumPacketSize with setter = u32, (anker: "_Toc3901051") - topic_alias_maximum: TopicAliasMaximum, + topic_alias_maximum: TopicAliasMaximum with setter = u32, (anker: "_Toc3901052") - request_response_information: RequestResponseInformation, + request_response_information: RequestResponseInformation with setter = u8, (anker: "_Toc3901053") - request_problem_information: RequestProblemInformation, + request_problem_information: RequestProblemInformation with setter = u8, (anker: "_Toc3901054") - user_properties: UserProperties<'a>, + user_properties: UserProperties<'a> with setter = crate::properties::UserProperty, (anker: "_Toc3901055") - authentication_method: AuthenticationMethod<'a>, + authentication_method: AuthenticationMethod<'a> with setter = String, (anker: "_Toc3901056") - authentication_data: AuthenticationData<'a>, + authentication_data: AuthenticationData<'a> with setter = Vec, } } @@ -217,17 +218,18 @@ mod tests { fn check_properties() { let mut props = ConnectProperties::new(); - props.set_session_expiry_interval(16u32); - props.set_user_properties(UserProperty { + props.with_session_expiry_interval(16u32); + props.with_user_properties(UserProperty { key: MqttString::from_str("foo").unwrap(), value: MqttString::from_str("bar").unwrap(), }); for _ in 0..5 { - props.set_user_properties(UserProperty { + props.with_user_properties(UserProperty { key: MqttString::from_str("foo").unwrap(), value: MqttString::from_str("bar").unwrap(), }); } + props.with_receive_maximum(4); let conn_props = props.as_ref(); From 68a6f28d390a7b9918719edd1ca0c1093b96209c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:37:08 +0100 Subject: [PATCH 18/67] Clean up client by moving to transport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 84 ++++-------------------------------------------- src/lib.rs | 1 + src/transport.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 78 deletions(-) create mode 100644 src/transport.rs diff --git a/src/client.rs b/src/client.rs index f456805b..3a034df1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,80 +4,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // -use futures::AsyncRead; -use futures::AsyncWrite; -use tokio::io::DuplexStream; -use tokio::net::TcpStream; -use tokio_util::compat::Compat as TokioCompat; -use tokio_util::compat::TokioAsyncReadCompatExt; - use crate::client_identifier::ClientIdentifier; use crate::keep_alive::KeepAlive; - -pub(crate) enum MqttConnection { - Tokio(TokioCompat), - Duplex(TokioCompat), -} - -impl AsyncRead for MqttConnection { - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut [u8], - ) -> std::task::Poll> { - match &mut *self { - MqttConnection::Tokio(t) => std::pin::pin!(t).poll_read(cx, buf), - MqttConnection::Duplex(d) => std::pin::pin!(d).poll_read(cx, buf), - } - } -} - -impl AsyncWrite for MqttConnection { - fn poll_write( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - match &mut *self { - MqttConnection::Tokio(t) => std::pin::pin!(t).poll_write(cx, buf), - MqttConnection::Duplex(d) => std::pin::pin!(d).poll_write(cx, buf), - } - } - - fn poll_flush( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - match &mut *self { - MqttConnection::Tokio(t) => std::pin::pin!(t).poll_flush(cx), - MqttConnection::Duplex(d) => std::pin::pin!(d).poll_flush(cx), - } - } - - fn poll_close( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - match &mut *self { - MqttConnection::Tokio(t) => std::pin::pin!(t).poll_close(cx), - MqttConnection::Duplex(d) => std::pin::pin!(d).poll_close(cx), - } - } -} - -pub enum MqttConnectTransport { - TokioTcp(TcpStream), - TokioDuplex(DuplexStream), -} - -impl From for MqttConnection { - fn from(value: MqttConnectTransport) -> Self { - match value { - MqttConnectTransport::TokioTcp(t) => MqttConnection::Tokio(t.compat()), - MqttConnectTransport::TokioDuplex(d) => MqttConnection::Duplex(d.compat()), - } - } -} +use crate::transport::MqttConnectTransport; +use crate::transport::MqttConnection; pub enum CleanStart { No, @@ -134,12 +64,6 @@ pub struct MqttClientConnector { properties: ConnectProperties, } -impl MqttClientConnector { - pub fn properties_mut(&mut self) -> &mut ConnectProperties { - &mut self.properties - } -} - impl MqttClientConnector { pub fn new( transport: MqttConnectTransport, @@ -171,6 +95,10 @@ impl MqttClientConnector { todo!() } + + pub fn properties_mut(&mut self) -> &mut ConnectProperties { + &mut self.properties + } } pub struct MqttClient { diff --git a/src/lib.rs b/src/lib.rs index 9229b4df..d47d1cda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,3 +12,4 @@ mod keep_alive; mod packet; mod properties; mod string; +mod transport; diff --git a/src/transport.rs b/src/transport.rs new file mode 100644 index 00000000..c1da6b5a --- /dev/null +++ b/src/transport.rs @@ -0,0 +1,77 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use futures::AsyncRead; +use futures::AsyncWrite; +use tokio::io::DuplexStream; +use tokio::net::TcpStream; +use tokio_util::compat::Compat as TokioCompat; +use tokio_util::compat::TokioAsyncReadCompatExt; + +pub(crate) enum MqttConnection { + Tokio(TokioCompat), + Duplex(TokioCompat), +} + +impl AsyncRead for MqttConnection { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t).poll_read(cx, buf), + MqttConnection::Duplex(d) => std::pin::pin!(d).poll_read(cx, buf), + } + } +} + +impl AsyncWrite for MqttConnection { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t).poll_write(cx, buf), + MqttConnection::Duplex(d) => std::pin::pin!(d).poll_write(cx, buf), + } + } + + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t).poll_flush(cx), + MqttConnection::Duplex(d) => std::pin::pin!(d).poll_flush(cx), + } + } + + fn poll_close( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t).poll_close(cx), + MqttConnection::Duplex(d) => std::pin::pin!(d).poll_close(cx), + } + } +} + +pub enum MqttConnectTransport { + TokioTcp(TcpStream), + TokioDuplex(DuplexStream), +} + +impl From for MqttConnection { + fn from(value: MqttConnectTransport) -> Self { + match value { + MqttConnectTransport::TokioTcp(t) => MqttConnection::Tokio(t.compat()), + MqttConnectTransport::TokioDuplex(d) => MqttConnection::Duplex(d.compat()), + } + } +} From 4ef49526958fec184838f5ddf011e18283343c28 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 09:38:25 +0100 Subject: [PATCH 19/67] Move packet.rs -> packets/mod.rs, define mods for all packets Signed-off-by: Matthias Beyer --- src/codecs.rs | 6 +++--- src/keep_alive.rs | 2 +- src/lib.rs | 2 +- src/packets/auth.rs | 5 +++++ src/packets/connack.rs | 5 +++++ src/packets/connect.rs | 5 +++++ src/packets/disconnect.rs | 5 +++++ src/{packet.rs => packets/mod.rs} | 16 ++++++++++++++++ src/packets/pingreq.rs | 5 +++++ src/packets/pingresp.rs | 5 +++++ src/packets/puback.rs | 5 +++++ src/packets/pubcomp.rs | 5 +++++ src/packets/publish.rs | 5 +++++ src/packets/pubrec.rs | 5 +++++ src/packets/pubrel.rs | 5 +++++ src/packets/suback.rs | 5 +++++ src/packets/subscribe.rs | 5 +++++ src/packets/unsuback.rs | 5 +++++ src/packets/unsubscribe.rs | 5 +++++ src/properties.rs | 2 +- 20 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 src/packets/auth.rs create mode 100644 src/packets/connack.rs create mode 100644 src/packets/connect.rs create mode 100644 src/packets/disconnect.rs rename src/{packet.rs => packets/mod.rs} (90%) create mode 100644 src/packets/pingreq.rs create mode 100644 src/packets/pingresp.rs create mode 100644 src/packets/puback.rs create mode 100644 src/packets/pubcomp.rs create mode 100644 src/packets/publish.rs create mode 100644 src/packets/pubrec.rs create mode 100644 src/packets/pubrel.rs create mode 100644 src/packets/suback.rs create mode 100644 src/packets/subscribe.rs create mode 100644 src/packets/unsuback.rs create mode 100644 src/packets/unsubscribe.rs diff --git a/src/codecs.rs b/src/codecs.rs index 8f582f4f..b4710d61 100644 --- a/src/codecs.rs +++ b/src/codecs.rs @@ -11,8 +11,8 @@ use tokio_util::codec::Encoder; use winnow::Partial; use yoke::Yoke; -use crate::packet::MqttPacket; -use crate::packet::MqttWriterError; +use crate::packets::MqttPacket; +use crate::packets::MqttWriterError; #[derive(Debug, thiserror::Error)] pub enum MqttPacketCodecError { @@ -71,7 +71,7 @@ impl Decoder for MqttPacketCodec { let cart = src.split_to(total_packet_length).freeze(); let packet = Yoke::try_attach_to_cart( - crate::packet::StableBytes(cart), + crate::packets::StableBytes(cart), |data| -> Result<_, MqttPacketCodecError> { FormatMqttPacket::parse_complete(data).map_err(|_| MqttPacketCodecError::Protocol) }, diff --git a/src/keep_alive.rs b/src/keep_alive.rs index b185fa1f..12cf6e9b 100644 --- a/src/keep_alive.rs +++ b/src/keep_alive.rs @@ -27,7 +27,7 @@ impl TryFrom for KeepAlive { fn try_from(value: Duration) -> Result { let secs = value.as_secs(); if secs > u16::MAX.into() { - return Err(KeepAliveError::OutOfBounds) + return Err(KeepAliveError::OutOfBounds); } let secs = secs as u16; diff --git a/src/lib.rs b/src/lib.rs index d47d1cda..340a0ff4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ mod client_identifier; mod codecs; mod error; mod keep_alive; -mod packet; +mod packets; mod properties; mod string; mod transport; diff --git a/src/packets/auth.rs b/src/packets/auth.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/auth.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/connack.rs b/src/packets/connack.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/connack.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/connect.rs b/src/packets/connect.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/connect.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/disconnect.rs b/src/packets/disconnect.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/disconnect.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packet.rs b/src/packets/mod.rs similarity index 90% rename from src/packet.rs rename to src/packets/mod.rs index c4ec1bc2..268d31c8 100644 --- a/src/packet.rs +++ b/src/packets/mod.rs @@ -16,6 +16,22 @@ use tokio_util::bytes::BytesMut; use yoke::CloneableCart; use yoke::Yoke; +pub mod auth; +pub mod connack; +pub mod connect; +pub mod disconnect; +pub mod pingreq; +pub mod pingresp; +pub mod puback; +pub mod pubcomp; +pub mod publish; +pub mod pubrec; +pub mod pubrel; +pub mod suback; +pub mod subscribe; +pub mod unsuback; +pub mod unsubscribe; + #[derive(Debug, Clone)] pub(crate) struct StableBytes(pub(crate) Bytes); diff --git a/src/packets/pingreq.rs b/src/packets/pingreq.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/pingreq.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/pingresp.rs b/src/packets/pingresp.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/pingresp.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/puback.rs b/src/packets/puback.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/puback.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/pubcomp.rs b/src/packets/pubcomp.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/pubcomp.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/publish.rs b/src/packets/publish.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/publish.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/pubrec.rs b/src/packets/pubrec.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/pubrec.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/pubrel.rs b/src/packets/pubrel.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/pubrel.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/suback.rs b/src/packets/suback.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/suback.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/subscribe.rs b/src/packets/subscribe.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/subscribe.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/unsuback.rs b/src/packets/unsuback.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/unsuback.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/packets/unsubscribe.rs b/src/packets/unsubscribe.rs new file mode 100644 index 00000000..75e7fb2d --- /dev/null +++ b/src/packets/unsubscribe.rs @@ -0,0 +1,5 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// diff --git a/src/properties.rs b/src/properties.rs index 85103af8..5cb7935d 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -71,7 +71,7 @@ macro_rules! define_properties { } pub(crate) use define_properties; -use crate::packet::VecWriter; +use crate::packets::VecWriter; use crate::string::MqttString; pub struct UserProperty { From 404376bb70edb10b1c59a70130f6dbee45f7db22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:43:38 +0100 Subject: [PATCH 20/67] Add UnsubscribeProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packets/unsubscribe.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/packets/unsubscribe.rs b/src/packets/unsubscribe.rs index 75e7fb2d..f8ef53e5 100644 --- a/src/packets/unsubscribe.rs +++ b/src/packets/unsubscribe.rs @@ -3,3 +3,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::unsubscribe::UnsubscribeProperties, + anker: "_Toc3901182", + pub struct UnsubscribeProperties { + (anker: "_Toc3901183") + subscription_identifier: SubscriptionIdentifier with setter = u32, + + (anker: "_Toc3901183") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +} From df5a3ab02d21ad980a1a4bcc87510ca5f5aa3cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:47:11 +0100 Subject: [PATCH 21/67] Add missing ankers to MUnsuback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- mqtt-format/src/v5/packets/unsuback.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mqtt-format/src/v5/packets/unsuback.rs b/mqtt-format/src/v5/packets/unsuback.rs index cc74131c..120ba490 100644 --- a/mqtt-format/src/v5/packets/unsuback.rs +++ b/mqtt-format/src/v5/packets/unsuback.rs @@ -27,8 +27,13 @@ crate::v5::reason_code::make_combined_reason_code! { } crate::v5::properties::define_properties! { + packet_type: MUnsuback, + anker: "_Toc3901190", pub struct UnsubackProperties<'i> { + (anker: "_Toc3901192") reason_string: ReasonString<'i>, + + (anker: "_Toc3901193") user_properties: UserProperties<'i>, } } From 9ab09ad1a39afcd9fddedac04e14fec2463026c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:47:22 +0100 Subject: [PATCH 22/67] Add UnsubackProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packets/unsuback.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/packets/unsuback.rs b/src/packets/unsuback.rs index 75e7fb2d..5913cbc5 100644 --- a/src/packets/unsuback.rs +++ b/src/packets/unsuback.rs @@ -3,3 +3,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::unsuback::UnsubackProperties, + anker: "_Toc3901190", + pub struct UnsubackProperties { + (anker: "_Toc3901192") + reason_string: ReasonString<'i> with setter = String, + + (anker: "_Toc3901193") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +} From 1d7638134a784dd8121823a2f5d6d3d2398b2e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:51:35 +0100 Subject: [PATCH 23/67] Add missing ankers to SubscribeProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- mqtt-format/src/v5/packets/subscribe.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mqtt-format/src/v5/packets/subscribe.rs b/mqtt-format/src/v5/packets/subscribe.rs index 6f68386a..803d73f3 100644 --- a/mqtt-format/src/v5/packets/subscribe.rs +++ b/mqtt-format/src/v5/packets/subscribe.rs @@ -24,8 +24,13 @@ use crate::v5::write::WriteMqttPacket; use crate::v5::MResult; define_properties! { + packet_type: MSubscribe, + anker: "_Toc3901164", pub struct SubscribeProperties<'i> { + (anker: "_Toc3901166") subscription_identifier: SubscriptionIdentifier, + + (anker: "_Toc3901167") user_properties: UserProperties<'i>, } } From cb70aa7797a748f519da26b0b3487ef94e9e97eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:51:49 +0100 Subject: [PATCH 24/67] Add SubscribeProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packets/subscribe.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/packets/subscribe.rs b/src/packets/subscribe.rs index 75e7fb2d..c40efaec 100644 --- a/src/packets/subscribe.rs +++ b/src/packets/subscribe.rs @@ -3,3 +3,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::subscribe::SubscribeProperties, + anker: "_Toc3901164", + pub struct SubscribeProperties { + (anker: "_Toc3901166") + subscription_identifier: SubscriptionIdentifier with setter = u32, + + (anker: "_Toc3901167") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +} From c81ff6cbaaccfc126b6f1a87e25975570c627640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:52:55 +0100 Subject: [PATCH 25/67] Add SubackProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packets/suback.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/packets/suback.rs b/src/packets/suback.rs index 75e7fb2d..042b3be8 100644 --- a/src/packets/suback.rs +++ b/src/packets/suback.rs @@ -3,3 +3,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::suback::SubackProperties, + anker: "_Toc3901174", + pub struct SubackProperties { + (anker: "_Toc3901175") + reason_string: ReasonString<'i> with setter = String, + + (anker: "_Toc3901176") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +} From 02d8a965f30ad8ad1aa5b8fda9215f2c850ea3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:53:59 +0100 Subject: [PATCH 26/67] Add PubrelProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packets/pubrel.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/packets/pubrel.rs b/src/packets/pubrel.rs index 75e7fb2d..09b44943 100644 --- a/src/packets/pubrel.rs +++ b/src/packets/pubrel.rs @@ -3,3 +3,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties!( + properties_type: mqtt_format::v5::packets::pubrel::PubrelProperties, + anker: "_Toc3901145", + pub struct PubrelProperties { + (anker: "_Toc3901147") + reason_string: ReasonString<'i> with setter = String, + + (anker: "_Toc3901148") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +); From 97b82113691eb204000a5ea3c29590a9e2a8659f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:55:13 +0100 Subject: [PATCH 27/67] Add PubrecProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packets/pubrec.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/packets/pubrec.rs b/src/packets/pubrec.rs index 75e7fb2d..c6f9e82e 100644 --- a/src/packets/pubrec.rs +++ b/src/packets/pubrec.rs @@ -3,3 +3,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::pubrec::PubrecProperties, + anker: "_Toc3901135", + pub struct PubrecProperties { + (anker: "_Toc3901137") + reason_string: ReasonString<'i> with setter = String, + + (anker: "_Toc3901138") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +} From f2fc5c63378a8af78a08864f60e22fae7b7bd64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 09:57:22 +0100 Subject: [PATCH 28/67] Add PublishProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packets/publish.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/packets/publish.rs b/src/packets/publish.rs index 75e7fb2d..64b88c10 100644 --- a/src/packets/publish.rs +++ b/src/packets/publish.rs @@ -3,3 +3,33 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::publish::PublishProperties, + anker: "_Toc3901109", + pub struct PublishProperties { + (anker: "_Toc3901111") + payload_format_indicator: PayloadFormatIndicator with setter = u8, + + (anker: "_Toc3901112") + message_expiry_interval: MessageExpiryInterval with setter = u32, + + (anker: "_Toc3901113") + topic_alias: TopicAlias with setter = u32, + + (anker: "_Toc3901114") + response_topic: ResponseTopic<'i> with setter = String, + + (anker: "_Toc3901115") + correlation_data: CorrelationData<'i> with setter = Vec, + + (anker: "_Toc3901116") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + + (anker: "_Toc3901117") + subscription_identifier: SubscriptionIdentifier with setter = u32, + + (anker: "_Toc3901118") + content_type: ContentType<'i> with setter = String, + } +} From 7c0bb15091ef0f4d757f190581baa90818e96845 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 09:45:16 +0100 Subject: [PATCH 29/67] Add AuthProperties Signed-off-by: Matthias Beyer --- src/packets/auth.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/packets/auth.rs b/src/packets/auth.rs index 75e7fb2d..828c8063 100644 --- a/src/packets/auth.rs +++ b/src/packets/auth.rs @@ -3,3 +3,21 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::auth::AuthProperties, + anker: "_Toc3901221", + pub struct AuthProperties { + (anker: "_Toc3901223") + authentication_method: AuthenticationMethod<'a> with setter = String, + + (anker: "_Toc3901224") + authentication_data: AuthenticationData<'a> with setter = Vec, + + (anker: "_Toc3901225") + reason_string: ReasonString<'a> with setter = String, + + (anker: "_Toc3901226") + user_properties: UserProperties<'a> with setter = crate::properties::UserProperty, + } +} From 30a955b3470478d520fd0a8e1de79544b6c00568 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 09:45:16 +0100 Subject: [PATCH 30/67] Add ConnackProperties Signed-off-by: Matthias Beyer --- src/packets/connack.rs | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/packets/connack.rs b/src/packets/connack.rs index 75e7fb2d..c4d6cd73 100644 --- a/src/packets/connack.rs +++ b/src/packets/connack.rs @@ -3,3 +3,60 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::connack::ConnackProperties, + anker: "_Toc3901080", + pub struct ConnackProperties { + (anker: "_Toc3901082") + session_expiry_interval: SessionExpiryInterval with setter = u32, + + (anker: "_Toc3901083") + receive_maximum: ReceiveMaximum with setter = u32, + + (anker: "_Toc3901084") + maximum_qos: MaximumQoS with setter = u8, + + (anker: "_Toc3901085") + retain_available: RetainAvailable with setter = u8, + + (anker: "_Toc3901086") + maximum_packet_size: MaximumPacketSize with setter = u32, + + (anker: "_Toc3901087") + assigned_client_identifier: AssignedClientIdentifier<'i> with setter = String, + + (anker: "_Toc3901088") + topic_alias_maximum: TopicAliasMaximum with setter = u32, + + (anker: "_Toc3901089") + reason_string: ReasonString<'i> with setter = String, + + (anker: "_Toc3901090") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + + (anker: "_Toc3901091") + wildcard_subscription_available: WildcardSubscriptionAvailable with setter = u8, + + (anker: "_Toc3901092") + subscription_identifiers_available: SubscriptionIdentifiersAvailable with setter = u8, + + (anker: "_Toc3901093") + shared_scubscription_available: SharedSubscriptionAvailable with setter = u8, + + (anker: "_Toc3901094") + server_keep_alive: ServerKeepAlive with setter = u32, + + (anker: "_Toc3901095") + response_information: ResponseInformation<'i> with setter = String, + + (anker: "_Toc3901096") + server_reference: ServerReference<'i> with setter = String, + + (anker: "_Toc3901097") + authentication_method: AuthenticationMethod<'i> with setter = String, + + (anker: "_Toc3901098") + authentication_data: AuthenticationData<'i> with setter = Vec, + } +} From 9487b39797586e4c39cbfd90fb5d7ac0521b080a Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 09:52:43 +0100 Subject: [PATCH 31/67] Move ConnectProperties to appropriate mod Signed-off-by: Matthias Beyer --- src/client.rs | 39 +++------------------------------------ src/packets/connect.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3a034df1..ba2e7225 100644 --- a/src/client.rs +++ b/src/client.rs @@ -23,45 +23,12 @@ impl CleanStart { } } -crate::properties::define_properties! { - properties_type: mqtt_format::v5::packets::connect::ConnectProperties, - anker: "_Toc3901046", - pub struct ConnectProperties { - (anker: "_Toc3901048") - session_expiry_interval: SessionExpiryInterval with setter = u32, - - (anker: "_Toc3901049") - receive_maximum: ReceiveMaximum with setter = u32, - - (anker: "_Toc3901050") - maximum_packet_size: MaximumPacketSize with setter = u32, - - (anker: "_Toc3901051") - topic_alias_maximum: TopicAliasMaximum with setter = u32, - - (anker: "_Toc3901052") - request_response_information: RequestResponseInformation with setter = u8, - - (anker: "_Toc3901053") - request_problem_information: RequestProblemInformation with setter = u8, - - (anker: "_Toc3901054") - user_properties: UserProperties<'a> with setter = crate::properties::UserProperty, - - (anker: "_Toc3901055") - authentication_method: AuthenticationMethod<'a> with setter = String, - - (anker: "_Toc3901056") - authentication_data: AuthenticationData<'a> with setter = Vec, - } -} - pub struct MqttClientConnector { transport: MqttConnectTransport, client_identifier: ClientIdentifier, clean_start: CleanStart, keep_alive: KeepAlive, - properties: ConnectProperties, + properties: crate::packets::connect::ConnectProperties, } impl MqttClientConnector { @@ -76,7 +43,7 @@ impl MqttClientConnector { client_identifier, clean_start, keep_alive, - properties: ConnectProperties::new(), + properties: crate::packets::connect::ConnectProperties::new(), } } @@ -96,7 +63,7 @@ impl MqttClientConnector { todo!() } - pub fn properties_mut(&mut self) -> &mut ConnectProperties { + pub fn properties_mut(&mut self) -> &mut crate::packets::connect::ConnectProperties { &mut self.properties } } diff --git a/src/packets/connect.rs b/src/packets/connect.rs index 75e7fb2d..8edbd4a7 100644 --- a/src/packets/connect.rs +++ b/src/packets/connect.rs @@ -3,3 +3,36 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::connect::ConnectProperties, + anker: "_Toc3901046", + pub struct ConnectProperties { + (anker: "_Toc3901048") + session_expiry_interval: SessionExpiryInterval with setter = u32, + + (anker: "_Toc3901049") + receive_maximum: ReceiveMaximum with setter = u32, + + (anker: "_Toc3901050") + maximum_packet_size: MaximumPacketSize with setter = u32, + + (anker: "_Toc3901051") + topic_alias_maximum: TopicAliasMaximum with setter = u32, + + (anker: "_Toc3901052") + request_response_information: RequestResponseInformation with setter = u8, + + (anker: "_Toc3901053") + request_problem_information: RequestProblemInformation with setter = u8, + + (anker: "_Toc3901054") + user_properties: UserProperties<'a> with setter = crate::properties::UserProperty, + + (anker: "_Toc3901055") + authentication_method: AuthenticationMethod<'a> with setter = String, + + (anker: "_Toc3901056") + authentication_data: AuthenticationData<'a> with setter = Vec, + } +} From 702e7fa245c062062864c669ac23a1f72301e071 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 09:45:16 +0100 Subject: [PATCH 32/67] Add DisconnectProperties Signed-off-by: Matthias Beyer --- src/packets/disconnect.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/packets/disconnect.rs b/src/packets/disconnect.rs index 75e7fb2d..d588363c 100644 --- a/src/packets/disconnect.rs +++ b/src/packets/disconnect.rs @@ -3,3 +3,21 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::disconnect::DisconnectProperties, + anker: "_Toc3901209", + pub struct DisconnectProperties { + (anker: "_Toc3901211") + session_expiry_interval: SessionExpiryInterval with setter = u32, + + (anker: "_Toc3901212") + reason_string: ReasonString<'i> with setter = String, + + (anker: "_Toc3901213") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + + (anker: "_Toc3901214") + server_reference: ServerReference<'i> with setter = String, + } +} From b187b5d5a5f6a1dc9c6112c6dbbda96f1d67e47e Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 09:45:16 +0100 Subject: [PATCH 33/67] Add PubackProperties Signed-off-by: Matthias Beyer --- src/packets/puback.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/packets/puback.rs b/src/packets/puback.rs index 75e7fb2d..cc001cf0 100644 --- a/src/packets/puback.rs +++ b/src/packets/puback.rs @@ -3,3 +3,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::puback::PubackProperties, + anker: "_Toc3901125", + pub struct PubackProperties { + (anker: "_Toc3901127") + reason_string: ReasonString<'i> with setter = String, + + (anker: "_Toc3901128") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +} From c6ddf98c031ab3e251d4263bdda5d3bd8e92c715 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 09:45:16 +0100 Subject: [PATCH 34/67] Add PubcompProperties Signed-off-by: Matthias Beyer --- src/packets/pubcomp.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/packets/pubcomp.rs b/src/packets/pubcomp.rs index 75e7fb2d..4ec170e3 100644 --- a/src/packets/pubcomp.rs +++ b/src/packets/pubcomp.rs @@ -3,3 +3,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::pubcomp::PubcompProperties, + anker: "_Toc3901153", + pub struct PubcompProperties { + (anker: "_Toc3901154") + reason_string: ReasonString<'i> with setter = String, + + (anker: "_Toc3901155") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +} From d4b256a1bada13e3b42ea6f3aac492bc9a0e9209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 10:07:48 +0100 Subject: [PATCH 35/67] Add MqttBytes type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/bytes.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 49 insertions(+) create mode 100644 src/bytes.rs diff --git a/src/bytes.rs b/src/bytes.rs new file mode 100644 index 00000000..f2f02461 --- /dev/null +++ b/src/bytes.rs @@ -0,0 +1,48 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct MqttBytes(Vec); + +impl MqttBytes { + pub const MAX_LEN: usize = u16::MAX as usize; +} + +#[derive(Debug, thiserror::Error)] +pub enum MqttBytesError { + #[error("A vector/slice of length {} is too long, max length is {}", .0, MqttBytes::MAX_LEN)] + TooLong(usize), +} + +impl AsRef<[u8]> for MqttBytes { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl TryFrom> for MqttBytes { + type Error = MqttBytesError; + + fn try_from(s: Vec) -> Result { + if s.len() > Self::MAX_LEN { + Err(MqttBytesError::TooLong(s.len())) + } else { + Ok(Self(s)) + } + } +} + +impl TryFrom<&[u8]> for MqttBytes { + type Error = MqttBytesError; + + fn try_from(s: &[u8]) -> Result { + if s.len() > Self::MAX_LEN { + Err(MqttBytesError::TooLong(s.len())) + } else { + Ok(Self(s.to_vec())) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 340a0ff4..3a35d4ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +mod bytes; mod client; mod client_identifier; mod codecs; From 8dc6534322bac28dd139419cc2a0769355f53e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 10:13:40 +0100 Subject: [PATCH 36/67] Add username/password to MqttClientConnector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index ba2e7225..e121351a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,8 +4,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +use crate::bytes::MqttBytes; use crate::client_identifier::ClientIdentifier; use crate::keep_alive::KeepAlive; +use crate::string::MqttString; use crate::transport::MqttConnectTransport; use crate::transport::MqttConnection; @@ -29,6 +31,8 @@ pub struct MqttClientConnector { clean_start: CleanStart, keep_alive: KeepAlive, properties: crate::packets::connect::ConnectProperties, + username: Option, + password: Option, } impl MqttClientConnector { @@ -44,16 +48,28 @@ impl MqttClientConnector { clean_start, keep_alive, properties: crate::packets::connect::ConnectProperties::new(), + username: None, + password: None, } } + pub fn with_username(&mut self, username: MqttString) -> &mut Self { + self.username = Some(username); + self + } + + pub fn with_password(&mut self, password: MqttBytes) -> &mut Self { + self.password = Some(password); + self + } + pub async fn connect(self) -> Result { let conn: MqttConnection = self.transport.into(); let conn_packet = mqtt_format::v5::packets::connect::MConnect { client_identifier: self.client_identifier.as_str(), - username: None, - password: None, + username: self.username.as_ref().map(AsRef::as_ref), + password: self.password.as_ref().map(AsRef::as_ref), clean_start: self.clean_start.as_bool(), will: None, properties: self.properties.as_ref(), From 8246073fc34555c2bcb19c136bc64ed075e369dc Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 10:10:34 +0100 Subject: [PATCH 37/67] Add documentation to setter functions Signed-off-by: Matthias Beyer --- src/lib.rs | 1 + src/properties.rs | 2 ++ src/util.rs | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 src/util.rs diff --git a/src/lib.rs b/src/lib.rs index 3a35d4ee..73759eff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,3 +14,4 @@ mod packets; mod properties; mod string; mod transport; +mod util; diff --git a/src/properties.rs b/src/properties.rs index 5cb7935d..01f9c617 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -46,6 +46,8 @@ macro_rules! define_properties { } $( + #[doc = core::concat!("Set the ", stringify!($prop_name), " property.") ] + $( #[doc = core::concat!("See also: ", crate::util::md_speclink!($prop_anker)) ] )? pub fn [](&mut self, value: $setter) -> &mut Self { ::apply(&mut self.$prop_name, value); diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 00000000..ba17ab35 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,22 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +macro_rules! speclink { + ($anker:literal) => { + core::concat!( + "https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#", + $anker + ) + }; +} +pub(crate) use speclink; + +macro_rules! md_speclink { + ($anker:literal) => { + core::concat!("[📖 Specification](", $crate::util::speclink!($anker), ")") + }; +} +pub(crate) use md_speclink; From f7aa71ff99df4e615dec2b1a4816c97aa103a456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 10:24:49 +0100 Subject: [PATCH 38/67] Fix ConnectWillProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was missing the user_properties field and documentation ankers. Signed-off-by: Marcel Müller --- mqtt-format/src/v5/packets/connect.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mqtt-format/src/v5/packets/connect.rs b/mqtt-format/src/v5/packets/connect.rs index 8e9be321..fe5bfa22 100644 --- a/mqtt-format/src/v5/packets/connect.rs +++ b/mqtt-format/src/v5/packets/connect.rs @@ -246,13 +246,23 @@ impl<'i> Will<'i> { } crate::v5::properties::define_properties! { + packet_type: Will, + anker: "_Toc3901060", pub struct ConnectWillProperties<'i> { + (anker: "_Toc3901062") will_delay_interval: WillDelayInterval, + (anker: "_Toc3901063") payload_format_indicator: PayloadFormatIndicator, + (anker: "_Toc3901064") message_expiry_interval: MessageExpiryInterval, + (anker: "_Toc3901065") content_type: ContentType<'i>, + (anker: "_Toc3901066") response_topic: ResponseTopic<'i>, + (anker: "_Toc3901067") correlation_data: CorrelationData<'i>, + (anker: "_Toc3901068") + user_properties: UserProperties<'i>, } } @@ -342,6 +352,7 @@ mod test { content_type: None, response_topic: None, correlation_data: None, + user_properties: None, }, topic: "crazy topic", payload: &[0xAB, 0xCD, 0xEF], @@ -378,6 +389,7 @@ mod test { content_type: Some(ContentType("json")), response_topic: Some(ResponseTopic("resp")), correlation_data: Some(CorrelationData(&[0xFF])), + user_properties: None, }, topic: "crazy topic", payload: &[0xAB, 0xCD, 0xEF], From 4c6746c96843aac522cdca62d58a4dbbb1169c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 10:25:11 +0100 Subject: [PATCH 39/67] Add ConnectWillProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/packets/connect.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/packets/connect.rs b/src/packets/connect.rs index 8edbd4a7..18fb67af 100644 --- a/src/packets/connect.rs +++ b/src/packets/connect.rs @@ -36,3 +36,30 @@ crate::properties::define_properties! { authentication_data: AuthenticationData<'a> with setter = Vec, } } + +crate::properties::define_properties! { + properties_type: mqtt_format::v5::packets::connect::ConnectWillProperties, + anker: "_Toc3901060", + pub struct ConnectWillProperties { + (anker: "_Toc3901062") + will_delay_interval: WillDelayInterval with setter = u16, + + (anker: "_Toc3901063") + payload_format_indicator: PayloadFormatIndicator with setter = u8, + + (anker: "_Toc3901064") + message_expiry_interval: MessageExpiryInterval with setter = u32, + + (anker: "_Toc3901065") + content_type: ContentType<'i> with setter = String, + + (anker: "_Toc3901066") + response_topic: ResponseTopic<'i> with setter = String, + + (anker: "_Toc3901067") + correlation_data: CorrelationData<'i> with setter = Vec, + + (anker: "_Toc3901068") + user_properties: UserProperties<'i> with setter = crate::properties::UserProperty, + } +} From 2568333107af04f6c3ffb119b003b75a968716e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 10:26:08 +0100 Subject: [PATCH 40/67] Add Will to MqttClientConnector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/client.rs b/src/client.rs index e121351a..37b7c206 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,6 +4,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +use mqtt_format::v5::packets::connect::ConnectWillProperties; + use crate::bytes::MqttBytes; use crate::client_identifier::ClientIdentifier; use crate::keep_alive::KeepAlive; @@ -25,6 +27,14 @@ impl CleanStart { } } +pub struct MqttWill { + properties: ConnectWillProperties, + topic: MqttString, + payload: MqttBytes, + qos: QualityOfService, + retain: bool, +} + pub struct MqttClientConnector { transport: MqttConnectTransport, client_identifier: ClientIdentifier, @@ -33,6 +43,7 @@ pub struct MqttClientConnector { properties: crate::packets::connect::ConnectProperties, username: Option, password: Option, + will: Option, } impl MqttClientConnector { @@ -50,6 +61,7 @@ impl MqttClientConnector { properties: crate::packets::connect::ConnectProperties::new(), username: None, password: None, + will: None, } } @@ -63,6 +75,11 @@ impl MqttClientConnector { self } + pub fn with_will(&mut self, will: MqttWill) -> &mut Self { + self.will = Some(will); + self + } + pub async fn connect(self) -> Result { let conn: MqttConnection = self.transport.into(); From 2dbe00f97e6c285555151514d44694605183162f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 10:37:02 +0100 Subject: [PATCH 41/67] Let the Codec directly take packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/codecs.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/codecs.rs b/src/codecs.rs index b4710d61..3b03dc7a 100644 --- a/src/codecs.rs +++ b/src/codecs.rs @@ -5,7 +5,6 @@ // use mqtt_format::v5::packets::MqttPacket as FormatMqttPacket; -use tokio_util::bytes::Bytes; use tokio_util::codec::Decoder; use tokio_util::codec::Encoder; use winnow::Partial; @@ -81,16 +80,15 @@ impl Decoder for MqttPacketCodec { } } -impl Encoder for MqttPacketCodec { +impl Encoder> for MqttPacketCodec { type Error = MqttPacketCodecError; fn encode( &mut self, - packet: Bytes, + packet: FormatMqttPacket<'_>, dst: &mut tokio_util::bytes::BytesMut, ) -> Result<(), Self::Error> { - dst.extend_from_slice(&packet); - + packet.write(&mut crate::packets::MqttWriter(dst))?; Ok(()) } } @@ -117,11 +115,8 @@ mod tests { let packet = FormatMqttPacket::Pingreq(MPingreq); - packet.write(&mut MqttWriter(&mut data)).unwrap(); - - let send_data = data.clone().freeze(); tokio::spawn(async move { - framed_client.send(send_data).await.unwrap(); + framed_client.send(packet).await.unwrap(); }); let recv_packet = framed_server.next().await.unwrap().unwrap(); From a059ff61dabaed7d1524c3a3144dbe02bd1ea12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 10:37:20 +0100 Subject: [PATCH 42/67] Clean up tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/codecs.rs | 8 ++------ src/properties.rs | 36 ++---------------------------------- 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/src/codecs.rs b/src/codecs.rs index 3b03dc7a..ead3f9a6 100644 --- a/src/codecs.rs +++ b/src/codecs.rs @@ -99,11 +99,9 @@ mod tests { use futures::StreamExt; use mqtt_format::v5::packets::pingreq::MPingreq; use mqtt_format::v5::packets::MqttPacket as FormatMqttPacket; - use tokio_util::bytes::BytesMut; use tokio_util::codec::Framed; use super::MqttPacketCodec; - use crate::packet::MqttWriter; #[tokio::test] async fn simple_test_codec() { @@ -111,14 +109,12 @@ mod tests { let mut framed_client = Framed::new(client, MqttPacketCodec); let mut framed_server = Framed::new(server, MqttPacketCodec); - let mut data = BytesMut::new(); - let packet = FormatMqttPacket::Pingreq(MPingreq); + let sent_packet = packet.clone(); tokio::spawn(async move { - framed_client.send(packet).await.unwrap(); + framed_client.send(sent_packet).await.unwrap(); }); - let recv_packet = framed_server.next().await.unwrap().unwrap(); assert_eq!(packet, *recv_packet.get()); diff --git a/src/properties.rs b/src/properties.rs index 01f9c617..4ee7f59a 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -180,42 +180,10 @@ mod tests { use std::str::FromStr; use super::UserProperty; - use crate::packet::VecWriter; + use crate::packets::connect::ConnectProperties; + use crate::packets::VecWriter; use crate::string::MqttString; - crate::properties::define_properties! { - properties_type: mqtt_format::v5::packets::connect::ConnectProperties, - anker: "_Toc3901046", - pub struct ConnectProperties { - (anker: "_Toc3901048") - session_expiry_interval: SessionExpiryInterval with setter = u32, - - (anker: "_Toc3901049") - receive_maximum: ReceiveMaximum with setter = u32, - - (anker: "_Toc3901050") - maximum_packet_size: MaximumPacketSize with setter = u32, - - (anker: "_Toc3901051") - topic_alias_maximum: TopicAliasMaximum with setter = u32, - - (anker: "_Toc3901052") - request_response_information: RequestResponseInformation with setter = u8, - - (anker: "_Toc3901053") - request_problem_information: RequestProblemInformation with setter = u8, - - (anker: "_Toc3901054") - user_properties: UserProperties<'a> with setter = crate::properties::UserProperty, - - (anker: "_Toc3901055") - authentication_method: AuthenticationMethod<'a> with setter = String, - - (anker: "_Toc3901056") - authentication_data: AuthenticationData<'a> with setter = Vec, - } - } - #[test] fn check_properties() { let mut props = ConnectProperties::new(); From 56f3bacd30b2044c8a7908f227720806687c046d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 10:27:35 +0100 Subject: [PATCH 43/67] Fix: Use own ConnectWillProperties type Signed-off-by: Matthias Beyer --- src/client.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/client.rs b/src/client.rs index 37b7c206..f8721f94 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,8 +4,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // -use mqtt_format::v5::packets::connect::ConnectWillProperties; - use crate::bytes::MqttBytes; use crate::client_identifier::ClientIdentifier; use crate::keep_alive::KeepAlive; @@ -28,7 +26,7 @@ impl CleanStart { } pub struct MqttWill { - properties: ConnectWillProperties, + properties: crate::packets::connect::ConnectWillProperties, topic: MqttString, payload: MqttBytes, qos: QualityOfService, From db941fe270be2c08f9f4c4ca6c42e217c6b7300d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 10:27:53 +0100 Subject: [PATCH 44/67] Fix: Import Qos from mqtt_format Signed-off-by: Matthias Beyer --- src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index f8721f94..7a2ec2a5 100644 --- a/src/client.rs +++ b/src/client.rs @@ -29,7 +29,7 @@ pub struct MqttWill { properties: crate::packets::connect::ConnectWillProperties, topic: MqttString, payload: MqttBytes, - qos: QualityOfService, + qos: mqtt_format::v5::fixed_header::QualityOfService, retain: bool, } From 156f006724c52a19119658b2d63c6c66337f7a4a Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 10:36:58 +0100 Subject: [PATCH 45/67] Add builder for MqttWill type Signed-off-by: Matthias Beyer --- Cargo.lock | 21 +++++++++++++++++++++ Cargo.toml | 1 + src/client.rs | 8 ++++++++ 3 files changed, 30 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index e343b5d1..ee32f65b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,6 +220,7 @@ dependencies = [ "thiserror", "tokio", "tokio-util", + "typed-builder", "winnow 0.6.5", "yoke", ] @@ -1084,6 +1085,26 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "typed-builder" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444d8748011b93cb168770e8092458cb0f8854f931ff82fdf6ddfbd72a9c933e" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/Cargo.toml b/Cargo.toml index fd8f1e83..2b424f6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,5 +26,6 @@ stable_deref_trait = "1.2.0" thiserror = "1.0.58" tokio = { version = "1.36.0", features = ["macros", "full"] } tokio-util = { version = "0.7.10", features = ["codec", "compat"] } +typed-builder = "0.18" winnow = "0.6.5" yoke = "0.7.3" diff --git a/src/client.rs b/src/client.rs index 7a2ec2a5..822c36a2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -25,7 +25,9 @@ impl CleanStart { } } +#[derive(typed_builder::TypedBuilder)] pub struct MqttWill { + #[builder(default = crate::packets::connect::ConnectWillProperties::new())] properties: crate::packets::connect::ConnectWillProperties, topic: MqttString, payload: MqttBytes, @@ -33,6 +35,12 @@ pub struct MqttWill { retain: bool, } +impl MqttWill { + pub fn get_properties_mut(&mut self) -> &mut crate::packets::connect::ConnectWillProperties { + &mut self.properties + } +} + pub struct MqttClientConnector { transport: MqttConnectTransport, client_identifier: ClientIdentifier, From 420b0bec1f5fc2b14cc72528eb9f8260f7d390c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 15:05:53 +0100 Subject: [PATCH 46/67] Make propertie fields pub(crate) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/properties.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/properties.rs b/src/properties.rs index 4ee7f59a..338a4d14 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -31,7 +31,7 @@ macro_rules! define_properties { #[derive(Clone, Debug, PartialEq)] pub struct $name { $( - $prop_name: Option> + pub(crate) $prop_name: Option> ),* } From 9d29896926ac9941ebbde42e2a24b03da4678390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 15:06:12 +0100 Subject: [PATCH 47/67] Make MqttConnection impl every AsyncRead/Write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/transport.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/transport.rs b/src/transport.rs index c1da6b5a..cbed93ed 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -4,8 +4,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // -use futures::AsyncRead; -use futures::AsyncWrite; +use futures::AsyncRead as FuturesAsyncRead; +use futures::AsyncWrite as FuturesAsyncWrite; +use tokio::io::AsyncRead as TokioAsyncRead; +use tokio::io::AsyncWrite as TokioAsyncWrite; use tokio::io::DuplexStream; use tokio::net::TcpStream; use tokio_util::compat::Compat as TokioCompat; @@ -16,7 +18,53 @@ pub(crate) enum MqttConnection { Duplex(TokioCompat), } -impl AsyncRead for MqttConnection { +impl TokioAsyncRead for MqttConnection { + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t.get_mut()).poll_read(cx, buf), + MqttConnection::Duplex(d) => std::pin::pin!(d.get_mut()).poll_read(cx, buf), + } + } +} + +impl TokioAsyncWrite for MqttConnection { + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t.get_mut()).poll_write(cx, buf), + MqttConnection::Duplex(d) => std::pin::pin!(d.get_mut()).poll_write(cx, buf), + } + } + + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t.get_mut()).poll_flush(cx), + MqttConnection::Duplex(d) => std::pin::pin!(d.get_mut()).poll_flush(cx), + } + } + + fn poll_shutdown( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut *self { + MqttConnection::Tokio(t) => std::pin::pin!(t.get_mut()).poll_shutdown(cx), + MqttConnection::Duplex(d) => std::pin::pin!(d.get_mut()).poll_shutdown(cx), + } + } +} + +impl FuturesAsyncRead for MqttConnection { fn poll_read( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -29,7 +77,7 @@ impl AsyncRead for MqttConnection { } } -impl AsyncWrite for MqttConnection { +impl FuturesAsyncWrite for MqttConnection { fn poll_write( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, From aafae7583c7283410e4f28de50ec3c5094e29ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 15:06:31 +0100 Subject: [PATCH 48/67] Use MqttConnection in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/codecs.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/codecs.rs b/src/codecs.rs index ead3f9a6..7f523e29 100644 --- a/src/codecs.rs +++ b/src/codecs.rs @@ -100,14 +100,18 @@ mod tests { use mqtt_format::v5::packets::pingreq::MPingreq; use mqtt_format::v5::packets::MqttPacket as FormatMqttPacket; use tokio_util::codec::Framed; + use tokio_util::compat::TokioAsyncReadCompatExt; use super::MqttPacketCodec; + use crate::transport::MqttConnection; #[tokio::test] async fn simple_test_codec() { let (client, server) = tokio::io::duplex(100); - let mut framed_client = Framed::new(client, MqttPacketCodec); - let mut framed_server = Framed::new(server, MqttPacketCodec); + let mut framed_client = + Framed::new(MqttConnection::Duplex(client.compat()), MqttPacketCodec); + let mut framed_server = + Framed::new(MqttConnection::Duplex(server.compat()), MqttPacketCodec); let packet = FormatMqttPacket::Pingreq(MPingreq); From 49adee90b25acdf3324b949254a0a00409207c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 15:07:07 +0100 Subject: [PATCH 49/67] Use will in connect packet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 822c36a2..ee11d9a2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -41,6 +41,18 @@ impl MqttWill { } } +impl MqttWill { + fn as_ref(&self) -> mqtt_format::v5::packets::connect::Will<'_> { + mqtt_format::v5::packets::connect::Will { + properties: self.properties.as_ref(), + topic: self.topic.as_ref(), + payload: self.payload.as_ref(), + will_qos: self.qos, + will_retain: self.retain, + } + } +} + pub struct MqttClientConnector { transport: MqttConnectTransport, client_identifier: ClientIdentifier, @@ -94,7 +106,7 @@ impl MqttClientConnector { username: self.username.as_ref().map(AsRef::as_ref), password: self.password.as_ref().map(AsRef::as_ref), clean_start: self.clean_start.as_bool(), - will: None, + will: self.will.as_ref().map(|w| w.as_ref()), properties: self.properties.as_ref(), keep_alive: self.keep_alive.as_u16(), }; From 302deff0cfb7731aadfee3e2fa229f23ce219019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 15:07:15 +0100 Subject: [PATCH 50/67] Add first rudimentary connection flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/src/client.rs b/src/client.rs index ee11d9a2..4f61cbab 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,8 +4,13 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +use futures::SinkExt; +use futures::StreamExt; +use tokio_util::codec::Framed; + use crate::bytes::MqttBytes; use crate::client_identifier::ClientIdentifier; +use crate::codecs::MqttPacketCodec; use crate::keep_alive::KeepAlive; use crate::string::MqttString; use crate::transport::MqttConnectTransport; @@ -99,7 +104,8 @@ impl MqttClientConnector { } pub async fn connect(self) -> Result { - let conn: MqttConnection = self.transport.into(); + let mut conn = + tokio_util::codec::Framed::new(MqttConnection::from(self.transport), MqttPacketCodec); let conn_packet = mqtt_format::v5::packets::connect::MConnect { client_identifier: self.client_identifier.as_str(), @@ -111,6 +117,50 @@ impl MqttClientConnector { keep_alive: self.keep_alive.as_u16(), }; + conn.send(mqtt_format::v5::packets::MqttPacket::Connect(conn_packet)) + .await + .map_err(|_| ())?; + + let Some(maybe_connack) = conn.next().await else { + return Err(()); + }; + + let Ok(maybe_connack) = maybe_connack else { + return Err(()); + }; + + let connack = loop { + let can_use_auth = self.properties.authentication_data.is_some(); + let auth = match maybe_connack.get() { + mqtt_format::v5::packets::MqttPacket::Connack(connack) => break connack, + mqtt_format::v5::packets::MqttPacket::Auth(auth) => { + if can_use_auth { + auth + } else { + // MQTT-4.12.0-6 + return Err(()); + } + } + _ => { + return Err(()); + } + }; + + // TODO: Use user-provided method to authenticate further + + todo!() + }; + + // TODO: Timeout here if the server doesn't respond + + if connack.reason_code == mqtt_format::v5::packets::connack::ConnackReasonCode::Success { + // TODO: Read properties, configure client + + return Ok(MqttClient { conn }); + } + + // TODO: Do something with error code + todo!() } @@ -120,11 +170,7 @@ impl MqttClientConnector { } pub struct MqttClient { - conn: MqttConnection, + conn: Framed, } -impl MqttClient { - pub(crate) fn new_with_connection(conn: MqttConnection) -> Self { - MqttClient { conn } - } -} +impl MqttClient {} From bd76baaaa11a5fae05f70d811d89609a081f034e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 15:22:25 +0100 Subject: [PATCH 51/67] Add cloudmqtt binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- Cargo.lock | 17 +++++++++++++---- Cargo.toml | 2 +- cloudmqtt-bin/Cargo.toml | 8 ++++++++ cloudmqtt-bin/src/main.rs | 9 +++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 cloudmqtt-bin/Cargo.toml create mode 100644 cloudmqtt-bin/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index ee32f65b..64ea5de9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,9 +171,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -193,9 +193,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", @@ -225,6 +225,15 @@ dependencies = [ "yoke", ] +[[package]] +name = "cloudmqtt-bin" +version = "0.1.0" +dependencies = [ + "clap", + "cloudmqtt", + "tokio", +] + [[package]] name = "colorchoice" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 2b424f6e..27c147ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories = ["embedded"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = ["mqtt-format", "mqtt-tester"] +members = ["cloudmqtt-bin", "mqtt-format", "mqtt-tester"] [dependencies] futures = "0.3.30" diff --git a/cloudmqtt-bin/Cargo.toml b/cloudmqtt-bin/Cargo.toml new file mode 100644 index 00000000..98164ae1 --- /dev/null +++ b/cloudmqtt-bin/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "cloudmqtt-bin" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/cloudmqtt-bin/src/main.rs b/cloudmqtt-bin/src/main.rs new file mode 100644 index 00000000..6f15778a --- /dev/null +++ b/cloudmqtt-bin/src/main.rs @@ -0,0 +1,9 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +fn main() { + println!("Hello, world!"); +} From 59bc5277a1870c0c0fab19a4220b5f0b384506f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 15:26:49 +0100 Subject: [PATCH 52/67] Remove unused type alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/properties.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/properties.rs b/src/properties.rs index 338a4d14..1a09deca 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -5,7 +5,6 @@ // pub type TypeOfProperty

=

::Inner; -pub type SetterTypeOfProperty

=

::Setter; macro_rules! define_properties { (@no_lt $name:ident $pat:ident $lt:lifetime) => { From 2e9f4fd8051d037a99461d45f6eb926586a5266a Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 27 Mar 2024 16:04:46 +0100 Subject: [PATCH 53/67] Fix: TopicAliasMaximum is a 2 byte integer Signed-off-by: Matthias Beyer --- mqtt-format/src/v5/variable_header.rs | 6 +++--- src/packets/connack.rs | 2 +- src/packets/connect.rs | 2 +- src/properties.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mqtt-format/src/v5/variable_header.rs b/mqtt-format/src/v5/variable_header.rs index 9e8fd7d9..85d84091 100644 --- a/mqtt-format/src/v5/variable_header.rs +++ b/mqtt-format/src/v5/variable_header.rs @@ -286,9 +286,9 @@ define_properties! {[ testvalues: [12, 14, 42, 1337], TopicAliasMaximum as 0x22 => - parse with parse_u32 as u32; - write with super::integers::write_u32; - with size |_| 4; + parse with parse_u16 as u16; + write with super::integers::write_u16; + with size |_| 2; testfnname: test_roundtrip_topicaliasmaximum; testvalues: [12, 14, 42, 1337], diff --git a/src/packets/connack.rs b/src/packets/connack.rs index c4d6cd73..63fcc629 100644 --- a/src/packets/connack.rs +++ b/src/packets/connack.rs @@ -27,7 +27,7 @@ crate::properties::define_properties! { assigned_client_identifier: AssignedClientIdentifier<'i> with setter = String, (anker: "_Toc3901088") - topic_alias_maximum: TopicAliasMaximum with setter = u32, + topic_alias_maximum: TopicAliasMaximum with setter = u16, (anker: "_Toc3901089") reason_string: ReasonString<'i> with setter = String, diff --git a/src/packets/connect.rs b/src/packets/connect.rs index 18fb67af..788fc596 100644 --- a/src/packets/connect.rs +++ b/src/packets/connect.rs @@ -18,7 +18,7 @@ crate::properties::define_properties! { maximum_packet_size: MaximumPacketSize with setter = u32, (anker: "_Toc3901051") - topic_alias_maximum: TopicAliasMaximum with setter = u32, + topic_alias_maximum: TopicAliasMaximum with setter = u16, (anker: "_Toc3901052") request_response_information: RequestResponseInformation with setter = u8, diff --git a/src/properties.rs b/src/properties.rs index 1a09deca..b62d0ff4 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -164,7 +164,7 @@ define_property_types! {[ mqtt_format::v5::variable_header::ServerReference<'i> => inner = String; setter = String; outer ref = &'a str, mqtt_format::v5::variable_header::ReasonString<'i> => inner = String; setter = String; outer ref = &'a str, mqtt_format::v5::variable_header::ReceiveMaximum => inner = u32; setter = u32; outer deref = u32, - mqtt_format::v5::variable_header::TopicAliasMaximum => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::TopicAliasMaximum => inner = u16; setter = u16; outer deref = u16, mqtt_format::v5::variable_header::TopicAlias => inner = u32; setter = u32; outer deref = u32, mqtt_format::v5::variable_header::MaximumQoS => inner = u8; setter = u8; outer deref = u8, mqtt_format::v5::variable_header::RetainAvailable => inner = u8; setter = u8; outer deref = u8, From 8e7bdb201e1828e189551837b16143720f1b8f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:11:54 +0100 Subject: [PATCH 54/67] Fix: ServerKeepAlive is a 2 byte integer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matthias Beyer Signed-off-by: Marcel Müller --- mqtt-format/src/v5/variable_header.rs | 6 +++--- src/packets/connack.rs | 2 +- src/properties.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mqtt-format/src/v5/variable_header.rs b/mqtt-format/src/v5/variable_header.rs index 85d84091..087e459f 100644 --- a/mqtt-format/src/v5/variable_header.rs +++ b/mqtt-format/src/v5/variable_header.rs @@ -211,9 +211,9 @@ define_properties! {[ testvalues: ["fooobarbar"], ServerKeepAlive as 0x13 => - parse with parse_u32 as u32; - write with super::integers::write_u32; - with size |_| 4; + parse with parse_u16 as u16; + write with super::integers::write_u16; + with size |_| 2; testfnname: test_roundtrip_serverkeepalive; testvalues: [12, 14, 42, 1337], diff --git a/src/packets/connack.rs b/src/packets/connack.rs index 63fcc629..bfdc3b7b 100644 --- a/src/packets/connack.rs +++ b/src/packets/connack.rs @@ -45,7 +45,7 @@ crate::properties::define_properties! { shared_scubscription_available: SharedSubscriptionAvailable with setter = u8, (anker: "_Toc3901094") - server_keep_alive: ServerKeepAlive with setter = u32, + server_keep_alive: ServerKeepAlive with setter = u16, (anker: "_Toc3901095") response_information: ResponseInformation<'i> with setter = String, diff --git a/src/properties.rs b/src/properties.rs index b62d0ff4..23cb7307 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -154,7 +154,7 @@ define_property_types! {[ mqtt_format::v5::variable_header::SubscriptionIdentifier => inner = u32; setter = u32; outer deref = u32, mqtt_format::v5::variable_header::SessionExpiryInterval => inner = u32; setter = u32; outer deref = u32, mqtt_format::v5::variable_header::AssignedClientIdentifier<'i> => inner = String; setter = String; outer ref = &'a str, - mqtt_format::v5::variable_header::ServerKeepAlive => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::ServerKeepAlive => inner = u16; setter = u16; outer deref = u16, mqtt_format::v5::variable_header::AuthenticationMethod<'i> => inner = String; setter = String; outer ref = &'a str, mqtt_format::v5::variable_header::AuthenticationData<'i> => inner = Vec; setter = Vec; outer ref = &'a [u8], mqtt_format::v5::variable_header::RequestProblemInformation => inner = u8; setter = u8; outer deref = u8, From 26d7e3daee0ffa6905b47d04ef3883a9250c945e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:12:10 +0100 Subject: [PATCH 55/67] Fix: Parse SubscriptionIdentifier as a var u32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matthias Beyer Signed-off-by: Marcel Müller --- mqtt-format/src/v5/variable_header.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mqtt-format/src/v5/variable_header.rs b/mqtt-format/src/v5/variable_header.rs index 087e459f..cb65f3b4 100644 --- a/mqtt-format/src/v5/variable_header.rs +++ b/mqtt-format/src/v5/variable_header.rs @@ -14,6 +14,7 @@ use super::integers::write_variable_u32; use super::write::WResult; use super::write::WriteMqttPacket; use super::MResult; +use crate::v5::integers::parse_variable_u32; #[derive(Debug, Clone, Copy, PartialEq)] pub struct PacketIdentifier(pub u16); @@ -190,9 +191,9 @@ define_properties! {[ ], SubscriptionIdentifier as 0x0B => - parse with parse_u32 as u32; - write with super::integers::write_u32; - with size |_| 4; + parse with parse_variable_u32 as u32; + write with super::integers::write_variable_u32; + with size |&v: &u32| super::integers::variable_u32_binary_size(v); testfnname: test_roundtrip_subscriptionidentifier; testvalues: [12, 14, 42, 1337], From 24a8225417463eaf14c7890fb7d4211920b20241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:14:02 +0100 Subject: [PATCH 56/67] Fix: ReceiveMaximum is a 2 byte integer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matthias Beyer Signed-off-by: Marcel Müller --- mqtt-format/src/v5/variable_header.rs | 6 +++--- src/packets/connack.rs | 2 +- src/packets/connect.rs | 2 +- src/properties.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mqtt-format/src/v5/variable_header.rs b/mqtt-format/src/v5/variable_header.rs index cb65f3b4..5fa29fda 100644 --- a/mqtt-format/src/v5/variable_header.rs +++ b/mqtt-format/src/v5/variable_header.rs @@ -280,9 +280,9 @@ define_properties! {[ testvalues: ["fooobarbar"], ReceiveMaximum as 0x21 => - parse with parse_u32 as u32; - write with super::integers::write_u32; - with size |_| 4; + parse with parse_u16 as u16; + write with super::integers::write_u16; + with size |_| 2; testfnname: test_roundtrip_receivemaximum; testvalues: [12, 14, 42, 1337], diff --git a/src/packets/connack.rs b/src/packets/connack.rs index bfdc3b7b..75035a09 100644 --- a/src/packets/connack.rs +++ b/src/packets/connack.rs @@ -12,7 +12,7 @@ crate::properties::define_properties! { session_expiry_interval: SessionExpiryInterval with setter = u32, (anker: "_Toc3901083") - receive_maximum: ReceiveMaximum with setter = u32, + receive_maximum: ReceiveMaximum with setter = u16, (anker: "_Toc3901084") maximum_qos: MaximumQoS with setter = u8, diff --git a/src/packets/connect.rs b/src/packets/connect.rs index 788fc596..ea0ac8ce 100644 --- a/src/packets/connect.rs +++ b/src/packets/connect.rs @@ -12,7 +12,7 @@ crate::properties::define_properties! { session_expiry_interval: SessionExpiryInterval with setter = u32, (anker: "_Toc3901049") - receive_maximum: ReceiveMaximum with setter = u32, + receive_maximum: ReceiveMaximum with setter = u16, (anker: "_Toc3901050") maximum_packet_size: MaximumPacketSize with setter = u32, diff --git a/src/properties.rs b/src/properties.rs index 23cb7307..d06fdeaf 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -163,7 +163,7 @@ define_property_types! {[ mqtt_format::v5::variable_header::ResponseInformation<'i> => inner = String; setter = String; outer ref = &'a str, mqtt_format::v5::variable_header::ServerReference<'i> => inner = String; setter = String; outer ref = &'a str, mqtt_format::v5::variable_header::ReasonString<'i> => inner = String; setter = String; outer ref = &'a str, - mqtt_format::v5::variable_header::ReceiveMaximum => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::ReceiveMaximum => inner = u16; setter = u16; outer deref = u16, mqtt_format::v5::variable_header::TopicAliasMaximum => inner = u16; setter = u16; outer deref = u16, mqtt_format::v5::variable_header::TopicAlias => inner = u32; setter = u32; outer deref = u32, mqtt_format::v5::variable_header::MaximumQoS => inner = u8; setter = u8; outer deref = u8, From 745a86488c3bf4032d5b1715b4263c46d6d9998c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:15:59 +0100 Subject: [PATCH 57/67] Fix: TopicAlias is a 2 byte integer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matthias Beyer Signed-off-by: Marcel Müller --- mqtt-format/src/v5/variable_header.rs | 6 +++--- src/packets/publish.rs | 2 +- src/properties.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mqtt-format/src/v5/variable_header.rs b/mqtt-format/src/v5/variable_header.rs index 5fa29fda..0fdfcaa4 100644 --- a/mqtt-format/src/v5/variable_header.rs +++ b/mqtt-format/src/v5/variable_header.rs @@ -294,9 +294,9 @@ define_properties! {[ testvalues: [12, 14, 42, 1337], TopicAlias as 0x23 => - parse with parse_u32 as u32; - write with super::integers::write_u32; - with size |_| 4; + parse with parse_u16 as u16; + write with super::integers::write_u16; + with size |_| 2; testfnname: test_roundtrip_topicalias; testvalues: [12, 14, 42, 1337], diff --git a/src/packets/publish.rs b/src/packets/publish.rs index 64b88c10..5b3a42a0 100644 --- a/src/packets/publish.rs +++ b/src/packets/publish.rs @@ -15,7 +15,7 @@ crate::properties::define_properties! { message_expiry_interval: MessageExpiryInterval with setter = u32, (anker: "_Toc3901113") - topic_alias: TopicAlias with setter = u32, + topic_alias: TopicAlias with setter = u16, (anker: "_Toc3901114") response_topic: ResponseTopic<'i> with setter = String, diff --git a/src/properties.rs b/src/properties.rs index d06fdeaf..6a7ff0b6 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -165,7 +165,7 @@ define_property_types! {[ mqtt_format::v5::variable_header::ReasonString<'i> => inner = String; setter = String; outer ref = &'a str, mqtt_format::v5::variable_header::ReceiveMaximum => inner = u16; setter = u16; outer deref = u16, mqtt_format::v5::variable_header::TopicAliasMaximum => inner = u16; setter = u16; outer deref = u16, - mqtt_format::v5::variable_header::TopicAlias => inner = u32; setter = u32; outer deref = u32, + mqtt_format::v5::variable_header::TopicAlias => inner = u16; setter = u16; outer deref = u16, mqtt_format::v5::variable_header::MaximumQoS => inner = u8; setter = u8; outer deref = u8, mqtt_format::v5::variable_header::RetainAvailable => inner = u8; setter = u8; outer deref = u8, mqtt_format::v5::variable_header::MaximumPacketSize => inner = u32; setter = u32; outer deref = u32, From a3ef3e7a02fb916f57981c22f24b02f5811141bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:18:41 +0100 Subject: [PATCH 58/67] Use an actual error in MqttClientConnector::connect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client.rs | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/client.rs b/src/client.rs index 4f61cbab..1cfc62e4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -11,6 +11,7 @@ use tokio_util::codec::Framed; use crate::bytes::MqttBytes; use crate::client_identifier::ClientIdentifier; use crate::codecs::MqttPacketCodec; +use crate::codecs::MqttPacketCodecError; use crate::keep_alive::KeepAlive; use crate::string::MqttString; use crate::transport::MqttConnectTransport; @@ -58,6 +59,21 @@ impl MqttWill { } } +#[derive(Debug, thiserror::Error)] +pub enum MqttClientConnectError { + #[error("An error occured while encoding or sending an MQTT Packet")] + Send(#[source] MqttPacketCodecError), + + #[error("An error occured while decoding or receiving an MQTT Packet")] + Receive(#[source] MqttPacketCodecError), + + #[error("The transport unexpectedly closed")] + TransportUnexpectedlyClosed, + + #[error("The server sent a response with a protocol error: {reason}")] + ServerProtocolError { reason: &'static str }, +} + pub struct MqttClientConnector { transport: MqttConnectTransport, client_identifier: ClientIdentifier, @@ -103,7 +119,8 @@ impl MqttClientConnector { self } - pub async fn connect(self) -> Result { + pub async fn connect(self) -> Result { + type Mcce = MqttClientConnectError; let mut conn = tokio_util::codec::Framed::new(MqttConnection::from(self.transport), MqttPacketCodec); @@ -119,14 +136,17 @@ impl MqttClientConnector { conn.send(mqtt_format::v5::packets::MqttPacket::Connect(conn_packet)) .await - .map_err(|_| ())?; + .map_err(Mcce::Send)?; let Some(maybe_connack) = conn.next().await else { - return Err(()); + return Err(Mcce::TransportUnexpectedlyClosed); }; - let Ok(maybe_connack) = maybe_connack else { - return Err(()); + let maybe_connack = match maybe_connack { + Ok(maybe_connack) => maybe_connack, + Err(e) => { + return Err(Mcce::Receive(e)); + } }; let connack = loop { @@ -138,11 +158,15 @@ impl MqttClientConnector { auth } else { // MQTT-4.12.0-6 - return Err(()); + return Err(Mcce::ServerProtocolError { + reason: "MQTT-4.12.0-6", + }); } } _ => { - return Err(()); + return Err(MqttClientConnectError::ServerProtocolError { + reason: "MQTT-3.1.4-5", + }); } }; From 3ca5d545f4ae9612b3ec3d49ada061d37db9a3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:18:57 +0100 Subject: [PATCH 59/67] Implement ClientIdentifier::as_str MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/client_identifier.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/client_identifier.rs b/src/client_identifier.rs index f0f5356d..bce8d405 100644 --- a/src/client_identifier.rs +++ b/src/client_identifier.rs @@ -56,7 +56,11 @@ impl ClientIdentifier { } pub fn as_str(&self) -> &str { - todo!() + match self { + ClientIdentifier::MinimalRequired(s) => s.0.as_ref(), + ClientIdentifier::PotentiallyServerProvided => "", + ClientIdentifier::PotentiallyAccepted(s) => s.0.as_ref(), + } } } From 2db170e45d3c78c65306ebcbf6aa7e4758e48cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:19:31 +0100 Subject: [PATCH 60/67] Expose the winnow error from parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is probably a temporary change, as the winnow error is not super useful right now anyway. Signed-off-by: Marcel Müller --- src/codecs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/codecs.rs b/src/codecs.rs index 7f523e29..9ab11b7e 100644 --- a/src/codecs.rs +++ b/src/codecs.rs @@ -23,6 +23,9 @@ pub enum MqttPacketCodecError { #[error("A protocol error occurred")] Protocol, + + #[error("Could not parse during decoding due to: {:?}", .0)] + Parsing(winnow::error::ErrMode), } pub(crate) struct MqttPacketCodec; @@ -72,7 +75,7 @@ impl Decoder for MqttPacketCodec { let packet = Yoke::try_attach_to_cart( crate::packets::StableBytes(cart), |data| -> Result<_, MqttPacketCodecError> { - FormatMqttPacket::parse_complete(data).map_err(|_| MqttPacketCodecError::Protocol) + FormatMqttPacket::parse_complete(data).map_err(MqttPacketCodecError::Parsing) }, )?; From e1709fb3ce68a0bb086152471bcd01a3a3fb7d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:20:25 +0100 Subject: [PATCH 61/67] Also reserve a byte if we don't know how many MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/codecs.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/codecs.rs b/src/codecs.rs index 9ab11b7e..6a0a37f7 100644 --- a/src/codecs.rs +++ b/src/codecs.rs @@ -54,6 +54,10 @@ impl Decoder for MqttPacketCodec { src.reserve(needed.into()); return Ok(None); } + Err(winnow::error::ErrMode::Incomplete(winnow::error::Needed::Unknown)) => { + src.reserve(1); + return Ok(None); + } _ => { return Err(MqttPacketCodecError::Protocol); } From d4acc4d2d427554abe7864bef04e6bd947d3be74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:20:46 +0100 Subject: [PATCH 62/67] Clear up decoding of packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplifies the method a bit, to make sure we are doing the right thing. Signed-off-by: Marcel Müller --- src/codecs.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/codecs.rs b/src/codecs.rs index 6a0a37f7..f782320d 100644 --- a/src/codecs.rs +++ b/src/codecs.rs @@ -47,9 +47,9 @@ impl Decoder for MqttPacketCodec { return Ok(None); } - let packet_size = + let remaining_length = match mqtt_format::v5::integers::parse_variable_u32(&mut Partial::new(&src[1..])) { - Ok(size) => size, + Ok(size) => size as usize, Err(winnow::error::ErrMode::Incomplete(winnow::error::Needed::Size(needed))) => { src.reserve(needed.into()); return Ok(None); @@ -63,10 +63,8 @@ impl Decoder for MqttPacketCodec { } }; - let remaining_length = packet_size as usize; - let total_packet_length = 1 - + mqtt_format::v5::integers::variable_u32_binary_size(packet_size) as usize + + mqtt_format::v5::integers::variable_u32_binary_size(remaining_length as u32) as usize + remaining_length; if src.len() < total_packet_length { From c694253a5523b0233efaa36f24396ed46ba0124d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:21:13 +0100 Subject: [PATCH 63/67] Add a test for MConnect packets with the Codec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/codecs.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/codecs.rs b/src/codecs.rs index f782320d..a5020b6a 100644 --- a/src/codecs.rs +++ b/src/codecs.rs @@ -102,6 +102,7 @@ impl Encoder> for MqttPacketCodec { mod tests { use futures::SinkExt; use futures::StreamExt; + use mqtt_format::v5::packets::connect::MConnect; use mqtt_format::v5::packets::pingreq::MPingreq; use mqtt_format::v5::packets::MqttPacket as FormatMqttPacket; use tokio_util::codec::Framed; @@ -128,4 +129,32 @@ mod tests { assert_eq!(packet, *recv_packet.get()); } + + #[tokio::test] + async fn test_connect_codec() { + let (client, server) = tokio::io::duplex(100); + let mut framed_client = + Framed::new(MqttConnection::Duplex(client.compat()), MqttPacketCodec); + let mut framed_server = + Framed::new(MqttConnection::Duplex(server.compat()), MqttPacketCodec); + + let packet = FormatMqttPacket::Connect(MConnect { + client_identifier: "test", + username: None, + password: None, + clean_start: false, + will: None, + properties: mqtt_format::v5::packets::connect::ConnectProperties::new(), + keep_alive: 0, + }); + + let sent_packet = packet.clone(); + tokio::spawn(async move { + framed_client.send(sent_packet.clone()).await.unwrap(); + framed_client.send(sent_packet).await.unwrap(); + }); + let recv_packet = framed_server.next().await.unwrap().unwrap(); + + assert_eq!(packet, *recv_packet.get()); + } } From 49d932302a198186c4d489d6093bbc223f56b826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:21:31 +0100 Subject: [PATCH 64/67] Make required modules public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 73759eff..6f2b685c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,14 +4,14 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // -mod bytes; -mod client; -mod client_identifier; +pub mod bytes; +pub mod client; +pub mod client_identifier; mod codecs; mod error; -mod keep_alive; +pub mod keep_alive; mod packets; mod properties; -mod string; -mod transport; +pub mod string; +pub mod transport; mod util; From 736f8da046483d810732f480e5e8816218599bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:21:50 +0100 Subject: [PATCH 65/67] Add a rudimentary mqtt client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- cloudmqtt-bin/Cargo.toml | 3 +++ cloudmqtt-bin/src/bin/client.rs | 38 +++++++++++++++++++++++++++++++++ cloudmqtt-bin/src/main.rs | 9 -------- 3 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 cloudmqtt-bin/src/bin/client.rs delete mode 100644 cloudmqtt-bin/src/main.rs diff --git a/cloudmqtt-bin/Cargo.toml b/cloudmqtt-bin/Cargo.toml index 98164ae1..6c766564 100644 --- a/cloudmqtt-bin/Cargo.toml +++ b/cloudmqtt-bin/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +clap = { version = "4.5.4", features = ["derive"] } +cloudmqtt = { version = "0.5.0", path = ".." } +tokio = { version = "1.36.0", features = ["full"] } diff --git a/cloudmqtt-bin/src/bin/client.rs b/cloudmqtt-bin/src/bin/client.rs new file mode 100644 index 00000000..5188edcf --- /dev/null +++ b/cloudmqtt-bin/src/bin/client.rs @@ -0,0 +1,38 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use clap::Parser; +use cloudmqtt::client::MqttClientConnector; +use cloudmqtt::transport::MqttConnectTransport; +use tokio::net::TcpStream; + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +struct Args { + #[arg(long)] + hostname: String, +} + +#[tokio::main] +async fn main() { + let args = Args::parse(); + + let socket = TcpStream::connect(args.hostname).await.unwrap(); + + let connection = MqttConnectTransport::TokioTcp(socket); + let client_id = cloudmqtt::client_identifier::ClientIdentifier::PotentiallyServerProvided; + + let connector = MqttClientConnector::new( + connection, + client_id, + cloudmqtt::client::CleanStart::Yes, + cloudmqtt::keep_alive::KeepAlive::Disabled, + ); + + let _client = connector.connect().await.unwrap(); + + println!("Yay, we connected! That's all for now"); +} diff --git a/cloudmqtt-bin/src/main.rs b/cloudmqtt-bin/src/main.rs deleted file mode 100644 index 6f15778a..00000000 --- a/cloudmqtt-bin/src/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// - -fn main() { - println!("Hello, world!"); -} From 16b9a4615527c9b1d3d82ddeaee7b74fdf247e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:37:22 +0100 Subject: [PATCH 66/67] Fix: Don't convert to BE before writing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- mqtt-format/src/v5/integers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mqtt-format/src/v5/integers.rs b/mqtt-format/src/v5/integers.rs index eb5c0949..58d26f69 100644 --- a/mqtt-format/src/v5/integers.rs +++ b/mqtt-format/src/v5/integers.rs @@ -29,7 +29,7 @@ pub fn parse_u16(input: &mut &Bytes) -> MResult { } pub fn write_u16(buffer: &mut W, u: u16) -> WResult { - buffer.write_u16(u.to_be())?; + buffer.write_u16(u)?; Ok(()) } From 958e026ebc4a6daef77594892e13c8abdafdcdcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 27 Mar 2024 16:38:42 +0100 Subject: [PATCH 67/67] Mark unused fields with underscore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are currently being implemented Signed-off-by: Marcel Müller --- src/client.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client.rs b/src/client.rs index 1cfc62e4..f31369db 100644 --- a/src/client.rs +++ b/src/client.rs @@ -151,7 +151,7 @@ impl MqttClientConnector { let connack = loop { let can_use_auth = self.properties.authentication_data.is_some(); - let auth = match maybe_connack.get() { + let _auth = match maybe_connack.get() { mqtt_format::v5::packets::MqttPacket::Connack(connack) => break connack, mqtt_format::v5::packets::MqttPacket::Auth(auth) => { if can_use_auth { @@ -180,7 +180,7 @@ impl MqttClientConnector { if connack.reason_code == mqtt_format::v5::packets::connack::ConnackReasonCode::Success { // TODO: Read properties, configure client - return Ok(MqttClient { conn }); + return Ok(MqttClient { _conn: conn }); } // TODO: Do something with error code @@ -194,7 +194,7 @@ impl MqttClientConnector { } pub struct MqttClient { - conn: Framed, + _conn: Framed, } impl MqttClient {}