use std::{fmt, str, time::Instant};
use crate::sctp::RtcSctp;
use crate::util::already_happened;
use crate::{Rtc, RtcError};
pub use crate::sctp::ChannelConfig;
pub use crate::sctp::Reliability;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ChannelId(usize);
#[derive(PartialEq, Eq)]
pub struct ChannelData {
pub id: ChannelId,
pub binary: bool,
pub data: Vec<u8>,
}
pub struct Channel<'a> {
sctp_stream_id: u16,
rtc: &'a mut Rtc,
}
impl<'a> Channel<'a> {
pub(crate) fn new(sctp_stream_id: u16, rtc: &'a mut Rtc) -> Self {
Channel {
rtc,
sctp_stream_id,
}
}
pub fn write(&mut self, binary: bool, buf: &[u8]) -> Result<usize, RtcError> {
Ok(self.rtc.sctp.write(self.sctp_stream_id, binary, buf)?)
}
}
impl fmt::Debug for ChannelData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ds = f.debug_struct("ChannelData");
ds.field("id", &self.id);
ds.field("binary", &self.binary);
let len = &self.data.len();
if self.binary {
ds.field("data", len);
} else {
match str::from_utf8(&self.data) {
Ok(s) => {
const MAX_LINE_WIDTH: usize = 79;
const REST_OF_LINE_WIDTH: usize =
"ChannelData { id: ChannelId(0), binary: false, data: \"\" }".len();
const TUPLE_WIDTH: usize = "(xxx, ..)".len();
const DATA_WIDTH: usize = MAX_LINE_WIDTH - REST_OF_LINE_WIDTH;
const PREFIX_WIDTH: usize = DATA_WIDTH - TUPLE_WIDTH;
if s.is_ascii() {
if len > &DATA_WIDTH {
let trunc: String = s.chars().take(PREFIX_WIDTH).collect();
ds.field("data", &format_args!("({}, \"{}\"..)", len, trunc));
} else {
ds.field("data", &s);
}
} else {
ds.field("data", len);
}
}
Err(e) => {
ds.field("data", &format_args!("{:?}", (len, &e)));
}
}
}
ds.finish()
}
}
#[derive(Debug, Default)]
pub(crate) struct ChannelHandler {
allocations: Vec<ChannelAllocation>,
next_channel_id: usize,
}
#[derive(Debug)]
struct ChannelAllocation {
id: ChannelId,
sctp_stream_id: Option<u16>,
config: Option<ChannelConfig>,
}
impl ChannelHandler {
pub fn new_channel(&mut self, config: &ChannelConfig) -> ChannelId {
let id = self.next_channel_id();
let sctp_stream_id = config.negotiated;
if let Some(sctp_stream_id) = sctp_stream_id {
let exists = self
.allocations
.iter()
.any(|a| a.sctp_stream_id == Some(sctp_stream_id));
assert!(
!exists,
"sctp_stream_id ({}) exists already",
sctp_stream_id
);
}
let alloc = ChannelAllocation {
id,
sctp_stream_id,
config: None,
};
debug!("Allocate channel id: {:?}", id);
self.allocations.push(alloc);
id
}
pub fn confirm(&mut self, id: ChannelId, config: ChannelConfig) {
let a = self
.allocations
.iter_mut()
.find(|a| a.id == id)
.expect("Entry for issued channel id");
a.config = Some(config);
}
pub fn channel_id_by_stream_id(&self, sctp_stream_id: u16) -> Option<ChannelId> {
self.allocations
.iter()
.find(|a| a.sctp_stream_id == Some(sctp_stream_id))
.map(|a| a.id)
}
pub fn stream_id_by_channel_id(&self, id: ChannelId) -> Option<u16> {
self.allocations
.iter()
.find(|a| a.id == id)
.and_then(|a| a.sctp_stream_id)
}
pub(crate) fn handle_timeout(&mut self, _now: Instant, sctp: &mut RtcSctp) {
if !sctp.is_inited() {
return;
}
self.do_allocations(sctp);
self.open_channels(sctp);
}
fn next_channel_id(&mut self) -> ChannelId {
let id = self.next_channel_id;
self.next_channel_id += 1;
ChannelId(id)
}
fn need_allocation(&self) -> bool {
self.allocations.iter().any(|a| a.sctp_stream_id.is_none())
}
fn need_open(&self) -> bool {
self.allocations.iter().any(|a| a.config.is_some())
}
fn do_allocations(&mut self, sctp: &mut RtcSctp) {
if !self.need_allocation() {
return;
}
let base = if sctp.is_client() { 0 } else { 1 };
let mut taken: Vec<u16> = self
.allocations
.iter()
.filter_map(|a| a.sctp_stream_id)
.collect();
for a in &mut self.allocations {
if a.sctp_stream_id.is_some() {
continue;
}
let mut proposed = base;
while taken.contains(&proposed) {
proposed += 2
}
debug!("Associate stream id {:?} => {}", a.id, proposed);
a.sctp_stream_id = Some(proposed);
taken.push(proposed);
}
}
fn open_channels(&mut self, sctp: &mut RtcSctp) {
for a in &mut self.allocations {
let Some(config) = a.config.take() else {
continue;
};
let Some(sctp_stream_id) = a.sctp_stream_id else {
continue;
};
debug!("Open stream for: {:?}", a.id);
sctp.open_stream(sctp_stream_id, config);
}
}
pub fn poll_timeout(&self, sctp: &RtcSctp) -> Option<Instant> {
if sctp.is_inited() && (self.need_allocation() || self.need_open()) {
Some(already_happened())
} else {
None
}
}
pub fn ensure_channel_id_for(&mut self, sctp_stream_id: u16) {
let exists = self
.allocations
.iter()
.any(|a| a.sctp_stream_id == Some(sctp_stream_id));
if !exists {
let id = self.next_channel_id();
let alloc = ChannelAllocation {
id,
sctp_stream_id: Some(sctp_stream_id),
config: None,
};
self.allocations.push(alloc);
}
}
pub fn close_channel(&mut self, id: ChannelId, sctp: &mut RtcSctp) {
if let Some(sctp_stream_id) = self
.allocations
.iter()
.find(|a| a.id == id)
.and_then(|s| s.sctp_stream_id)
{
sctp.close_stream(sctp_stream_id);
}
}
pub fn remove_channel(&mut self, id: ChannelId) {
self.allocations.retain(|a| a.id != id)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn channel_id_allocation() {
let mut handler = ChannelHandler::default();
assert_eq!(handler.new_channel(&Default::default()), ChannelId(0));
assert_eq!(handler.new_channel(&Default::default()), ChannelId(1));
handler.remove_channel(ChannelId(0));
assert_eq!(handler.new_channel(&Default::default()), ChannelId(2));
assert_eq!(handler.new_channel(&Default::default()), ChannelId(3));
}
}