Skip to main content

ant_quic/high_level/
recv_stream.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8use std::{
9    future::{Future, poll_fn},
10    io,
11    pin::Pin,
12    task::{Context, Poll, ready},
13};
14
15use crate::{Chunk, Chunks, ClosedStream, ConnectionError, ReadableError, StreamId};
16use bytes::Bytes;
17use thiserror::Error;
18use tokio::io::ReadBuf;
19
20use super::connection::ConnectionRef;
21use crate::VarInt;
22
23/// A stream that can only be used to receive data
24///
25/// `stop(0)` is implicitly called on drop unless:
26/// - A variant of [`ReadError`] has been yielded by a read call
27/// - [`stop()`] was called explicitly
28///
29/// # Cancellation
30///
31/// A `read` method is said to be *cancel-safe* when dropping its future before the future becomes
32/// ready cannot lead to loss of stream data. This is true of methods which succeed immediately when
33/// any progress is made, and is not true of methods which might need to perform multiple reads
34/// internally before succeeding. Each `read` method documents whether it is cancel-safe.
35///
36/// # Common issues
37///
38/// ## Data never received on a locally-opened stream
39///
40/// Peers are not notified of streams until they or a later-numbered stream are used to send
41/// data. If a bidirectional stream is locally opened but never used to send, then the peer may
42/// never see it. Application protocols should always arrange for the endpoint which will first
43/// transmit on a stream to be the endpoint responsible for opening it.
44///
45/// ## Data never received on a remotely-opened stream
46///
47/// Verify that the stream you are receiving is the same one that the server is sending on, e.g. by
48/// logging the [`id`] of each. Streams are always accepted in the same order as they are created,
49/// i.e. ascending order by [`StreamId`]. For example, even if a sender first transmits on
50/// bidirectional stream 1, the first stream yielded by Connection's accept_bi method on the receiver
51/// will be bidirectional stream 0.
52///
53/// [`ReadError`]: crate::ReadError
54/// [`stop()`]: RecvStream::stop
55/// [`SendStream::finish`]: crate::SendStream::finish
56/// [`WriteError::Stopped`]: crate::WriteError::Stopped
57/// [`id`]: RecvStream::id
58/// `Connection::accept_bi`: See the Connection's accept_bi method
59#[derive(Debug)]
60pub struct RecvStream {
61    conn: ConnectionRef,
62    stream: StreamId,
63    is_0rtt: bool,
64    all_data_read: bool,
65    reset: Option<VarInt>,
66}
67
68impl RecvStream {
69    pub(crate) fn new(conn: ConnectionRef, stream: StreamId, is_0rtt: bool) -> Self {
70        Self {
71            conn,
72            stream,
73            is_0rtt,
74            all_data_read: false,
75            reset: None,
76        }
77    }
78
79    /// Read data contiguously from the stream.
80    ///
81    /// Yields the number of bytes read into `buf` on success, or `None` if the stream was finished.
82    ///
83    /// This operation is cancel-safe.
84    pub async fn read(&mut self, buf: &mut [u8]) -> Result<Option<usize>, ReadError> {
85        Read {
86            stream: self,
87            buf: ReadBuf::new(buf),
88        }
89        .await
90    }
91
92    /// Read an exact number of bytes contiguously from the stream.
93    ///
94    /// See [`read()`] for details. This operation is *not* cancel-safe.
95    ///
96    /// [`read()`]: RecvStream::read
97    pub async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> {
98        ReadExact {
99            stream: self,
100            buf: ReadBuf::new(buf),
101        }
102        .await
103    }
104
105    /// Attempts to read from the stream into the provided buffer
106    ///
107    /// On success, returns `Poll::Ready(Ok(num_bytes_read))` and places data into `buf`. If this
108    /// returns zero bytes read (and `buf` has a non-zero length), that indicates that the remote
109    /// side has [`finish`]ed the stream and the local side has already read all bytes.
110    ///
111    /// If no data is available for reading, this returns `Poll::Pending` and arranges for the
112    /// current task (via `cx.waker()`) to be notified when the stream becomes readable or is
113    /// closed.
114    ///
115    /// [`finish`]: crate::SendStream::finish
116    pub fn poll_read(
117        &mut self,
118        cx: &mut Context,
119        buf: &mut [u8],
120    ) -> Poll<Result<usize, ReadError>> {
121        let mut buf = ReadBuf::new(buf);
122        ready!(self.poll_read_buf(cx, &mut buf))?;
123        Poll::Ready(Ok(buf.filled().len()))
124    }
125
126    /// Attempts to read from the stream into the provided buffer, which may be uninitialized
127    ///
128    /// On success, returns `Poll::Ready(Ok(()))` and places data into the unfilled portion of
129    /// `buf`. If this does not write any bytes to `buf` (and `buf.remaining()` is non-zero), that
130    /// indicates that the remote side has [`finish`]ed the stream and the local side has already
131    /// read all bytes.
132    ///
133    /// If no data is available for reading, this returns `Poll::Pending` and arranges for the
134    /// current task (via `cx.waker()`) to be notified when the stream becomes readable or is
135    /// closed.
136    ///
137    /// [`finish`]: crate::SendStream::finish
138    pub fn poll_read_buf(
139        &mut self,
140        cx: &mut Context,
141        buf: &mut ReadBuf<'_>,
142    ) -> Poll<Result<(), ReadError>> {
143        if buf.remaining() == 0 {
144            return Poll::Ready(Ok(()));
145        }
146
147        self.poll_read_generic(cx, true, |chunks| {
148            let mut read = false;
149            loop {
150                if buf.remaining() == 0 {
151                    // We know `read` is `true` because `buf.remaining()` was not 0 before
152                    return ReadStatus::Readable(());
153                }
154
155                match chunks.next(buf.remaining()) {
156                    Ok(Some(chunk)) => {
157                        buf.put_slice(&chunk.bytes);
158                        read = true;
159                    }
160                    res => return (if read { Some(()) } else { None }, res.err()).into(),
161                }
162            }
163        })
164        .map(|res| res.map(|_| ()))
165    }
166
167    /// Read the next segment of data
168    ///
169    /// Yields `None` if the stream was finished. Otherwise, yields a segment of data and its
170    /// offset in the stream. If `ordered` is `true`, the chunk's offset will be immediately after
171    /// the last data yielded by `read()` or `read_chunk()`. If `ordered` is `false`, segments may
172    /// be received in any order, and the `Chunk`'s `offset` field can be used to determine
173    /// ordering in the caller. Unordered reads are less prone to head-of-line blocking within a
174    /// stream, but require the application to manage reassembling the original data.
175    ///
176    /// Slightly more efficient than `read` due to not copying. Chunk boundaries do not correspond
177    /// to peer writes, and hence cannot be used as framing.
178    ///
179    /// This operation is cancel-safe.
180    pub async fn read_chunk(
181        &mut self,
182        max_length: usize,
183        ordered: bool,
184    ) -> Result<Option<Chunk>, ReadError> {
185        ReadChunk {
186            stream: self,
187            max_length,
188            ordered,
189        }
190        .await
191    }
192
193    /// Attempts to read a chunk from the stream.
194    ///
195    /// On success, returns `Poll::Ready(Ok(Some(chunk)))`. If `Poll::Ready(Ok(None))`
196    /// is returned, it implies that EOF has been reached.
197    ///
198    /// If no data is available for reading, the method returns `Poll::Pending`
199    /// and arranges for the current task (via cx.waker()) to receive a notification
200    /// when the stream becomes readable or is closed.
201    fn poll_read_chunk(
202        &mut self,
203        cx: &mut Context,
204        max_length: usize,
205        ordered: bool,
206    ) -> Poll<Result<Option<Chunk>, ReadError>> {
207        self.poll_read_generic(cx, ordered, |chunks| match chunks.next(max_length) {
208            Ok(Some(chunk)) => ReadStatus::Readable(chunk),
209            res => (None, res.err()).into(),
210        })
211    }
212
213    /// Read the next segments of data
214    ///
215    /// Fills `bufs` with the segments of data beginning immediately after the
216    /// last data yielded by `read` or `read_chunk`, or `None` if the stream was
217    /// finished.
218    ///
219    /// Slightly more efficient than `read` due to not copying. Chunk boundaries
220    /// do not correspond to peer writes, and hence cannot be used as framing.
221    ///
222    /// This operation is cancel-safe.
223    pub async fn read_chunks(&mut self, bufs: &mut [Bytes]) -> Result<Option<usize>, ReadError> {
224        ReadChunks { stream: self, bufs }.await
225    }
226
227    /// Foundation of [`Self::read_chunks`]
228    fn poll_read_chunks(
229        &mut self,
230        cx: &mut Context,
231        bufs: &mut [Bytes],
232    ) -> Poll<Result<Option<usize>, ReadError>> {
233        if bufs.is_empty() {
234            return Poll::Ready(Ok(Some(0)));
235        }
236
237        self.poll_read_generic(cx, true, |chunks| {
238            let mut read = 0;
239            loop {
240                if read >= bufs.len() {
241                    // We know `read > 0` because `bufs` cannot be empty here
242                    return ReadStatus::Readable(read);
243                }
244
245                match chunks.next(usize::MAX) {
246                    Ok(Some(chunk)) => {
247                        bufs[read] = chunk.bytes;
248                        read += 1;
249                    }
250                    res => return (if read == 0 { None } else { Some(read) }, res.err()).into(),
251                }
252            }
253        })
254    }
255
256    /// Convenience method to read all remaining data into a buffer
257    ///
258    /// Fails with [`ReadToEndError::TooLong`] on reading more than `size_limit` bytes, discarding
259    /// all data read. Uses unordered reads to be more efficient than using `AsyncRead` would
260    /// allow. `size_limit` should be set to limit worst-case memory use.
261    ///
262    /// The returned buffer is guaranteed to be contiguous stream data: if any range between the
263    /// first and last byte this call observed was consumed elsewhere and never delivered (for
264    /// example by an earlier `read_to_end` future that was dropped mid-read, or by prior
265    /// unordered reads), this fails with [`ReadToEndError::MissingData`] instead of silently
266    /// filling the holes.
267    ///
268    /// This operation is *not* cancel-safe: dropping the returned future after it has consumed
269    /// stream data discards that data irrecoverably. A subsequent `read_to_end` returns the
270    /// remaining contiguous data, or fails with [`ReadToEndError::MissingData`] if the discard
271    /// left a hole in what it observes.
272    ///
273    /// `ReadToEndError::TooLong`: Error returned when size limit is exceeded
274    pub async fn read_to_end(&mut self, size_limit: usize) -> Result<Vec<u8>, ReadToEndError> {
275        ReadToEnd {
276            stream: self,
277            size_limit,
278            read: Vec::new(),
279            start: u64::MAX,
280            end: 0,
281        }
282        .await
283    }
284
285    /// Stop accepting data
286    ///
287    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
288    /// attempts to operate on a stream will yield `ClosedStream` errors.
289    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
290        let mut conn = self.conn.state.lock("RecvStream::stop");
291        if self.is_0rtt && conn.check_0rtt().is_err() {
292            return Ok(());
293        }
294        conn.inner.recv_stream(self.stream).stop(error_code)?;
295        conn.wake();
296        self.all_data_read = true;
297        Ok(())
298    }
299
300    /// Check if this stream has been opened during 0-RTT.
301    ///
302    /// In which case any non-idempotent request should be considered dangerous at the application
303    /// level. Because read data is subject to replay attacks.
304    pub fn is_0rtt(&self) -> bool {
305        self.is_0rtt
306    }
307
308    /// Get the identity of this stream
309    pub fn id(&self) -> StreamId {
310        self.stream
311    }
312
313    /// Completes when the stream has been reset by the peer or otherwise closed
314    ///
315    /// Yields `Some` with the reset error code when the stream is reset by the peer. Yields `None`
316    /// when the stream was previously [`stop()`](Self::stop)ed, or when the stream was
317    /// [`finish()`](crate::SendStream::finish)ed by the peer and all data has been received, after
318    /// which it is no longer meaningful for the stream to be reset.
319    ///
320    /// This operation is cancel-safe.
321    pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError> {
322        poll_fn(|cx| {
323            let mut conn = self.conn.state.lock("RecvStream::reset");
324            if self.is_0rtt && conn.check_0rtt().is_err() {
325                return Poll::Ready(Err(ResetError::ZeroRttRejected));
326            }
327
328            if let Some(code) = self.reset {
329                return Poll::Ready(Ok(Some(code)));
330            }
331
332            match conn.inner.recv_stream(self.stream).received_reset() {
333                Err(_) => Poll::Ready(Ok(None)),
334                Ok(Some(error_code)) => {
335                    // Stream state has just now been freed, so the connection may need to issue new
336                    // stream ID flow control credit
337                    conn.wake();
338                    Poll::Ready(Ok(Some(error_code)))
339                }
340                Ok(None) => {
341                    if let Some(e) = &conn.error {
342                        return Poll::Ready(Err(e.clone().into()));
343                    }
344                    // Resets always notify readers, since a reset is an immediate read error. We
345                    // could introduce a dedicated channel to reduce the risk of spurious wakeups,
346                    // but that increased complexity is probably not justified, as an application
347                    // that is expecting a reset is not likely to receive large amounts of data.
348                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
349                    Poll::Pending
350                }
351            }
352        })
353        .await
354    }
355
356    /// Handle common logic related to reading out of a receive stream
357    ///
358    /// This takes an `FnMut` closure that takes care of the actual reading process, matching
359    /// the detailed read semantics for the calling function with a particular return type.
360    /// The closure can read from the passed `&mut Chunks` and has to return the status after
361    /// reading: the amount of data read, and the status after the final read call.
362    fn poll_read_generic<T, U>(
363        &mut self,
364        cx: &mut Context,
365        ordered: bool,
366        mut read_fn: T,
367    ) -> Poll<Result<Option<U>, ReadError>>
368    where
369        T: FnMut(&mut Chunks) -> ReadStatus<U>,
370    {
371        use crate::ReadError::*;
372        if self.all_data_read {
373            return Poll::Ready(Ok(None));
374        }
375
376        let mut conn = self.conn.state.lock("RecvStream::poll_read");
377        if self.is_0rtt {
378            conn.check_0rtt().map_err(|()| ReadError::ZeroRttRejected)?;
379        }
380
381        // If we stored an error during a previous call, return it now. This can happen if a
382        // `read_fn` both wants to return data and also returns an error in its final stream status.
383        let status = match self.reset {
384            Some(code) => ReadStatus::Failed(None, Reset(code)),
385            None => {
386                let mut recv = conn.inner.recv_stream(self.stream);
387                let mut chunks = recv.read(ordered)?;
388                let status = read_fn(&mut chunks);
389                if chunks.finalize().should_transmit() {
390                    conn.wake();
391                }
392                status
393            }
394        };
395
396        match status {
397            ReadStatus::Readable(read) => Poll::Ready(Ok(Some(read))),
398            ReadStatus::Finished(read) => {
399                self.all_data_read = true;
400                Poll::Ready(Ok(read))
401            }
402            ReadStatus::Failed(read, Blocked) => match read {
403                Some(val) => Poll::Ready(Ok(Some(val))),
404                None => {
405                    if let Some(ref x) = conn.error {
406                        return Poll::Ready(Err(ReadError::ConnectionLost(x.clone())));
407                    }
408                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
409                    Poll::Pending
410                }
411            },
412            ReadStatus::Failed(read, Reset(error_code)) => match read {
413                None => {
414                    self.all_data_read = true;
415                    self.reset = Some(error_code);
416                    Poll::Ready(Err(ReadError::Reset(error_code)))
417                }
418                done => {
419                    self.reset = Some(error_code);
420                    Poll::Ready(Ok(done))
421                }
422            },
423            ReadStatus::Failed(_read, ConnectionClosed) => {
424                self.all_data_read = true;
425                Poll::Ready(Err(ReadError::ConnectionLost(
426                    ConnectionError::LocallyClosed,
427                )))
428            }
429        }
430    }
431}
432
433enum ReadStatus<T> {
434    Readable(T),
435    Finished(Option<T>),
436    Failed(Option<T>, crate::ReadError),
437}
438
439impl<T> From<(Option<T>, Option<crate::ReadError>)> for ReadStatus<T> {
440    fn from(status: (Option<T>, Option<crate::ReadError>)) -> Self {
441        match status {
442            (read, None) => Self::Finished(read),
443            (read, Some(e)) => Self::Failed(read, e),
444        }
445    }
446}
447
448/// Future produced by `RecvStream::read_to_end()`.
449struct ReadToEnd<'a> {
450    stream: &'a mut RecvStream,
451    read: Vec<(Bytes, u64)>,
452    start: u64,
453    end: u64,
454    size_limit: usize,
455}
456
457impl Future for ReadToEnd<'_> {
458    type Output = Result<Vec<u8>, ReadToEndError>;
459    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
460        loop {
461            match ready!(self.stream.poll_read_chunk(cx, usize::MAX, false))? {
462                Some(chunk) => {
463                    self.start = self.start.min(chunk.offset);
464                    let end = chunk.bytes.len() as u64 + chunk.offset;
465                    if (end - self.start) > self.size_limit as u64 {
466                        return Poll::Ready(Err(ReadToEndError::TooLong));
467                    }
468                    self.end = self.end.max(end);
469                    self.read.push((chunk.bytes, chunk.offset));
470                }
471                None => {
472                    if self.end == 0 {
473                        // Never received anything
474                        return Poll::Ready(Ok(Vec::new()));
475                    }
476                    let (start, end) = (self.start, self.end);
477                    return match assemble_unordered_chunks(&mut self.read, start, end) {
478                        Some(buffer) => Poll::Ready(Ok(buffer)),
479                        // End-of-stream was signaled but ranges in [start, end)
480                        // were consumed elsewhere (e.g. by a dropped earlier
481                        // read) and can never be re-read. Fail loud rather
482                        // than fabricate zero-filled bytes.
483                        None => Poll::Ready(Err(ReadToEndError::MissingData)),
484                    };
485                }
486            }
487        }
488    }
489}
490
491/// Assemble unordered chunks into a contiguous buffer
492///
493/// Returns `None` unless the chunks tile `[start, end)` exactly — each byte
494/// present once, with no gaps and no overlaps. A `None` means part of the
495/// stream was consumed but never delivered to this reader; zero-filling the
496/// holes (the previous behavior) would silently corrupt the result.
497fn assemble_unordered_chunks(chunks: &mut [(Bytes, u64)], start: u64, end: u64) -> Option<Vec<u8>> {
498    chunks.sort_unstable_by_key(|&(_, offset)| offset);
499    let mut buffer = Vec::with_capacity((end - start) as usize);
500    for (data, offset) in chunks.iter() {
501        let expected = start + buffer.len() as u64;
502        if *offset > expected {
503            // Gap: a range in [start, end) was consumed but never delivered
504            // to this reader. Zero-filling it (the previous behavior) would
505            // silently corrupt the result.
506            return None;
507        }
508        // Benign overlap (duplicate delivery, e.g. across the
509        // ordered->unordered transition): skip the already-assembled prefix.
510        let skip = (expected - *offset) as usize;
511        if skip < data.len() {
512            buffer.extend_from_slice(&data[skip..]);
513        }
514    }
515    // `end` is the maximum chunk end, so full coverage always lands on it
516    if start + buffer.len() as u64 == end {
517        Some(buffer)
518    } else {
519        None
520    }
521}
522
523/// Errors from [`RecvStream::read_to_end`]
524#[derive(Debug, Error, Clone, PartialEq, Eq)]
525pub enum ReadToEndError {
526    /// An error occurred during reading
527    #[error("read error: {0}")]
528    Read(#[from] ReadError),
529    /// The stream is larger than the user-supplied limit
530    #[error("stream too long")]
531    TooLong,
532    /// The stream ended, but ranges of it were consumed and never delivered to this call
533    ///
534    /// Returning a buffer would require fabricating the missing bytes (previously they were
535    /// silently zero-filled — a data-integrity hazard for consumers without payload checksums).
536    /// This happens after an earlier `read_to_end` future on this stream was dropped mid-read
537    /// (`read_to_end` is not cancel-safe), or when prior unordered reads left a hole between
538    /// the chunks this call observed.
539    #[error("stream ended with undelivered data ranges")]
540    MissingData,
541}
542
543impl tokio::io::AsyncRead for RecvStream {
544    fn poll_read(
545        self: Pin<&mut Self>,
546        cx: &mut Context<'_>,
547        buf: &mut ReadBuf<'_>,
548    ) -> Poll<io::Result<()>> {
549        ready!(Self::poll_read_buf(self.get_mut(), cx, buf))?;
550        Poll::Ready(Ok(()))
551    }
552}
553
554impl Drop for RecvStream {
555    fn drop(&mut self) {
556        let mut conn = self.conn.state.lock("RecvStream::drop");
557
558        // clean up any previously registered wakers
559        conn.blocked_readers.remove(&self.stream);
560
561        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
562            return;
563        }
564        if !self.all_data_read {
565            // Ignore ClosedStream errors
566            let _ = conn.inner.recv_stream(self.stream).stop(0u32.into());
567            conn.wake();
568        }
569    }
570}
571
572/// Errors that arise from reading from a stream.
573#[derive(Debug, Error, Clone, PartialEq, Eq)]
574pub enum ReadError {
575    /// The peer abandoned transmitting data on this stream
576    ///
577    /// Carries an application-defined error code.
578    #[error("stream reset by peer: error {0}")]
579    Reset(VarInt),
580    /// The connection was lost
581    #[error("connection lost")]
582    ConnectionLost(#[from] ConnectionError),
583    /// The stream has already been stopped, finished, or reset
584    #[error("closed stream")]
585    ClosedStream,
586    /// Attempted an ordered read following an unordered read
587    ///
588    /// Performing an unordered read allows discontinuities to arise in the receive buffer of a
589    /// stream which cannot be recovered, making further ordered reads impossible.
590    #[error("ordered read after unordered read")]
591    IllegalOrderedRead,
592    /// This was a 0-RTT stream and the server rejected it
593    ///
594    /// Can only occur on clients for 0-RTT streams, which can be opened using
595    /// [`Connecting::into_0rtt()`].
596    ///
597    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
598    #[error("0-RTT rejected")]
599    ZeroRttRejected,
600}
601
602impl From<ReadableError> for ReadError {
603    fn from(e: ReadableError) -> Self {
604        match e {
605            ReadableError::ClosedStream => Self::ClosedStream,
606            ReadableError::IllegalOrderedRead => Self::IllegalOrderedRead,
607            ReadableError::ConnectionClosed => Self::ConnectionLost(ConnectionError::LocallyClosed),
608        }
609    }
610}
611
612impl From<ResetError> for ReadError {
613    fn from(e: ResetError) -> Self {
614        match e {
615            ResetError::ConnectionLost(e) => Self::ConnectionLost(e),
616            ResetError::ZeroRttRejected => Self::ZeroRttRejected,
617        }
618    }
619}
620
621impl From<ReadError> for io::Error {
622    fn from(x: ReadError) -> Self {
623        use ReadError::*;
624        let kind = match x {
625            Reset { .. } | ZeroRttRejected => io::ErrorKind::ConnectionReset,
626            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
627            IllegalOrderedRead => io::ErrorKind::InvalidInput,
628        };
629        Self::new(kind, x)
630    }
631}
632
633/// Errors that arise while waiting for a stream to be reset
634#[derive(Debug, Error, Clone, PartialEq, Eq)]
635pub enum ResetError {
636    /// The connection was lost
637    #[error("connection lost")]
638    ConnectionLost(#[from] ConnectionError),
639    /// This was a 0-RTT stream and the server rejected it
640    ///
641    /// Can only occur on clients for 0-RTT streams, which can be opened using
642    /// [`Connecting::into_0rtt()`].
643    ///
644    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
645    #[error("0-RTT rejected")]
646    ZeroRttRejected,
647}
648
649impl From<ResetError> for io::Error {
650    fn from(x: ResetError) -> Self {
651        use ResetError::*;
652        let kind = match x {
653            ZeroRttRejected => io::ErrorKind::ConnectionReset,
654            ConnectionLost(_) => io::ErrorKind::NotConnected,
655        };
656        Self::new(kind, x)
657    }
658}
659
660/// Future produced by [`RecvStream::read()`].
661///
662/// [`RecvStream::read()`]: crate::RecvStream::read
663struct Read<'a> {
664    stream: &'a mut RecvStream,
665    buf: ReadBuf<'a>,
666}
667
668impl Future for Read<'_> {
669    type Output = Result<Option<usize>, ReadError>;
670
671    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
672        let this = self.get_mut();
673        ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
674        match this.buf.filled().len() {
675            0 if this.buf.capacity() != 0 => Poll::Ready(Ok(None)),
676            n => Poll::Ready(Ok(Some(n))),
677        }
678    }
679}
680
681/// Future produced by `RecvStream::read_exact()`.
682struct ReadExact<'a> {
683    stream: &'a mut RecvStream,
684    buf: ReadBuf<'a>,
685}
686
687impl Future for ReadExact<'_> {
688    type Output = Result<(), ReadExactError>;
689    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
690        let this = self.get_mut();
691        let mut remaining = this.buf.remaining();
692        while remaining > 0 {
693            ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
694            let new = this.buf.remaining();
695            if new == remaining {
696                return Poll::Ready(Err(ReadExactError::FinishedEarly(this.buf.filled().len())));
697            }
698            remaining = new;
699        }
700        Poll::Ready(Ok(()))
701    }
702}
703
704/// Errors that arise from reading from a stream.
705#[derive(Debug, Error, Clone, PartialEq, Eq)]
706pub enum ReadExactError {
707    /// The stream finished before all bytes were read
708    #[error("stream finished early ({0} bytes read)")]
709    FinishedEarly(usize),
710    /// A read error occurred
711    #[error(transparent)]
712    ReadError(#[from] ReadError),
713}
714
715/// Future produced by `RecvStream::read_chunk()`.
716struct ReadChunk<'a> {
717    stream: &'a mut RecvStream,
718    max_length: usize,
719    ordered: bool,
720}
721
722impl Future for ReadChunk<'_> {
723    type Output = Result<Option<Chunk>, ReadError>;
724    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
725        let (max_length, ordered) = (self.max_length, self.ordered);
726        self.stream.poll_read_chunk(cx, max_length, ordered)
727    }
728}
729
730/// Future produced by `RecvStream::read_chunks()`.
731struct ReadChunks<'a> {
732    stream: &'a mut RecvStream,
733    bufs: &'a mut [Bytes],
734}
735
736impl Future for ReadChunks<'_> {
737    type Output = Result<Option<usize>, ReadError>;
738    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
739        let this = self.get_mut();
740        this.stream.poll_read_chunks(cx, this.bufs)
741    }
742}
743
744#[cfg(test)]
745mod tests {
746    use super::*;
747
748    fn chunks_of(payload: &[u8], ranges: &[(usize, usize)]) -> Vec<(Bytes, u64)> {
749        ranges
750            .iter()
751            .map(|&(start, end)| (Bytes::copy_from_slice(&payload[start..end]), start as u64))
752            .collect()
753    }
754
755    #[test]
756    fn assemble_accepts_exact_tiling_in_any_order() {
757        let payload: Vec<u8> = (0..10_706).map(|i| (i % 250 + 1) as u8).collect();
758        let mut chunks = chunks_of(&payload, &[(4344, 10_706), (0, 1448), (1448, 4344)]);
759        let buffer =
760            assemble_unordered_chunks(&mut chunks, 0, 10_706).expect("exact tiling must assemble");
761        assert_eq!(buffer, payload);
762    }
763
764    #[test]
765    fn assemble_accepts_nonzero_start() {
766        let payload: Vec<u8> = (0..4096).map(|i| (i % 250 + 1) as u8).collect();
767        let mut chunks = chunks_of(&payload, &[(2048, 4096), (1024, 2048)]);
768        let buffer = assemble_unordered_chunks(&mut chunks, 1024, 4096)
769            .expect("suffix tiling must assemble");
770        assert_eq!(buffer, &payload[1024..]);
771    }
772
773    /// The captured corruption geometry: a 10,706-byte message whose range
774    /// [1085, 3981) (2×1448 bytes) was consumed by a dropped reader. The old
775    /// code shipped this as `Ok` with the hole zero-filled; it must now be
776    /// rejected.
777    #[test]
778    fn assemble_rejects_captured_zero_gap_geometry() {
779        let payload: Vec<u8> = (0..10_706).map(|i| (i % 250 + 1) as u8).collect();
780        let mut chunks = chunks_of(&payload, &[(0, 1085), (3981, 10_706)]);
781        assert!(assemble_unordered_chunks(&mut chunks, 0, 10_706).is_none());
782    }
783
784    /// Overlaps are benign duplicate delivery (e.g. across the
785    /// ordered->unordered transition) — the overlapping prefix is skipped
786    /// and assembly succeeds with each byte appearing exactly once. Only
787    /// gaps (missing data) are fatal.
788    #[test]
789    fn assemble_tolerates_overlapping_chunks() {
790        let payload: Vec<u8> = (0..4096).map(|i| (i % 250 + 1) as u8).collect();
791        let mut chunks = chunks_of(&payload, &[(0, 2048), (1024, 4096)]);
792        let buffer = assemble_unordered_chunks(&mut chunks, 0, 4096).expect("overlap is benign");
793        assert_eq!(buffer, payload);
794    }
795
796    #[test]
797    fn assemble_tolerates_duplicate_chunk() {
798        let payload: Vec<u8> = (0..4096).map(|i| (i % 250 + 1) as u8).collect();
799        let mut chunks = chunks_of(&payload, &[(0, 2048), (0, 2048), (2048, 4096)]);
800        let buffer = assemble_unordered_chunks(&mut chunks, 0, 4096).expect("duplicate is benign");
801        assert_eq!(buffer, payload);
802    }
803}