autoconnect_common/
protocol.rs

1//! Definition of Internal Router and Websocket protocol messages
2//!
3//! This module is a structured definition of several protocol. Both
4//! messages received from the client and messages sent from the server are
5//! defined here. The `derive(Deserialize)` and `derive(Serialize)` annotations
6//! are used to generate the ability to serialize these structures to JSON,
7//! using the `serde` crate. More docs for serde can be found at
8//! <https://serde.rs>
9use std::collections::HashMap;
10use std::str::FromStr;
11
12use serde_derive::{Deserialize, Serialize};
13use strum_macros::{AsRefStr, Display, EnumString};
14use uuid::Uuid;
15
16use autopush_common::notification::Notification;
17
18/// Message types for WebPush protocol messages.
19///
20/// This enum should be used instead of string literals when referring to message types.
21/// String serialization is handled automatically via the strum traits.
22///
23/// Example:
24/// ```
25///  use autoconnect_common::protocol::MessageType;
26///
27/// let message_type = MessageType::Hello;
28/// let message_str = message_type.as_ref();  // Returns "hello"
29/// ```
30#[derive(Debug, Clone, Copy, PartialEq, Eq, AsRefStr, Display, EnumString)]
31#[strum(serialize_all = "snake_case")]
32pub enum MessageType {
33    Hello,
34    Register,
35    Unregister,
36    BroadcastSubscribe,
37    Ack,
38    Nack,
39    Ping,
40    Notification,
41    Broadcast,
42}
43
44impl MessageType {
45    /// Returns the expected message type string for error messages
46    pub fn expected_msg(&self) -> String {
47        format!(r#"Expected messageType="{}""#, self.as_ref())
48    }
49}
50
51#[derive(Debug, Eq, PartialEq, Serialize)]
52#[serde(untagged)]
53pub enum BroadcastValue {
54    Value(String),
55    Nested(HashMap<String, BroadcastValue>),
56}
57
58#[derive(Debug, Default)]
59// Used for the server to flag a webpush client to deliver a Notification or Check storage
60pub enum ServerNotification {
61    CheckStorage,
62    Notification(Notification),
63    #[default]
64    Disconnect,
65}
66
67#[derive(Debug, Deserialize)]
68#[serde(tag = "messageType", rename_all = "snake_case")]
69pub enum ClientMessage {
70    Hello {
71        uaid: Option<String>,
72        #[serde(rename = "channelIDs", skip_serializing_if = "Option::is_none")]
73        _channel_ids: Option<Vec<Uuid>>,
74        #[serde(skip_serializing_if = "Option::is_none")]
75        broadcasts: Option<HashMap<String, String>>,
76    },
77
78    Register {
79        #[serde(rename = "channelID")]
80        channel_id: String,
81        key: Option<String>,
82    },
83
84    Unregister {
85        #[serde(rename = "channelID")]
86        channel_id: Uuid,
87        code: Option<u32>,
88    },
89
90    BroadcastSubscribe {
91        broadcasts: HashMap<String, String>,
92    },
93
94    Ack {
95        updates: Vec<ClientAck>,
96    },
97
98    Nack {
99        code: Option<i32>,
100        version: String,
101    },
102
103    Ping,
104}
105
106impl ClientMessage {
107    /// Get the message type of this message
108    pub fn message_type(&self) -> MessageType {
109        match self {
110            ClientMessage::Hello { .. } => MessageType::Hello,
111            ClientMessage::Register { .. } => MessageType::Register,
112            ClientMessage::Unregister { .. } => MessageType::Unregister,
113            ClientMessage::BroadcastSubscribe { .. } => MessageType::BroadcastSubscribe,
114            ClientMessage::Ack { .. } => MessageType::Ack,
115            ClientMessage::Nack { .. } => MessageType::Nack,
116            ClientMessage::Ping => MessageType::Ping,
117        }
118    }
119}
120
121impl FromStr for ClientMessage {
122    type Err = serde_json::error::Error;
123
124    fn from_str(s: &str) -> Result<Self, Self::Err> {
125        // parse empty object "{}" as a Ping
126        serde_json::from_str::<HashMap<(), ()>>(s)
127            .map(|_| ClientMessage::Ping)
128            .or_else(|_| serde_json::from_str(s))
129    }
130}
131
132/// Returned ACKnowledgement of the received message by the User Agent.
133/// This is the payload for the `messageType:ack` packet.
134///
135#[derive(Debug, Deserialize)]
136pub struct ClientAck {
137    /// The channel_id which received messages
138    #[serde(rename = "channelID")]
139    pub channel_id: Uuid,
140    /// The corresponding version number for the message.
141    pub version: String,
142    /// An optional code categorizing the status of the ACK
143    pub code: Option<u16>,
144}
145
146impl ClientAck {
147    #[cfg(feature = "reliable_report")]
148    pub fn reliability_state(&self) -> autopush_common::reliability::ReliabilityState {
149        match self.code.unwrap_or(100) {
150            101 => autopush_common::reliability::ReliabilityState::DecryptionError,
151            102 => autopush_common::reliability::ReliabilityState::NotDelivered,
152            // 100 (ignore/treat anything else as 100)
153            _ => autopush_common::reliability::ReliabilityState::Delivered,
154        }
155    }
156}
157
158#[derive(Debug, Serialize)]
159#[serde(tag = "messageType", rename_all = "snake_case")]
160pub enum ServerMessage {
161    Hello {
162        uaid: String,
163        status: u32,
164        // This is required for output, but will always be "true"
165        use_webpush: bool,
166        broadcasts: HashMap<String, BroadcastValue>,
167    },
168
169    Register {
170        #[serde(rename = "channelID")]
171        channel_id: Uuid,
172        status: u32,
173        #[serde(rename = "pushEndpoint")]
174        push_endpoint: String,
175    },
176
177    Unregister {
178        #[serde(rename = "channelID")]
179        channel_id: Uuid,
180        status: u32,
181    },
182
183    Broadcast {
184        broadcasts: HashMap<String, BroadcastValue>,
185    },
186
187    Notification(Notification),
188
189    Ping,
190}
191
192impl ServerMessage {
193    /// Get the message type of this message
194    pub fn message_type(&self) -> MessageType {
195        match self {
196            ServerMessage::Hello { .. } => MessageType::Hello,
197            ServerMessage::Register { .. } => MessageType::Register,
198            ServerMessage::Unregister { .. } => MessageType::Unregister,
199            ServerMessage::Broadcast { .. } => MessageType::Broadcast,
200            ServerMessage::Notification(..) => MessageType::Notification,
201            ServerMessage::Ping => MessageType::Ping,
202        }
203    }
204
205    pub fn to_json(&self) -> Result<String, serde_json::error::Error> {
206        match self {
207            // Both client and server understand the verbose `{"messageType": "ping"}` and the abbreviated `{}`
208            // as valid ping messages. The server defaults to the shorter `{}` form.
209            ServerMessage::Ping => Ok("{}".to_string()),
210            _ => serde_json::to_string(self),
211        }
212    }
213}