noq/
send_stream.rs

1use std::{
2    future::{Future, poll_fn},
3    io,
4    pin::{Pin, pin},
5    task::{Context, Poll},
6};
7
8use bytes::Bytes;
9use pin_project_lite::pin_project;
10use proto::{ClosedStream, ConnectionError, FinishError, StreamId};
11use thiserror::Error;
12use tokio::sync::futures::OwnedNotified;
13
14use crate::{
15    VarInt,
16    connection::{ConnectionRef, State},
17};
18
19/// A stream that can only be used to send data
20///
21/// If dropped, streams that haven't been explicitly [`reset()`] will be implicitly [`finish()`]ed,
22/// continuing to (re)transmit previously written data until it has been fully acknowledged or the
23/// connection is closed.
24///
25/// # Cancellation
26///
27/// A `write` method is said to be *cancel-safe* when dropping its future before the future becomes
28/// ready will always result in no data being written to the stream. This is true of methods which
29/// succeed immediately when any progress is made, and is not true of methods which might need to
30/// perform multiple writes internally before succeeding. Each `write` method documents whether it
31/// is cancel-safe.
32///
33/// [`reset()`]: SendStream::reset
34/// [`finish()`]: SendStream::finish
35#[derive(Debug)]
36pub struct SendStream {
37    conn: ConnectionRef,
38    stream: StreamId,
39    is_0rtt: bool,
40}
41
42impl SendStream {
43    pub(crate) fn new(conn: ConnectionRef, stream: StreamId, is_0rtt: bool) -> Self {
44        Self {
45            conn,
46            stream,
47            is_0rtt,
48        }
49    }
50
51    /// Write a buffer into this stream, returning how many bytes were written
52    ///
53    /// Unless this method errors, it waits until some amount of `buf` can be written into this
54    /// stream, and then writes as much as it can without waiting again. Due to congestion and flow
55    /// control, this may be shorter than `buf.len()`. On success this yields the length of the
56    /// prefix that was written.
57    ///
58    /// # Cancel safety
59    ///
60    /// This method is cancellation safe. If this does not resolve, no bytes were written.
61    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, WriteError> {
62        poll_fn(|cx| self.execute_poll(cx, |s| s.write(buf))).await
63    }
64
65    /// Write a buffer into this stream in its entirety
66    ///
67    /// This method repeatedly calls [`write`](Self::write) until all bytes are written, or an
68    /// error occurs.
69    ///
70    /// # Cancel safety
71    ///
72    /// This method is *not* cancellation safe. Even if this does not resolve, some prefix of `buf`
73    /// may have been written when previously polled.
74    pub async fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteError> {
75        while !buf.is_empty() {
76            let written = self.write(buf).await?;
77            buf = &buf[written..];
78        }
79        Ok(())
80    }
81
82    /// Writes [`Bytes`] from a slice of buffers into this stream.
83    ///
84    /// Returns how many bytes were written.
85    ///
86    /// Bytes to try to write are provided to this method as an array of cheaply cloneable chunks.
87    /// Unless this method errors, it waits until some amount of those bytes can be written into
88    /// this stream, and then writes as much as it can without waiting again. Due to congestion and
89    /// flow control, this may be less than the total number of bytes.
90    ///
91    /// On success, this method both mutates `bufs` and returns the number of bytes written:
92    ///
93    /// - `bufs` is advanced past chunks that were fully written.
94    /// - If a [`Bytes`] chunk was partially written, the chunk at the new front of `bufs` is [split
95    ///   to](Bytes::split_to) contain only the suffix of bytes that were not written.
96    ///
97    /// # Cancel safety
98    ///
99    /// This method is cancellation safe. If this does not resolve, no bytes were written.
100    pub async fn write_many_chunks(
101        &mut self,
102        bufs: &mut &mut [Bytes],
103    ) -> Result<usize, WriteError> {
104        poll_fn(|cx| self.execute_poll(cx, |s| s.write_chunks(bufs))).await
105    }
106
107    /// Writes a single [`Bytes`] into this stream in its entirety.
108    ///
109    /// Bytes to write are provided to this method as a single cheaply cloneable chunk. This
110    /// method repeatedly calls [`write_many_chunks`](Self::write_many_chunks) until all bytes
111    /// are written, or an error occurs.
112    ///
113    /// # Cancel safety
114    ///
115    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
116    /// been written when previously polled.
117    pub async fn write_chunk(&mut self, buf: Bytes) -> Result<(), WriteError> {
118        self.write_all_chunks(&mut [buf]).await
119    }
120
121    /// Writes a slice of [`Bytes`] into this stream in its entirety.
122    ///
123    /// Bytes to write are provided to this method as an array of cheaply cloneable chunks. This
124    /// method repeatedly calls [`write_many_chunks`](Self::write_many_chunks) until all bytes are
125    /// written, or an error occurs.
126    ///
127    /// # Cancel safety
128    ///
129    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
130    /// been written when previously polled.
131    pub async fn write_all_chunks(&mut self, bufs: &mut [Bytes]) -> Result<(), WriteError> {
132        let mut bufs = &mut bufs[..];
133        while !bufs.is_empty() {
134            self.write_many_chunks(&mut bufs).await?;
135        }
136        Ok(())
137    }
138
139    fn execute_poll<F, R>(
140        &mut self,
141        cx: &mut Context<'_>,
142        write_fn: F,
143    ) -> Poll<Result<R, WriteError>>
144    where
145        F: FnOnce(&mut proto::SendStream<'_>) -> Result<R, proto::WriteError>,
146    {
147        use proto::WriteError::*;
148        let mut conn = self.conn.lock_and_wake("SendStream::poll_write");
149        if self.is_0rtt && conn.check_0rtt().is_err() {
150            conn.skip_waking();
151            return Poll::Ready(Err(WriteError::ZeroRttRejected));
152        }
153        if let Some(conn_err) = conn.error.clone() {
154            conn.skip_waking();
155            return Poll::Ready(Err(WriteError::ConnectionLost(conn_err)));
156        }
157
158        let result = match write_fn(&mut conn.inner.send_stream(self.stream)) {
159            Ok(result) => result,
160            Err(Blocked) => {
161                conn.blocked_writers.insert(self.stream, cx.waker().clone());
162                conn.skip_waking();
163                return Poll::Pending;
164            }
165            Err(Stopped(error_code)) => {
166                conn.skip_waking();
167                return Poll::Ready(Err(WriteError::Stopped(error_code)));
168            }
169            Err(ClosedStream) => {
170                conn.skip_waking();
171                return Poll::Ready(Err(WriteError::ClosedStream));
172            }
173        };
174
175        Poll::Ready(Ok(result))
176    }
177
178    /// Notify the peer that no more data will ever be written to this stream
179    ///
180    /// It is an error to write to a [`SendStream`] after `finish()`ing it. [`reset()`](Self::reset)
181    /// may still be called after `finish` to abandon transmission of any stream data that might
182    /// still be buffered.
183    ///
184    /// To wait for the peer to receive all buffered stream data, see [`stopped()`](Self::stopped).
185    ///
186    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
187    /// called. This error is harmless and serves only to indicate that the caller may have
188    /// incorrect assumptions about the stream's state.
189    pub fn finish(&mut self) -> Result<(), ClosedStream> {
190        let mut conn = self.conn.lock_and_wake("finish");
191        if let Err(e) = conn.inner.send_stream(self.stream).finish() {
192            conn.skip_waking();
193            match e {
194                FinishError::ClosedStream => Err(ClosedStream::default()),
195                // Harmless. If the application needs to know about stopped streams at this point,
196                // it should call `stopped`.
197                FinishError::Stopped(_) => Ok(()),
198            }
199        } else {
200            Ok(())
201        }
202    }
203
204    /// Close the send stream immediately.
205    ///
206    /// No new data can be written after calling this method. Locally buffered data is dropped, and
207    /// previously transmitted data will no longer be retransmitted if lost. If an attempt has
208    /// already been made to finish the stream, the peer may still receive all written data.
209    ///
210    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
211    /// called. This error is harmless and serves only to indicate that the caller may have
212    /// incorrect assumptions about the stream's state.
213    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
214        let mut conn = self.conn.lock_and_wake("SendStream::reset");
215        if self.is_0rtt && conn.check_0rtt().is_err() {
216            conn.skip_waking();
217            return Ok(());
218        }
219        conn.inner.send_stream(self.stream).reset(error_code)?;
220        Ok(())
221    }
222
223    /// Set the priority of the send stream
224    ///
225    /// Every send stream has an initial priority of 0. Locally buffered data from streams with
226    /// higher priority will be transmitted before data from streams with lower priority. Changing
227    /// the priority of a stream with pending data may only take effect after that data has been
228    /// transmitted. Using many different priority levels per connection may have a negative
229    /// impact on performance.
230    pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream> {
231        let mut conn = self.conn.lock_without_waking("SendStream::set_priority");
232        conn.inner.send_stream(self.stream).set_priority(priority)?;
233        Ok(())
234    }
235
236    /// Get the priority of the send stream
237    pub fn priority(&self) -> Result<i32, ClosedStream> {
238        let mut conn = self.conn.lock_without_waking("SendStream::priority");
239        conn.inner.send_stream(self.stream).priority()
240    }
241
242    /// Completes when the peer stops the stream or reads the stream to completion
243    ///
244    /// Yields `Some` with the stop error code if the peer stops the stream. Yields `None` if the
245    /// local side [`finish()`](Self::finish)es the stream and then the peer acknowledges receipt
246    /// of all stream data (although not necessarily the processing of it), after which the peer
247    /// closing the stream is no longer meaningful.
248    ///
249    /// For a variety of reasons, the peer may not send acknowledgements immediately upon receiving
250    /// data. As such, relying on `stopped` to know when the peer has read a stream to completion
251    /// may introduce more latency than using an application-level response of some sort.
252    ///
253    /// Clients may wish to await this after finishing a unidirectional 0-RTT stream to reliably
254    /// determine whether the stream was rejected.
255    pub fn stopped(&self) -> Stopped {
256        let notified = {
257            // Create an `OwnedNotified` to move into the future. By creating it before the first
258            // poll, we make sure that we don't miss any notifications.
259            let mut conn = self.conn.lock_without_waking("SendStream::stopped");
260            conn.stopped
261                .entry(self.stream)
262                .or_default()
263                .clone()
264                .notified_owned()
265        };
266        Stopped {
267            conn: self.conn.clone(),
268            stream: self.stream,
269            is_0rtt: self.is_0rtt,
270            notified,
271        }
272    }
273
274    /// Get the identity of this stream
275    pub fn id(&self) -> StreamId {
276        self.stream
277    }
278
279    /// Attempt to write bytes from buf into the stream.
280    ///
281    /// On success, returns Poll::Ready(Ok(num_bytes_written)).
282    ///
283    /// If the stream is not ready for writing, the method returns Poll::Pending and arranges
284    /// for the current task (via cx.waker().wake_by_ref()) to receive a notification when the
285    /// stream becomes writable or is closed.
286    pub fn poll_write(
287        self: Pin<&mut Self>,
288        cx: &mut Context<'_>,
289        buf: &[u8],
290    ) -> Poll<Result<usize, WriteError>> {
291        pin!(self.get_mut().write(buf)).as_mut().poll(cx)
292    }
293}
294
295/// Check if a send stream is stopped.
296///
297/// Returns `Some` if the stream is stopped or the connection is closed.
298/// Returns `None` if the stream is not stopped.
299fn send_stream_stopped(
300    conn: &mut State,
301    stream: StreamId,
302    is_0rtt: bool,
303) -> Option<Result<Option<VarInt>, StoppedError>> {
304    if is_0rtt && conn.check_0rtt().is_err() {
305        return Some(Err(StoppedError::ZeroRttRejected));
306    }
307    match conn.inner.send_stream(stream).stopped() {
308        Err(ClosedStream { .. }) => Some(Ok(None)),
309        Ok(Some(error_code)) => Some(Ok(Some(error_code))),
310        Ok(None) => conn.error.clone().map(|error| Err(error.into())),
311    }
312}
313
314#[cfg(feature = "futures-io")]
315impl futures_io::AsyncWrite for SendStream {
316    fn poll_write(
317        self: Pin<&mut Self>,
318        cx: &mut Context<'_>,
319        buf: &[u8],
320    ) -> Poll<io::Result<usize>> {
321        self.poll_write(cx, buf).map_err(Into::into)
322    }
323
324    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
325        Poll::Ready(Ok(()))
326    }
327
328    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
329        Poll::Ready(self.get_mut().finish().map_err(Into::into))
330    }
331}
332
333impl tokio::io::AsyncWrite for SendStream {
334    fn poll_write(
335        self: Pin<&mut Self>,
336        cx: &mut Context<'_>,
337        buf: &[u8],
338    ) -> Poll<io::Result<usize>> {
339        self.poll_write(cx, buf).map_err(Into::into)
340    }
341
342    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
343        Poll::Ready(Ok(()))
344    }
345
346    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
347        Poll::Ready(self.get_mut().finish().map_err(Into::into))
348    }
349}
350
351impl Drop for SendStream {
352    fn drop(&mut self) {
353        let mut conn = self.conn.lock_and_wake("SendStream::drop");
354
355        // clean up any previously registered wakers
356        conn.blocked_writers.remove(&self.stream);
357
358        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
359            conn.skip_waking();
360            return;
361        }
362        match conn.inner.send_stream(self.stream).finish() {
363            Ok(()) => {}
364            Err(FinishError::Stopped(reason)) => {
365                if conn.inner.send_stream(self.stream).reset(reason).is_err() {
366                    conn.skip_waking()
367                }
368            }
369            // Already finished or reset, which is fine.
370            Err(FinishError::ClosedStream) => {
371                conn.skip_waking();
372            }
373        }
374    }
375}
376
377/// Errors that arise from writing to a stream
378#[derive(Debug, Error, Clone, PartialEq, Eq)]
379pub enum WriteError {
380    /// The peer is no longer accepting data on this stream
381    ///
382    /// Carries an application-defined error code.
383    #[error("sending stopped by peer: error {0}")]
384    Stopped(VarInt),
385    /// The connection was lost
386    #[error("connection lost")]
387    ConnectionLost(#[from] ConnectionError),
388    /// The stream has already been finished or reset
389    #[error("closed stream")]
390    ClosedStream,
391    /// This was a 0-RTT stream and the server rejected it
392    ///
393    /// Can only occur on clients for 0-RTT streams, which can be opened using
394    /// [`Connecting::into_0rtt()`].
395    ///
396    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
397    #[error("0-RTT rejected")]
398    ZeroRttRejected,
399}
400
401impl From<ClosedStream> for WriteError {
402    #[inline]
403    fn from(_: ClosedStream) -> Self {
404        Self::ClosedStream
405    }
406}
407
408impl From<StoppedError> for WriteError {
409    fn from(x: StoppedError) -> Self {
410        match x {
411            StoppedError::ConnectionLost(e) => Self::ConnectionLost(e),
412            StoppedError::ZeroRttRejected => Self::ZeroRttRejected,
413        }
414    }
415}
416
417impl From<WriteError> for io::Error {
418    fn from(x: WriteError) -> Self {
419        use WriteError::*;
420        let kind = match x {
421            Stopped(_) | ZeroRttRejected => io::ErrorKind::ConnectionReset,
422            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
423        };
424        Self::new(kind, x)
425    }
426}
427
428/// Errors that arise while monitoring for a send stream stop from the peer
429#[derive(Debug, Error, Clone, PartialEq, Eq)]
430pub enum StoppedError {
431    /// The connection was lost
432    #[error("connection lost")]
433    ConnectionLost(#[from] ConnectionError),
434    /// This was a 0-RTT stream and the server rejected it
435    ///
436    /// Can only occur on clients for 0-RTT streams, which can be opened using
437    /// [`Connecting::into_0rtt()`].
438    ///
439    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
440    #[error("0-RTT rejected")]
441    ZeroRttRejected,
442}
443
444impl From<StoppedError> for io::Error {
445    fn from(x: StoppedError) -> Self {
446        use StoppedError::*;
447        let kind = match x {
448            ZeroRttRejected => io::ErrorKind::ConnectionReset,
449            ConnectionLost(_) => io::ErrorKind::NotConnected,
450        };
451        Self::new(kind, x)
452    }
453}
454
455pin_project! {
456    /// Future returned from [`SendStream::stopped`].
457    #[derive(Debug)]
458    pub struct Stopped {
459        conn: ConnectionRef,
460        stream: StreamId,
461        is_0rtt: bool,
462        #[pin]
463        notified: OwnedNotified,
464    }
465}
466
467impl Future for Stopped {
468    type Output = Result<Option<VarInt>, StoppedError>;
469
470    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
471        let mut this = self.project();
472        loop {
473            let mut conn = this.conn.lock_without_waking("SendStream::stopped");
474            // Check if the stream is stopped before polling the notify. This makes sure that
475            // no wakeups are missed.
476            if let Some(output) = send_stream_stopped(&mut conn, *this.stream, *this.is_0rtt) {
477                return Poll::Ready(output);
478            }
479            std::task::ready!(this.notified.as_mut().poll(cx));
480        }
481    }
482}
483
484#[cfg(test)]
485mod tests {
486    fn check_is_send_sync<A: Send + Sync>() {}
487
488    #[allow(dead_code)]
489    fn test_bounds() {
490        check_is_send_sync::<super::Stopped>();
491    }
492}