stun_agent/
events.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::StunPacket;
use log::warn;
use std::time::Duration;
use stun_rs::{StunMessage, TransactionId};

/// Stun client events
#[derive(Debug)]
pub enum StuntClientEvent {
    /// Notification used by the STUN client to send a STUN packet to the server
    OutputPacket(StunPacket),
    /// This event sets a timeout for a transaction identified by the [`TransactionId`].
    /// Once the timeout is reached, the client must call the [`on_timeout`](`crate::StunClient::on_timeout`) method on
    /// the [`StunClient`](`crate::StunClient`) instance.
    /// If multiple timeouts are scheduled for different outgoing transactions,
    /// the client will only be notified about the most recent timeout.
    RestransmissionTimeOut((TransactionId, Duration)),
    /// Notification used to retry a transaction identified by the [`TransactionId`].
    Retry(TransactionId),
    /// Event used by the STUN client to notify that a transaction identified by the [`TransactionId`]
    /// has failed. The [`StunTransactionError`] indicates the reason of the failure.
    TransactionFailed((TransactionId, StunTransactionError)),
    /// Event used by the STUN client to notify that a STUN message has been received.
    StunMessageReceived(StunMessage),
}

/// Errors that can be raised by the STUN client when a transaction fails.
#[derive(Debug, Eq, PartialEq)]
pub enum StunTransactionError {
    /// The transaction has been canceled by the client.
    DoNotRetry,
    /// [`Fingerprint`](stun_rs::attributes::stun::Fingerprint) validation failed.
    InvalidFingerprint,
    /// Outgoing transaction not found.
    NotFound,
    /// The transaction has been canceled due to an integrity validation failure.
    ProtectionViolated,
    /// The transaction has timed out.
    TimedOut,
}

#[derive(Debug, Default)]
pub struct TransactionEventHandler {
    events: Vec<StuntClientEvent>,
}

impl TransactionEventHandler {
    pub fn init(&mut self) -> TransactionEvents {
        TransactionEvents {
            handler: self,
            events: Vec::new(),
        }
    }

    pub fn events(&mut self) -> Vec<StuntClientEvent> {
        std::mem::take(&mut self.events)
    }
}

#[derive(Debug)]
pub struct TransactionEvents<'a> {
    handler: &'a mut TransactionEventHandler,
    events: Vec<StuntClientEvent>,
}

impl TransactionEvents<'_> {
    pub fn push(&mut self, event: StuntClientEvent) {
        self.events.push(event);
    }
}

impl Drop for TransactionEvents<'_> {
    fn drop(&mut self) {
        if self.events.is_empty() {
            // No events to commit
            return;
        }

        std::mem::swap(&mut self.handler.events, &mut self.events);

        if !self.events.is_empty() {
            warn!(
                "Transaction events were committed without consuming {} the events.",
                self.events.len()
            );
        }
    }
}

#[cfg(test)]
mod stun_event_tests {
    use stun_rs::TransactionId;

    use super::*;

    fn init_logging() {
        let _ = env_logger::builder().is_test(true).try_init();
    }

    #[test]
    fn test_transaction_events_drop() {
        init_logging();

        let mut handler = TransactionEventHandler::default();

        let events = handler.events();
        assert!(events.is_empty());

        {
            let mut events = handler.init();

            events.push(StuntClientEvent::Retry(TransactionId::default()));
            events.push(StuntClientEvent::Retry(TransactionId::default()));
        }

        // Drop the events must commit the events
        let events = handler.events();
        assert_eq!(events.len(), 2);

        // No more events to consum
        let events = handler.events();
        assert_eq!(events.len(), 0);
    }

    #[test]
    fn test_transaction_events_drop_no_events() {
        init_logging();

        let mut handler = TransactionEventHandler::default();
        {
            let mut _events = handler.init();
        }

        let events = handler.events();
        assert!(events.is_empty());
    }

    #[test]
    fn test_transaction_events_drop_no_commit() {
        init_logging();

        let mut handler = TransactionEventHandler::default();
        {
            let mut events = handler.init();
            events.push(StuntClientEvent::Retry(TransactionId::default()));
        }

        {
            // Init another transaction when there must be one event
            // that was not consumed previously, that event will be dropped
            let mut events = handler.init();
            events.push(StuntClientEvent::Retry(TransactionId::default()));
            events.push(StuntClientEvent::Retry(TransactionId::default()));
            events.push(StuntClientEvent::Retry(TransactionId::default()));
        }

        // There must be only three events
        let events = handler.events();
        assert_eq!(events.len(), 3);
    }
}