Skip to content
File

Blob: firmware/crates/radio-webrtc/src/network/tests.rs

rust130 lines
1//! Network drain failures after a real local ICE/DTLS/SCTP handshake.
2use super::*;
3use crate::{Certificate, PeerState};
4use std::{
5 net::Ipv4Addr,
6 sync::Arc,
7 thread,
8 time::{Duration, Instant},
9};
10use str0m::{Candidate, Rtc, change::SdpOffer, crypto::dtls::DtlsCert};
11 
12const CERT: &[u8] = include_bytes!("../../tests/fixtures/certificate.der");
13const KEY: &[u8] = include_bytes!("../../tests/fixtures/key.der");
14 
15struct Remote {
16 rtc: Rtc,
17 socket: UdpSocket,
18}
19 
20impl Remote {
21 fn answer(offer: &str) -> (Self, String) {
22 let socket = UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
23 socket.set_nonblocking(true).unwrap();
24 let mut rtc = Rtc::builder()
25 .set_crypto_provider(Arc::new(str0m_rust_crypto::default_provider()))
26 .set_dtls_cert(DtlsCert {
27 certificate: CERT.to_vec(),
28 private_key: KEY.to_vec(),
29 })
30 .clear_codecs()
31 .enable_opus(true)
32 .build(Instant::now());
33 rtc.add_local_candidate(Candidate::host(socket.local_addr().unwrap(), "udp").unwrap());
34 let mut remote = Self { rtc, socket };
35 remote.drain();
36 let answer = remote
37 .rtc
38 .sdp_api()
39 .accept_offer(SdpOffer::from_sdp_string(offer).unwrap())
40 .unwrap();
41 remote.drain();
42 remote
43 .rtc
44 .direct_api()
45 .create_data_channel(crate::channels::config("server-events", 0, true));
46 remote.drain();
47 (remote, answer.to_sdp_string())
48 }
49 
50 fn drain(&mut self) {
51 loop {
52 match self.rtc.poll_output().unwrap() {
53 Output::Timeout(_) => return,
54 Output::Transmit(packet) => {
55 self.socket
56 .send_to(&packet.contents, packet.destination)
57 .unwrap();
58 }
59 Output::Event(_) => {}
60 }
61 }
62 }
63 
64 fn poll(&mut self) {
65 let mut buffer = [0; 2048];
66 loop {
67 let (length, source) = match self.socket.recv_from(&mut buffer) {
68 Ok(packet) => packet,
69 Err(error) if error.kind() == ErrorKind::WouldBlock => break,
70 Err(error) => panic!("remote UDP receive failed: {error}"),
71 };
72 let receive = Receive {
73 proto: Protocol::Udp,
74 source,
75 destination: self.socket.local_addr().unwrap(),
76 contents: buffer[..length].try_into().unwrap(),
77 };
78 self.rtc
79 .handle_input(Input::Receive(Instant::now(), receive))
80 .unwrap();
81 self.drain();
82 }
83 self.rtc
84 .handle_input(Input::Timeout(Instant::now()))
85 .unwrap();
86 self.drain();
87 }
88}
89 
90fn connected_peer() -> (Peer, Remote) {
91 let certificate = Certificate::from_der(CERT.to_vec(), KEY.to_vec()).unwrap();
92 let (mut peer, offer) = Peer::open(
93 Ipv4Addr::LOCALHOST,
94 certificate,
95 Arc::new(str0m_rust_crypto::default_provider()),
96 )
97 .unwrap();
98 let (mut remote, answer) = Remote::answer(&offer);
99 peer.answer(&answer).unwrap();
100 let deadline = Instant::now() + Duration::from_secs(10);
101 while peer.state() != PeerState::Connected {
102 assert!(Instant::now() < deadline, "SCTP connection timed out");
103 peer.poll().unwrap();
104 remote.poll();
105 thread::sleep(Duration::from_millis(1));
106 }
107 (peer, remote)
108}
109 
110#[test]
111fn hard_udp_send_failure_is_returned_after_queued_protocol_output_is_drained() {
112 let (mut peer, _remote) = connected_peer();
113 peer.rtc.close().unwrap();
114 
115 let mut attempts = 0;
116 let error = peer
117 .drain_with(|_, packet| {
118 attempts += 1;
119 assert!(!packet.contents.is_empty());
120 Err(io::Error::new(
121 ErrorKind::NetworkUnreachable,
122 "injected UDP failure",
123 ))
124 })
125 .unwrap_err();
126 
127 assert!(attempts > 0, "close emitted no network packets");
128 assert_eq!(error.to_string(), "UDP send failed");
129}