mqute_codec/protocol/v5/
disconnect.rs1use crate::codec::util::{decode_byte, decode_variable_integer, encode_variable_integer};
8use crate::codec::{Decode, Encode, RawPacket};
9use crate::protocol::util::len_bytes;
10use crate::protocol::v5::property::{
11 property_decode, property_encode, property_len, Property, PropertyFrame,
12};
13use crate::protocol::v5::reason::ReasonCode;
14use crate::protocol::{FixedHeader, PacketType};
15use crate::Error;
16use bytes::{Buf, BufMut, Bytes, BytesMut};
17
18fn validate_disconnect_reason_code(code: ReasonCode) -> bool {
27 matches!(code.into(), 0 | 4 | 128..=131 | 135 | 137 | 139 | 141..=144 | 147..=162)
28}
29
30#[derive(Debug, Default, Clone, PartialEq, Eq)]
32pub struct DisconnectProperties {
33 pub session_expiry_interval: Option<u32>,
35 pub reason_string: Option<String>,
37 pub user_properties: Vec<(String, String)>,
39 pub server_reference: Option<String>,
41}
42
43impl PropertyFrame for DisconnectProperties {
44 fn encoded_len(&self) -> usize {
46 let mut len = 0usize;
47
48 len += property_len!(&self.session_expiry_interval);
49 len += property_len!(&self.reason_string);
50 len += property_len!(&self.user_properties);
51 len += property_len!(&self.server_reference);
52
53 len
54 }
55
56 fn encode(&self, buf: &mut BytesMut) {
58 property_encode!(
59 &self.session_expiry_interval,
60 Property::SessionExpiryInterval,
61 buf
62 );
63
64 property_encode!(&self.reason_string, Property::ReasonString, buf);
65 property_encode!(&self.user_properties, Property::UserProp, buf);
66 property_encode!(&self.server_reference, Property::ServerReference, buf);
67 }
68
69 fn decode(buf: &mut Bytes) -> Result<Option<Self>, Error>
71 where
72 Self: Sized,
73 {
74 if buf.is_empty() {
75 return Ok(None);
76 }
77 let mut session_expiry_interval: Option<u32> = None;
78 let mut reason_string: Option<String> = None;
79 let mut user_properties: Vec<(String, String)> = Vec::new();
80 let mut server_reference: Option<String> = None;
81
82 while buf.has_remaining() {
83 let property: Property = decode_byte(buf)?.try_into()?;
84 match property {
85 Property::SessionExpiryInterval => {
86 property_decode!(&mut session_expiry_interval, buf);
87 }
88 Property::ReasonString => {
89 property_decode!(&mut reason_string, buf);
90 }
91 Property::UserProp => {
92 property_decode!(&mut user_properties, buf);
93 }
94 Property::ServerReference => {
95 property_decode!(&mut server_reference, buf);
96 }
97 _ => return Err(Error::PropertyMismatch),
98 }
99 }
100
101 Ok(Some(DisconnectProperties {
102 session_expiry_interval,
103 reason_string,
104 user_properties,
105 server_reference,
106 }))
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
111struct DisconnectHeader {
112 code: ReasonCode,
113 properties: Option<DisconnectProperties>,
114}
115
116impl DisconnectHeader {
117 pub(crate) fn new(code: ReasonCode, properties: Option<DisconnectProperties>) -> Self {
118 if !validate_disconnect_reason_code(code) {
119 panic!("Invalid reason code {code}")
120 }
121 DisconnectHeader { code, properties }
122 }
123
124 pub(crate) fn encoded_len(&self) -> usize {
125 if self.code == ReasonCode::NormalDisconnection && self.properties.is_none() {
126 return 0;
127 }
128
129 let properties_len = self
130 .properties
131 .as_ref()
132 .map(|properties| properties.encoded_len())
133 .unwrap_or(0);
134
135 1 + len_bytes(properties_len) + properties_len
136 }
137
138 pub(crate) fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
139 if self.code == ReasonCode::NormalDisconnection && self.properties.is_none() {
141 return Ok(());
142 }
143
144 buf.put_u8(self.code.into());
145
146 let properties_len = self
147 .properties
148 .as_ref()
149 .map(|properties| properties.encoded_len())
150 .unwrap_or(0) as u32;
151
152 encode_variable_integer(buf, properties_len)?;
154
155 if let Some(properties) = self.properties.as_ref() {
157 properties.encode(buf);
158 }
159
160 Ok(())
161 }
162
163 pub(crate) fn decode(payload: &mut Bytes) -> Result<Self, Error> {
164 if payload.is_empty() {
165 return Ok(DisconnectHeader {
166 code: ReasonCode::NormalDisconnection,
167 properties: None,
168 });
169 }
170
171 let code: ReasonCode = decode_byte(payload)?.try_into().map(|code| {
172 if code == ReasonCode::Success {
173 ReasonCode::NormalDisconnection
174 } else {
175 code
176 }
177 })?;
178
179 if !validate_disconnect_reason_code(code) {
180 return Err(Error::InvalidReasonCode(code.into()));
181 }
182
183 let properties_len = decode_variable_integer(payload)? as usize;
184 if payload.len() < properties_len + len_bytes(properties_len) {
185 return Err(Error::MalformedPacket);
186 }
187
188 payload.advance(len_bytes(properties_len));
190
191 let mut properties_buf = payload.split_to(properties_len);
192
193 let properties = DisconnectProperties::decode(&mut properties_buf)?;
195 Ok(DisconnectHeader { code, properties })
196 }
197}
198
199#[derive(Debug, Clone, PartialEq, Eq)]
216pub struct Disconnect {
217 header: DisconnectHeader,
218}
219
220impl Disconnect {
221 pub fn new(code: ReasonCode, properties: Option<DisconnectProperties>) -> Self {
223 Disconnect {
224 header: DisconnectHeader::new(code, properties),
225 }
226 }
227
228 pub fn code(&self) -> ReasonCode {
230 self.header.code
231 }
232
233 pub fn properties(&self) -> Option<DisconnectProperties> {
235 self.header.properties.clone()
236 }
237}
238
239impl Encode for Disconnect {
240 fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
242 let header = FixedHeader::new(PacketType::Disconnect, self.payload_len());
243 header.encode(buf)?;
244
245 self.header.encode(buf)
246 }
247
248 fn payload_len(&self) -> usize {
250 self.header.encoded_len()
251 }
252}
253
254impl Decode for Disconnect {
255 fn decode(mut packet: RawPacket) -> Result<Self, Error> {
257 if packet.header.packet_type() != PacketType::Disconnect
258 || !packet.header.flags().is_default()
259 {
260 return Err(Error::MalformedPacket);
261 }
262
263 let header = DisconnectHeader::decode(&mut packet.payload)?;
264 Ok(Disconnect { header })
265 }
266}