rport 0.2.51

A p2p port forwarding client using WebRTC datachannels
use anyhow::Result;
use rustrtc::{IceServer, PeerConnection, RtcConfiguration};
use std::{sync::Arc, time::Duration};

use crate::config::IceServerConfig;

use crate::config::RportConfig;

#[derive(Clone)]
pub struct WebRTCConfig {
    pub ice_servers: Vec<IceServerConfig>,
    pub enable_upnp: bool,
    pub ice_disconnect_threshold: Option<Duration>,
    pub ice_disconnect_grace: Option<Duration>,
    pub ice_connection_timeout: Option<Duration>,
    pub sctp_rto_initial: Option<Duration>,
    pub sctp_rto_min: Option<Duration>,
    pub sctp_rto_max: Option<Duration>,
    pub sctp_max_association_retransmits: Option<u32>,
    pub sctp_receive_window: Option<usize>,
}

impl WebRTCConfig {
    pub fn new(
        _server: String,
        _token: String,
        ice_servers: Vec<IceServerConfig>,
        enable_upnp: bool,
        cfg: &RportConfig,
    ) -> Self {
        Self {
            ice_servers,
            enable_upnp,
            ice_disconnect_threshold: cfg.ice_disconnect_threshold.map(Duration::from_secs),
            ice_disconnect_grace: cfg.ice_disconnect_grace.map(Duration::from_secs),
            ice_connection_timeout: cfg.ice_connection_timeout.map(Duration::from_secs),
            sctp_rto_initial: cfg.sctp_rto_initial_ms.map(Duration::from_millis),
            sctp_rto_min: cfg.sctp_rto_min_ms.map(Duration::from_millis),
            sctp_rto_max: cfg.sctp_rto_max_sec.map(Duration::from_secs),
            sctp_max_association_retransmits: cfg.sctp_max_association_retransmits,
            sctp_receive_window: cfg.sctp_receive_window_kb.map(|kb| kb * 1024),
        }
    }

    fn build_rtc_config(&self, extra_ice_servers: Vec<IceServer>) -> RtcConfiguration {
        let mut ice_servers = self.get_ice_servers();
        ice_servers.extend(extra_ice_servers);
        RtcConfiguration {
            ice_servers,
            sctp_rto_initial: self.sctp_rto_initial.unwrap_or_else(|| Duration::from_millis(400)),
            sctp_rto_min: self.sctp_rto_min.unwrap_or_else(|| Duration::from_millis(200)),
            sctp_rto_max: self.sctp_rto_max.unwrap_or_else(|| Duration::from_secs(30)),
            sctp_max_association_retransmits: self.sctp_max_association_retransmits.unwrap_or(20),
            sctp_receive_window: self.sctp_receive_window.unwrap_or(2 * 1024 * 1024),
            ice_connection_timeout: self.ice_connection_timeout.unwrap_or_else(|| Duration::from_secs(300)),
            ice_disconnect_threshold: self.ice_disconnect_threshold.unwrap_or_else(|| Duration::from_secs(30)),
            ice_disconnect_grace: self.ice_disconnect_grace.unwrap_or_else(|| Duration::from_secs(15)),
            enable_upnp: self.enable_upnp,
            prefer_srflx_over_natted_host: true,
            ..Default::default()
        }
    }

    pub fn get_ice_servers(&self) -> Vec<IceServer> {
        if !self.ice_servers.is_empty() {
            return self.ice_servers.clone().into_iter().map(|c| c.into()).collect();
        }
        vec![IceServerConfig::default().into()]
    }

    pub async fn create_peer_connection(&self) -> Result<Arc<PeerConnection>> {
        self.create_peer_connection_with([].as_slice()).await
    }

    pub async fn create_peer_connection_with(&self, extra_ice_servers: &[IceServerConfig]) -> Result<Arc<PeerConnection>> {
        let extra = extra_ice_servers.iter().map(|c| IceServer::from(c.clone())).collect();
        let config = self.build_rtc_config(extra);
        let peer_connection = Arc::new(PeerConnection::new(config));
        Ok(peer_connection)
    }
}