Skip to main content

moq_net/
transport.rs

1//! The transport interface a MoQ session runs over.
2//!
3//! This crate is poll-only: every entry point ([`crate::Client::connect`],
4//! [`crate::Server::accept`]) is generic over the *poll* half of
5//! `web_transport_trait` ([`web_transport_trait::poll`]), never its async half.
6//! The [`poll::Session`], [`poll::SendStream`], and [`poll::RecvStream`] traits
7//! here bundle those poll traits with the bounds the session machinery needs
8//! (cloning, thread affinity) and provide async helper methods on top, the same
9//! layering the rest of the crate uses (`async fn` wraps `poll_*`).
10//!
11//! A transport that only implements the async half cannot be used directly:
12//! implement the poll interface for it (typically inside the transport itself,
13//! where its wakers live) rather than wrapping futures around it here.
14
15/// The poll-based transport traits this crate requires.
16///
17/// These mirror [`web_transport_trait::poll`], adding the bounds the session
18/// machinery needs and async helper methods; they are implemented automatically
19/// for any type implementing the upstream poll traits with those bounds.
20pub mod poll {
21	use std::task::{Context, Poll, ready};
22
23	use bytes::{Buf, BufMut, Bytes};
24	use web_transport_trait::{MaybeSend, MaybeSync, poll};
25
26	/// The transport session a MoQ session runs over.
27	///
28	/// This is [`web_transport_trait::poll::Session`] plus the bounds the protocol
29	/// drivers need: `Clone` so each concurrently pending operation gets its own
30	/// handle (each clone carries its own in-progress state, per the poll contract),
31	/// and `'static` so drivers can own it for the session's lifetime. There is
32	/// deliberately no thread-affinity bound: a pinned `!Send` transport drives a
33	/// `!Send` machine on its own thread. The [`Boxable`] subset is for the parts
34	/// that still erase into `Send` boxes. It is implemented automatically.
35	///
36	/// The async methods are helpers over the required `poll_*` methods, so callers
37	/// can `.await` operations without giving up the ability to poll them.
38	pub trait Session: poll::Session<SendStream: SendStream, RecvStream: RecvStream> + Clone + 'static {
39		/// Accept the next unidirectional stream opened by the peer.
40		fn accept_uni(&mut self) -> AcceptUni<'_, Self> {
41			AcceptUni(self)
42		}
43
44		/// Accept the next bidirectional stream opened by the peer.
45		fn accept_bi(&mut self) -> AcceptBi<'_, Self> {
46			AcceptBi(self)
47		}
48
49		/// Open a unidirectional stream, waiting for stream credit if necessary.
50		fn open_uni(&mut self) -> OpenUni<'_, Self> {
51			OpenUni(self)
52		}
53
54		/// Open a bidirectional stream, waiting for stream credit if necessary.
55		fn open_bi(&mut self) -> OpenBi<'_, Self> {
56			OpenBi(self)
57		}
58
59		/// Receive the next datagram from the peer.
60		fn recv_datagram(&mut self) -> RecvDatagram<'_, Self> {
61			RecvDatagram(self)
62		}
63
64		/// Send a datagram, best-effort: if the transport has no room for it right
65		/// now, the datagram is dropped, exactly as the network is allowed to do.
66		fn send_datagram(&mut self, payload: &[u8]) -> Result<(), Self::Error> {
67			let mut cx = Context::from_waker(std::task::Waker::noop());
68			match self.poll_send_datagram(&mut cx, payload) {
69				Poll::Ready(res) => res,
70				Poll::Pending => Ok(()),
71			}
72		}
73
74		/// Wait until the session is closed by either side, returning the reason.
75		fn closed(&mut self) -> SessionClosed<'_, Self> {
76			SessionClosed(self)
77		}
78	}
79
80	/// One in-flight `poll_*` operation as a [`Future`]: the helpers below are
81	/// named types (not `impl Future`) so their `Send`-ness stays inferred from
82	/// the transport; an opaque return type in a trait would hide it.
83	macro_rules! poll_future {
84		($(#[$doc:meta])* $name:ident, $bound:path, $poll:ident, $out:ty) => {
85			$(#[$doc])*
86			pub struct $name<'a, S: ?Sized>(&'a mut S);
87
88			impl<S: $bound> Future for $name<'_, S> {
89				type Output = $out;
90
91				fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
92					self.0.$poll(cx)
93				}
94			}
95		};
96	}
97
98	poll_future!(
99		/// A pending [`Session::accept_uni`].
100		AcceptUni, poll::Session, poll_accept_uni, Result<S::RecvStream, S::Error>);
101	poll_future!(
102		/// A pending [`Session::accept_bi`].
103		AcceptBi, poll::Session, poll_accept_bi, Result<poll::BiStreams<S>, S::Error>);
104	poll_future!(
105		/// A pending [`Session::open_uni`].
106		OpenUni, poll::Session, poll_open_uni, Result<S::SendStream, S::Error>);
107	poll_future!(
108		/// A pending [`Session::open_bi`].
109		OpenBi, poll::Session, poll_open_bi, Result<poll::BiStreams<S>, S::Error>);
110	poll_future!(
111		/// A pending [`Session::recv_datagram`].
112		RecvDatagram, poll::Session, poll_recv_datagram, Result<Bytes, S::Error>);
113	poll_future!(
114		/// A pending [`Session::closed`].
115		SessionClosed, poll::Session, poll_closed, S::Error);
116	poll_future!(
117		/// A pending [`SendStream::closed`].
118		SendClosed, poll::SendStream, poll_closed, Result<(), S::Error>);
119	poll_future!(
120		/// A pending [`RecvStream::closed`].
121		RecvClosed, poll::RecvStream, poll_closed, Result<(), S::Error>);
122
123	impl<S> Session for S where S: poll::Session<SendStream: SendStream, RecvStream: RecvStream> + Clone + 'static {}
124
125	/// A transport whose session, streams, and errors can be captured by the
126	/// boxed drivers (`Send` boxes on native): what the moq-transport path
127	/// requires until it too becomes named machines. Implemented automatically.
128	pub trait Boxable:
129		Session<SendStream: MaybeSend, RecvStream: MaybeSend, Error: MaybeSend> + MaybeSend + MaybeSync
130	{
131	}
132
133	impl<S> Boxable for S where
134		S: Session<SendStream: MaybeSend, RecvStream: MaybeSend, Error: MaybeSend> + MaybeSend + MaybeSync
135	{
136	}
137
138	/// An outgoing transport stream: [`web_transport_trait::poll::SendStream`]
139	/// plus the `'static` bound the drivers need, with async helpers.
140	pub trait SendStream: poll::SendStream + 'static {
141		/// Write some of the buffer, returning how many bytes were accepted.
142		fn write<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self> {
143			Write { stream: self, buf }
144		}
145
146		/// Write some of the buffer, advancing it by the bytes accepted.
147		fn write_buf<'a, B: Buf>(&'a mut self, buf: &'a mut B) -> WriteBuf<'a, Self, B> {
148			WriteBuf { stream: self, buf }
149		}
150
151		/// Write the entire chunk to the stream.
152		fn write_chunk(&mut self, chunk: Bytes) -> WriteChunk<'_, Self> {
153			WriteChunk { stream: self, chunk }
154		}
155
156		/// Wait until the stream is closed by either side.
157		fn closed(&mut self) -> SendClosed<'_, Self> {
158			SendClosed(self)
159		}
160	}
161
162	/// A pending [`SendStream::write`].
163	pub struct Write<'a, S: ?Sized> {
164		stream: &'a mut S,
165		buf: &'a [u8],
166	}
167
168	impl<S: poll::SendStream> Future for Write<'_, S> {
169		type Output = Result<usize, S::Error>;
170
171		fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
172			let this = &mut *self;
173			this.stream.poll_write(cx, this.buf)
174		}
175	}
176
177	/// A pending [`SendStream::write_buf`].
178	pub struct WriteBuf<'a, S: ?Sized, B> {
179		stream: &'a mut S,
180		buf: &'a mut B,
181	}
182
183	impl<S: poll::SendStream, B: Buf> Future for WriteBuf<'_, S, B> {
184		type Output = Result<usize, S::Error>;
185
186		fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
187			let this = &mut *self;
188			this.stream.poll_write_buf(cx, this.buf)
189		}
190	}
191
192	/// A pending [`SendStream::write_chunk`].
193	pub struct WriteChunk<'a, S: ?Sized> {
194		stream: &'a mut S,
195		chunk: Bytes,
196	}
197
198	impl<S: poll::SendStream> Future for WriteChunk<'_, S> {
199		type Output = Result<(), S::Error>;
200
201		fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
202			let this = &mut *self;
203			while !this.chunk.is_empty() {
204				ready!(this.stream.poll_write_buf(cx, &mut this.chunk))?;
205			}
206			Poll::Ready(Ok(()))
207		}
208	}
209
210	impl<S> SendStream for S where S: poll::SendStream + 'static {}
211
212	/// An incoming transport stream: [`web_transport_trait::poll::RecvStream`]
213	/// plus the `'static` bound the drivers need, with async helpers.
214	pub trait RecvStream: poll::RecvStream + 'static {
215		/// Read some bytes into the slice, or `None` once the stream is finished.
216		fn read<'a>(&'a mut self, dst: &'a mut [u8]) -> Read<'a, Self> {
217			Read { stream: self, dst }
218		}
219
220		/// Read some bytes into the buffer, advancing it, or `None` once finished.
221		fn read_buf<'a, B: BufMut>(&'a mut self, buf: &'a mut B) -> ReadBuf<'a, Self, B> {
222			ReadBuf { stream: self, buf }
223		}
224
225		/// Read the next chunk of data, up to `max` bytes, or `None` once finished.
226		fn read_chunk(&mut self, max: usize) -> ReadChunk<'_, Self> {
227			ReadChunk { stream: self, max }
228		}
229
230		/// Wait until the stream is closed by either side.
231		fn closed(&mut self) -> RecvClosed<'_, Self> {
232			RecvClosed(self)
233		}
234	}
235
236	/// A pending [`RecvStream::read`].
237	pub struct Read<'a, S: ?Sized> {
238		stream: &'a mut S,
239		dst: &'a mut [u8],
240	}
241
242	impl<S: poll::RecvStream> Future for Read<'_, S> {
243		type Output = Result<Option<usize>, S::Error>;
244
245		fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
246			let this = &mut *self;
247			this.stream.poll_read(cx, this.dst)
248		}
249	}
250
251	/// A pending [`RecvStream::read_buf`].
252	pub struct ReadBuf<'a, S: ?Sized, B> {
253		stream: &'a mut S,
254		buf: &'a mut B,
255	}
256
257	impl<S: poll::RecvStream, B: BufMut> Future for ReadBuf<'_, S, B> {
258		type Output = Result<Option<usize>, S::Error>;
259
260		fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
261			let this = &mut *self;
262			this.stream.poll_read_buf(cx, this.buf)
263		}
264	}
265
266	/// A pending [`RecvStream::read_chunk`].
267	pub struct ReadChunk<'a, S: ?Sized> {
268		stream: &'a mut S,
269		max: usize,
270	}
271
272	impl<S: poll::RecvStream> Future for ReadChunk<'_, S> {
273		type Output = Result<Option<Bytes>, S::Error>;
274
275		fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
276			let this = &mut *self;
277			this.stream.poll_read_chunk(cx, this.max)
278		}
279	}
280
281	impl<S> RecvStream for S where S: poll::RecvStream + 'static {}
282}