Skip to main content

rtc_sctp/association/
stream.rs

1use crate::association::Association;
2use crate::association::state::AssociationState;
3use crate::chunk::chunk_payload_data::{ChunkPayloadData, PayloadProtocolIdentifier};
4use crate::queue::reassembly_queue::{Chunks, ReassemblyQueue};
5use crate::{ErrorCauseCode, Event, Side};
6use shared::error::{Error, Result};
7
8use crate::util::{ByteSlice, BytesArray, BytesChunk, BytesSource};
9use bytes::Bytes;
10use log::{debug, error, trace};
11use std::fmt;
12
13/// Identifier for a stream within a particular association
14pub type StreamId = u16;
15
16/// Application events about streams
17#[derive(Debug, PartialEq, Eq)]
18#[non_exhaustive]
19pub enum StreamEvent {
20    /// One or more new streams has been opened
21    Opened {
22        /// Which stream was opened.
23        id: StreamId,
24    },
25    /// A currently open stream has data or errors waiting to be read
26    Readable {
27        /// Which stream is now readable
28        id: StreamId,
29    },
30    /// A formerly write-blocked stream might be ready for a write or have been stopped
31    ///
32    /// Only generated for streams that are currently open.
33    Writable {
34        /// Which stream is now writable
35        id: StreamId,
36    },
37    /// A finished stream has been fully acknowledged or stopped
38    Finished {
39        /// Which stream has been finished
40        id: StreamId,
41    },
42    /// The peer asked us to stop sending on an outgoing stream
43    Stopped {
44        /// Which stream has been stopped
45        id: StreamId,
46        /// Error code supplied by the peer
47        error_code: ErrorCauseCode,
48    },
49    /// At least one new stream of a certain directionality may be opened
50    Available,
51    /// The number of bytes of outgoing data buffered is lower than the threshold.
52    BufferedAmountLow {
53        /// Which stream is now readable
54        id: StreamId,
55    },
56    /// The number of bytes of outgoing data buffered is higher than the threshold.
57    BufferedAmountHigh {
58        /// Which stream is now readable
59        id: StreamId,
60    },
61    /// Outgoing buffered data was released (acknowledged OR abandoned), carrying
62    /// the exact number of user payload bytes freed for this stream. Unlike the
63    /// edge-triggered [`StreamEvent::BufferedAmountLow`], this fires on every
64    /// release with the byte delta, so upper layers can keep their own
65    /// send-buffer accounting (e.g. a synchronous back-pressure counter) exact.
66    BufferedAmountReleased {
67        /// Which stream released buffered bytes
68        id: StreamId,
69        /// User payload bytes released
70        n_bytes: usize,
71    },
72}
73
74/// Reliability type for stream
75#[derive(Default, Debug, Copy, Clone, PartialEq)]
76pub enum ReliabilityType {
77    /// ReliabilityTypeReliable is used for reliable transmission
78    #[default]
79    Reliable = 0,
80    /// ReliabilityTypeRexmit is used for partial reliability by retransmission count
81    Rexmit = 1,
82    /// ReliabilityTypeTimed is used for partial reliability by retransmission duration
83    Timed = 2,
84}
85
86impl fmt::Display for ReliabilityType {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        let s = match *self {
89            ReliabilityType::Reliable => "Reliable",
90            ReliabilityType::Rexmit => "Rexmit",
91            ReliabilityType::Timed => "Timed",
92        };
93        write!(f, "{}", s)
94    }
95}
96
97impl From<u8> for ReliabilityType {
98    fn from(v: u8) -> ReliabilityType {
99        match v {
100            1 => ReliabilityType::Rexmit,
101            2 => ReliabilityType::Timed,
102            _ => ReliabilityType::Reliable,
103        }
104    }
105}
106
107/// Stream represents an SCTP stream
108pub struct Stream<'a> {
109    pub(crate) stream_identifier: StreamId,
110    pub(crate) association: &'a mut Association,
111}
112
113impl Stream<'_> {
114    /// read reads a packet of len(p) bytes, dropping the Payload Protocol Identifier.
115    /// Returns EOF when the stream is reset or an error if the stream is closed
116    /// otherwise.
117    pub fn read(&mut self) -> Result<Option<Chunks>> {
118        self.read_sctp()
119    }
120
121    /// read_sctp reads a packet of len(p) bytes and returns the associated Payload
122    /// Protocol Identifier.
123    /// Returns EOF when the stream is reset or an error if the stream is closed
124    /// otherwise.
125    pub fn read_sctp(&mut self) -> Result<Option<Chunks>> {
126        if let Some(s) = self.association.streams.get_mut(&self.stream_identifier)
127            && (s.state == RecvSendState::ReadWritable || s.state == RecvSendState::Readable)
128        {
129            Ok(s.reassembly_queue.read())
130        } else {
131            Err(Error::ErrStreamClosed)
132        }
133    }
134
135    /// write_sctp writes len(p) bytes from p to the DTLS connection
136    pub fn write_sctp(&mut self, p: &Bytes, ppi: PayloadProtocolIdentifier) -> Result<usize> {
137        self.write_source(&mut BytesChunk::new(p), ppi)
138    }
139
140    /// Send data on the given stream.
141    ///
142    /// Uses the deafult payload protocol (PPI).
143    ///
144    /// Returns the number of bytes successfully written.
145    pub fn write(&mut self, data: &[u8]) -> Result<usize> {
146        self.write_with_ppi(data, self.get_default_payload_type()?)
147    }
148
149    /// Send data on the given stream, with a specific payload protocol.
150    ///
151    /// Returns the number of bytes successfully written.
152    pub fn write_with_ppi(&mut self, data: &[u8], ppi: PayloadProtocolIdentifier) -> Result<usize> {
153        self.write_source(&mut ByteSlice::from_slice(data), ppi)
154    }
155
156    /// write writes len(p) bytes from p with the default Payload Protocol Identifier
157    pub fn write_chunk(&mut self, p: &Bytes) -> Result<usize> {
158        self.write_source(&mut BytesChunk::new(p), self.get_default_payload_type()?)
159    }
160
161    /// Send an owned [`Bytes`] on the stream with a specific payload protocol.
162    ///
163    /// Unlike [`write_with_ppi`](Self::write_with_ppi), which takes a `&[u8]` and
164    /// must copy it into a freshly allocated buffer, this enqueues the payload
165    /// zero-copy: each fragment is a refcounted slice of `data`. Prefer this on the
166    /// hot send path when the caller already owns the payload as `Bytes`.
167    ///
168    /// Returns the number of bytes successfully written.
169    pub fn write_chunk_with_ppi(
170        &mut self,
171        data: &Bytes,
172        ppi: PayloadProtocolIdentifier,
173    ) -> Result<usize> {
174        self.write_source(&mut BytesChunk::new(data), ppi)
175    }
176
177    /// Send data on the given stream
178    ///
179    /// Returns the number of bytes and chunks successfully written.
180    /// Note that this method might also write a partial chunk. In this case
181    /// it will not count this chunk as fully written. However
182    /// the chunk will be advanced and contain only non-written data after the call.
183    pub fn write_chunks(&mut self, data: &mut [Bytes]) -> Result<usize> {
184        self.write_source(
185            &mut BytesArray::from_chunks(data),
186            self.get_default_payload_type()?,
187        )
188    }
189
190    /// write_source writes BytesSource to the DTLS connection
191    fn write_source<B: BytesSource>(
192        &mut self,
193        source: &mut B,
194        ppi: PayloadProtocolIdentifier,
195    ) -> Result<usize> {
196        if !self.is_writable() {
197            return Err(Error::ErrStreamClosed);
198        }
199
200        if source.remaining() > self.association.max_message_size() as usize {
201            return Err(Error::ErrOutboundPacketTooLarge);
202        }
203
204        let state: AssociationState = self.association.state();
205        match state {
206            AssociationState::ShutdownSent
207            | AssociationState::ShutdownAckSent
208            | AssociationState::ShutdownPending
209            | AssociationState::ShutdownReceived => return Err(Error::ErrStreamClosed),
210            _ => {}
211        };
212
213        let (p, _) = source.pop_chunk(self.association.max_message_size() as usize);
214
215        if let Some(s) = self.association.streams.get_mut(&self.stream_identifier) {
216            let (is_buffered_amount_high, chunks) = s.packetize(&p, ppi);
217
218            if is_buffered_amount_high {
219                trace!("StreamEvent::BufferedAmountHigh");
220                self.association
221                    .events
222                    .push_back(Event::Stream(StreamEvent::BufferedAmountHigh {
223                        id: self.stream_identifier,
224                    }))
225            }
226
227            self.association.send_payload_data(chunks)?;
228
229            Ok(p.len())
230        } else {
231            Err(Error::ErrStreamClosed)
232        }
233    }
234
235    /// Whether this stream has data or an error waiting to be read.
236    pub fn is_readable(&self) -> bool {
237        if let Some(s) = self.association.streams.get(&self.stream_identifier) {
238            s.state == RecvSendState::Readable || s.state == RecvSendState::ReadWritable
239        } else {
240            false
241        }
242    }
243
244    /// Whether this stream can currently accept more data.
245    pub fn is_writable(&self) -> bool {
246        if let Some(s) = self.association.streams.get(&self.stream_identifier) {
247            s.state == RecvSendState::Writable || s.state == RecvSendState::ReadWritable
248        } else {
249            false
250        }
251    }
252
253    /// stop closes the read-direction of the stream.
254    /// Future calls to read are not permitted after calling stop.
255    pub fn stop(&mut self) -> Result<()> {
256        let mut reset = false;
257        if let Some(s) = self.association.streams.get_mut(&self.stream_identifier) {
258            if s.state == RecvSendState::Readable || s.state == RecvSendState::ReadWritable {
259                reset = true;
260            }
261            s.state = ((s.state as u8) & 0x2).into();
262        }
263
264        if reset {
265            // Reset the outgoing stream
266            // https://tools.ietf.org/html/rfc6525
267            self.association
268                .send_reset_request(self.stream_identifier)?;
269        }
270
271        Ok(())
272    }
273
274    /// finish closes the write-direction of the stream.
275    /// Future calls to write are not permitted after calling Close.
276    pub fn finish(&mut self) -> Result<()> {
277        if let Some(s) = self.association.streams.get_mut(&self.stream_identifier) {
278            s.state = ((s.state as u8) & 0x1).into();
279        }
280        Ok(())
281    }
282
283    /// Shuts down the read, write, or both halves of this stream.
284    ///
285    /// This function will cause all pending and future I/O on the specified portions to return
286    /// immediately with an appropriate value (see the documentation of `Shutdown`).
287    ///
288    /// Resets the stream when both halves of this stream are shutdown.
289    pub fn close(&mut self) -> Result<()> {
290        self.finish()?;
291        self.stop()
292    }
293
294    /// stream_identifier returns the Stream identifier associated to the stream.
295    pub fn stream_identifier(&self) -> StreamId {
296        self.stream_identifier
297    }
298
299    /// set_default_payload_type sets the default payload type used by write.
300    pub fn set_default_payload_type(
301        &mut self,
302        default_payload_type: PayloadProtocolIdentifier,
303    ) -> Result<()> {
304        if let Some(s) = self.association.streams.get_mut(&self.stream_identifier) {
305            s.default_payload_type = default_payload_type;
306            Ok(())
307        } else {
308            Err(Error::ErrStreamClosed)
309        }
310    }
311
312    /// get_default_payload_type returns the payload type associated to the stream.
313    pub fn get_default_payload_type(&self) -> Result<PayloadProtocolIdentifier> {
314        if let Some(s) = self.association.streams.get(&self.stream_identifier) {
315            Ok(s.default_payload_type)
316        } else {
317            Err(Error::ErrStreamClosed)
318        }
319    }
320
321    /// set_reliability_params sets reliability parameters for this stream.
322    pub fn set_reliability_params(
323        &mut self,
324        unordered: bool,
325        rel_type: ReliabilityType,
326        rel_val: u32,
327    ) -> Result<()> {
328        if let Some(s) = self.association.streams.get_mut(&self.stream_identifier) {
329            debug!(
330                "[{}] reliability params: ordered={} type={} value={}",
331                s.side, !unordered, rel_type, rel_val
332            );
333            s.unordered = unordered;
334            s.reliability_type = rel_type;
335            s.reliability_value = rel_val;
336            Ok(())
337        } else {
338            Err(Error::ErrStreamClosed)
339        }
340    }
341
342    /// buffered_amount returns the number of bytes of data currently queued to be sent over this stream.
343    pub fn buffered_amount(&self) -> Result<usize> {
344        if let Some(s) = self.association.streams.get(&self.stream_identifier) {
345            Ok(s.buffered_amount)
346        } else {
347            Err(Error::ErrStreamClosed)
348        }
349    }
350
351    /// buffered_amount_low_threshold returns the number of bytes of buffered outgoing data that is
352    /// considered "low" Defaults to 0.
353    pub fn buffered_amount_low_threshold(&self) -> Result<usize> {
354        if let Some(s) = self.association.streams.get(&self.stream_identifier) {
355            Ok(s.buffered_amount_low)
356        } else {
357            Err(Error::ErrStreamClosed)
358        }
359    }
360
361    /// set_buffered_amount_low_threshold is used to update the threshold.
362    /// See buffered_amount_low_threshold().
363    pub fn set_buffered_amount_low_threshold(&mut self, th: usize) -> Result<()> {
364        if let Some(s) = self.association.streams.get_mut(&self.stream_identifier) {
365            s.buffered_amount_low = th;
366            Ok(())
367        } else {
368            Err(Error::ErrStreamClosed)
369        }
370    }
371
372    /// buffered_amount_high_threshold returns the number of bytes of buffered outgoing data that is
373    /// considered "high" Defaults to u32::MAX.
374    pub fn buffered_amount_high_threshold(&self) -> Result<usize> {
375        if let Some(s) = self.association.streams.get(&self.stream_identifier) {
376            Ok(s.buffered_amount_high)
377        } else {
378            Err(Error::ErrStreamClosed)
379        }
380    }
381
382    /// set_buffered_amount_high_threshold is used to update the threshold.
383    /// See buffered_amount_high_threshold().
384    pub fn set_buffered_amount_high_threshold(&mut self, th: usize) -> Result<()> {
385        if let Some(s) = self.association.streams.get_mut(&self.stream_identifier) {
386            s.buffered_amount_high = th;
387            Ok(())
388        } else {
389            Err(Error::ErrStreamClosed)
390        }
391    }
392}
393
394#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
395pub enum RecvSendState {
396    #[default]
397    Closed = 0,
398    Readable = 1,
399    Writable = 2,
400    ReadWritable = 3,
401}
402
403impl From<u8> for RecvSendState {
404    fn from(v: u8) -> Self {
405        match v {
406            1 => RecvSendState::Readable,
407            2 => RecvSendState::Writable,
408            3 => RecvSendState::ReadWritable,
409            _ => RecvSendState::Closed,
410        }
411    }
412}
413
414/// StreamState represents the state of an SCTP stream
415#[derive(Default, Debug)]
416pub struct StreamState {
417    pub(crate) side: Side,
418    pub(crate) max_payload_size: u32,
419    pub(crate) stream_identifier: StreamId,
420    pub(crate) default_payload_type: PayloadProtocolIdentifier,
421    pub(crate) reassembly_queue: ReassemblyQueue,
422    pub(crate) sequence_number: u16,
423    pub(crate) state: RecvSendState,
424    pub(crate) unordered: bool,
425    pub(crate) reliability_type: ReliabilityType,
426    pub(crate) reliability_value: u32,
427    pub(crate) buffered_amount: usize,
428    pub(crate) buffered_amount_low: usize,
429    pub(crate) buffered_amount_high: usize,
430}
431impl StreamState {
432    pub(crate) fn new(
433        side: Side,
434        stream_identifier: StreamId,
435        max_payload_size: u32,
436        default_payload_type: PayloadProtocolIdentifier,
437    ) -> Self {
438        StreamState {
439            side,
440            stream_identifier,
441            max_payload_size,
442            default_payload_type,
443            reassembly_queue: ReassemblyQueue::new(stream_identifier),
444            sequence_number: 0,
445            state: RecvSendState::ReadWritable,
446            unordered: false,
447            reliability_type: ReliabilityType::Reliable,
448            reliability_value: 0,
449            buffered_amount: 0,
450            buffered_amount_low: 0,
451            buffered_amount_high: u32::MAX as usize,
452        }
453    }
454
455    pub(crate) fn handle_data(&mut self, pd: &ChunkPayloadData) -> bool {
456        self.reassembly_queue.push(pd.clone())
457    }
458
459    pub(crate) fn handle_forward_tsn_for_ordered(&mut self, ssn: u16) {
460        if self.unordered {
461            return; // unordered chunks are handled by handleForwardUnordered method
462        }
463
464        // Remove all chunks older than or equal to the new TSN from
465        // the reassembly_queue.
466        self.reassembly_queue.forward_tsn_for_ordered(ssn);
467    }
468
469    pub(crate) fn handle_forward_tsn_for_unordered(&mut self, new_cumulative_tsn: u32) {
470        if !self.unordered {
471            return; // ordered chunks are handled by handleForwardTSNOrdered method
472        }
473
474        // Remove all chunks older than or equal to the new TSN from
475        // the reassembly_queue.
476        self.reassembly_queue
477            .forward_tsn_for_unordered(new_cumulative_tsn);
478    }
479
480    fn packetize(
481        &mut self,
482        raw: &Bytes,
483        ppi: PayloadProtocolIdentifier,
484    ) -> (bool, Vec<ChunkPayloadData>) {
485        let mut i = 0;
486        let mut remaining = raw.len();
487
488        // From draft-ietf-rtcweb-data-protocol-09, section 6:
489        //   All Data Channel Establishment Protocol messages MUST be sent using
490        //   ordered delivery and reliable transmission.
491        let unordered = ppi != PayloadProtocolIdentifier::Dcep && self.unordered;
492
493        let mut chunks = vec![];
494
495        let head_abandoned = false;
496        let head_all_inflight = false;
497        while remaining != 0 {
498            let fragment_size = std::cmp::min(self.max_payload_size as usize, remaining); //self.association.max_payload_size
499
500            // Copy the userdata since we'll have to store it until acked
501            // and the caller may re-use the buffer in the mean time
502            let user_data = raw.slice(i..i + fragment_size);
503
504            let chunk = ChunkPayloadData {
505                stream_identifier: self.stream_identifier,
506                user_data,
507                unordered,
508                beginning_fragment: i == 0,
509                ending_fragment: remaining - fragment_size == 0,
510                immediate_sack: false,
511                payload_type: ppi,
512                stream_sequence_number: self.sequence_number,
513                abandoned: head_abandoned, // all fragmented chunks use the same abandoned
514                all_inflight: head_all_inflight, // all fragmented chunks use the same all_inflight
515                ..Default::default()
516            };
517
518            chunks.push(chunk);
519
520            remaining -= fragment_size;
521            i += fragment_size;
522        }
523
524        // RFC 4960 Sec 6.6
525        // Note: When transmitting ordered and unordered data, an endpoint does
526        // not increment its Stream Sequence Number when transmitting a DATA
527        // chunk with U flag set to 1.
528        if !unordered {
529            self.sequence_number = self.sequence_number.wrapping_add(1);
530        }
531
532        let old_amount = self.buffered_amount;
533        let n_bytes_added = raw.len();
534        self.buffered_amount += raw.len();
535        let new_amount = self.buffered_amount;
536
537        trace!(
538            "[{}] new_amount = {}, old_amount = {}, buffered_amount_high = {}, n_bytes_added = {}",
539            self.side, new_amount, old_amount, self.buffered_amount_high, n_bytes_added,
540        );
541
542        let is_buffered_amount_high =
543            old_amount < self.buffered_amount_high && new_amount >= self.buffered_amount_high;
544
545        (is_buffered_amount_high, chunks)
546    }
547
548    /// This method is called by association to notify this stream
549    /// of the specified amount of outgoing data has been delivered to the peer.
550    pub(crate) fn on_buffer_released(&mut self, n_bytes_released: i64) -> bool {
551        if n_bytes_released <= 0 {
552            return false;
553        }
554
555        let old_amount = self.buffered_amount;
556        let new_amount = if old_amount < n_bytes_released as usize {
557            self.buffered_amount = 0;
558            error!(
559                "[{}] released buffer size {} should be <= {}",
560                self.side, n_bytes_released, 0,
561            );
562            0
563        } else {
564            self.buffered_amount -= n_bytes_released as usize;
565
566            old_amount - n_bytes_released as usize
567        };
568
569        trace!(
570            "[{}] new_amount = {}, old_amount = {}, buffered_amount_low = {}, n_bytes_released = {}",
571            self.side, new_amount, old_amount, self.buffered_amount_low, n_bytes_released,
572        );
573
574        old_amount > self.buffered_amount_low && new_amount <= self.buffered_amount_low
575    }
576
577    pub(crate) fn get_num_bytes_in_reassembly_queue(&self) -> usize {
578        // No lock is required as it reads the size with atomic load function.
579        self.reassembly_queue.get_num_bytes()
580    }
581}