socketioxide_parser_common/
lib.rs

1#![warn(
2    clippy::all,
3    clippy::todo,
4    clippy::empty_enum,
5    clippy::mem_forget,
6    clippy::unused_self,
7    clippy::filter_map_next,
8    clippy::needless_continue,
9    clippy::needless_borrow,
10    clippy::match_wildcard_for_single_variants,
11    clippy::if_let_mutex,
12    clippy::await_holding_lock,
13    clippy::match_on_vec_items,
14    clippy::imprecise_flops,
15    clippy::suboptimal_flops,
16    clippy::lossy_float_literal,
17    clippy::rest_pat_in_fully_bound_structs,
18    clippy::fn_params_excessive_bools,
19    clippy::exit,
20    clippy::inefficient_to_string,
21    clippy::linkedlist,
22    clippy::macro_use_imports,
23    clippy::option_option,
24    clippy::verbose_file_reads,
25    clippy::unnested_or_patterns,
26    rust_2018_idioms,
27    rust_2024_compatibility,
28    future_incompatible,
29    nonstandard_style,
30    missing_docs
31)]
32
33//! The common parser sub-crate for the socketioxide crate.
34//!
35//! This is the default parser implementation.
36//!
37//! It is used to parse and serialize the common packet format of the socket.io protocol:
38//! ```text
39//! <packet type>[<# of binary attachments>-][<namespace>,][<acknowledgment id>][JSON-stringified payload without binary]
40//! + binary attachments extracted
41//! ```
42use std::{collections::VecDeque, sync::atomic::Ordering};
43
44use bytes::Bytes;
45
46use serde::{Deserialize, Serialize};
47use socketioxide_core::{
48    Str, Value,
49    packet::{Packet, PacketData},
50    parser::{Parse, ParseError, ParserError, ParserState},
51};
52
53mod de;
54mod ser;
55mod value;
56
57/// Parse and serialize from and into the socket.io common packet format.
58/// See details in the [socket.io protocol doc](https://socket.io/fr/docs/v4/socket-io-protocol/#packet-encoding).
59#[derive(Debug, Default, Clone, Copy)]
60pub struct CommonParser;
61
62impl Parse for CommonParser {
63    fn encode(self, packet: Packet) -> Value {
64        ser::serialize_packet(packet)
65    }
66
67    fn decode_str(self, state: &ParserState, value: Str) -> Result<Packet, ParseError> {
68        let (packet, incoming_binary_cnt) = de::deserialize_packet(value)?;
69        if packet.inner.is_binary() {
70            let incoming_binary_cnt = incoming_binary_cnt.ok_or(ParseError::InvalidAttachments)?;
71            if !is_bin_packet_complete(&packet.inner, incoming_binary_cnt) {
72                *state.partial_bin_packet.lock().unwrap() = Some(packet);
73                state
74                    .incoming_binary_cnt
75                    .store(incoming_binary_cnt, Ordering::Release);
76                Err(ParseError::NeedsMoreBinaryData)
77            } else {
78                Ok(packet)
79            }
80        } else {
81            Ok(packet)
82        }
83    }
84
85    fn decode_bin(self, state: &ParserState, data: Bytes) -> Result<Packet, ParseError> {
86        let packet = &mut *state.partial_bin_packet.lock().unwrap();
87        match packet {
88            Some(Packet {
89                inner:
90                    PacketData::BinaryEvent(Value::Str(_, binaries), _)
91                    | PacketData::BinaryAck(Value::Str(_, binaries), _),
92                ..
93            }) => {
94                let binaries = binaries.get_or_insert(VecDeque::new());
95                // We copy the data to avoid holding a ref to the engine.io
96                // websocket buffer too long.
97                binaries.push_back(Bytes::copy_from_slice(&data));
98                if state.incoming_binary_cnt.load(Ordering::Relaxed) > binaries.len() {
99                    Err(ParseError::NeedsMoreBinaryData)
100                } else {
101                    Ok(packet.take().unwrap())
102                }
103            }
104            _ => Err(ParseError::UnexpectedBinaryPacket),
105        }
106    }
107
108    #[inline]
109    fn encode_value<T: ?Sized + Serialize>(
110        self,
111        data: &T,
112        event: Option<&str>,
113    ) -> Result<Value, ParserError> {
114        value::to_value(data, event).map_err(ParserError::new)
115    }
116
117    #[inline]
118    fn decode_value<'de, T: Deserialize<'de>>(
119        self,
120        value: &'de mut Value,
121        with_event: bool,
122    ) -> Result<T, ParserError> {
123        value::from_value(value, with_event).map_err(ParserError::new)
124    }
125
126    fn decode_default<'de, T: Deserialize<'de>>(
127        self,
128        value: Option<&'de Value>,
129    ) -> Result<T, ParserError> {
130        if let Some(value) = value {
131            let data = value
132                .as_str()
133                .expect("CommonParser only supports string values");
134            serde_json::from_str(data).map_err(ParserError::new)
135        } else {
136            serde_json::from_str("{}").map_err(ParserError::new)
137        }
138    }
139
140    fn encode_default<T: ?Sized + Serialize>(self, data: &T) -> Result<Value, ParserError> {
141        let value = serde_json::to_string(data).map_err(ParserError::new)?;
142        Ok(Value::Str(Str::from(value), None))
143    }
144
145    #[inline]
146    fn read_event(self, value: &Value) -> Result<&str, ParserError> {
147        value::read_event(value).map_err(ParserError::new)
148    }
149}
150
151/// Check if the binary packet is complete, it means that all payloads have been received
152fn is_bin_packet_complete(packet: &PacketData, incoming_binary_cnt: usize) -> bool {
153    match &packet {
154        PacketData::BinaryEvent(Value::Str(_, binaries), _)
155        | PacketData::BinaryAck(Value::Str(_, binaries), _) => {
156            incoming_binary_cnt == binaries.as_ref().map(VecDeque::len).unwrap_or(0)
157        }
158        _ => true,
159    }
160}
161
162#[cfg(test)]
163mod test {
164    use std::collections::HashMap;
165
166    use crate::is_bin_packet_complete;
167
168    use super::*;
169    use serde_json::json;
170    use socketioxide_core::{Sid, packet::ConnectPacket};
171
172    fn to_event_value(data: &impl serde::Serialize, event: &str) -> Value {
173        CommonParser.encode_value(data, Some(event)).unwrap()
174    }
175
176    fn to_value(data: &impl serde::Serialize) -> Value {
177        CommonParser.encode_value(data, None).unwrap()
178    }
179    fn to_connect_value(data: &impl serde::Serialize) -> Value {
180        Value::Str(Str::from(serde_json::to_string(data).unwrap()), None)
181    }
182    fn encode(packet: Packet) -> String {
183        match CommonParser.encode(packet) {
184            Value::Str(d, _) => d.into(),
185            Value::Bytes(_) => panic!("testing only returns str"),
186        }
187    }
188    fn decode(value: String) -> Packet {
189        CommonParser
190            .decode_str(&Default::default(), value.into())
191            .unwrap()
192    }
193
194    #[test]
195    fn packet_decode_connect() {
196        let sid = Sid::new();
197        let payload = format!("0{}", json!({ "sid": sid }));
198        let packet = decode(payload);
199        let value = to_connect_value(&ConnectPacket { sid });
200        assert_eq!(Packet::connect("/", Some(value.clone())), packet);
201
202        let payload = format!("0/admin™,{}", json!({ "sid": sid }));
203        let packet = decode(payload);
204        assert_eq!(Packet::connect("/admin™", Some(value)), packet);
205    }
206
207    #[test]
208    fn packet_encode_connect() {
209        let sid = Sid::new();
210        let value = to_connect_value(&ConnectPacket { sid });
211        let payload = format!("0{}", json!({ "sid": sid }));
212        let packet = encode(Packet::connect("/", Some(value.clone())));
213        assert_eq!(packet, payload);
214
215        let payload = format!("0/admin™,{}", json!({ "sid": sid }));
216        let packet: String = encode(Packet::connect("/admin™", Some(value)));
217        assert_eq!(packet, payload);
218    }
219
220    // Disconnect
221
222    #[test]
223    fn packet_decode_disconnect() {
224        let payload = "1".to_string();
225        let packet = decode(payload);
226        assert_eq!(Packet::disconnect("/"), packet);
227
228        let payload = "1/admin™,".to_string();
229        let packet = decode(payload);
230        assert_eq!(Packet::disconnect("/admin™"), packet);
231    }
232
233    #[test]
234    fn packet_encode_disconnect() {
235        let payload = "1".to_string();
236        let packet = encode(Packet::disconnect("/"));
237        assert_eq!(packet, payload);
238
239        let payload = "1/admin™,".to_string();
240        let packet = encode(Packet::disconnect("/admin™"));
241        assert_eq!(packet, payload);
242    }
243
244    // Event(String, Value, Option<i64>),
245    #[test]
246    fn packet_decode_event() {
247        let payload = format!("2{}", json!(["event", { "data": "value" }]));
248        let packet = decode(payload);
249
250        assert_eq!(
251            Packet::event("/", to_event_value(&json!({"data": "value"}), "event")),
252            packet
253        );
254
255        // Check with ack ID
256        let payload = format!("21{}", json!(["event", { "data": "value" }]));
257        let packet = decode(payload);
258
259        let mut comparison_packet =
260            Packet::event("/", to_event_value(&json!({"data": "value"}), "event"));
261        comparison_packet.inner.set_ack_id(1);
262        assert_eq!(packet, comparison_packet);
263
264        // Check with NS
265        let payload = format!("2/admin™,{}", json!(["event", { "data": "value™" }]));
266        let packet = decode(payload);
267
268        assert_eq!(
269            Packet::event(
270                "/admin™",
271                to_event_value(&json!({"data": "value™"}), "event")
272            ),
273            packet
274        );
275
276        // Check with ack ID and NS
277        let payload = format!("2/admin™,1{}", json!(["event", { "data": "value™" }]));
278        let mut packet = decode(payload);
279        packet.inner.set_ack_id(1);
280
281        let mut comparison_packet = Packet::event(
282            "/admin™",
283            to_event_value(&json!({"data": "value™"}), "event"),
284        );
285        comparison_packet.inner.set_ack_id(1);
286
287        assert_eq!(packet, comparison_packet);
288    }
289
290    #[test]
291    fn packet_encode_event() {
292        let payload = format!("2{}", json!(["event", { "data": "value™" }]));
293        let packet = encode(Packet::event(
294            "/",
295            to_event_value(&json!({ "data": "value™" }), "event"),
296        ));
297
298        assert_eq!(packet, payload);
299
300        // Encode empty data
301        let payload = format!("2{}", json!(["event", []]));
302        let packet = encode(Packet::event("/", to_event_value(&json!([]), "event")));
303
304        assert_eq!(packet, payload);
305
306        // Encode with ack ID
307        let payload = format!("21{}", json!(["event", { "data": "value™" }]));
308        let mut packet = Packet::event("/", to_event_value(&json!({ "data": "value™" }), "event"));
309        packet.inner.set_ack_id(1);
310        let packet = encode(packet);
311
312        assert_eq!(packet, payload);
313
314        // Encode with NS
315        let payload = format!("2/admin™,{}", json!(["event", { "data": "value™" }]));
316        let packet = encode(Packet::event(
317            "/admin™",
318            to_event_value(&json!({"data": "value™"}), "event"),
319        ));
320
321        assert_eq!(packet, payload);
322
323        // Encode with NS and ack ID
324        let payload = format!("2/admin™,1{}", json!(["event", { "data": "value™" }]));
325        let mut packet = Packet::event(
326            "/admin™",
327            to_event_value(&json!({"data": "value™"}), "event"),
328        );
329        packet.inner.set_ack_id(1);
330        let packet = encode(packet);
331        assert_eq!(packet, payload);
332    }
333
334    // EventAck(Value, i64),
335    #[test]
336    fn packet_decode_event_ack() {
337        let payload = "354[\"data\"]".to_string();
338        let packet = decode(payload);
339
340        assert_eq!(Packet::ack("/", to_value(&json!("data")), 54), packet);
341
342        let payload = "3/admin™,54[\"data\"]".to_string();
343        let packet = decode(payload);
344
345        assert_eq!(Packet::ack("/admin™", to_value(&json!("data")), 54), packet);
346    }
347
348    #[test]
349    fn packet_encode_event_ack() {
350        let payload = "354[\"data\"]".to_string();
351        let packet = encode(Packet::ack("/", to_value(&json!("data")), 54));
352        assert_eq!(packet, payload);
353
354        let payload = "3/admin™,54[\"data\"]".to_string();
355        let packet = encode(Packet::ack("/admin™", to_value(&json!("data")), 54));
356        assert_eq!(packet, payload);
357    }
358
359    #[test]
360    fn packet_encode_connect_error() {
361        let payload = format!("4{}", json!({ "message": "Invalid namespace" }));
362        let packet = encode(Packet::connect_error("/", "Invalid namespace"));
363        assert_eq!(packet, payload);
364
365        let payload = format!("4/admin™,{}", json!({ "message": "Invalid namespace" }));
366        let packet = encode(Packet::connect_error("/admin™", "Invalid namespace"));
367        assert_eq!(packet, payload);
368    }
369
370    // BinaryEvent(String, BinaryPacket, Option<i64>),
371    #[test]
372    fn packet_encode_binary_event() {
373        let json = json!(["event", { "data": "value™" }, { "_placeholder": true, "num": 0}]);
374
375        let payload = format!("51-{}", json);
376        let packet = encode(Packet::event(
377            "/",
378            to_event_value(
379                &(json!({ "data": "value™" }), Bytes::from_static(&[1])),
380                "event",
381            ),
382        ));
383
384        assert_eq!(packet, payload);
385
386        // Encode with ack ID
387        let payload = format!("51-254{}", json);
388        let mut packet = Packet::event(
389            "/",
390            to_event_value(
391                &(json!({ "data": "value™" }), Bytes::from_static(&[1])),
392                "event",
393            ),
394        );
395        packet.inner.set_ack_id(254);
396        let packet = encode(packet);
397
398        assert_eq!(packet, payload);
399
400        // Encode with NS
401        let payload = format!("51-/admin™,{}", json);
402        let packet = encode(Packet::event(
403            "/admin™",
404            to_event_value(
405                &(json!({ "data": "value™" }), Bytes::from_static(&[1])),
406                "event",
407            ),
408        ));
409
410        assert_eq!(packet, payload);
411
412        // Encode with NS and ack ID
413        let payload = format!("51-/admin™,254{}", json);
414        let mut packet = Packet::event(
415            "/admin™",
416            to_event_value(
417                &(json!({ "data": "value™" }), Bytes::from_static(&[1])),
418                "event",
419            ),
420        );
421        packet.inner.set_ack_id(254);
422        let packet = encode(packet);
423        assert_eq!(packet, payload);
424    }
425
426    #[test]
427    fn packet_decode_binary_event() {
428        let json = json!(["event", { "data": "value™" },{ "_placeholder": true, "num": 0},{ "_placeholder": true, "num": 1}]);
429        let comparison_packet = |ack, ns: &'static str| {
430            let data = to_event_value(
431                &(
432                    json!({"data": "value™"}),
433                    Bytes::from_static(&[1]),
434                    Bytes::from_static(&[2]),
435                ),
436                "event",
437            );
438            Packet {
439                inner: PacketData::BinaryEvent(data, ack),
440                ns: ns.into(),
441            }
442        };
443        let state = ParserState::default();
444        let payload = format!("52-{}", json);
445        assert!(matches!(
446            CommonParser.decode_str(&state, payload.into()),
447            Err(ParseError::NeedsMoreBinaryData)
448        ));
449        assert!(matches!(
450            CommonParser.decode_bin(&state, Bytes::from_static(&[1])),
451            Err(ParseError::NeedsMoreBinaryData)
452        ));
453        let packet = CommonParser
454            .decode_bin(&state, Bytes::from_static(&[2]))
455            .unwrap();
456
457        assert_eq!(packet, comparison_packet(None, "/"));
458
459        // Check with ack ID
460        let state = ParserState::default();
461        let payload = format!("52-254{}", json);
462        assert!(matches!(
463            CommonParser.decode_str(&state, payload.into()),
464            Err(ParseError::NeedsMoreBinaryData)
465        ));
466        assert!(matches!(
467            CommonParser.decode_bin(&state, Bytes::from_static(&[1])),
468            Err(ParseError::NeedsMoreBinaryData)
469        ));
470        let packet = CommonParser
471            .decode_bin(&state, Bytes::from_static(&[2]))
472            .unwrap();
473
474        assert_eq!(packet, comparison_packet(Some(254), "/"));
475
476        // Check with NS
477        let state = ParserState::default();
478        let payload = format!("52-/admin™,{}", json);
479        assert!(matches!(
480            CommonParser.decode_str(&state, payload.into()),
481            Err(ParseError::NeedsMoreBinaryData)
482        ));
483        assert!(matches!(
484            CommonParser.decode_bin(&state, Bytes::from_static(&[1])),
485            Err(ParseError::NeedsMoreBinaryData)
486        ));
487        let packet = CommonParser
488            .decode_bin(&state, Bytes::from_static(&[2]))
489            .unwrap();
490
491        assert_eq!(packet, comparison_packet(None, "/admin™"));
492
493        // Check with ack ID and NS
494        let state = ParserState::default();
495        let payload = format!("52-/admin™,254{}", json);
496        assert!(matches!(
497            CommonParser.decode_str(&state, payload.into()),
498            Err(ParseError::NeedsMoreBinaryData)
499        ));
500        assert!(matches!(
501            CommonParser.decode_bin(&state, Bytes::from_static(&[1])),
502            Err(ParseError::NeedsMoreBinaryData)
503        ));
504        let packet = CommonParser
505            .decode_bin(&state, Bytes::from_static(&[2]))
506            .unwrap();
507
508        assert_eq!(packet, comparison_packet(Some(254), "/admin™"));
509    }
510
511    // BinaryAck(BinaryPacket, i64),
512    #[test]
513    fn packet_encode_binary_ack() {
514        let json = json!([{ "data": "value™" }, { "_placeholder": true, "num": 0}]);
515
516        let payload = format!("61-54{}", json);
517        let packet = encode(Packet::ack(
518            "/",
519            to_value(&(json!({ "data": "value™" }), Bytes::from_static(&[1]))),
520            54,
521        ));
522
523        assert_eq!(packet, payload);
524
525        // Encode with NS
526        let payload = format!("61-/admin™,54{}", json);
527        let packet = encode(Packet::ack(
528            "/admin™",
529            to_value(&(json!({ "data": "value™" }), Bytes::from_static(&[1]))),
530            54,
531        ));
532
533        assert_eq!(packet, payload);
534    }
535
536    #[test]
537    fn packet_decode_binary_ack() {
538        let json = json!([{ "data": "value™" }, { "_placeholder": true, "num": 0}]);
539        let comparison_packet = |ack, ns: &'static str| Packet {
540            inner: PacketData::BinaryAck(
541                to_value(&(json!({ "data": "value™" }), Bytes::from_static(&[1]))),
542                ack,
543            ),
544            ns: ns.into(),
545        };
546
547        let payload = format!("61-54{}", json);
548        let state = ParserState::default();
549        assert!(matches!(
550            CommonParser.decode_str(&state, payload.into()),
551            Err(ParseError::NeedsMoreBinaryData)
552        ));
553        let packet = CommonParser
554            .decode_bin(&state, Bytes::from_static(&[1]))
555            .unwrap();
556
557        assert_eq!(packet, comparison_packet(54, "/"));
558
559        // Check with NS
560        let state = ParserState::default();
561        let payload = format!("61-/admin™,54{}", json);
562        assert!(matches!(
563            CommonParser.decode_str(&state, payload.into()),
564            Err(ParseError::NeedsMoreBinaryData)
565        ));
566        let packet = CommonParser
567            .decode_bin(&state, Bytes::from_static(&[1]))
568            .unwrap();
569        assert_eq!(packet, comparison_packet(54, "/admin™"));
570    }
571
572    #[test]
573    fn packet_reject_invalid_binary_event() {
574        let payload = "5invalid".to_owned();
575        let err = CommonParser
576            .decode_str(&Default::default(), payload.into())
577            .unwrap_err();
578
579        assert!(matches!(err, ParseError::InvalidAttachments));
580    }
581
582    #[test]
583    fn decode_default_none() {
584        // Common parser should deserialize by default to an empty map to match the behavior of the
585        // socket.io client when deserializing incoming connect message without an auth payload.
586        let data = CommonParser.decode_default::<HashMap<String, ()>>(None);
587        assert!(matches!(data, Ok(d) if d.is_empty()));
588    }
589
590    #[test]
591    fn decode_default_some() {
592        // Common parser should deserialize by default to an empty map to match the behavior of the
593        // socket.io client when deserializing incoming connect message without an auth payload.
594        let data =
595            CommonParser.decode_default::<String>(Some(&Value::Str("\"test\"".into(), None)));
596        assert!(matches!(data, Ok(d) if d == "test"));
597    }
598
599    #[test]
600    fn encode_default() {
601        let data = CommonParser.encode_default(&20);
602        assert!(matches!(data, Ok(Value::Str(d, None)) if d == "20"));
603    }
604
605    #[test]
606    fn read_event() {
607        let data = Value::Str(r#"["event",{"data":1,"complex":[132,12]}]"#.into(), None);
608        let event = CommonParser.read_event(&data).unwrap();
609        assert_eq!(event, "event");
610    }
611
612    #[test]
613    fn unexpected_bin_packet() {
614        let err = CommonParser.decode_bin(&Default::default(), Bytes::new());
615        assert!(matches!(err, Err(ParseError::UnexpectedBinaryPacket)));
616    }
617    #[test]
618    fn check_is_bin_packet_complete() {
619        let data = PacketData::BinaryEvent(Value::Str("".into(), Some(vec![].into())), None);
620        assert!(!is_bin_packet_complete(&data, 2));
621        assert!(is_bin_packet_complete(&data, 0));
622        let data =
623            PacketData::BinaryAck(Value::Str("".into(), Some(vec![Bytes::new()].into())), 12);
624        assert!(is_bin_packet_complete(&data, 1));
625        assert!(!is_bin_packet_complete(&data, 2));
626
627        // any other packet
628        let data = PacketData::Connect(None);
629        assert!(is_bin_packet_complete(&data, 0));
630        assert!(is_bin_packet_complete(&data, 1));
631    }
632}