//! Definition of Internal Router and Websocket protocol messages //! //! This module is a structured definition of several protocol. Both //! messages received from the client and messages sent from the server are //! defined here. The `derive(Deserialize)` and `derive(Serialize)` annotations //! are used to generate the ability to serialize these structures to JSON, //! using the `serde` crate. More docs for serde can be found at //! use std::collections::HashMap; use std::str::FromStr; use serde_derive::{Deserialize, Serialize}; use strum_macros::{AsRefStr, Display, EnumString}; use uuid::Uuid; use autopush_common::notification::Notification; /// Message types for WebPush protocol messages. /// /// This enum should be used instead of string literals when referring to message types. /// String serialization is handled automatically via the strum traits. /// /// Example: /// ``` /// use autoconnect_common::protocol::MessageType; /// /// let message_type = MessageType::Hello; /// let message_str = message_type.as_ref(); // Returns "hello" /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, AsRefStr, Display, EnumString)] #[strum(serialize_all = "snake_case")] pub enum MessageType { Hello, Register, Unregister, BroadcastSubscribe, Ack, Nack, Ping, Notification, Broadcast, } impl MessageType { /// Returns the expected message type string for error messages pub fn expected_msg(&self) -> String { format!(r#"Expected messageType="{}""#, self.as_ref()) } } #[derive(Debug, Eq, PartialEq, Serialize)] #[serde(untagged)] pub enum BroadcastValue { Value(String), Nested(HashMap), } #[derive(Debug, Default)] // Used for the server to flag a webpush client to deliver a Notification or Check storage pub enum ServerNotification { CheckStorage, Notification(Notification), #[default] Disconnect, } #[derive(Debug, Deserialize)] #[serde(tag = "messageType", rename_all = "snake_case")] pub enum ClientMessage { Hello { uaid: Option, #[serde(rename = "channelIDs", skip_serializing_if = "Option::is_none")] _channel_ids: Option>, #[serde(skip_serializing_if = "Option::is_none")] broadcasts: Option>, }, Register { #[serde(rename = "channelID")] channel_id: String, key: Option, }, Unregister { #[serde(rename = "channelID")] channel_id: Uuid, code: Option, }, BroadcastSubscribe { broadcasts: HashMap, }, Ack { updates: Vec, }, Nack { code: Option, version: String, }, Ping, } impl ClientMessage { /// Get the message type of this message pub fn message_type(&self) -> MessageType { match self { ClientMessage::Hello { .. } => MessageType::Hello, ClientMessage::Register { .. } => MessageType::Register, ClientMessage::Unregister { .. } => MessageType::Unregister, ClientMessage::BroadcastSubscribe { .. } => MessageType::BroadcastSubscribe, ClientMessage::Ack { .. } => MessageType::Ack, ClientMessage::Nack { .. } => MessageType::Nack, ClientMessage::Ping => MessageType::Ping, } } } impl FromStr for ClientMessage { type Err = serde_json::error::Error; fn from_str(s: &str) -> Result { // parse empty object "{}" as a Ping serde_json::from_str::>(s) .map(|_| ClientMessage::Ping) .or_else(|_| serde_json::from_str(s)) } } /// Returned ACKnowledgement of the received message by the User Agent. /// This is the payload for the `messageType:ack` packet. /// #[derive(Debug, Deserialize)] pub struct ClientAck { /// The channel_id which received messages #[serde(rename = "channelID")] pub channel_id: Uuid, /// The corresponding version number for the message. pub version: String, /// An optional code categorizing the status of the ACK pub code: Option, } impl ClientAck { #[cfg(feature = "reliable_report")] pub fn reliability_state(&self) -> autopush_common::reliability::ReliabilityState { match self.code.unwrap_or(100) { 101 => autopush_common::reliability::ReliabilityState::DecryptionError, 102 => autopush_common::reliability::ReliabilityState::NotDelivered, // 100 (ignore/treat anything else as 100) _ => autopush_common::reliability::ReliabilityState::Delivered, } } } #[derive(Debug, Serialize)] #[serde(tag = "messageType", rename_all = "snake_case")] pub enum ServerMessage { Hello { uaid: String, status: u32, // This is required for output, but will always be "true" use_webpush: bool, broadcasts: HashMap, }, Register { #[serde(rename = "channelID")] channel_id: Uuid, status: u32, #[serde(rename = "pushEndpoint")] push_endpoint: String, }, Unregister { #[serde(rename = "channelID")] channel_id: Uuid, status: u32, }, Broadcast { broadcasts: HashMap, }, Notification(Notification), Ping, } impl ServerMessage { /// Get the message type of this message pub fn message_type(&self) -> MessageType { match self { ServerMessage::Hello { .. } => MessageType::Hello, ServerMessage::Register { .. } => MessageType::Register, ServerMessage::Unregister { .. } => MessageType::Unregister, ServerMessage::Broadcast { .. } => MessageType::Broadcast, ServerMessage::Notification(..) => MessageType::Notification, ServerMessage::Ping => MessageType::Ping, } } pub fn to_json(&self) -> Result { match self { // Both client and server understand the verbose `{"messageType": "ping"}` and the abbreviated `{}` // as valid ping messages. The server defaults to the shorter `{}` form. ServerMessage::Ping => Ok("{}".to_string()), _ => serde_json::to_string(self), } } }