1use std::{fmt, time::Duration};
2
3use base64::{Engine, engine::general_purpose};
4use bytes::Bytes;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use smallvec::SmallVec;
7
8use crate::{ProtocolVersion, Sid, Str, TransportType};
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum Packet {
13 Open(OpenPacket),
15 Close,
17 Ping,
20 Pong,
23
24 PingUpgrade,
26 PongUpgrade,
28
29 Message(Str),
31 Upgrade,
33
34 Noop,
36
37 Binary(Bytes), BinaryV3(Bytes), }
53
54#[derive(Debug)]
56pub enum PacketParseError {
57 InvalidConnectPacket(serde_json::Error),
59 InvalidPacketType(Option<char>),
61 InvalidPacketPayload,
63 InvalidPacketLen,
65 InvalidUtf8Boundary(std::str::Utf8Error),
67 Base64Decode(base64::DecodeError),
69 PayloadTooLarge {
71 max: u64,
73 },
74}
75impl fmt::Display for PacketParseError {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 match self {
78 PacketParseError::InvalidConnectPacket(e) => write!(f, "invalid connect packet: {e}"),
79 PacketParseError::InvalidPacketType(c) => write!(f, "invalid packet type: {c:?}"),
80 PacketParseError::InvalidPacketPayload => write!(f, "invalid packet payload"),
81 PacketParseError::InvalidPacketLen => write!(f, "invalid packet length"),
82 PacketParseError::InvalidUtf8Boundary(err) => write!(
83 f,
84 "invalid utf8 boundary when parsing payload into packet chunks: {err}"
85 ),
86 PacketParseError::Base64Decode(err) => write!(f, "base64 decode error: {err}"),
87 PacketParseError::PayloadTooLarge { max } => {
88 write!(f, "payload too large: max {max}")
89 }
90 }
91 }
92}
93impl From<base64::DecodeError> for PacketParseError {
94 fn from(err: base64::DecodeError) -> Self {
95 PacketParseError::Base64Decode(err)
96 }
97}
98impl From<std::string::FromUtf8Error> for PacketParseError {
99 fn from(err: std::string::FromUtf8Error) -> Self {
100 PacketParseError::InvalidUtf8Boundary(err.utf8_error())
101 }
102}
103impl From<std::str::Utf8Error> for PacketParseError {
104 fn from(err: std::str::Utf8Error) -> Self {
105 PacketParseError::InvalidUtf8Boundary(err)
106 }
107}
108impl From<serde_json::Error> for PacketParseError {
109 fn from(err: serde_json::Error) -> Self {
110 PacketParseError::InvalidConnectPacket(err)
111 }
112}
113impl std::error::Error for PacketParseError {}
114
115impl Packet {
116 pub fn is_binary(&self) -> bool {
118 matches!(self, Packet::Binary(_) | Packet::BinaryV3(_))
119 }
120
121 pub fn into_message(self) -> Str {
123 match self {
124 Packet::Message(msg) => msg,
125 _ => panic!("Packet is not a message"),
126 }
127 }
128
129 pub fn into_binary(self) -> Bytes {
131 match self {
132 Packet::Binary(data) => data,
133 Packet::BinaryV3(data) => data,
134 _ => panic!("Packet is not a binary"),
135 }
136 }
137
138 pub fn get_size_hint(&self, b64: bool) -> usize {
144 match self {
145 Packet::Open(_) => 156, Packet::Close => 1,
147 Packet::Ping => 1,
148 Packet::Pong => 1,
149 Packet::PingUpgrade => 6,
150 Packet::PongUpgrade => 6,
151 Packet::Message(msg) => 1 + msg.len(),
152 Packet::Upgrade => 1,
153 Packet::Noop => 1,
154 Packet::Binary(data) => {
155 if b64 {
156 1 + base64::encoded_len(data.len(), true).unwrap_or(usize::MAX - 1)
157 } else {
158 1 + data.len()
159 }
160 }
161 Packet::BinaryV3(data) => {
162 if b64 {
163 2 + base64::encoded_len(data.len(), true).unwrap_or(usize::MAX - 2)
164 } else {
165 1 + data.len()
166 }
167 }
168 }
169 }
170}
171
172impl From<Packet> for Bytes {
173 fn from(value: Packet) -> Self {
174 String::from(value).into()
175 }
176}
177
178impl From<Packet> for String {
180 fn from(packet: Packet) -> String {
181 let len = packet.get_size_hint(true);
182 let mut buffer = String::with_capacity(len);
183 match packet {
184 Packet::Open(open) => {
185 buffer.push('0');
186 buffer.push_str(&serde_json::to_string(&open).unwrap());
187 }
188 Packet::Close => buffer.push('1'),
189 Packet::Ping => buffer.push('2'),
190 Packet::Pong => buffer.push('3'),
191 Packet::PingUpgrade => buffer.push_str("2probe"),
192 Packet::PongUpgrade => buffer.push_str("3probe"),
193 Packet::Message(msg) => {
194 buffer.push('4');
195 buffer.push_str(&msg);
196 }
197 Packet::Upgrade => buffer.push('5'),
198 Packet::Noop => buffer.push('6'),
199 Packet::Binary(data) => {
200 buffer.push('b');
201 general_purpose::STANDARD.encode_string(data, &mut buffer);
202 }
203 Packet::BinaryV3(data) => {
204 buffer.push_str("b4");
205 general_purpose::STANDARD.encode_string(data, &mut buffer);
206 }
207 };
208 buffer
209 }
210}
211
212impl Packet {
214 pub fn parse(
216 protocol: ProtocolVersion,
217 value: impl Into<Str>,
218 ) -> Result<Self, PacketParseError> {
219 let value = value.into();
220 let packet_type = value
221 .as_bytes()
222 .first()
223 .ok_or(PacketParseError::InvalidPacketType(None))?;
224 let is_upgrade = value.len() == 6 && &value[1..6] == "probe";
225 let res = match packet_type {
226 b'1' => Packet::Close,
227 b'2' if is_upgrade => Packet::PingUpgrade,
228 b'2' => Packet::Ping,
229 b'3' if is_upgrade => Packet::PongUpgrade,
230 b'3' => Packet::Pong,
231 b'4' => Packet::Message(value.slice(1..)),
232 b'5' => Packet::Upgrade,
233 b'6' => Packet::Noop,
234 b'b' if protocol == ProtocolVersion::V3 => Packet::BinaryV3(
235 general_purpose::STANDARD
236 .decode(value.slice(2..).as_bytes())?
237 .into(),
238 ),
239 b'b' => Packet::Binary(
240 general_purpose::STANDARD
241 .decode(value.slice(1..).as_bytes())?
242 .into(),
243 ),
244 c => Err(PacketParseError::InvalidPacketType(Some(*c as char)))?,
245 };
246 Ok(res)
247 }
248}
249
250#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
252#[serde(rename_all = "camelCase")]
253pub struct OpenPacket {
254 pub sid: Sid,
256
257 pub upgrades: SmallVec<[TransportType; 1]>,
259
260 #[serde(
262 serialize_with = "serialize_duration_millis",
263 deserialize_with = "deserialize_duration_from_millis"
264 )]
265 pub ping_interval: Duration,
266
267 #[serde(
269 serialize_with = "serialize_duration_millis",
270 deserialize_with = "deserialize_duration_from_millis"
271 )]
272 pub ping_timeout: Duration,
273
274 pub max_payload: u64,
277}
278
279pub fn serialize_duration_millis<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
281where
282 S: Serializer,
283{
284 serializer.serialize_u64(duration.as_millis() as u64)
286}
287
288pub fn deserialize_duration_from_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
290where
291 D: Deserializer<'de>,
292{
293 let millis = u64::deserialize(deserializer)?;
294 Ok(Duration::from_millis(millis))
295}
296
297impl Default for OpenPacket {
299 fn default() -> Self {
300 Self {
301 sid: Sid::ZERO,
302 upgrades: smallvec::smallvec![TransportType::Websocket],
303 ping_interval: Duration::from_millis(25000),
304 ping_timeout: Duration::from_millis(20000),
305 max_payload: 100000,
306 }
307 }
308}
309
310pub type PacketBuf = SmallVec<[Packet; 2]>;
316
317#[cfg(test)]
318mod tests {
319
320 use super::*;
321 use std::time::Duration;
322
323 #[test]
324 fn test_open_packet() {
325 let sid = Sid::new();
326 let packet = Packet::Open(OpenPacket {
327 sid,
328 upgrades: smallvec::smallvec![TransportType::Websocket],
329 ping_interval: Duration::from_millis(25000),
330 ping_timeout: Duration::from_millis(20000),
331 max_payload: 100000,
332 });
333 let packet_str: String = packet.into();
334 assert_eq!(
335 packet_str,
336 format!(
337 "0{{\"sid\":\"{sid}\",\"upgrades\":[\"websocket\"],\"pingInterval\":25000,\"pingTimeout\":20000,\"maxPayload\":100000}}"
338 )
339 );
340 }
341
342 #[test]
343 fn test_message_packet() {
344 let packet = Packet::Message("hello".into());
345 let packet_str: String = packet.into();
346 assert_eq!(packet_str, "4hello");
347 }
348
349 #[test]
350 fn test_binary_packet() {
351 let packet = Packet::Binary(vec![1, 2, 3].into());
352 let packet_str: String = packet.into();
353 assert_eq!(packet_str, "bAQID");
354 }
355
356 #[test]
357 fn test_binary_packet_v4_deserialize_payload_starting_with_4() {
358 let data = vec![0xE0, 0xE1, 0xE2];
359 let packet_str: String = Packet::Binary(data.clone().into()).into();
361 assert_eq!(packet_str, "b4OHi");
362
363 let packet = Packet::parse(ProtocolVersion::V4, packet_str).unwrap();
364 assert_eq!(packet, Packet::Binary(data.into()));
365 }
366
367 #[test]
368 fn test_binary_packet_v3() {
369 let packet = Packet::BinaryV3(vec![1, 2, 3].into());
370 let packet_str: String = packet.into();
371 assert_eq!(packet_str, "b4AQID");
372 }
373
374 #[test]
375 fn test_packet_get_size_hint() {
376 let open = OpenPacket {
378 sid: Sid::new(),
379 ping_interval: Duration::MAX,
380 ping_timeout: Duration::MAX,
381 max_payload: u64::MAX,
382 upgrades: smallvec::smallvec![TransportType::Websocket],
383 };
384 let size = serde_json::to_string(&open).unwrap().len();
385 let packet = Packet::Open(open);
386 assert_eq!(packet.get_size_hint(false), size);
387
388 let packet = Packet::Close;
389 assert_eq!(packet.get_size_hint(false), 1);
390
391 let packet = Packet::Ping;
392 assert_eq!(packet.get_size_hint(false), 1);
393
394 let packet = Packet::Pong;
395 assert_eq!(packet.get_size_hint(false), 1);
396
397 let packet = Packet::PingUpgrade;
398 assert_eq!(packet.get_size_hint(false), 6);
399
400 let packet = Packet::PongUpgrade;
401 assert_eq!(packet.get_size_hint(false), 6);
402
403 let packet = Packet::Message("hello".into());
404 assert_eq!(packet.get_size_hint(false), 6);
405
406 let packet = Packet::Upgrade;
407 assert_eq!(packet.get_size_hint(false), 1);
408
409 let packet = Packet::Noop;
410 assert_eq!(packet.get_size_hint(false), 1);
411
412 let packet = Packet::Binary(vec![1, 2, 3].into());
413 assert_eq!(packet.get_size_hint(false), 4);
414 assert_eq!(packet.get_size_hint(true), 5);
415
416 let packet = Packet::BinaryV3(vec![1, 2, 3].into());
417 assert_eq!(packet.get_size_hint(false), 4);
418 assert_eq!(packet.get_size_hint(true), 6);
419 }
420}