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