stun_agent/
events.rs

1use crate::StunPacket;
2use log::warn;
3use std::time::Duration;
4use stun_rs::{StunMessage, TransactionId};
5
6/// Stun client events
7#[derive(Debug)]
8pub enum StuntClientEvent {
9    /// Notification used by the STUN client to send a STUN packet to the server
10    OutputPacket(StunPacket),
11    /// This event sets a timeout for a transaction identified by the [`TransactionId`].
12    /// Once the timeout is reached, the client must call the [`on_timeout`](`crate::StunClient::on_timeout`) method on
13    /// the [`StunClient`](`crate::StunClient`) instance.
14    /// If multiple timeouts are scheduled for different outgoing transactions,
15    /// the client will only be notified about the most recent timeout.
16    RestransmissionTimeOut((TransactionId, Duration)),
17    /// Notification used to retry a transaction identified by the [`TransactionId`].
18    Retry(TransactionId),
19    /// Event used by the STUN client to notify that a transaction identified by the [`TransactionId`]
20    /// has failed. The [`StunTransactionError`] indicates the reason of the failure.
21    TransactionFailed((TransactionId, StunTransactionError)),
22    /// Event used by the STUN client to notify that a STUN message has been received.
23    StunMessageReceived(StunMessage),
24}
25
26/// Errors that can be raised by the STUN client when a transaction fails.
27#[derive(Debug, Eq, PartialEq)]
28pub enum StunTransactionError {
29    /// The transaction has been canceled by the client.
30    DoNotRetry,
31    /// [`Fingerprint`](stun_rs::attributes::stun::Fingerprint) validation failed.
32    InvalidFingerprint,
33    /// Outgoing transaction not found.
34    NotFound,
35    /// The transaction has been canceled due to an integrity validation failure.
36    ProtectionViolated,
37    /// The transaction has timed out.
38    TimedOut,
39}
40
41#[derive(Debug, Default)]
42pub struct TransactionEventHandler {
43    events: Vec<StuntClientEvent>,
44}
45
46impl TransactionEventHandler {
47    pub fn init(&mut self) -> TransactionEvents {
48        TransactionEvents {
49            handler: self,
50            events: Vec::new(),
51        }
52    }
53
54    pub fn events(&mut self) -> Vec<StuntClientEvent> {
55        std::mem::take(&mut self.events)
56    }
57}
58
59#[derive(Debug)]
60pub struct TransactionEvents<'a> {
61    handler: &'a mut TransactionEventHandler,
62    events: Vec<StuntClientEvent>,
63}
64
65impl TransactionEvents<'_> {
66    pub fn push(&mut self, event: StuntClientEvent) {
67        self.events.push(event);
68    }
69}
70
71impl Drop for TransactionEvents<'_> {
72    fn drop(&mut self) {
73        if self.events.is_empty() {
74            // No events to commit
75            return;
76        }
77
78        std::mem::swap(&mut self.handler.events, &mut self.events);
79
80        if !self.events.is_empty() {
81            warn!(
82                "Transaction events were committed without consuming {} the events.",
83                self.events.len()
84            );
85        }
86    }
87}
88
89#[cfg(test)]
90mod stun_event_tests {
91    use stun_rs::TransactionId;
92
93    use super::*;
94
95    fn init_logging() {
96        let _ = env_logger::builder().is_test(true).try_init();
97    }
98
99    #[test]
100    fn test_transaction_events_drop() {
101        init_logging();
102
103        let mut handler = TransactionEventHandler::default();
104
105        let events = handler.events();
106        assert!(events.is_empty());
107
108        {
109            let mut events = handler.init();
110
111            events.push(StuntClientEvent::Retry(TransactionId::default()));
112            events.push(StuntClientEvent::Retry(TransactionId::default()));
113        }
114
115        // Drop the events must commit the events
116        let events = handler.events();
117        assert_eq!(events.len(), 2);
118
119        // No more events to consum
120        let events = handler.events();
121        assert_eq!(events.len(), 0);
122    }
123
124    #[test]
125    fn test_transaction_events_drop_no_events() {
126        init_logging();
127
128        let mut handler = TransactionEventHandler::default();
129        {
130            let mut _events = handler.init();
131        }
132
133        let events = handler.events();
134        assert!(events.is_empty());
135    }
136
137    #[test]
138    fn test_transaction_events_drop_no_commit() {
139        init_logging();
140
141        let mut handler = TransactionEventHandler::default();
142        {
143            let mut events = handler.init();
144            events.push(StuntClientEvent::Retry(TransactionId::default()));
145        }
146
147        {
148            // Init another transaction when there must be one event
149            // that was not consumed previously, that event will be dropped
150            let mut events = handler.init();
151            events.push(StuntClientEvent::Retry(TransactionId::default()));
152            events.push(StuntClientEvent::Retry(TransactionId::default()));
153            events.push(StuntClientEvent::Retry(TransactionId::default()));
154        }
155
156        // There must be only three events
157        let events = handler.events();
158        assert_eq!(events.len(), 3);
159    }
160}