1use std::ffi::CString;
3use std::marker::{PhantomData, PhantomPinned};
4use std::net::{IpAddr, SocketAddr};
5use std::ptr;
6
7use libjuice_sys as sys;
8
9use crate::log::ensure_logging;
10use crate::{Error, Result};
11
12pub struct Credentials {
13 username: CString,
14 password: CString,
15 quota: Option<i32>,
16}
17
18impl Credentials {
19 pub fn new<T: Into<Vec<u8>>>(username: T, password: T, quota: Option<i32>) -> Result<Self> {
20 Ok(Self {
21 username: CString::new(username).map_err(|_| Error::InvalidArgument)?,
22 password: CString::new(password).map_err(|_| Error::InvalidArgument)?,
23 quota,
24 })
25 }
26}
27
28#[derive(Default)]
30pub struct Builder {
31 credentials: Vec<Credentials>,
32 bind_address: Option<CString>,
33 external_address: Option<CString>,
34 port: u16,
35 max_allocations: i32,
36 max_peers: i32,
37 relay_port_range: Option<(u16, u16)>,
38 realm: Option<CString>,
39}
40
41pub struct Server {
43 server: *mut sys::juice_server_t,
44 _marker: PhantomData<(sys::juice_server, PhantomPinned)>,
45}
46
47impl Builder {
48 pub fn build(self) -> Result<Server> {
50 ensure_logging();
51
52 let mut credentials = self
53 .credentials
54 .iter()
55 .map(|cred| sys::juice_server_credentials {
56 username: cred.username.as_ptr(),
57 password: cred.password.as_ptr(),
58 allocations_quota: cred.quota.unwrap_or_default(),
59 })
60 .collect::<Vec<_>>();
61
62 let credentials = if credentials.is_empty() {
63 return Err(Error::InvalidArgument);
64 } else {
65 credentials.as_mut_ptr()
66 };
67
68 let port_range = self.relay_port_range.unwrap_or_default();
69
70 let bind_address = self
71 .bind_address
72 .as_ref()
73 .map(|v| v.as_ptr())
74 .unwrap_or(ptr::null());
75
76 let external_address = self
77 .external_address
78 .as_ref()
79 .map(|v| v.as_ptr())
80 .unwrap_or(ptr::null());
81
82 let realm = self
83 .realm
84 .as_ref()
85 .map(|v| v.as_ptr())
86 .unwrap_or(ptr::null());
87
88 let config = sys::juice_server_config {
89 credentials,
90 credentials_count: self.credentials.len() as _,
91 bind_address,
92 external_address,
93 max_allocations: self.max_allocations,
94 max_peers: self.max_peers,
95 port: self.port,
96 relay_port_range_begin: port_range.0,
97 relay_port_range_end: port_range.1,
98 realm,
99 };
100
101 let ptr = unsafe { sys::juice_server_create(&config as _) };
103
104 if ptr.is_null() {
105 Err(Error::Failed)
106 } else {
107 Ok(Server {
108 server: ptr,
109 _marker: Default::default(),
110 })
111 }
112 }
113
114 pub fn with_credentials<I: Iterator<Item = Credentials>>(
119 mut self,
120 credentials_list: I,
121 ) -> Self {
122 self.credentials = credentials_list.collect();
123 self
124 }
125
126 pub fn add_credentials(mut self, cred: Credentials) -> Self {
128 self.credentials.push(cred);
129 self
130 }
131
132 pub fn bind_address(mut self, addr: &SocketAddr) -> Self {
134 self.bind_address = Some(CString::new(addr.ip().to_string()).unwrap());
135 self.port = addr.port();
136 self
137 }
138
139 pub fn with_external_address(mut self, addr: &IpAddr) -> Self {
140 self.external_address = Some(CString::new(addr.to_string()).unwrap());
141 self
142 }
143
144 pub fn with_port_range(mut self, begin: u16, end: u16) -> Self {
146 self.relay_port_range = Some((begin, end));
147 self
148 }
149
150 pub fn with_realm<T: Into<Vec<u8>>>(mut self, realm: T) -> Result<Self> {
152 self.realm = Some(CString::new(realm).map_err(|_| Error::InvalidArgument)?);
153 Ok(self)
154 }
155
156 pub fn with_allocations_limit(mut self, limit: u32) -> Self {
157 self.max_allocations = std::cmp::min(limit, i32::MAX as u32) as i32;
158 self
159 }
160
161 pub fn with_peers_limit(mut self, limit: u32) -> Self {
162 self.max_peers = std::cmp::min(limit, i32::MAX as u32) as i32;
163 self
164 }
165}
166
167unsafe impl Send for Server {}
168
169unsafe impl Sync for Server {}
170
171impl Server {
172 pub fn builder() -> Builder {
174 Default::default()
175 }
176
177 pub fn get_port(&self) -> u16 {
179 unsafe { sys::juice_server_get_port(self.server) }
180 }
181}
182
183impl Drop for Server {
184 fn drop(&mut self) {
185 unsafe { sys::juice_server_destroy(self.server) }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn build() {
195 crate::test_util::logger_init();
196 let creds = Credentials::new("a", "b", None).unwrap();
197
198 let _ = Server::builder()
199 .add_credentials(creds)
200 .build()
201 .ok()
202 .unwrap();
203 }
204}