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);
}
}