use crate::data_channel::RTCDataChannelId;
use crate::data_channel::internal::RTCDataChannelInternal;
use crate::data_channel::message::RTCDataChannelMessage;
use crate::data_channel::state::RTCDataChannelState;
use crate::peer_connection::event::data_channel_event::RTCDataChannelEvent;
use crate::peer_connection::event::{
RTCEventInternal, RTCPeerConnectionEvent, TaggedRTCEventInternal,
};
use crate::peer_connection::message::internal::{
ApplicationMessage, DTLSMessage, DataChannelEvent, RTCMessageInternal, TaggedRTCMessageInternal,
};
use crate::statistics::accumulator::RTCStatsAccumulator;
use log::{debug, warn};
use sctp::PayloadProtocolIdentifier;
use shared::TransportContext;
use shared::error::{Error, Result};
use std::collections::{HashMap, VecDeque};
use std::time::{Duration, Instant};
pub(crate) struct DataChannelHandlerContext {
pub(crate) read_outs: VecDeque<TaggedRTCMessageInternal>,
pub(crate) write_outs: VecDeque<TaggedRTCMessageInternal>,
pub(crate) event_outs: VecDeque<TaggedRTCEventInternal>,
now: Instant,
}
impl DataChannelHandlerContext {
pub(crate) fn new(now: Instant) -> Self {
Self {
read_outs: VecDeque::new(),
write_outs: VecDeque::new(),
event_outs: VecDeque::new(),
now,
}
}
fn observe(&mut self, now: Instant) {
self.now = now.max(self.now);
}
}
pub(crate) struct DataChannelHandler<'a> {
ctx: &'a mut DataChannelHandlerContext,
data_channels: &'a mut HashMap<RTCDataChannelId, RTCDataChannelInternal>,
stats: &'a mut RTCStatsAccumulator,
dcep_handshake_timeout: Option<Duration>,
}
impl<'a> DataChannelHandler<'a> {
pub(crate) fn new(
ctx: &'a mut DataChannelHandlerContext,
data_channels: &'a mut HashMap<RTCDataChannelId, RTCDataChannelInternal>,
stats: &'a mut RTCStatsAccumulator,
dcep_handshake_timeout: Option<Duration>,
) -> Self {
DataChannelHandler {
ctx,
data_channels,
stats,
dcep_handshake_timeout,
}
}
pub(crate) fn name(&self) -> &'static str {
"DataChannelHandler"
}
fn emit_data_channel_opened(
&mut self,
now: Instant,
transport: TransportContext,
id: RTCDataChannelId,
) -> Result<()> {
let dc = self
.data_channels
.get(&id)
.ok_or(Error::ErrDataChannelNotExisted)?;
self.ctx.read_outs.push_back(TaggedRTCMessageInternal {
now,
transport,
message: RTCMessageInternal::Dtls(DTLSMessage::DataChannel(ApplicationMessage {
data_channel_id: id,
data_channel_event: DataChannelEvent::Open,
})),
});
self.stats.peer_connection.on_data_channel_opened();
self.stats
.get_or_create_data_channel(id, &dc.label, &dc.protocol)
.on_state_changed(RTCDataChannelState::Open);
Ok(())
}
}
impl<'a>
sansio::Protocol<TaggedRTCMessageInternal, TaggedRTCMessageInternal, TaggedRTCEventInternal>
for DataChannelHandler<'a>
{
type Rout = TaggedRTCMessageInternal;
type Wout = TaggedRTCMessageInternal;
type Eout = TaggedRTCEventInternal;
type Error = Error;
type Time = Instant;
fn handle_read(&mut self, msg: TaggedRTCMessageInternal) -> Result<()> {
let now = msg.now;
self.ctx.observe(now);
if let RTCMessageInternal::Dtls(DTLSMessage::Sctp(message)) = msg.message {
debug!(
"recv SCTP DataChannelMessage from {:?}",
msg.transport.peer_addr
);
let stream_id = message.stream_id;
let transport = msg.transport;
let opened = if let Some(data_channel_internal) = self.data_channels.get_mut(&stream_id)
{
if data_channel_internal.ready_state == RTCDataChannelState::Closed {
return Ok(());
}
let mut opened = false;
let data_channel = data_channel_internal
.data_channel
.as_mut()
.ok_or(Error::ErrDataChannelNotExisted)?;
data_channel.handle_read(message)?;
if data_channel.is_handshake_complete()
&& data_channel_internal.ready_state == RTCDataChannelState::Connecting
{
data_channel_internal.ready_state = RTCDataChannelState::Open;
data_channel_internal.handshake_deadline = None;
opened = true;
}
opened
} else {
let data_channel_internal = RTCDataChannelInternal::accept(
message.association_handle,
message.stream_id,
message.ppi,
&message.payload,
)?;
self.data_channels
.insert(message.stream_id, data_channel_internal);
true
};
if opened {
self.emit_data_channel_opened(now, transport, stream_id)?;
}
let (label, protocol) = {
let dc = self
.data_channels
.get(&stream_id)
.ok_or(Error::ErrDataChannelNotExisted)?;
(dc.label.clone(), dc.protocol.clone())
};
let is_open = self
.data_channels
.get(&stream_id)
.is_some_and(|dc| dc.ready_state == RTCDataChannelState::Open);
let data_channel = self
.data_channels
.get_mut(&stream_id)
.ok_or(Error::ErrDataChannelNotExisted)?
.data_channel
.as_mut()
.ok_or(Error::ErrDataChannelNotExisted)?;
if is_open {
while let Some(data_channel_message) = data_channel.poll_read() {
let payload_len = data_channel_message.payload.len();
debug!("recv application message {:?}", msg.transport.peer_addr);
self.stats
.get_or_create_data_channel(stream_id, &label, &protocol)
.on_message_received(payload_len);
let message_data = if matches!(
data_channel_message.ppi,
PayloadProtocolIdentifier::StringEmpty
| PayloadProtocolIdentifier::BinaryEmpty
) {
Default::default()
} else {
data_channel_message.payload
};
self.ctx.read_outs.push_back(TaggedRTCMessageInternal {
now: msg.now,
transport: msg.transport,
message: RTCMessageInternal::Dtls(DTLSMessage::DataChannel(
ApplicationMessage {
data_channel_id: stream_id,
data_channel_event: DataChannelEvent::Message(
RTCDataChannelMessage {
is_string: matches!(
data_channel_message.ppi,
PayloadProtocolIdentifier::String
| PayloadProtocolIdentifier::StringEmpty
),
data: message_data,
},
),
},
)),
});
}
}
while let Some(data_channel_message) = data_channel.poll_write() {
debug!("send data channel message from handle_read");
self.ctx.write_outs.push_back(TaggedRTCMessageInternal {
now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(data_channel_message)),
});
}
} else {
debug!("bypass DataChannel read {:?}", msg.transport.peer_addr);
self.ctx.read_outs.push_back(msg);
}
Ok(())
}
fn poll_read(&mut self) -> Option<Self::Rout> {
self.ctx.read_outs.pop_front()
}
fn handle_write(&mut self, msg: TaggedRTCMessageInternal) -> Result<()> {
let now = msg.now;
self.ctx.observe(now);
if let RTCMessageInternal::Dtls(DTLSMessage::DataChannel(message)) = msg.message {
debug!("send application message {:?}", msg.transport.peer_addr);
if let DataChannelEvent::Message(RTCDataChannelMessage { is_string, data }) =
message.data_channel_event
{
let data_len = data.len();
let channel_id = message.data_channel_id;
let dc_internal = self
.data_channels
.get(&channel_id)
.ok_or(Error::ErrDataChannelNotExisted)?;
let label = dc_internal.label.clone();
let protocol = dc_internal.protocol.clone();
let data_channel = self
.data_channels
.get_mut(&channel_id)
.ok_or(Error::ErrDataChannelNotExisted)?
.data_channel
.as_mut()
.ok_or(Error::ErrDataChannelNotExisted)?;
let data_channel_message =
::datachannel::data_channel::DataChannel::get_data_channel_message(
is_string, data,
);
data_channel.handle_write(data_channel_message)?;
self.stats
.get_or_create_data_channel(channel_id, &label, &protocol)
.on_message_sent(data_len);
while let Some(data_channel_message) = data_channel.poll_write() {
debug!("send data channel message from handle_write");
self.ctx.write_outs.push_back(TaggedRTCMessageInternal {
now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(data_channel_message)),
});
}
} else {
warn!(
"drop unsupported DATACHANNEL message to {}",
msg.transport.peer_addr
);
}
} else {
debug!("bypass DataChannel write {:?}", msg.transport.peer_addr);
self.ctx.write_outs.push_back(msg);
}
Ok(())
}
fn poll_write(&mut self) -> Option<Self::Wout> {
for data_channel_internal in self.data_channels.values_mut() {
if let Some(data_channel) = data_channel_internal.data_channel.as_mut() {
while let Some(data_channel_message) = data_channel.poll_write() {
debug!("send data channel message from poll_write");
self.ctx.write_outs.push_back(TaggedRTCMessageInternal {
now: self.ctx.now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(data_channel_message)),
});
}
}
}
self.ctx.write_outs.pop_front()
}
fn handle_event(&mut self, evt: TaggedRTCEventInternal) -> Result<()> {
let now = evt.now;
match evt.event {
RTCEventInternal::SCTPHandshakeComplete(association_handle) => {
let mut opened = Vec::new();
for data_channel_internal in self.data_channels.values_mut() {
if data_channel_internal.ready_state == RTCDataChannelState::Connecting
&& data_channel_internal.data_channel.is_none()
{
data_channel_internal.dial(association_handle)?;
if data_channel_internal.negotiated {
opened.push(data_channel_internal.id);
} else {
data_channel_internal.handshake_deadline =
self.dcep_handshake_timeout.map(|timeout| now + timeout);
}
let data_channel = data_channel_internal
.data_channel
.as_mut()
.ok_or(Error::ErrDataChannelNotExisted)?;
while let Some(data_channel_message) = data_channel.poll_write() {
debug!("send data channel message from handle_event");
self.ctx.write_outs.push_back(TaggedRTCMessageInternal {
now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(
data_channel_message,
)),
});
}
}
}
for id in opened {
self.emit_data_channel_opened(now, TransportContext::default(), id)?;
}
}
RTCEventInternal::SCTPStreamClosed(_association_handle, stream_id) => {
if let Some(dc) = self.data_channels.remove(&stream_id) {
if !dc.close_emitted {
self.stats.peer_connection.on_data_channel_closed();
if let Some(dc_stats) = self.stats.data_channels.get_mut(&stream_id) {
dc_stats.on_state_changed(RTCDataChannelState::Closed);
}
self.ctx.event_outs.push_back(TaggedRTCEventInternal {
now,
event: RTCEventInternal::RTCPeerConnectionEvent(
RTCPeerConnectionEvent::OnDataChannel(
RTCDataChannelEvent::OnClose(stream_id),
),
),
});
}
}
}
RTCEventInternal::SCTPBufferReleased(_association_handle, stream_id, n_bytes) => {
if let Some(dc) = self.data_channels.get_mut(&stream_id) {
dc.outstanding_bytes = dc.outstanding_bytes.saturating_sub(n_bytes);
}
}
event => {
self.ctx
.event_outs
.push_back(TaggedRTCEventInternal { now, event });
}
}
Ok(())
}
fn poll_event(&mut self) -> Option<Self::Eout> {
self.ctx.event_outs.pop_front()
}
fn handle_timeout(&mut self, now: Instant) -> Result<()> {
self.ctx.observe(now);
let mut timed_out = Vec::new();
for dc in self.data_channels.values() {
if let Some(deadline) = dc.handshake_deadline
&& dc.ready_state == RTCDataChannelState::Connecting
&& deadline <= now
{
timed_out.push(dc.id);
}
}
for id in timed_out {
if let Some(dc) = self.data_channels.get_mut(&id) {
dc.handshake_deadline = None;
dc.ready_state = RTCDataChannelState::Closed;
if let Some(data_channel) = dc.data_channel.as_mut() {
data_channel.close()?;
}
self.stats.peer_connection.on_data_channel_closed();
self.stats
.get_or_create_data_channel(id, &dc.label, &dc.protocol)
.on_state_changed(RTCDataChannelState::Closed);
dc.close_emitted = true;
self.ctx.event_outs.push_back(TaggedRTCEventInternal {
now,
event: RTCEventInternal::RTCPeerConnectionEvent(
RTCPeerConnectionEvent::OnDataChannel(RTCDataChannelEvent::OnClose(id)),
),
});
}
}
Ok(())
}
fn poll_timeout(&mut self) -> Option<Instant> {
self.data_channels
.values()
.filter_map(|dc| {
if dc.ready_state == RTCDataChannelState::Connecting {
dc.handshake_deadline
} else {
None
}
})
.min()
}
fn close(&mut self) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data_channel::parameters::DataChannelParameters;
use crate::statistics::accumulator::RTCStatsAccumulator;
use bytes::BytesMut;
use datachannel::data_channel::DataChannelMessage;
use datachannel::message::Message;
use datachannel::message::message_channel_ack::DataChannelAck;
use sansio::Protocol;
use shared::marshal::Marshal;
fn in_band_channel(id: u16) -> RTCDataChannelInternal {
RTCDataChannelInternal::new(
id,
DataChannelParameters {
label: "timing-test".to_string(),
protocol: String::new(),
ordered: true,
max_packet_life_time: None,
max_retransmits: None,
negotiated: None,
},
)
}
fn negotiated_channel(id: u16) -> RTCDataChannelInternal {
RTCDataChannelInternal::new(
id,
DataChannelParameters {
label: "timing-test".to_string(),
protocol: String::new(),
ordered: true,
max_packet_life_time: None,
max_retransmits: None,
negotiated: Some(id),
},
)
}
fn ack(association_handle: usize, stream_id: u16) -> DataChannelMessage {
let ack = Message::DataChannelAck(DataChannelAck {})
.marshal()
.unwrap();
DataChannelMessage {
association_handle,
stream_id,
ppi: PayloadProtocolIdentifier::Dcep,
payload: BytesMut::from(&ack[..]),
negotiated: false,
}
}
fn data_message(association_handle: usize, stream_id: u16, data: &[u8]) -> DataChannelMessage {
DataChannelMessage {
association_handle,
stream_id,
ppi: PayloadProtocolIdentifier::String,
payload: BytesMut::from(data),
negotiated: false,
}
}
fn message_events(ctx: &DataChannelHandlerContext) -> Vec<DataChannelEvent> {
ctx.read_outs
.iter()
.filter_map(|m| match &m.message {
RTCMessageInternal::Dtls(DTLSMessage::DataChannel(app)) => {
Some(app.data_channel_event.clone())
}
_ => None,
})
.collect()
}
#[test]
fn in_band_channel_fires_open_only_when_ack_is_processed() {
let now = Instant::now();
let mut ctx = DataChannelHandlerContext::new(now);
let mut data_channels = HashMap::new();
data_channels.insert(1, in_band_channel(1));
let mut stats = RTCStatsAccumulator::new();
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_event(TaggedRTCEventInternal {
now,
event: RTCEventInternal::SCTPHandshakeComplete(0),
})
.unwrap();
}
let dc = data_channels.get(&1).unwrap();
assert!(
dc.data_channel.is_some(),
"SCTPHandshakeComplete must dial the in-band channel"
);
assert_eq!(
dc.ready_state,
RTCDataChannelState::Connecting,
"an in-band channel must stay Connecting after the SCTP handshake"
);
assert!(
ctx.read_outs.iter().all(|m| !matches!(
&m.message,
RTCMessageInternal::Dtls(DTLSMessage::DataChannel(app))
if matches!(app.data_channel_event, DataChannelEvent::Open)
)),
"no open event may fire at SCTPHandshakeComplete time"
);
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_read(TaggedRTCMessageInternal {
now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(ack(0, 1))),
})
.unwrap();
}
assert_eq!(
data_channels.get(&1).unwrap().ready_state,
RTCDataChannelState::Open,
"ready_state flips to Open exactly when the ACK is processed"
);
let open_events: Vec<u16> = ctx
.read_outs
.iter()
.filter_map(|m| match &m.message {
RTCMessageInternal::Dtls(DTLSMessage::DataChannel(app))
if matches!(app.data_channel_event, DataChannelEvent::Open) =>
{
Some(app.data_channel_id)
}
_ => None,
})
.collect();
assert_eq!(
open_events,
vec![1],
"exactly one open event, fired on the ACK"
);
assert_eq!(
stats.peer_connection.data_channels_opened, 1,
"open stats recorded exactly once"
);
}
#[test]
fn negotiated_channel_fires_open_at_handshake_complete() {
let now = Instant::now();
let mut ctx = DataChannelHandlerContext::new(now);
let mut data_channels = HashMap::new();
data_channels.insert(1, negotiated_channel(1));
let mut stats = RTCStatsAccumulator::new();
let mut handler = DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_event(TaggedRTCEventInternal {
now,
event: RTCEventInternal::SCTPHandshakeComplete(0),
})
.unwrap();
let dc = data_channels.get(&1).unwrap();
assert_eq!(
dc.ready_state,
RTCDataChannelState::Open,
"a negotiated channel is open immediately at SCTPHandshakeComplete"
);
assert!(
dc.handshake_deadline.is_none(),
"a negotiated channel has no DCEP handshake deadline"
);
let open_events: Vec<u16> = ctx
.read_outs
.iter()
.filter_map(|m| match &m.message {
RTCMessageInternal::Dtls(DTLSMessage::DataChannel(app))
if matches!(app.data_channel_event, DataChannelEvent::Open) =>
{
Some(app.data_channel_id)
}
_ => None,
})
.collect();
assert_eq!(
open_events,
vec![1],
"exactly one open event at SCTPHandshakeComplete for a negotiated channel"
);
assert_eq!(
stats.peer_connection.data_channels_opened, 1,
"open stats recorded for the negotiated channel"
);
}
#[test]
fn emit_data_channel_opened_missing_channel_returns_error() {
let now = Instant::now();
let mut ctx = DataChannelHandlerContext::new(now);
let mut data_channels = HashMap::new();
let mut stats = RTCStatsAccumulator::new();
let mut handler = DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
let err = handler
.emit_data_channel_opened(now, TransportContext::default(), 99)
.unwrap_err();
assert_eq!(err, Error::ErrDataChannelNotExisted);
}
#[test]
fn sctp_handshake_complete_does_not_redial() {
let now = Instant::now();
let mut ctx = DataChannelHandlerContext::new(now);
let mut data_channels = HashMap::new();
data_channels.insert(1, in_band_channel(1));
let mut stats = RTCStatsAccumulator::new();
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_event(TaggedRTCEventInternal {
now,
event: RTCEventInternal::SCTPHandshakeComplete(0),
})
.unwrap();
}
let first_writes = ctx.write_outs.len();
assert!(
first_writes >= 1,
"dialing must queue the DATA_CHANNEL_OPEN"
);
ctx.write_outs.clear();
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_event(TaggedRTCEventInternal {
now,
event: RTCEventInternal::SCTPHandshakeComplete(0),
})
.unwrap();
}
assert_eq!(
ctx.write_outs.len(),
0,
"a second SCTPHandshakeComplete must not re-dial the channel"
);
}
#[test]
fn ack_on_closed_channel_is_ignored() {
let now = Instant::now();
let mut ctx = DataChannelHandlerContext::new(now);
let mut data_channels = HashMap::new();
let mut dc = in_band_channel(1);
dc.ready_state = RTCDataChannelState::Closed;
data_channels.insert(1, dc);
let mut stats = RTCStatsAccumulator::new();
let mut handler = DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_read(TaggedRTCMessageInternal {
now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(ack(0, 1))),
})
.unwrap();
assert_eq!(
data_channels.get(&1).unwrap().ready_state,
RTCDataChannelState::Closed,
"a closed channel must ignore a late ACK"
);
assert!(
!message_events(&ctx)
.iter()
.any(|e| matches!(e, DataChannelEvent::Open)),
"no open event may fire for a closed channel"
);
}
#[test]
fn in_band_channel_times_out_without_ack() {
let now = Instant::now();
let timeout = Duration::from_millis(100);
let mut ctx = DataChannelHandlerContext::new(now);
let mut data_channels = HashMap::new();
data_channels.insert(1, in_band_channel(1));
let mut stats = RTCStatsAccumulator::new();
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, Some(timeout));
handler
.handle_event(TaggedRTCEventInternal {
now,
event: RTCEventInternal::SCTPHandshakeComplete(0),
})
.unwrap();
}
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, Some(timeout));
let deadline = handler
.poll_timeout()
.expect("a dialed in-band channel must have a deadline");
assert!(deadline <= now + timeout);
}
let later = now + timeout + Duration::from_secs(1);
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, Some(timeout));
handler.handle_timeout(later).unwrap();
}
let dc = data_channels.get(&1).unwrap();
assert_eq!(
dc.ready_state,
RTCDataChannelState::Closed,
"a timed-out channel must be Closed"
);
assert!(
dc.handshake_deadline.is_none(),
"timeout must clear the deadline"
);
assert!(
!message_events(&ctx)
.iter()
.any(|e| matches!(e, DataChannelEvent::Open)),
"no open event for a timed-out channel"
);
let closes = ctx
.event_outs
.iter()
.filter(|e| {
matches!(
&e.event,
RTCEventInternal::RTCPeerConnectionEvent(
RTCPeerConnectionEvent::OnDataChannel(RTCDataChannelEvent::OnClose(1))
)
)
})
.count();
assert_eq!(closes, 1, "exactly one OnClose for the timed-out channel");
assert_eq!(stats.peer_connection.data_channels_opened, 0);
assert_eq!(stats.peer_connection.data_channels_closed, 1);
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, Some(timeout));
assert!(
handler.poll_timeout().is_none(),
"no deadline after the timeout fired"
);
}
}
#[test]
fn pre_open_data_is_buffered_until_open() {
let now = Instant::now();
let mut ctx = DataChannelHandlerContext::new(now);
let mut data_channels = HashMap::new();
data_channels.insert(1, in_band_channel(1));
let mut stats = RTCStatsAccumulator::new();
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_event(TaggedRTCEventInternal {
now,
event: RTCEventInternal::SCTPHandshakeComplete(0),
})
.unwrap();
}
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_read(TaggedRTCMessageInternal {
now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(data_message(
0, 1, b"hello",
))),
})
.unwrap();
}
assert!(
!message_events(&ctx)
.iter()
.any(|e| matches!(e, DataChannelEvent::Message(_))),
"no message may be delivered before the channel is open"
);
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, None);
handler
.handle_read(TaggedRTCMessageInternal {
now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(ack(0, 1))),
})
.unwrap();
}
let events = message_events(&ctx);
assert!(
matches!(events.first(), Some(DataChannelEvent::Open)),
"open event must fire first"
);
assert!(
matches!(events.get(1), Some(DataChannelEvent::Message(_))),
"the buffered message must be delivered after the open event"
);
}
#[test]
fn pre_open_data_is_dropped_on_timeout() {
let now = Instant::now();
let timeout = Duration::from_millis(100);
let mut ctx = DataChannelHandlerContext::new(now);
let mut data_channels = HashMap::new();
data_channels.insert(1, in_band_channel(1));
let mut stats = RTCStatsAccumulator::new();
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, Some(timeout));
handler
.handle_event(TaggedRTCEventInternal {
now,
event: RTCEventInternal::SCTPHandshakeComplete(0),
})
.unwrap();
}
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, Some(timeout));
handler
.handle_read(TaggedRTCMessageInternal {
now,
transport: TransportContext::default(),
message: RTCMessageInternal::Dtls(DTLSMessage::Sctp(data_message(
0, 1, b"bye",
))),
})
.unwrap();
}
let later = now + timeout + Duration::from_secs(1);
{
let mut handler =
DataChannelHandler::new(&mut ctx, &mut data_channels, &mut stats, Some(timeout));
handler.handle_timeout(later).unwrap();
}
assert!(
!message_events(&ctx)
.iter()
.any(|e| matches!(e, DataChannelEvent::Message(_))),
"a buffered message must never be delivered after the timeout"
);
}
}