Skip to main content

mqute_codec/protocol/
header.rs

1//! # MQTT Protocol - Fixed Header and Flags
2//!
3//! This module provides structures and utilities for handling the fixed header and flags
4//! in the MQTT protocol.
5//!
6//! The MQTT protocol uses a fixed header to describe the type of packet and its properties.
7//! The `FixedHeader` struct represents this header, while the `Flags` struct encapsulates
8//! the control flags (DUP, QoS, and RETAIN) associated with the packet.
9
10use crate::codec;
11use crate::protocol::util;
12use crate::protocol::{PacketType, QoS};
13use crate::Error;
14use bytes::{Buf, BufMut, BytesMut};
15use std::cmp::PartialEq;
16
17/// Represents the control flags in an MQTT packet.
18///
19/// # Examples
20///
21/// ```rust
22/// use mqute_codec::protocol::QoS;
23/// use mqute_codec::protocol::Flags;
24///
25/// // Create default flags
26/// let default_flags = Flags::default();
27/// assert_eq!(default_flags.dup, false);
28/// assert_eq!(default_flags.qos, QoS::AtMostOnce);
29/// assert_eq!(default_flags.retain, false);
30///
31/// // Create custom flags
32/// let custom_flags = Flags::new(QoS::AtLeastOnce);
33/// assert_eq!(custom_flags.qos, QoS::AtLeastOnce);
34/// ```
35#[derive(Debug, Copy, Clone, PartialEq, Eq)]
36pub struct Flags {
37    /// Indicates if the packet is a duplicate.
38    pub dup: bool,
39
40    /// The Quality of Service level (0, 1, or 2).
41    pub qos: QoS,
42
43    /// Indicates if the message should be retained by the broker.
44    pub retain: bool,
45}
46
47impl Default for Flags {
48    /// Creates default `Flags` with:
49    /// - `dup`: `false`
50    /// - `qos`: `QoS::AtMostOnce`
51    /// - `retain`: `false`
52    fn default() -> Self {
53        Flags {
54            dup: false,
55            qos: QoS::AtMostOnce,
56            retain: false,
57        }
58    }
59}
60
61impl Flags {
62    /// Creates new `Flags` with the specified QoS level.
63    ///
64    /// # Examples
65    ///
66    /// ```rust
67    /// use mqute_codec::protocol::QoS;
68    /// use mqute_codec::protocol::Flags;
69    ///
70    /// let flags = Flags::new(QoS::ExactlyOnce);
71    /// assert_eq!(flags.qos, QoS::ExactlyOnce);
72    /// ```
73    pub fn new(qos: QoS) -> Self {
74        Flags {
75            dup: false,
76            qos,
77            retain: false,
78        }
79    }
80
81    /// Checks if the flags are set to their default values.
82    ///
83    /// # Examples
84    ///
85    /// ```rust
86    /// use mqute_codec::protocol::Flags;
87    ///
88    /// let flags = Flags::default();
89    /// assert!(flags.is_default());
90    /// ```
91    pub fn is_default(&self) -> bool {
92        *self == Self::default()
93    }
94}
95
96/// Represents the fixed header of an MQTT packet.
97///
98/// # Examples
99///
100/// ```
101/// use mqute_codec::protocol::{FixedHeader, PacketType};
102///
103/// // Create a fixed header for a CONNECT packet
104/// let header = FixedHeader::new(PacketType::Connect, 10);
105/// assert_eq!(header.packet_type(), PacketType::Connect);
106/// assert_eq!(header.remaining_len(), 10);
107/// ```
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct FixedHeader {
110    /// The first byte of the packet, encoding the packet type and flags.
111    control_byte: u8,
112
113    /// The length of the remaining payload.
114    remaining_len: usize,
115}
116
117impl FixedHeader {
118    /// Creates a new `FixedHeader` with the specified packet type and remaining length.
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use mqute_codec::protocol::{FixedHeader, PacketType};
124    ///
125    /// let header = FixedHeader::new(PacketType::Publish, 20);
126    /// assert_eq!(header.packet_type(), PacketType::Publish);
127    /// ```
128    pub fn new(packet: PacketType, remaining_len: usize) -> Self {
129        let control_byte = build_control_byte(packet, Flags::default());
130
131        FixedHeader {
132            control_byte,
133            remaining_len,
134        }
135    }
136
137    /// Attempts to create a `FixedHeader` from a control byte and remaining length.
138    ///
139    /// # Errors
140    /// Returns an error if the packet type is invalid.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// use mqute_codec::protocol::{FixedHeader, PacketType};
146    /// use mqute_codec::Error;
147    ///
148    /// let header = FixedHeader::try_from(0x30, 10).unwrap();
149    /// assert_eq!(header.packet_type(), PacketType::Publish);
150    /// ```
151    pub fn try_from(control_byte: u8, remaining_len: usize) -> Result<Self, Error> {
152        let _: PacketType = fetch_packet_type(control_byte).try_into()?;
153
154        // Bits 2-1 of the control byte are interpreted as a QoS value by
155        // `flags()`. The value 0b11 (3) is reserved by the MQTT spec and must
156        // never appear on the wire. Reject it here so that `flags()` never
157        // has to deal with an invalid QoS later on.
158        let qos_bits = (control_byte >> 1) & 0x03;
159        if qos_bits == 0x03 {
160            return Err(Error::InvalidQos(qos_bits));
161        }
162
163        Ok(FixedHeader {
164            control_byte,
165            remaining_len,
166        })
167    }
168
169    /// Creates a `FixedHeader` with custom flags.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use mqute_codec::protocol::{FixedHeader, PacketType, Flags};
175    /// use mqute_codec::protocol::QoS;
176    ///
177    /// let flags = Flags::new(QoS::AtLeastOnce);
178    /// let header = FixedHeader::with_flags(PacketType::Publish, flags, 15);
179    /// assert_eq!(header.flags().qos, QoS::AtLeastOnce);
180    /// ```
181    pub fn with_flags(packet_type: PacketType, flags: Flags, remaining_len: usize) -> Self {
182        let control_byte = build_control_byte(packet_type, flags);
183        FixedHeader {
184            control_byte,
185            remaining_len,
186        }
187    }
188
189    /// Returns the packet type encoded in the control byte.
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// use mqute_codec::protocol::{FixedHeader, PacketType};
195    ///
196    /// let header = FixedHeader::new(PacketType::Subscribe, 5);
197    /// assert_eq!(header.packet_type(), PacketType::Subscribe);
198    /// ```
199    pub fn packet_type(&self) -> PacketType {
200        fetch_packet_type(self.control_byte).try_into().unwrap()
201    }
202
203    /// Extracts and returns the flags from the control byte.
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// use mqute_codec::protocol::{FixedHeader, PacketType, Flags};
209    /// use mqute_codec::protocol::QoS;
210    ///
211    /// let header = FixedHeader::new(PacketType::Publish, 10);
212    /// let flags = header.flags();
213    /// assert_eq!(flags.qos, QoS::AtMostOnce);
214    /// ```
215    pub fn flags(&self) -> Flags {
216        let flags = self.control_byte & 0x0F;
217        let dup: bool = (flags & 0x08) != 0;
218        // Safe: `try_from`/`new`/`with_flags` guarantee the QoS bits are
219        // never the reserved value 3, so this conversion cannot fail.
220        let qos = ((flags >> 1) & 0x03)
221            .try_into()
222            .expect("QoS bits are validated when the FixedHeader is constructed");
223        let retain = flags & 0x01 != 0;
224
225        Flags { dup, qos, retain }
226    }
227
228    /// Returns the remaining length of the payload.
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// use mqute_codec::protocol::{FixedHeader, PacketType};
234    ///
235    /// let header = FixedHeader::new(PacketType::Publish, 25);
236    /// assert_eq!(header.remaining_len(), 25);
237    /// ```
238    pub fn remaining_len(&self) -> usize {
239        self.remaining_len
240    }
241
242    /// Returns the length of the fixed header in bytes.
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use mqute_codec::protocol::{FixedHeader, PacketType};
248    ///
249    /// let header = FixedHeader::new(PacketType::Publish, 10);
250    /// assert_eq!(header.fixed_len(), 2); // 1 byte for control byte, 1 byte for remaining length
251    /// ```
252    pub fn fixed_len(&self) -> usize {
253        util::len_bytes(self.remaining_len) + 1
254    }
255
256    /// Returns the total length of the packet (fixed header + payload).
257    ///
258    /// # Examples
259    ///
260    /// ```
261    /// use mqute_codec::protocol::{FixedHeader, PacketType};
262    ///
263    /// let header = FixedHeader::new(PacketType::Publish, 10);
264    /// assert_eq!(header.packet_len(), 12); // 2 bytes for fixed header, 10 bytes for payload
265    /// ```
266    pub fn packet_len(&self) -> usize {
267        self.remaining_len + self.fixed_len()
268    }
269
270    /// Encodes the fixed header into a buffer.
271    ///
272    /// # Errors
273    /// Returns an error if encoding fails.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// use mqute_codec::protocol::{FixedHeader, PacketType};
279    /// use bytes::BytesMut;
280    ///
281    /// let mut buf = BytesMut::new();
282    /// let header = FixedHeader::new(PacketType::Publish, 10);
283    /// header.encode(&mut buf).unwrap();
284    /// ```
285    pub fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
286        buf.put_u8(self.control_byte);
287        codec::util::encode_variable_integer(buf, self.remaining_len as u32)
288    }
289
290    /// Decodes a fixed header from a buffer.
291    ///
292    /// # Errors
293    /// Returns an error if decoding fails or the payload size exceeds the limit.
294    ///
295    /// # Examples
296    ///
297    /// ```
298    /// use mqute_codec::protocol::{FixedHeader, PacketType};
299    /// use bytes::BytesMut;
300    ///
301    /// let mut buf = BytesMut::from(&[0x30, 0x04,
302    ///                                0x00, 0x00,
303    ///                                0x00, 0x00][..]); // Publish packet with remaining length 4
304    /// let header = FixedHeader::decode(&buf, None).unwrap();
305    /// assert_eq!(header.packet_type(), PacketType::Publish);
306    /// assert_eq!(header.remaining_len(), 4);
307    /// ```
308    pub fn decode(buf: &[u8], inbound_max_size: Option<usize>) -> Result<Self, Error> {
309        let buf_len = buf.len();
310        if buf_len < 2 {
311            return Err(Error::NotEnoughBytes(2 - buf_len));
312        }
313
314        let mut buf = buf;
315        let control_byte = buf.get_u8();
316        let remaining_len = codec::util::decode_variable_integer(buf)? as usize;
317
318        let header = FixedHeader::try_from(control_byte, remaining_len)?;
319
320        if let Some(max_size) = inbound_max_size
321            && header.remaining_len > max_size
322        {
323            return Err(Error::PayloadSizeLimitExceeded(header.remaining_len));
324        }
325
326        let packet_len = header.packet_len();
327        if buf_len < packet_len {
328            return Err(Error::NotEnoughBytes(packet_len - buf_len));
329        }
330
331        Ok(header)
332    }
333}
334
335/// Extracts the packet type from the control byte.
336#[inline]
337fn fetch_packet_type(control_byte: u8) -> u8 {
338    control_byte >> 4
339}
340
341/// Builds the control byte from the packet type and flags.
342const fn build_control_byte(packet_type: PacketType, flags: Flags) -> u8 {
343    let byte = (packet_type as u8) << 4;
344    let flags = (flags.dup as u8) << 3 | (flags.qos as u8) << 1 | (flags.retain as u8);
345    byte | flags
346}
347
348#[cfg(test)]
349mod tests {
350    use super::*;
351
352    #[test]
353    fn try_from_rejects_reserved_qos_for_publish() {
354        // Publish packet type (0x30) with QoS bits set to the reserved value 3
355        // (0b11 at bits 2-1): dup=0, qos=3, retain=0 -> 0b0000_0110.
356        let control_byte = 0x30 | 0x06;
357        let result = FixedHeader::try_from(control_byte, 0);
358        assert!(matches!(result, Err(Error::InvalidQos(3))));
359    }
360
361    #[test]
362    fn try_from_rejects_reserved_qos_regardless_of_packet_type() {
363        // The reserved QoS bit pattern is invalid on the wire for any packet
364        // type, not just Publish (e.g. PubAck: 0x40 | 0b0110).
365        let control_byte = 0x40 | 0x06;
366        let result = FixedHeader::try_from(control_byte, 0);
367        assert!(matches!(result, Err(Error::InvalidQos(3))));
368    }
369
370    #[test]
371    fn try_from_accepts_all_valid_qos_values() {
372        for (qos_bits, expected) in [
373            (0u8, QoS::AtMostOnce),
374            (1u8, QoS::AtLeastOnce),
375            (2u8, QoS::ExactlyOnce),
376        ] {
377            let control_byte = 0x30 | (qos_bits << 1);
378            let header = FixedHeader::try_from(control_byte, 0).unwrap();
379            // Must not panic and must report the expected QoS.
380            assert_eq!(header.flags().qos, expected);
381        }
382    }
383
384    #[test]
385    fn decode_rejects_reserved_qos_without_panicking() {
386        // A full wire-format buffer for a Publish packet with reserved QoS
387        // bits must be rejected gracefully rather than panicking.
388        let buf = [0x30 | 0x06, 0x00];
389        let result = FixedHeader::decode(&buf, None);
390        assert!(matches!(result, Err(Error::InvalidQos(3))));
391    }
392
393    #[test]
394    fn new_and_with_flags_never_produce_invalid_qos() {
395        // Constructors driven by the type-safe `Flags`/`QoS` API can never
396        // produce the reserved bit pattern, so `flags()` must not panic.
397        let header = FixedHeader::new(PacketType::Publish, 0);
398        assert_eq!(header.flags(), Flags::default());
399
400        for qos in [QoS::AtMostOnce, QoS::AtLeastOnce, QoS::ExactlyOnce] {
401            let header = FixedHeader::with_flags(PacketType::Publish, Flags::new(qos), 0);
402            assert_eq!(header.flags().qos, qos);
403        }
404    }
405}