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