Skip to main content

rmqtt_codec/v5/packet/
disconnect.rs

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