#[cfg(test)]
mod data_channel_test;
use crate::error::Result;
use crate::{
error::Error, message::message_channel_ack::*, message::message_channel_open::*, message::*,
};
use sctp::{
association::Association, chunk::chunk_payload_data::PayloadProtocolIdentifier, stream::*,
};
use util::marshal::*;
use bytes::{Buf, Bytes};
use derive_builder::Builder;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
const RECEIVE_MTU: usize = 8192;
#[derive(Eq, PartialEq, Default, Clone, Debug, Builder)]
pub struct Config {
#[builder(default)]
pub channel_type: ChannelType,
#[builder(default)]
pub negotiated: bool,
#[builder(default)]
pub priority: u16,
#[builder(default)]
pub reliability_parameter: u32,
#[builder(default)]
pub label: String,
#[builder(default)]
pub protocol: String,
}
#[derive(Debug, Default, Clone)]
pub struct DataChannel {
pub config: Config,
stream: Arc<Stream>,
messages_sent: Arc<AtomicUsize>,
messages_received: Arc<AtomicUsize>,
bytes_sent: Arc<AtomicUsize>,
bytes_received: Arc<AtomicUsize>,
}
impl DataChannel {
pub fn new(stream: Arc<Stream>, config: Config) -> Self {
Self {
config,
stream,
..Default::default()
}
}
pub async fn dial(
association: &Arc<Association>,
identifier: u16,
config: Config,
) -> Result<Self> {
let stream = association
.open_stream(identifier, PayloadProtocolIdentifier::Binary)
.await?;
Self::client(stream, config).await
}
pub async fn accept(association: &Arc<Association>, config: Config) -> Result<Self> {
let stream = association
.accept_stream()
.await
.ok_or(Error::ErrStreamClosed)?;
stream.set_default_payload_type(PayloadProtocolIdentifier::Binary);
Self::server(stream, config).await
}
pub async fn client(stream: Arc<Stream>, config: Config) -> Result<Self> {
if !config.negotiated {
let msg = Message::DataChannelOpen(DataChannelOpen {
channel_type: config.channel_type,
priority: config.priority,
reliability_parameter: config.reliability_parameter,
label: config.label.bytes().collect(),
protocol: config.protocol.bytes().collect(),
})
.marshal()?;
stream
.write_sctp(&msg, PayloadProtocolIdentifier::Dcep)
.await?;
}
Ok(DataChannel::new(stream, config))
}
pub async fn server(stream: Arc<Stream>, mut config: Config) -> Result<Self> {
let mut buf = vec![0u8; RECEIVE_MTU];
let (n, ppi) = stream.read_sctp(&mut buf).await?;
if ppi != PayloadProtocolIdentifier::Dcep {
return Err(Error::InvalidPayloadProtocolIdentifier(ppi as u8));
}
let mut read_buf = &buf[..n];
let msg = Message::unmarshal(&mut read_buf)?;
if let Message::DataChannelOpen(dco) = msg {
config.channel_type = dco.channel_type;
config.priority = dco.priority;
config.reliability_parameter = dco.reliability_parameter;
config.label = String::from_utf8(dco.label)?;
config.protocol = String::from_utf8(dco.protocol)?;
} else {
return Err(Error::InvalidMessageType(msg.message_type() as u8));
};
let data_channel = DataChannel::new(stream, config);
data_channel.write_data_channel_ack().await?;
data_channel.commit_reliability_params();
Ok(data_channel)
}
pub async fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.read_data_channel(buf).await.map(|(n, _)| n)
}
pub async fn read_data_channel(&self, buf: &mut [u8]) -> Result<(usize, bool)> {
loop {
let (mut n, ppi) = match self.stream.read_sctp(buf).await {
Ok((n, ppi)) => (n, ppi),
Err(err) => {
self.stream.close().await?;
return Err(err.into());
}
};
let mut is_string = false;
match ppi {
PayloadProtocolIdentifier::Dcep => {
let mut data = &buf[..n];
match self.handle_dcep(&mut data).await {
Ok(()) => {}
Err(err) => {
log::error!("Failed to handle DCEP: {:?}", err);
}
}
continue;
}
PayloadProtocolIdentifier::String | PayloadProtocolIdentifier::StringEmpty => {
is_string = true;
}
_ => {}
};
match ppi {
PayloadProtocolIdentifier::StringEmpty | PayloadProtocolIdentifier::BinaryEmpty => {
n = 0;
}
_ => {}
};
self.messages_received.fetch_add(1, Ordering::SeqCst);
self.bytes_received.fetch_add(n, Ordering::SeqCst);
return Ok((n, is_string));
}
}
pub fn messages_sent(&self) -> usize {
self.messages_sent.load(Ordering::SeqCst)
}
pub fn messages_received(&self) -> usize {
self.messages_received.load(Ordering::SeqCst)
}
pub fn bytes_sent(&self) -> usize {
self.bytes_sent.load(Ordering::SeqCst)
}
pub fn bytes_received(&self) -> usize {
self.bytes_received.load(Ordering::SeqCst)
}
pub fn stream_identifier(&self) -> u16 {
self.stream.stream_identifier()
}
async fn handle_dcep<B>(&self, data: &mut B) -> Result<()>
where
B: Buf,
{
let msg = Message::unmarshal(data)?;
match msg {
Message::DataChannelOpen(_) => {
log::debug!("Received DATA_CHANNEL_OPEN");
let _ = self.write_data_channel_ack().await?;
}
Message::DataChannelAck(_) => {
log::debug!("Received DATA_CHANNEL_ACK");
self.commit_reliability_params();
}
};
Ok(())
}
pub async fn write(&self, data: &Bytes) -> Result<usize> {
self.write_data_channel(data, false).await
}
pub async fn write_data_channel(&self, data: &Bytes, is_string: bool) -> Result<usize> {
let data_len = data.len();
let ppi = match (is_string, data_len) {
(false, 0) => PayloadProtocolIdentifier::BinaryEmpty,
(false, _) => PayloadProtocolIdentifier::Binary,
(true, 0) => PayloadProtocolIdentifier::StringEmpty,
(true, _) => PayloadProtocolIdentifier::String,
};
self.messages_sent.fetch_add(1, Ordering::SeqCst);
self.bytes_sent.fetch_add(data_len, Ordering::SeqCst);
if data_len == 0 {
let _ = self
.stream
.write_sctp(&Bytes::from_static(&[0]), ppi)
.await?;
Ok(0)
} else {
Ok(self.stream.write_sctp(data, ppi).await?)
}
}
async fn write_data_channel_ack(&self) -> Result<usize> {
let ack = Message::DataChannelAck(DataChannelAck {}).marshal()?;
Ok(self
.stream
.write_sctp(&ack, PayloadProtocolIdentifier::Dcep)
.await?)
}
pub async fn close(&self) -> Result<()> {
Ok(self.stream.close().await?)
}
pub fn buffered_amount(&self) -> usize {
self.stream.buffered_amount()
}
pub fn buffered_amount_low_threshold(&self) -> usize {
self.stream.buffered_amount_low_threshold()
}
pub fn set_buffered_amount_low_threshold(&self, threshold: usize) {
self.stream.set_buffered_amount_low_threshold(threshold)
}
pub async fn on_buffered_amount_low(&self, f: OnBufferedAmountLowFn) {
self.stream.on_buffered_amount_low(f).await
}
fn commit_reliability_params(&self) {
let (unordered, reliability_type) = match self.config.channel_type {
ChannelType::Reliable => (false, ReliabilityType::Reliable),
ChannelType::ReliableUnordered => (true, ReliabilityType::Reliable),
ChannelType::PartialReliableRexmit => (false, ReliabilityType::Rexmit),
ChannelType::PartialReliableRexmitUnordered => (true, ReliabilityType::Rexmit),
ChannelType::PartialReliableTimed => (false, ReliabilityType::Timed),
ChannelType::PartialReliableTimedUnordered => (true, ReliabilityType::Timed),
};
self.stream.set_reliability_params(
unordered,
reliability_type,
self.config.reliability_parameter,
);
}
}