Skip to main content

rtc_stun/
client.rs

1use bytes::BytesMut;
2use shared::error::*;
3use std::collections::{HashMap, VecDeque};
4use std::io::BufReader;
5use std::net::SocketAddr;
6use std::ops::Add;
7use std::time::{Duration, Instant};
8
9use crate::agent::*;
10use crate::message::*;
11use shared::{TaggedBytesMut, TransportContext, TransportMessage, TransportProtocol};
12
13const DEFAULT_TIMEOUT_RATE: Duration = Duration::from_millis(5);
14const DEFAULT_RTO: Duration = Duration::from_millis(300);
15const DEFAULT_MAX_ATTEMPTS: u32 = 7;
16const DEFAULT_MAX_BUFFER_SIZE: usize = 8;
17
18/// ClientTransaction represents transaction in progress.
19/// If transaction is succeed or failed, f will be called
20/// provided by event.
21/// Concurrent access is invalid.
22#[derive(Debug, Clone)]
23pub struct ClientTransaction {
24    id: TransactionId,
25    attempt: u32,
26    start: Instant,
27    rto: Duration,
28    raw: Vec<u8>,
29}
30
31impl ClientTransaction {
32    pub(crate) fn next_timeout(&self, now: Instant) -> Instant {
33        now.add((self.attempt + 1) * self.rto)
34    }
35}
36
37struct ClientSettings {
38    buffer_size: usize,
39    rto: Duration,
40    rto_rate: Duration,
41    max_attempts: u32,
42    closed: bool,
43}
44
45impl Default for ClientSettings {
46    fn default() -> Self {
47        ClientSettings {
48            buffer_size: DEFAULT_MAX_BUFFER_SIZE,
49            rto: DEFAULT_RTO,
50            rto_rate: DEFAULT_TIMEOUT_RATE,
51            max_attempts: DEFAULT_MAX_ATTEMPTS,
52            closed: false,
53        }
54    }
55}
56
57#[derive(Default)]
58pub struct ClientBuilder {
59    settings: ClientSettings,
60}
61
62impl ClientBuilder {
63    /// with_rto sets client RTO as defined in STUN RFC.
64    pub fn with_rto(mut self, rto: Duration) -> Self {
65        self.settings.rto = rto;
66        self
67    }
68
69    /// with_timeout_rate sets RTO timer minimum resolution.
70    pub fn with_timeout_rate(mut self, d: Duration) -> Self {
71        self.settings.rto_rate = d;
72        self
73    }
74
75    /// with_buffer_size sets buffer size.
76    pub fn with_buffer_size(mut self, buffer_size: usize) -> Self {
77        self.settings.buffer_size = buffer_size;
78        self
79    }
80
81    /// with_no_retransmit disables retransmissions and sets RTO to
82    /// DEFAULT_MAX_ATTEMPTS * DEFAULT_RTO which will be effectively time out
83    /// if not set.
84    /// Useful for TCP connections where transport handles RTO.
85    pub fn with_no_retransmit(mut self) -> Self {
86        self.settings.max_attempts = 0;
87        if self.settings.rto == Duration::from_secs(0) {
88            self.settings.rto = DEFAULT_MAX_ATTEMPTS * DEFAULT_RTO;
89        }
90        self
91    }
92
93    pub fn new() -> Self {
94        ClientBuilder {
95            settings: ClientSettings::default(),
96        }
97    }
98
99    pub fn build(
100        self,
101        local: SocketAddr,
102        remote: SocketAddr,
103        protocol: TransportProtocol,
104    ) -> Result<Client> {
105        Ok(Client::new(local, remote, protocol, self.settings))
106    }
107}
108
109/// Client simulates "connection" to STUN server.
110pub struct Client {
111    local: SocketAddr,
112    remote: SocketAddr,
113    transport_protocol: TransportProtocol,
114    agent: Agent,
115    settings: ClientSettings,
116    transactions: HashMap<TransactionId, ClientTransaction>,
117    transmits: VecDeque<TransportMessage<BytesMut>>,
118}
119
120impl Client {
121    fn new(
122        local: SocketAddr,
123        remote: SocketAddr,
124        transport_protocol: TransportProtocol,
125        settings: ClientSettings,
126    ) -> Self {
127        Self {
128            local,
129            remote,
130            transport_protocol,
131            agent: Agent::new(),
132            settings,
133            transactions: HashMap::new(),
134            transmits: VecDeque::new(),
135        }
136    }
137
138    pub fn local_addr(&self) -> SocketAddr {
139        self.local
140    }
141
142    pub fn peer_addr(&self) -> SocketAddr {
143        self.remote
144    }
145}
146
147impl sansio::Protocol<TaggedBytesMut, Message, ()> for Client {
148    type Rout = ();
149    type Wout = TaggedBytesMut;
150    type Eout = StunEvent;
151    type Error = Error;
152    type Time = Instant;
153
154    fn handle_read(&mut self, msg: TaggedBytesMut) -> Result<()> {
155        let mut stun_msg = Message::new();
156        let mut reader = BufReader::new(&msg.message[..]);
157        stun_msg.read_from(&mut reader)?;
158        self.agent.handle_event(ClientAgent::Process(stun_msg))
159    }
160
161    fn poll_read(&mut self) -> Option<Self::Rout> {
162        None
163    }
164
165    fn handle_write(&mut self, m: Message) -> Result<()> {
166        if self.settings.closed {
167            return Err(Error::ErrClientClosed);
168        }
169
170        let payload = BytesMut::from(&m.raw[..]);
171
172        let ct = ClientTransaction {
173            id: m.transaction_id,
174            attempt: 0,
175            start: Instant::now(),
176            rto: self.settings.rto,
177            raw: m.raw,
178        };
179        let deadline = ct.next_timeout(ct.start);
180        self.transactions.entry(ct.id).or_insert(ct);
181        self.agent
182            .handle_event(ClientAgent::Start(m.transaction_id, deadline))?;
183
184        self.transmits.push_back(TransportMessage {
185            now: Instant::now(),
186            transport: TransportContext {
187                local_addr: self.local,
188                peer_addr: self.remote,
189                ecn: None,
190                transport_protocol: self.transport_protocol,
191            },
192            message: payload,
193        });
194
195        Ok(())
196    }
197
198    /// Returns packets to transmit
199    ///
200    /// It should be polled for transmit after:
201    /// - the application performed some I/O
202    /// - a call was made to `handle_read`
203    /// - a call was made to `handle_write`
204    /// - a call was made to `handle_timeout`
205    fn poll_write(&mut self) -> Option<Self::Wout> {
206        self.transmits.pop_front()
207    }
208
209    fn poll_event(&mut self) -> Option<Self::Eout> {
210        while let Some(event) = self.agent.poll_event() {
211            let mut ct = if self.transactions.contains_key(&event.id) {
212                self.transactions.remove(&event.id).unwrap()
213            } else {
214                continue;
215            };
216
217            if let StunEvent::Message(_) = &event.evt {
218                return Some(event.evt);
219            }
220            if ct.attempt >= self.settings.max_attempts {
221                return Some(event.evt);
222            }
223
224            // Doing re-transmission.
225            ct.attempt += 1;
226
227            let payload = BytesMut::from(&ct.raw[..]);
228            let timeout = ct.next_timeout(Instant::now());
229            let id = ct.id;
230
231            // Starting client transaction.
232            self.transactions.entry(ct.id).or_insert(ct);
233
234            // Starting agent transaction.
235            if self
236                .agent
237                .handle_event(ClientAgent::Start(id, timeout))
238                .is_err()
239            {
240                self.transactions.remove(&id);
241                return Some(event.evt);
242            }
243
244            // Writing message to connection again.
245            self.transmits.push_back(TransportMessage {
246                now: Instant::now(),
247                transport: TransportContext {
248                    local_addr: self.local,
249                    peer_addr: self.remote,
250                    ecn: None,
251                    transport_protocol: self.transport_protocol,
252                },
253                message: payload,
254            });
255        }
256
257        None
258    }
259
260    fn poll_timeout(&mut self) -> Option<Self::Time> {
261        self.agent.poll_timeout()
262    }
263
264    fn handle_timeout(&mut self, now: Instant) -> Result<()> {
265        self.agent.handle_event(ClientAgent::Collect(now))
266    }
267
268    fn close(&mut self) -> Result<()> {
269        if self.settings.closed {
270            return Err(Error::ErrClientClosed);
271        }
272        self.settings.closed = true;
273        self.agent.handle_event(ClientAgent::Close)
274    }
275}