Skip to main content

ntex_mqtt/v3/codec/
packet.rs

1use std::num::NonZeroU16;
2
3use ntex_bytes::{ByteString, Bytes};
4
5use crate::types::{QoS, packet_type};
6
7prim_enum! {
8    /// Connect Return Code
9    pub enum ConnectAckReason {
10        /// Connection accepted
11        ConnectionAccepted = 0,
12        /// Connection Refused, unacceptable protocol version
13        UnacceptableProtocolVersion = 1,
14        /// Connection Refused, identifier rejected
15        IdentifierRejected = 2,
16        /// Connection Refused, Server unavailable
17        ServiceUnavailable = 3,
18        /// Connection Refused, bad user name or password
19        BadUserNameOrPassword = 4,
20        /// Connection Refused, not authorized
21        NotAuthorized = 5,
22        /// Reserved
23        Reserved = 6
24    }
25}
26
27impl ConnectAckReason {
28    pub fn reason(self) -> &'static str {
29        match self {
30            ConnectAckReason::ConnectionAccepted => "Connection Accepted",
31            ConnectAckReason::UnacceptableProtocolVersion => {
32                "Connection Refused, unacceptable protocol version"
33            }
34            ConnectAckReason::IdentifierRejected => "Connection Refused, identifier rejected",
35            ConnectAckReason::ServiceUnavailable => "Connection Refused, Server unavailable",
36            ConnectAckReason::BadUserNameOrPassword => {
37                "Connection Refused, bad user name or password"
38            }
39            ConnectAckReason::NotAuthorized => "Connection Refused, not authorized",
40            ConnectAckReason::Reserved => "Connection Refused",
41        }
42    }
43}
44
45#[derive(Debug, PartialEq, Eq, Clone)]
46/// Connection Will
47pub struct LastWill {
48    /// the `QoS` level to be used when publishing the `Will Message`.
49    pub qos: QoS,
50    /// the `Will Message` is to be Retained when it is published.
51    pub retain: bool,
52    /// the `Will Topic`
53    pub topic: ByteString,
54    /// defines the Application Message that is to be published to the `Will Topic`
55    pub message: Bytes,
56}
57
58#[derive(Default, Debug, PartialEq, Eq, Clone)]
59/// Connect packet content
60pub struct Connect {
61    /// the handling of the Session state.
62    pub clean_session: bool,
63    /// a time interval measured in seconds.
64    pub keep_alive: u16,
65    /// Will Message be stored on the Server and associated with the Network Connection.
66    pub last_will: Option<LastWill>,
67    /// identifies the Client to the Server.
68    pub client_id: ByteString,
69    /// username can be used by the Server for authentication and authorization.
70    pub username: Option<ByteString>,
71    /// password can be used by the Server for authentication and authorization.
72    pub password: Option<Bytes>,
73}
74
75impl Connect {
76    #[must_use]
77    /// Set `client_id` value
78    pub fn client_id<T>(mut self, client_id: T) -> Self
79    where
80        ByteString: From<T>,
81    {
82        self.client_id = client_id.into();
83        self
84    }
85}
86
87#[derive(Debug, PartialEq, Eq, Clone)]
88/// `Publish` message
89pub struct Publish {
90    /// this might be re-delivery of an earlier attempt to send the Packet.
91    pub dup: bool,
92    pub retain: bool,
93    /// the level of assurance for delivery of an Application Message.
94    pub qos: QoS,
95    /// the information channel to which payload data is published.
96    pub topic: ByteString,
97    /// only present in PUBLISH Packets where the `QoS` level is 1 or 2.
98    pub packet_id: Option<NonZeroU16>,
99    /// publish packet payload size
100    pub payload_size: u32,
101}
102
103#[derive(Debug, PartialEq, Eq, Copy, Clone)]
104/// `ConnectAck` message
105pub struct ConnectAck {
106    pub return_code: ConnectAckReason,
107    /// enables a Client to establish whether the Client and Server have a consistent view
108    /// about whether there is already stored Session state.
109    pub session_present: bool,
110}
111
112#[derive(Debug, PartialEq, Eq, Copy, Clone)]
113/// Subscribe Return Code
114pub enum SubscribeReturnCode {
115    Success(QoS),
116    Failure,
117}
118
119#[derive(Debug, PartialEq, Eq, Clone)]
120/// MQTT Control Packets
121pub enum Packet {
122    /// Client request to connect to Server
123    Connect(Box<Connect>),
124    /// Connect acknowledgment
125    ConnectAck(ConnectAck),
126    /// Publish acknowledgment
127    PublishAck {
128        /// Packet Identifier
129        packet_id: NonZeroU16,
130    },
131    /// Publish received (assured delivery part 1)
132    PublishReceived {
133        /// Packet Identifier
134        packet_id: NonZeroU16,
135    },
136    /// Publish release (assured delivery part 2)
137    PublishRelease {
138        /// Packet Identifier
139        packet_id: NonZeroU16,
140    },
141    /// Publish complete (assured delivery part 3)
142    PublishComplete {
143        /// Packet Identifier
144        packet_id: NonZeroU16,
145    },
146    /// Client subscribe request
147    Subscribe {
148        /// Packet Identifier
149        packet_id: NonZeroU16,
150        /// the list of Topic Filters and `QoS` to which the Client wants to subscribe.
151        topic_filters: Vec<(ByteString, QoS)>,
152    },
153    /// Subscribe acknowledgment
154    SubscribeAck {
155        packet_id: NonZeroU16,
156        /// corresponds to a Topic Filter in the SUBSCRIBE Packet being acknowledged.
157        status: Vec<SubscribeReturnCode>,
158    },
159    /// Unsubscribe request
160    Unsubscribe {
161        /// Packet Identifier
162        packet_id: NonZeroU16,
163        /// the list of Topic Filters that the Client wishes to unsubscribe from.
164        topic_filters: Vec<ByteString>,
165    },
166    /// Unsubscribe acknowledgment
167    UnsubscribeAck {
168        /// Packet Identifier
169        packet_id: NonZeroU16,
170    },
171    /// PING request
172    PingRequest,
173    /// PING response
174    PingResponse,
175    /// Client is disconnecting
176    Disconnect,
177}
178
179impl From<Connect> for Packet {
180    fn from(val: Connect) -> Packet {
181        Packet::Connect(Box::new(val))
182    }
183}
184
185impl Packet {
186    pub fn packet_type(&self) -> u8 {
187        match self {
188            Packet::Connect(_) => packet_type::CONNECT,
189            Packet::ConnectAck { .. } => packet_type::CONNACK,
190            Packet::PublishAck { .. } => packet_type::PUBACK,
191            Packet::PublishReceived { .. } => packet_type::PUBREC,
192            Packet::PublishRelease { .. } => packet_type::PUBREL,
193            Packet::PublishComplete { .. } => packet_type::PUBCOMP,
194            Packet::Subscribe { .. } => packet_type::SUBSCRIBE,
195            Packet::SubscribeAck { .. } => packet_type::SUBACK,
196            Packet::Unsubscribe { .. } => packet_type::UNSUBSCRIBE,
197            Packet::UnsubscribeAck { .. } => packet_type::UNSUBACK,
198            Packet::PingRequest => packet_type::PINGREQ,
199            Packet::PingResponse => packet_type::PINGRESP,
200            Packet::Disconnect => packet_type::DISCONNECT,
201        }
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208
209    #[test]
210    fn test_ack_reason() {
211        assert_eq!(ConnectAckReason::ConnectionAccepted.reason(), "Connection Accepted");
212        assert_eq!(
213            ConnectAckReason::UnacceptableProtocolVersion.reason(),
214            "Connection Refused, unacceptable protocol version"
215        );
216        assert_eq!(
217            ConnectAckReason::IdentifierRejected.reason(),
218            "Connection Refused, identifier rejected"
219        );
220        assert_eq!(
221            ConnectAckReason::ServiceUnavailable.reason(),
222            "Connection Refused, Server unavailable"
223        );
224        assert_eq!(
225            ConnectAckReason::BadUserNameOrPassword.reason(),
226            "Connection Refused, bad user name or password"
227        );
228        assert_eq!(
229            ConnectAckReason::NotAuthorized.reason(),
230            "Connection Refused, not authorized"
231        );
232    }
233}