socketioxide_core/packet.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
//! Socket.io packet implementation.
//! The [`Packet`] is the base unit of data that is sent over the engine.io socket.
use serde::{Deserialize, Serialize};
pub use engineioxide::{sid::Sid, Str};
use crate::Value;
/// The socket.io packet type.
/// Each packet has a type and a namespace
#[derive(Debug, Clone, PartialEq)]
pub struct Packet {
/// The packet data
pub inner: PacketData,
/// The namespace the packet belongs to
pub ns: Str,
}
impl Packet {
/// Send a connect packet with a default payload for v5 and no payload for v4
pub fn connect(ns: impl Into<Str>, value: Option<Value>) -> Self {
Self {
inner: PacketData::Connect(value),
ns: ns.into(),
}
}
/// Create a disconnect packet for the given namespace
pub fn disconnect(ns: impl Into<Str>) -> Self {
Self {
inner: PacketData::Disconnect,
ns: ns.into(),
}
}
}
impl Packet {
/// Create a connect error packet for the given namespace with a message
pub fn connect_error(ns: impl Into<Str>, message: impl Into<String>) -> Self {
Self {
inner: PacketData::ConnectError(message.into()),
ns: ns.into(),
}
}
/// Create an event packet for the given namespace.
/// If the there is adjacent binary data, it will be a binary packet.
pub fn event(ns: impl Into<Str>, data: Value) -> Self {
Self {
inner: match data {
Value::Str(_, Some(ref bins)) if !bins.is_empty() => {
PacketData::BinaryEvent(data, None)
}
_ => PacketData::Event(data, None),
},
ns: ns.into(),
}
}
/// Create an ack packet for the given namespace.
/// If the there is adjacent binary data, it will be a binary packet.
pub fn ack(ns: impl Into<Str>, data: Value, ack: i64) -> Self {
Self {
inner: match data {
Value::Str(_, Some(ref bins)) if !bins.is_empty() => {
PacketData::BinaryAck(data, ack)
}
_ => PacketData::EventAck(data, ack),
},
ns: ns.into(),
}
}
}
/// | Type | ID | Usage |
/// |---------------|-----|---------------------------------------------------------------------------------------|
/// | CONNECT | 0 | Used during the [connection to a namespace](#connection-to-a-namespace). |
/// | DISCONNECT | 1 | Used when [disconnecting from a namespace](#disconnection-from-a-namespace). |
/// | EVENT | 2 | Used to [send data](#sending-and-receiving-data) to the other side. |
/// | ACK | 3 | Used to [acknowledge](#acknowledgement) an event. |
/// | CONNECT_ERROR | 4 | Used during the [connection to a namespace](#connection-to-a-namespace). |
/// | BINARY_EVENT | 5 | Used to [send binary data](#sending-and-receiving-data) to the other side. |
/// | BINARY_ACK | 6 | Used to [acknowledge](#acknowledgement) an event (the response includes binary data). |
#[derive(Debug, Clone, PartialEq)]
pub enum PacketData {
/// Connect packet with optional payload (only used with v5 for response)
Connect(Option<Value>),
/// Disconnect packet, used to disconnect from a namespace
Disconnect,
/// Event packet with optional ack id, to request an ack from the other side
Event(Value, Option<i64>),
/// Event ack packet, to acknowledge an event
EventAck(Value, i64),
/// Connect error packet, sent when the namespace is invalid
ConnectError(String),
/// Binary event packet with optional ack id, to request an ack from the other side
BinaryEvent(Value, Option<i64>),
/// Binary ack packet, to acknowledge an event with binary data
BinaryAck(Value, i64),
}
impl PacketData {
/// Returns the index of the packet type
pub fn index(&self) -> usize {
match self {
PacketData::Connect(_) => 0,
PacketData::Disconnect => 1,
PacketData::Event(_, _) => 2,
PacketData::EventAck(_, _) => 3,
PacketData::ConnectError(_) => 4,
PacketData::BinaryEvent(_, _) => 5,
PacketData::BinaryAck(_, _) => 6,
}
}
/// Set the ack id for the packet
/// It will only set the ack id for the packets that support it
pub fn set_ack_id(&mut self, ack_id: i64) {
match self {
PacketData::Event(_, ack) | PacketData::BinaryEvent(_, ack) => *ack = Some(ack_id),
_ => {}
};
}
/// Check if the packet is a binary packet (either binary event or binary ack)
pub fn is_binary(&self) -> bool {
matches!(
self,
PacketData::BinaryEvent(_, _) | PacketData::BinaryAck(_, _)
)
}
}
/// Connect packet sent by the client
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectPacket {
/// The socket ID
pub sid: Sid,
}
#[cfg(test)]
mod tests {
use super::{Packet, PacketData, Value};
use bytes::Bytes;
#[test]
fn should_create_bin_packet_with_adjacent_binary() {
let val = Value::Str(
"test".into(),
Some(vec![Bytes::from_static(&[1, 2, 3])].into()),
);
assert!(matches!(
Packet::event("/", val.clone()).inner,
PacketData::BinaryEvent(v, None) if v == val));
assert!(matches!(
Packet::ack("/", val.clone(), 120).inner,
PacketData::BinaryAck(v, 120) if v == val));
}
#[test]
fn should_create_default_packet_with_base_data() {
let val = Value::Str("test".into(), None);
let val1 = Value::Bytes(Bytes::from_static(b"test"));
assert!(matches!(
Packet::event("/", val.clone()).inner,
PacketData::Event(v, None) if v == val));
assert!(matches!(
Packet::ack("/", val.clone(), 120).inner,
PacketData::EventAck(v, 120) if v == val));
assert!(matches!(
Packet::event("/", val1.clone()).inner,
PacketData::Event(v, None) if v == val1));
assert!(matches!(
Packet::ack("/", val1.clone(), 120).inner,
PacketData::EventAck(v, 120) if v == val1));
}
}