File
Blob: firmware/crates/radio-webrtc/src/network/tests.rs
| 1 | //! Network drain failures after a real local ICE/DTLS/SCTP handshake. |
| 2 | use super::*; |
| 3 | use crate::{Certificate, PeerState}; |
| 4 | use std::{ |
| 5 | net::Ipv4Addr, |
| 6 | sync::Arc, |
| 7 | thread, |
| 8 | time::{Duration, Instant}, |
| 9 | }; |
| 10 | use str0m::{Candidate, Rtc, change::SdpOffer, crypto::dtls::DtlsCert}; |
| 11 | |
| 12 | const CERT: &[u8] = include_bytes!("../../tests/fixtures/certificate.der"); |
| 13 | const KEY: &[u8] = include_bytes!("../../tests/fixtures/key.der"); |
| 14 | |
| 15 | struct Remote { |
| 16 | rtc: Rtc, |
| 17 | socket: UdpSocket, |
| 18 | } |
| 19 | |
| 20 | impl 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 | |
| 90 | fn 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] |
| 111 | fn 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 | } |