p2p/webrtc/signaling_method/
mod.rs

1mod http;
2pub use http::HttpSignalingInfo;
3
4use std::{fmt, str::FromStr};
5
6use binprot_derive::{BinProtRead, BinProtWrite};
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10use crate::PeerId;
11
12#[derive(BinProtWrite, BinProtRead, Eq, PartialEq, Ord, PartialOrd, Debug, Clone)]
13pub enum SignalingMethod {
14    Http(HttpSignalingInfo),
15    Https(HttpSignalingInfo),
16    /// Proxy used as an SSL gateway to the actual signaling server.
17    HttpsProxy(u16, HttpSignalingInfo),
18    P2p {
19        relay_peer_id: PeerId,
20    },
21}
22
23impl SignalingMethod {
24    pub fn can_connect_directly(&self) -> bool {
25        match self {
26            Self::Http(_) | Self::Https(_) | Self::HttpsProxy(_, _) => true,
27            Self::P2p { .. } => false,
28        }
29    }
30
31    /// If method is http or https, it will return url to which an
32    /// offer can be sent.
33    pub fn http_url(&self) -> Option<String> {
34        let (http, info) = match self {
35            Self::Http(info) => ("http", info),
36            Self::Https(info) => ("https", info),
37            Self::HttpsProxy(cluster_id, info) => {
38                return Some(format!(
39                    "https://{}:{}/clusters/{}/mina/webrtc/signal",
40                    info.host, info.port, cluster_id
41                ));
42            }
43            _ => return None,
44        };
45        Some(format!(
46            "{http}://{}:{}/mina/webrtc/signal",
47            info.host, info.port,
48        ))
49    }
50
51    pub fn p2p_relay_peer_id(&self) -> Option<PeerId> {
52        match self {
53            Self::P2p { relay_peer_id } => Some(*relay_peer_id),
54            _ => None,
55        }
56    }
57}
58
59impl fmt::Display for SignalingMethod {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        match self {
62            Self::Http(signaling) => {
63                write!(f, "/http")?;
64                signaling.fmt(f)
65            }
66            Self::Https(signaling) => {
67                write!(f, "/https")?;
68                signaling.fmt(f)
69            }
70            Self::HttpsProxy(cluster_id, signaling) => {
71                write!(f, "/https_proxy/{cluster_id}")?;
72                signaling.fmt(f)
73            }
74            Self::P2p { relay_peer_id } => {
75                write!(f, "/p2p/{relay_peer_id}")
76            }
77        }
78    }
79}
80
81#[derive(Error, Serialize, Deserialize, Debug, Clone)]
82pub enum SignalingMethodParseError {
83    #[error("not enough args for the signaling method")]
84    NotEnoughArgs,
85    #[error("unknown signaling method: `{0}`")]
86    UnknownSignalingMethod(String),
87    #[error("invalid cluster id")]
88    InvalidClusterId,
89    #[error("host parse error: {0}")]
90    HostParseError(String),
91    #[error("host parse error: {0}")]
92    PortParseError(String),
93}
94
95impl FromStr for SignalingMethod {
96    type Err = SignalingMethodParseError;
97
98    fn from_str(s: &str) -> Result<Self, Self::Err> {
99        if s.is_empty() {
100            return Err(SignalingMethodParseError::NotEnoughArgs);
101        }
102
103        let method_end_index = s[1..]
104            .find('/')
105            .map(|i| i + 1)
106            .filter(|i| s.len() > *i)
107            .ok_or(SignalingMethodParseError::NotEnoughArgs)?;
108
109        let rest = &s[method_end_index..];
110        match &s[1..method_end_index] {
111            "http" => Ok(Self::Http(rest.parse()?)),
112            "https" => Ok(Self::Https(rest.parse()?)),
113            "https_proxy" => {
114                let mut iter = rest.splitn(3, '/').filter(|v| !v.trim().is_empty());
115                let (cluster_id, rest) = (
116                    iter.next()
117                        .ok_or(SignalingMethodParseError::NotEnoughArgs)?,
118                    iter.next()
119                        .ok_or(SignalingMethodParseError::NotEnoughArgs)?,
120                );
121                let cluster_id: u16 = cluster_id
122                    .parse()
123                    .or(Err(SignalingMethodParseError::InvalidClusterId))?;
124                Ok(Self::HttpsProxy(cluster_id, rest.parse()?))
125            }
126            method => Err(SignalingMethodParseError::UnknownSignalingMethod(
127                method.to_owned(),
128            )),
129        }
130    }
131}
132
133impl Serialize for SignalingMethod {
134    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135    where
136        S: serde::Serializer,
137    {
138        serializer.serialize_str(&self.to_string())
139    }
140}
141
142impl<'de> serde::Deserialize<'de> for SignalingMethod {
143    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144    where
145        D: serde::Deserializer<'de>,
146    {
147        let s: String = Deserialize::deserialize(deserializer)?;
148        s.parse().map_err(serde::de::Error::custom)
149    }
150}