ntex_mqtt/v5/codec/packet/
disconnect.rs

1use ntex_bytes::{Buf, BufMut, ByteString, Bytes, BytesMut};
2
3use crate::error::{DecodeError, EncodeError};
4use crate::utils::{self, Decode, Property};
5use crate::v5::codec::{UserProperties, UserProperty, encode::*, property_type as pt};
6
7/// DISCONNECT message
8#[derive(Debug, PartialEq, Eq, Clone)]
9pub struct Disconnect {
10    pub reason_code: DisconnectReasonCode,
11    pub session_expiry_interval_secs: Option<u32>,
12    pub server_reference: Option<ByteString>,
13    pub reason_string: Option<ByteString>,
14    pub user_properties: UserProperties,
15}
16
17prim_enum! {
18    /// DISCONNECT reason codes
19    pub enum DisconnectReasonCode {
20        NormalDisconnection = 0,
21        DisconnectWithWillMessage = 4,
22        UnspecifiedError = 128,
23        MalformedPacket = 129,
24        ProtocolError = 130,
25        ImplementationSpecificError = 131,
26        NotAuthorized = 135,
27        ServerBusy = 137,
28        ServerShuttingDown = 139,
29        BadAuthenticationMethod = 140,
30        KeepAliveTimeout = 141,
31        SessionTakenOver = 142,
32        TopicFilterInvalid = 143,
33        TopicNameInvalid = 144,
34        ReceiveMaximumExceeded = 147,
35        TopicAliasInvalid = 148,
36        PacketTooLarge = 149,
37        MessageRateTooHigh = 150,
38        QuotaExceeded = 151,
39        AdministrativeAction = 152,
40        PayloadFormatInvalid = 153,
41        RetainNotSupported = 154,
42        QosNotSupported = 155,
43        UseAnotherServer = 156,
44        ServerMoved = 157,
45        SharedSubscriptionNotSupported = 158,
46        ConnectionRateExceeded = 159,
47        MaximumConnectTime = 160,
48        SubscriptionIdentifiersNotSupported = 161,
49        WildcardSubscriptionsNotSupported = 162
50    }
51}
52
53impl Disconnect {
54    /// Create new instance of `Disconnect` with specified code
55    pub fn new(reason_code: DisconnectReasonCode) -> Self {
56        Self {
57            reason_code,
58            session_expiry_interval_secs: None,
59            server_reference: None,
60            reason_string: None,
61            user_properties: Vec::new(),
62        }
63    }
64
65    pub(crate) fn decode(src: &mut Bytes) -> Result<Self, DecodeError> {
66        let disconnect = if src.has_remaining() {
67            let reason_code = src.get_u8().try_into()?;
68
69            if src.has_remaining() {
70                let mut session_exp_secs = None;
71                let mut server_reference = None;
72                let mut reason_string = None;
73                let mut user_properties = Vec::new();
74
75                let prop_src = &mut utils::take_properties(src)?;
76                while prop_src.has_remaining() {
77                    match prop_src.get_u8() {
78                        pt::SESS_EXPIRY_INT => session_exp_secs.read_value(prop_src)?,
79                        pt::REASON_STRING => reason_string.read_value(prop_src)?,
80                        pt::USER => user_properties.push(UserProperty::decode(prop_src)?),
81                        pt::SERVER_REF => server_reference.read_value(prop_src)?,
82                        _ => return Err(DecodeError::MalformedPacket),
83                    }
84                }
85                ensure!(!src.has_remaining(), DecodeError::InvalidLength);
86
87                Self {
88                    reason_code,
89                    session_expiry_interval_secs: session_exp_secs,
90                    server_reference,
91                    reason_string,
92                    user_properties,
93                }
94            } else {
95                Self { reason_code, ..Default::default() }
96            }
97        } else {
98            Self::default()
99        };
100        Ok(disconnect)
101    }
102}
103
104impl Default for Disconnect {
105    fn default() -> Self {
106        Self {
107            reason_code: DisconnectReasonCode::NormalDisconnection,
108            session_expiry_interval_secs: None,
109            server_reference: None,
110            reason_string: None,
111            user_properties: Vec::new(),
112        }
113    }
114}
115
116impl EncodeLtd for Disconnect {
117    fn encoded_size(&self, limit: u32) -> usize {
118        const HEADER_LEN: usize = 1; // reason code
119
120        let mut prop_len = encoded_property_size(&self.session_expiry_interval_secs)
121            + encoded_property_size(&self.server_reference);
122        let diag_len = encoded_size_opt_props(
123            &self.user_properties,
124            &self.reason_string,
125            reduce_limit(limit, prop_len + HEADER_LEN + 4),
126        ); // exclude other props and max of 4 bytes for property length value
127        prop_len += diag_len;
128        HEADER_LEN + var_int_len(prop_len) as usize + prop_len
129    }
130
131    fn encode(&self, buf: &mut BytesMut, size: u32) -> Result<(), EncodeError> {
132        let start_len = buf.len();
133        buf.put_u8(self.reason_code.into());
134
135        let prop_len = var_int_len_from_size(size - 1);
136        utils::write_variable_length(prop_len, buf);
137        encode_property(&self.session_expiry_interval_secs, pt::SESS_EXPIRY_INT, buf)?;
138        encode_property(&self.server_reference, pt::SERVER_REF, buf)?;
139        encode_opt_props(
140            &self.user_properties,
141            &self.reason_string,
142            buf,
143            size - (buf.len() - start_len) as u32,
144        )
145    }
146}