Skip to main content

rtc/peer_connection/transport/ice/
server.rs

1use serde::{Deserialize, Serialize};
2
3use shared::error::{Error, Result};
4
5/// ICEServer describes a single STUN and TURN server that can be used by
6/// the ICEAgent to establish a connection with a peer.
7///
8/// ## Specifications
9///
10/// * [W3C]
11///
12/// [W3C]: https://w3c.github.io/webrtc-pc/#dom-rtciceserver
13#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Hash)]
14pub struct RTCIceServer {
15    pub urls: Vec<String>,
16    pub username: String,
17    pub credential: String,
18}
19
20impl RTCIceServer {
21    pub(crate) fn parse_url(&self, url_str: &str) -> Result<ice::url::Url> {
22        ice::url::Url::parse_url(url_str)
23    }
24
25    pub(crate) fn validate(&self) -> Result<()> {
26        self.urls()?;
27        Ok(())
28    }
29
30    pub(crate) fn urls(&self) -> Result<Vec<ice::url::Url>> {
31        let mut urls = vec![];
32
33        for url_str in &self.urls {
34            let mut url = self.parse_url(url_str)?;
35            if url.scheme == ice::url::SchemeType::Turn || url.scheme == ice::url::SchemeType::Turns
36            {
37                // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.2)
38                if self.username.is_empty() || self.credential.is_empty() {
39                    return Err(Error::ErrNoTurnCredentials);
40                }
41
42                url.username.clone_from(&self.username);
43                url.password.clone_from(&self.credential);
44            }
45
46            urls.push(url);
47        }
48
49        Ok(urls)
50    }
51}
52
53#[cfg(test)]
54mod test {
55    use super::*;
56
57    #[test]
58    fn test_ice_server_validate_success() {
59        let tests = vec![
60            (
61                RTCIceServer {
62                    urls: vec!["turn:192.158.29.39?transport=udp".to_owned()],
63                    username: "unittest".to_owned(),
64                    credential: "placeholder".to_owned(),
65                },
66                true,
67            ),
68            (
69                RTCIceServer {
70                    urls: vec!["turn:[2001:db8:1234:5678::1]?transport=udp".to_owned()],
71                    username: "unittest".to_owned(),
72                    credential: "placeholder".to_owned(),
73                },
74                true,
75            ),
76            /*TODO:(ICEServer{
77                URLs:     []string{"turn:192.158.29.39?transport=udp"},
78                Username: "unittest".to_owned(),
79                Credential: OAuthCredential{
80                    MACKey:      "WmtzanB3ZW9peFhtdm42NzUzNG0=",
81                    AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ5VhNDgeMR3+ZlZ35byg972fW8QjpEl7bx91YLBPFsIhsxloWcXPhA==",
82                },
83                CredentialType: ICECredentialTypeOauth,
84            }, true),*/
85        ];
86
87        for (ice_server, expected_validate) in tests {
88            let result = ice_server.urls();
89            assert_eq!(result.is_ok(), expected_validate);
90        }
91    }
92
93    #[test]
94    fn test_ice_server_validate_failure() {
95        let tests = vec![
96            (
97                RTCIceServer {
98                    urls: vec!["turn:192.158.29.39?transport=udp".to_owned()],
99                    ..Default::default()
100                },
101                Error::ErrNoTurnCredentials,
102            ),
103            (
104                RTCIceServer {
105                    urls: vec!["turn:192.158.29.39?transport=udp".to_owned()],
106                    username: "unittest".to_owned(),
107                    credential: String::new(),
108                },
109                Error::ErrNoTurnCredentials,
110            ),
111            (
112                RTCIceServer {
113                    urls: vec!["turn:192.158.29.39?transport=udp".to_owned()],
114                    username: "unittest".to_owned(),
115                    credential: String::new(),
116                },
117                Error::ErrNoTurnCredentials,
118            ),
119            (
120                RTCIceServer {
121                    urls: vec!["turn:192.158.29.39?transport=udp".to_owned()],
122                    username: "unittest".to_owned(),
123                    credential: String::new(),
124                },
125                Error::ErrNoTurnCredentials,
126            ),
127        ];
128
129        for (ice_server, expected_err) in tests {
130            if let Err(err) = ice_server.urls() {
131                assert_eq!(err, expected_err, "{ice_server:?} with err {err:?}");
132            } else {
133                panic!("expected error, but got ok");
134            }
135        }
136    }
137
138    #[test]
139    fn test_ice_server_validate_failure_err_stun_query() {
140        let tests = vec![(
141            RTCIceServer {
142                urls: vec!["stun:google.de?transport=udp".to_owned()],
143                username: "unittest".to_owned(),
144                credential: String::new(),
145            },
146            Error::ErrStunQuery,
147        )];
148
149        for (ice_server, expected_err) in tests {
150            if let Err(err) = ice_server.urls() {
151                assert_eq!(err, expected_err, "{ice_server:?} with err {err:?}");
152            } else {
153                panic!("expected error, but got ok");
154            }
155        }
156    }
157}