mqute_codec/protocol/v5/
disconnect.rs1use crate::Error;
8use crate::codec::util::{decode_byte, decode_variable_integer, encode_variable_integer};
9use crate::codec::{Decode, Encode, RawPacket};
10use crate::protocol::util::len_bytes;
11use crate::protocol::v5::property::{
12 Property, PropertyFrame, property_decode, property_encode, property_len,
13};
14use crate::protocol::v5::reason::ReasonCode;
15use crate::protocol::{FixedHeader, PacketType};
16use bytes::{Buf, BufMut, Bytes, BytesMut};
17use std::time::Duration;
18
19fn validate_disconnect_reason_code(code: ReasonCode) -> bool {
28 matches!(code.into(), 0 | 4 | 128..=131 | 135 | 137 | 139 | 141..=144 | 147..=162)
29}
30
31#[derive(Debug, Default, Clone, PartialEq, Eq)]
47pub struct DisconnectProperties {
48 pub session_expiry_interval: Option<Duration>,
50 pub reason_string: Option<String>,
52 pub user_properties: Vec<(String, String)>,
54 pub server_reference: Option<String>,
56}
57
58impl PropertyFrame for DisconnectProperties {
59 fn encoded_len(&self) -> usize {
61 let mut len = 0usize;
62
63 len += property_len!(&self.session_expiry_interval);
64 len += property_len!(&self.reason_string);
65 len += property_len!(&self.user_properties);
66 len += property_len!(&self.server_reference);
67
68 len
69 }
70
71 fn encode(&self, buf: &mut BytesMut) {
73 property_encode!(
74 &self.session_expiry_interval,
75 Property::SessionExpiryInterval,
76 buf
77 );
78
79 property_encode!(&self.reason_string, Property::ReasonString, buf);
80 property_encode!(&self.user_properties, Property::UserProp, buf);
81 property_encode!(&self.server_reference, Property::ServerReference, buf);
82 }
83
84 fn decode(buf: &mut Bytes) -> Result<Option<Self>, Error>
86 where
87 Self: Sized,
88 {
89 if buf.is_empty() {
90 return Ok(None);
91 }
92 let mut session_expiry_interval: Option<Duration> = None;
93 let mut reason_string: Option<String> = None;
94 let mut user_properties: Vec<(String, String)> = Vec::new();
95 let mut server_reference: Option<String> = None;
96
97 while buf.has_remaining() {
98 let property: Property = decode_byte(buf)?.try_into()?;
99 match property {
100 Property::SessionExpiryInterval => {
101 property_decode!(&mut session_expiry_interval, buf);
102 }
103 Property::ReasonString => {
104 property_decode!(&mut reason_string, buf);
105 }
106 Property::UserProp => {
107 property_decode!(&mut user_properties, buf);
108 }
109 Property::ServerReference => {
110 property_decode!(&mut server_reference, buf);
111 }
112 _ => return Err(Error::PropertyMismatch),
113 }
114 }
115
116 Ok(Some(DisconnectProperties {
117 session_expiry_interval,
118 reason_string,
119 user_properties,
120 server_reference,
121 }))
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
126struct DisconnectHeader {
127 code: ReasonCode,
128 properties: Option<DisconnectProperties>,
129}
130
131impl DisconnectHeader {
132 pub(crate) fn new(code: ReasonCode, properties: Option<DisconnectProperties>) -> Self {
133 if !validate_disconnect_reason_code(code) {
134 panic!("Invalid reason code {code}")
135 }
136 DisconnectHeader { code, properties }
137 }
138
139 pub(crate) fn encoded_len(&self) -> usize {
140 if self.code == ReasonCode::NormalDisconnection && self.properties.is_none() {
141 return 0;
142 }
143
144 let properties_len = self
145 .properties
146 .as_ref()
147 .map(|properties| properties.encoded_len())
148 .unwrap_or(0);
149
150 1 + len_bytes(properties_len) + properties_len
151 }
152
153 pub(crate) fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
154 if self.code == ReasonCode::NormalDisconnection && self.properties.is_none() {
156 return Ok(());
157 }
158
159 buf.put_u8(self.code.into());
160
161 let properties_len = self
162 .properties
163 .as_ref()
164 .map(|properties| properties.encoded_len())
165 .unwrap_or(0) as u32;
166
167 encode_variable_integer(buf, properties_len)?;
169
170 if let Some(properties) = self.properties.as_ref() {
172 properties.encode(buf);
173 }
174
175 Ok(())
176 }
177
178 pub(crate) fn decode(payload: &mut Bytes) -> Result<Self, Error> {
179 if payload.is_empty() {
180 return Ok(DisconnectHeader {
181 code: ReasonCode::NormalDisconnection,
182 properties: None,
183 });
184 }
185
186 let code: ReasonCode = decode_byte(payload)?.try_into().map(|code| {
187 if code == ReasonCode::Success {
188 ReasonCode::NormalDisconnection
189 } else {
190 code
191 }
192 })?;
193
194 if !validate_disconnect_reason_code(code) {
195 return Err(Error::InvalidReasonCode(code.into()));
196 }
197
198 let properties_len = decode_variable_integer(payload)? as usize;
199 if payload.len() < properties_len + len_bytes(properties_len) {
200 return Err(Error::MalformedPacket);
201 }
202
203 payload.advance(len_bytes(properties_len));
205
206 let mut properties_buf = payload.split_to(properties_len);
207
208 let properties = DisconnectProperties::decode(&mut properties_buf)?;
210 Ok(DisconnectHeader { code, properties })
211 }
212}
213
214#[derive(Debug, Clone, PartialEq, Eq)]
234pub struct Disconnect {
235 header: DisconnectHeader,
236}
237
238impl Disconnect {
239 pub fn new(code: ReasonCode, properties: Option<DisconnectProperties>) -> Self {
241 Disconnect {
242 header: DisconnectHeader::new(code, properties),
243 }
244 }
245
246 pub fn code(&self) -> ReasonCode {
248 self.header.code
249 }
250
251 pub fn properties(&self) -> Option<DisconnectProperties> {
253 self.header.properties.clone()
254 }
255}
256
257impl Encode for Disconnect {
258 fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
260 let header = FixedHeader::new(PacketType::Disconnect, self.payload_len());
261 header.encode(buf)?;
262
263 self.header.encode(buf)
264 }
265
266 fn payload_len(&self) -> usize {
268 self.header.encoded_len()
269 }
270}
271
272impl Decode for Disconnect {
273 fn decode(mut packet: RawPacket) -> Result<Self, Error> {
275 if packet.header.packet_type() != PacketType::Disconnect
276 || !packet.header.flags().is_default()
277 {
278 return Err(Error::MalformedPacket);
279 }
280
281 let header = DisconnectHeader::decode(&mut packet.payload)?;
282 Ok(Disconnect { header })
283 }
284}