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