noq_proto/connection/streams/
send.rs

1use bytes::Bytes;
2use thiserror::Error;
3
4use crate::{
5    VarInt,
6    connection::{send_buffer::SendBuffer, streams::BytesOrSlice},
7    frame,
8};
9
10#[derive(Debug)]
11pub(super) struct Send {
12    pub(super) max_data: u64,
13    pub(super) state: SendState,
14    pub(super) pending: SendBuffer,
15    pub(super) priority: i32,
16    /// Whether a frame containing a FIN bit must be transmitted.
17    ///
18    /// Even if we don't have any new data.
19    pub(super) fin_pending: bool,
20    /// Whether this stream is in the `connection_blocked` list of `Streams`
21    pub(super) connection_blocked: bool,
22    /// The reason the peer wants us to stop, if `STOP_SENDING` was received
23    pub(super) stop_reason: Option<VarInt>,
24}
25
26impl Send {
27    pub(super) fn new(max_data: VarInt) -> Box<Self> {
28        Box::new(Self {
29            max_data: max_data.into(),
30            state: SendState::Ready,
31            pending: SendBuffer::new(),
32            priority: 0,
33            fin_pending: false,
34            connection_blocked: false,
35            stop_reason: None,
36        })
37    }
38
39    /// Whether the stream has been reset
40    pub(super) fn is_reset(&self) -> bool {
41        matches!(self.state, SendState::ResetSent)
42    }
43
44    pub(super) fn finish(&mut self) -> Result<(), FinishError> {
45        if let Some(error_code) = self.stop_reason {
46            Err(FinishError::Stopped(error_code))
47        } else if self.state == SendState::Ready {
48            self.state = SendState::DataSent {
49                finish_acked: false,
50            };
51            self.fin_pending = true;
52            Ok(())
53        } else {
54            Err(FinishError::ClosedStream)
55        }
56    }
57
58    pub(super) fn write<'a, S: BytesSource<'a>>(
59        &mut self,
60        source: &'a mut S,
61        limit: u64,
62    ) -> Result<Written, WriteError> {
63        if !self.is_writable() {
64            return Err(WriteError::ClosedStream);
65        }
66        if let Some(error_code) = self.stop_reason {
67            return Err(WriteError::Stopped(error_code));
68        }
69        let budget = self.max_data - self.pending.offset();
70        if budget == 0 {
71            return Err(WriteError::Blocked);
72        }
73        let mut limit = limit.min(budget) as usize;
74
75        let mut result = Written::default();
76        loop {
77            let (chunk, chunks_consumed) = source.pop_chunk(limit);
78            result.chunks += chunks_consumed;
79            result.bytes += chunk.len();
80
81            if chunk.is_empty() {
82                break;
83            }
84
85            limit -= chunk.len();
86            self.pending.write(chunk);
87        }
88
89        Ok(result)
90    }
91
92    /// Update stream state due to a reset sent by the local application
93    pub(super) fn reset(&mut self) {
94        use SendState::*;
95        if let DataSent { .. } | Ready = self.state {
96            self.state = ResetSent;
97        }
98    }
99
100    /// Handle STOP_SENDING
101    ///
102    /// Returns true if the stream was stopped due to this frame, and false
103    /// if it had been stopped before
104    pub(super) fn try_stop(&mut self, error_code: VarInt) -> bool {
105        if self.stop_reason.is_none() {
106            self.stop_reason = Some(error_code);
107            true
108        } else {
109            false
110        }
111    }
112
113    /// Returns whether the stream has been finished and all data has been acknowledged by the peer
114    pub(super) fn ack(&mut self, frame: frame::StreamMeta) -> bool {
115        self.pending.ack(frame.offsets);
116        match self.state {
117            SendState::DataSent {
118                ref mut finish_acked,
119            } => {
120                *finish_acked |= frame.fin;
121                *finish_acked && self.pending.is_fully_acked()
122            }
123            _ => false,
124        }
125    }
126
127    /// Handle increase to stream-level flow control limit
128    ///
129    /// Returns whether the stream was unblocked
130    pub(super) fn increase_max_data(&mut self, offset: u64) -> bool {
131        if offset <= self.max_data || self.state != SendState::Ready {
132            return false;
133        }
134        let was_blocked = self.pending.offset() == self.max_data;
135        self.max_data = offset;
136        was_blocked
137    }
138
139    pub(super) fn offset(&self) -> u64 {
140        self.pending.offset()
141    }
142
143    pub(super) fn is_pending(&self) -> bool {
144        self.pending.has_unsent_data() || self.fin_pending
145    }
146
147    pub(super) fn is_writable(&self) -> bool {
148        matches!(self.state, SendState::Ready)
149    }
150}
151
152/// A [`BytesSource`] implementation for `&'a mut [Bytes]`
153///
154/// The type allows to dequeue [`Bytes`] chunks from an array of chunks, up to
155/// a configured limit.
156pub(crate) struct BytesArray<'a> {
157    /// The wrapped slice of `Bytes`
158    chunks: &'a mut [Bytes],
159    /// The amount of chunks consumed from this source
160    consumed: usize,
161}
162
163impl<'a> BytesArray<'a> {
164    pub(crate) fn from_chunks(chunks: &'a mut [Bytes]) -> Self {
165        Self {
166            chunks,
167            consumed: 0,
168        }
169    }
170}
171
172impl<'a> BytesSource<'a> for BytesArray<'a> {
173    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
174    where
175        'a: 'b,
176    {
177        // The loop exists to skip empty chunks while still marking them as
178        // consumed
179        let mut chunks_consumed = 0;
180
181        while self.consumed < self.chunks.len() {
182            let chunk = &mut self.chunks[self.consumed];
183
184            if chunk.len() <= limit {
185                let chunk = std::mem::take(chunk);
186                self.consumed += 1;
187                chunks_consumed += 1;
188                if chunk.is_empty() {
189                    continue;
190                }
191                return (chunk, chunks_consumed);
192            } else if limit > 0 {
193                let chunk = chunk.split_to(limit);
194                return (chunk, chunks_consumed);
195            } else {
196                break;
197            }
198        }
199
200        (Bytes::new(), chunks_consumed)
201    }
202}
203
204/// A [`BytesSource`] implementation for `&[u8]`
205///
206/// The type allows to dequeue a single [`Bytes`] chunk, which will be lazily
207/// created from a reference. This allows to defer the allocation until it is
208/// known how much data needs to be copied.
209pub(crate) struct ByteSlice<'a> {
210    /// The wrapped byte slice
211    data: &'a [u8],
212}
213
214impl<'a> ByteSlice<'a> {
215    pub(crate) fn from_slice(data: &'a [u8]) -> Self {
216        Self { data }
217    }
218}
219
220impl<'a> BytesSource<'a> for ByteSlice<'a> {
221    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
222    where
223        'a: 'b,
224    {
225        let limit = limit.min(self.data.len());
226        if limit == 0 {
227            return (&[][..], 0);
228        }
229
230        let chunk = &self.data[..limit];
231        self.data = &self.data[chunk.len()..];
232
233        let chunks_consumed = usize::from(self.data.is_empty());
234        (chunk, chunks_consumed)
235    }
236}
237
238/// A source of one or more buffers which can be converted into `Bytes` buffers on demand
239///
240/// The purpose of this data type is to defer conversion as long as possible,
241/// so that no heap allocation is required in case no data is writable.
242pub(super) trait BytesSource<'a> {
243    /// Returns the next chunk from the source of owned chunks.
244    ///
245    /// This method will consume parts of the source.
246    /// Calling it will yield `Bytes` elements up to the configured `limit`.
247    ///
248    /// The method returns a tuple:
249    /// - The first item is the yielded `Bytes` element. The element will be empty if the limit is
250    ///   zero or no more data is available.
251    /// - The second item returns how many complete chunks inside the source had had been consumed.
252    ///   This can be less than 1, if a chunk inside the source had been truncated in order to
253    ///   adhere to the limit. It can also be more than 1, if zero-length chunks had been skipped.
254    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
255    where
256        'a: 'b;
257}
258
259/// Indicates how many bytes and chunks had been transferred in a write operation
260#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
261pub(crate) struct Written {
262    /// The amount of bytes which had been written
263    pub(crate) bytes: usize,
264    /// The amount of full chunks which had been written
265    ///
266    /// If a chunk was only partially written, it will not be counted by this field.
267    pub(crate) chunks: usize,
268}
269
270/// Errors triggered while writing to a send stream
271#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
272pub enum WriteError {
273    /// The peer is not able to accept additional data, or the connection is congested.
274    ///
275    /// If the peer issues additional flow control credit, a [`StreamEvent::Writable`] event will
276    /// be generated, indicating that retrying the write might succeed.
277    ///
278    /// [`StreamEvent::Writable`]: crate::StreamEvent::Writable
279    #[error("unable to accept further writes")]
280    Blocked,
281    /// The peer is no longer accepting data on this stream, and it has been implicitly reset. The
282    /// stream cannot be finished or further written to.
283    ///
284    /// Carries an application-defined error code.
285    ///
286    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
287    #[error("stopped by peer: code {0}")]
288    Stopped(VarInt),
289    /// The stream has not been opened or has already been finished or reset
290    #[error("closed stream")]
291    ClosedStream,
292}
293
294#[derive(Debug, Copy, Clone, Eq, PartialEq)]
295pub(super) enum SendState {
296    /// Sending new data
297    Ready,
298    /// Stream was finished; now sending retransmits only
299    DataSent { finish_acked: bool },
300    /// Sent RESET
301    ResetSent,
302}
303
304/// Reasons why attempting to finish a stream might fail
305#[derive(Debug, Error, Clone, PartialEq, Eq)]
306pub enum FinishError {
307    /// The peer is no longer accepting data on this stream. No
308    /// [`StreamEvent::Finished`] event will be emitted for this stream.
309    ///
310    /// Carries an application-defined error code.
311    ///
312    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
313    #[error("stopped by peer: code {0}")]
314    Stopped(VarInt),
315    /// The stream has not been opened or was already finished or reset
316    #[error("closed stream")]
317    ClosedStream,
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323
324    #[test]
325    fn bytes_array() {
326        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
327        for limit in 0..full.len() {
328            let mut chunks = [
329                Bytes::from_static(b""),
330                Bytes::from_static(b"Hello "),
331                Bytes::from_static(b"Wo"),
332                Bytes::from_static(b""),
333                Bytes::from_static(b"r"),
334                Bytes::from_static(b"ld"),
335                Bytes::from_static(b""),
336                Bytes::from_static(b" 12345678"),
337                Bytes::from_static(b"9 ABCDE"),
338                Bytes::from_static(b"F"),
339                Bytes::from_static(b"GHJIJKLMNOPQRSTUVWXYZ"),
340            ];
341            let num_chunks = chunks.len();
342            let last_chunk_len = chunks[chunks.len() - 1].len();
343
344            let mut array = BytesArray::from_chunks(&mut chunks);
345
346            let mut buf = Vec::new();
347            let mut chunks_popped = 0;
348            let mut chunks_consumed = 0;
349            let mut remaining = limit;
350            loop {
351                let (chunk, consumed) = array.pop_chunk(remaining);
352                chunks_consumed += consumed;
353
354                if !chunk.is_empty() {
355                    buf.extend_from_slice(chunk.as_ref());
356                    remaining -= chunk.len();
357                    chunks_popped += 1;
358                } else {
359                    break;
360                }
361            }
362
363            assert_eq!(&buf[..], &full[..limit]);
364
365            if limit == full.len() {
366                // Full consumption of the last chunk
367                assert_eq!(chunks_consumed, num_chunks);
368                // Since there are empty chunks, we consume more than there are popped
369                assert_eq!(chunks_consumed, chunks_popped + 3);
370            } else if limit > full.len() - last_chunk_len {
371                // Partial consumption of the last chunk
372                assert_eq!(chunks_consumed, num_chunks - 1);
373                assert_eq!(chunks_consumed, chunks_popped + 2);
374            }
375        }
376    }
377
378    #[test]
379    fn byte_slice() {
380        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
381        for limit in 0..full.len() {
382            let mut array = ByteSlice::from_slice(&full[..]);
383
384            let mut buf = Vec::new();
385            let mut chunks_popped = 0;
386            let mut chunks_consumed = 0;
387            let mut remaining = limit;
388            loop {
389                let (chunk, consumed) = array.pop_chunk(remaining);
390                chunks_consumed += consumed;
391
392                if !chunk.is_empty() {
393                    buf.extend_from_slice(chunk.as_ref());
394                    remaining -= chunk.len();
395                    chunks_popped += 1;
396                } else {
397                    break;
398                }
399            }
400
401            assert_eq!(&buf[..], &full[..limit]);
402            if limit != 0 {
403                assert_eq!(chunks_popped, 1);
404            } else {
405                assert_eq!(chunks_popped, 0);
406            }
407
408            if limit == full.len() {
409                assert_eq!(chunks_consumed, 1);
410            } else {
411                assert_eq!(chunks_consumed, 0);
412            }
413        }
414    }
415}