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}