Skip to main content

moq_uring/quic/noq/
connection.rs

1//! The connection: shared state, the driver task, and the session handle.
2
3use std::cell::RefCell;
4use std::net::SocketAddr;
5use std::rc::{Rc, Weak};
6use std::task::{Context, Poll};
7use std::time::Instant;
8
9use bytes::Bytes;
10use moq_noq_proto::{ConnectionHandle, Dir, StreamId, VarInt};
11use rustc_hash::FxHashMap;
12
13use super::super::{Error, SEGMENT};
14use super::endpoint;
15use crate::udp;
16use crate::worker::Owner;
17
18/// The state shared by every handle and the driver, single-threaded behind
19/// `Rc<RefCell>`.
20pub(crate) type Shared = Rc<Inner>;
21
22/// One GSO train is at most 64 segments; stay a hair under the kernel cap.
23const TRAIN_SEGMENTS: usize = 63;
24
25/// Everything the handles and the driver share, single-threaded.
26///
27/// The connection and the bookkeeping live in separate `RefCell`s so a stream
28/// operation can mutate the connection, drop that borrow, and then register a
29/// waiter without ever holding both.
30pub(crate) struct Inner {
31	pub(crate) conn: RefCell<moq_noq_proto::Connection>,
32	pub(crate) state: RefCell<State>,
33	/// The worker driving this connection, which anything layered on it
34	/// (the WebTransport handshake, say) runs on too.
35	pub(crate) owner: Owner,
36}
37
38pub(crate) struct State {
39	/// Wakes the driver; handles kick it after mutating the connection so
40	/// fresh egress reaches the wire.
41	driver: kio::WaiterList,
42
43	established: bool,
44	establish_waiters: kio::WaiterList,
45
46	/// Whoever is waiting for a peer-initiated stream. noq-proto hands the
47	/// ids out in order, so the queue is its own.
48	accept_bi_waiters: kio::WaiterList,
49	accept_uni_waiters: kio::WaiterList,
50
51	/// Whoever is blocked on the peer's MAX_STREAMS credit.
52	open_waiters: kio::WaiterList,
53
54	/// Per-stream read/write parking, keyed by stream id.
55	///
56	/// FxHash rather than SipHash on all four of these: they sit on the
57	/// driver's per-event path, and noq-proto assigns the ids.
58	readable: FxHashMap<StreamId, kio::WaiterList>,
59	writable: FxHashMap<StreamId, kio::WaiterList>,
60	/// Send streams waiting for their end: a FIN the peer acknowledged, or a
61	/// `STOP_SENDING`.
62	finishing: FxHashMap<StreamId, kio::WaiterList>,
63	/// Every send stream a handle still holds, and how it ended once the
64	/// driver has seen that happen. That is what makes a later close mean
65	/// "already delivered" rather than "we never found out".
66	///
67	/// Keyed on the live handles, because the verdict arrives as an event: a
68	/// stream finished and then dropped before its FIN was acknowledged would
69	/// otherwise leave an entry nobody can ever remove, one per group, for as
70	/// long as the connection lives.
71	sends: FxHashMap<StreamId, Option<End>>,
72
73	datagram_recv_waiters: kio::WaiterList,
74	datagram_send_waiters: kio::WaiterList,
75
76	/// The terminal error, set exactly once; everything fails with it after.
77	closed: Option<Error>,
78	closed_waiters: kio::WaiterList,
79
80	/// The socket died under us: the driver stops where it stands rather than
81	/// waiting out a drain it could never transmit.
82	dead: bool,
83
84	/// The close this side asked for, until the driver has put it on the wire.
85	/// noq raises no event for it, so this is what the terminal error is
86	/// built from.
87	local_close: Option<(u64, String)>,
88}
89
90impl State {
91	fn new() -> Self {
92		Self {
93			driver: kio::WaiterList::new(),
94			established: false,
95			establish_waiters: kio::WaiterList::new(),
96			accept_bi_waiters: kio::WaiterList::new(),
97			accept_uni_waiters: kio::WaiterList::new(),
98			open_waiters: kio::WaiterList::new(),
99			readable: FxHashMap::default(),
100			writable: FxHashMap::default(),
101			finishing: FxHashMap::default(),
102			sends: FxHashMap::default(),
103			datagram_recv_waiters: kio::WaiterList::new(),
104			datagram_send_waiters: kio::WaiterList::new(),
105			closed: None,
106			closed_waiters: kio::WaiterList::new(),
107			dead: false,
108			local_close: None,
109		}
110	}
111
112	/// Drop send stream `id`'s bookkeeping, parking included.
113	fn forget_send(&mut self, id: StreamId) {
114		self.sends.remove(&id);
115		self.finishing.remove(&id);
116		self.writable.remove(&id);
117	}
118
119	/// The same for the read half. Only its own table: the two halves of a
120	/// bidirectional stream are separate handles, and the other one may still
121	/// be parked.
122	fn forget_recv(&mut self, id: StreamId) {
123		self.readable.remove(&id);
124	}
125
126	/// Terminate with `err` (the first one wins) and wake absolutely everyone,
127	/// the driver included: an externally failed driver has to notice.
128	fn fail(&mut self, err: Error) {
129		if self.closed.is_none() {
130			self.closed = Some(err);
131		}
132		self.driver.wake();
133		self.closed_waiters.wake();
134		self.establish_waiters.wake();
135		self.accept_bi_waiters.wake();
136		self.accept_uni_waiters.wake();
137		self.open_waiters.wake();
138		self.datagram_recv_waiters.wake();
139		self.datagram_send_waiters.wake();
140		for waiters in self.readable.values_mut() {
141			waiters.wake();
142		}
143		for waiters in self.writable.values_mut() {
144			waiters.wake();
145		}
146		for waiters in self.finishing.values_mut() {
147			waiters.wake();
148		}
149	}
150}
151
152impl Inner {
153	/// The terminal error, if the connection has one.
154	pub(crate) fn closed(&self) -> Option<Error> {
155		self.state.borrow().closed.clone()
156	}
157
158	/// Wake the driver so it flushes what a handle just queued.
159	pub(crate) fn kick(&self) {
160		self.state.borrow_mut().driver.wake();
161	}
162
163	/// Terminate from outside (the endpoint's socket died): everything fails
164	/// with `err`, and the driver exits on its next poll.
165	pub(crate) fn fail(&self, err: Error) {
166		let mut state = self.state.borrow_mut();
167		state.dead = true;
168		state.fail(err);
169	}
170
171	/// Park `waiter` until stream `id` is writable again.
172	pub(crate) fn park_writable(&self, id: StreamId, waiter: &kio::Waiter) {
173		let mut state = self.state.borrow_mut();
174		waiter.register(state.writable.entry(id).or_default());
175	}
176
177	/// Park `waiter` until stream `id` is readable again.
178	pub(crate) fn park_readable(&self, id: StreamId, waiter: &kio::Waiter) {
179		let mut state = self.state.borrow_mut();
180		waiter.register(state.readable.entry(id).or_default());
181	}
182
183	/// Park `waiter` until send stream `id` reaches its end.
184	pub(crate) fn park_finishing(&self, id: StreamId, waiter: &kio::Waiter) {
185		let mut state = self.state.borrow_mut();
186		waiter.register(state.finishing.entry(id).or_default());
187	}
188
189	/// Start tracking send stream `id`, which a handle now owns.
190	///
191	/// Seeded from noq rather than empty: a peer can open a bidirectional
192	/// stream and stop it before the application ever accepts it, and that
193	/// event arrives with no handle to record it against. noq still knows,
194	/// so a `poll_closed` on the fresh handle reports the stop instead of
195	/// waiting for an event that has already been and gone.
196	pub(crate) fn track(&self, id: StreamId) {
197		// Err means noq has no send half for the id, which is nothing to
198		// report either way.
199		let stopped = self.conn.borrow_mut().send_stream(id).stopped().ok().flatten();
200		self.state
201			.borrow_mut()
202			.sends
203			.insert(id, stopped.map(|code| End::Stopped(code.into_inner())));
204	}
205
206	/// How send stream `id` ended, if the driver saw it end while a handle
207	/// still held it.
208	pub(crate) fn ended(&self, id: StreamId) -> Option<End> {
209		self.state.borrow().sends.get(&id).copied().flatten()
210	}
211
212	/// Forget send stream `id`'s bookkeeping; called when its handle drops.
213	///
214	/// The parking table goes with it. A stream reset on the way out is never
215	/// reported writable again, so nothing else would ever remove the entry,
216	/// and a peer withholding flow control credit could have streams opened
217	/// and cancelled against it without bound.
218	pub(crate) fn forget_send(&self, id: StreamId) {
219		self.state.borrow_mut().forget_send(id);
220	}
221
222	/// The same for the read half, which parks on its own table.
223	pub(crate) fn forget_recv(&self, id: StreamId) {
224		self.state.borrow_mut().forget_recv(id);
225	}
226
227	/// Wake anyone parked on stream `id` being readable.
228	pub(crate) fn wake_readable(&self, id: StreamId) {
229		let mut state = self.state.borrow_mut();
230		if let Some(mut waiters) = state.readable.remove(&id) {
231			waiters.wake();
232		}
233	}
234
235	/// Close the connection with a full-width application code.
236	///
237	/// The [`web_transport_trait::poll::Session::close`] surface narrows codes
238	/// to `u32`; the WebTransport layer maps its codes into HTTP/3's error
239	/// space, which needs the whole varint range.
240	pub(crate) fn close_code(&self, code: u64, reason: &str) {
241		self.conn.borrow_mut().close(
242			Instant::now(),
243			VarInt::from_u64(code).unwrap_or(VarInt::MAX),
244			Bytes::copy_from_slice(reason.as_bytes()),
245		);
246		// noq raises no event for a close the application asked for, so the
247		// terminal error is ours to publish. Not here though: the driver
248		// publishes it once it has staged the CONNECTION_CLOSE, so a caller
249		// that stops driving the worker the moment `poll_closed` resolves has
250		// at least handed the packet over first.
251		let mut state = self.state.borrow_mut();
252		state.local_close.get_or_insert((code, reason.to_string()));
253		state.driver.wake();
254	}
255}
256
257/// A QUIC connection driven by a [`crate::Worker`], usable as a MoQ transport.
258///
259/// Created by [`Endpoint`](super::Endpoint) (or its
260/// [`client::connect`](crate::quic::client::connect) /
261/// [`server::accept`](crate::quic::server::accept) shorthands), already
262/// established. Clones share the connection; each carries its own parking so
263/// concurrent pending operations don't trample each other's wakeups. Dropping
264/// every handle (and every stream) drops the driver's `Rc` peers, but the
265/// driver itself keeps the connection alive until it ends; close explicitly
266/// with [`close`](web_transport_trait::poll::Session::close) (which moq's
267/// session machine does).
268///
269/// That close only records the code: the driver task is what frames the
270/// CONNECTION_CLOSE and hands it to the socket. Drive the worker until
271/// [`poll_closed`](web_transport_trait::poll::Session::poll_closed) resolves
272/// before stopping it, or the packet is never built and the peer idles out
273/// instead.
274pub struct Connection {
275	shared: Shared,
276	// Retains this clone's waiter registrations across polls.
277	park: kio::Park,
278	/// The negotiated ALPN, cached at establishment so `protocol()` can
279	/// borrow from the handle.
280	alpn: Option<String>,
281	/// The SNI the client presented, cached likewise.
282	server_name: Option<String>,
283	/// The peer's address when the handshake completed.
284	remote: SocketAddr,
285}
286
287impl Connection {
288	/// Close the connection with a full-width application code.
289	///
290	/// The [`web_transport_trait::poll::Session::close`] surface narrows codes
291	/// to `u32`; the WebTransport layer maps its codes into HTTP/3's error
292	/// space, which needs the whole varint range.
293	pub(crate) fn close_code(&self, code: u64, reason: &str) {
294		self.shared.close_code(code, reason);
295	}
296
297	/// The worker driving this connection.
298	pub(crate) fn owner(&self) -> &Owner {
299		&self.shared.owner
300	}
301
302	/// The peer's certificate chain in DER, leaf first, or `None` if it
303	/// presented none.
304	///
305	/// A server only sees one when it asked
306	/// ([`ClientAuth`](crate::quic::server::ClientAuth)), and TLS already
307	/// validated it against the configured roots by the time this connection
308	/// exists: an invalid chain fails the handshake instead. So a `Some` here
309	/// is an authenticated peer, and the chain is what names it.
310	pub fn peer_chain(&self) -> Option<Vec<Vec<u8>>> {
311		let conn = self.shared.conn.borrow();
312		let identity = conn.crypto_session().peer_identity()?;
313		let chain = identity
314			.downcast::<Vec<rustls::pki_types::CertificateDer<'static>>>()
315			.ok()?;
316		Some(chain.iter().map(|cert| cert.to_vec()).collect())
317	}
318
319	/// The peer's address as of the handshake; a peer that migrates later
320	/// keeps its connection but not this value.
321	pub fn remote_addr(&self) -> SocketAddr {
322		self.remote
323	}
324
325	/// The SNI the client presented, or `None` if it sent none. Always `None`
326	/// on a dialed connection.
327	pub fn server_name(&self) -> Option<&str> {
328		self.server_name.as_deref()
329	}
330}
331
332impl Clone for Connection {
333	fn clone(&self) -> Self {
334		Self {
335			shared: self.shared.clone(),
336			park: kio::Park::default(),
337			alpn: self.alpn.clone(),
338			server_name: self.server_name.clone(),
339			remote: self.remote,
340		}
341	}
342}
343
344/// Build a connection's shared state and its driver.
345///
346/// The driver future does timers, event sweeps, and egress; ingress arrives
347/// from the endpoint's demux task, which feeds noq-proto directly and
348/// [kicks](Inner::kick) the driver. The caller spawns the future and reclaims
349/// the connection's bookkeeping once it resolves.
350pub(crate) fn launch(
351	owner: &Owner,
352	socket: Rc<udp::Socket>,
353	endpoint: Weak<endpoint::Inner>,
354	key: ConnectionHandle,
355	conn: moq_noq_proto::Connection,
356) -> (Shared, impl Future<Output = ()> + use<>) {
357	let shared = Rc::new(Inner {
358		conn: RefCell::new(conn),
359		state: RefCell::new(State::new()),
360		owner: owner.clone(),
361	});
362
363	let mut driver = Driver {
364		shared: shared.clone(),
365		socket,
366		endpoint,
367		key,
368		deadline: owner.timer(),
369		scratch: Vec::with_capacity(TRAIN_SEGMENTS * SEGMENT),
370		blocked: false,
371	};
372	let future = async move { kio::wait(|waiter| driver.poll(waiter)).await };
373	(shared, future)
374}
375
376/// Own a connection until its handshake is handed to a public handle.
377///
378/// The driver and endpoint keep their own references, so dropping a pending
379/// establishment future without closing it leaves both alive until timeout.
380struct EstablishGuard {
381	shared: Option<Shared>,
382}
383
384impl Drop for EstablishGuard {
385	fn drop(&mut self) {
386		if let Some(shared) = self.shared.take() {
387			shared.close_code(0, "QUIC handshake abandoned");
388		}
389	}
390}
391
392/// Wait out the handshake, yielding the connection's public handle.
393pub(crate) async fn establish(shared: Shared) -> Result<Connection, Error> {
394	let mut guard = EstablishGuard {
395		shared: Some(shared.clone()),
396	};
397	let result = kio::wait(|waiter| {
398		let mut state = shared.state.borrow_mut();
399		if state.established {
400			return Poll::Ready(Ok(()));
401		}
402		if let Some(err) = &state.closed {
403			return Poll::Ready(Err(err.clone()));
404		}
405		waiter.register(&mut state.establish_waiters);
406		Poll::Pending
407	})
408	.await;
409	if result.is_err() {
410		// The driver already reached a terminal state.
411		guard.shared = None;
412	}
413	result?;
414
415	let (alpn, server_name, remote) = {
416		let conn = shared.conn.borrow();
417		let handshake = conn
418			.crypto_session()
419			.handshake_data()
420			.and_then(|data| data.downcast::<moq_noq_proto::crypto::rustls::HandshakeData>().ok());
421		let (alpn, server_name) = match handshake {
422			Some(data) => (
423				data.protocol.map(|proto| String::from_utf8_lossy(&proto).into_owned()),
424				data.server_name,
425			),
426			None => (None, None),
427		};
428		// Multipath is never negotiated here, so the first path is the only one.
429		let remote = conn
430			.network_path(moq_noq_proto::PathId::ZERO)
431			.expect("an established connection has its first path")
432			.remote();
433		(alpn, server_name, remote)
434	};
435
436	let conn = Connection {
437		shared,
438		park: kio::Park::default(),
439		alpn,
440		server_name,
441		remote,
442	};
443	guard.shared = None;
444	Ok(conn)
445}
446
447impl web_transport_trait::poll::Session for Connection {
448	type SendStream = super::SendStream;
449	type RecvStream = super::RecvStream;
450	type Error = Error;
451
452	fn poll_accept_uni(&mut self, cx: &mut Context<'_>) -> Poll<Result<Self::RecvStream, Self::Error>> {
453		let waiter = self.park.hold(cx);
454		if let Some(id) = self.shared.conn.borrow_mut().streams().accept(Dir::Uni) {
455			return Poll::Ready(Ok(super::RecvStream::new(self.shared.clone(), id)));
456		}
457		let mut state = self.shared.state.borrow_mut();
458		if let Some(err) = &state.closed {
459			return Poll::Ready(Err(err.clone()));
460		}
461		waiter.register(&mut state.accept_uni_waiters);
462		Poll::Pending
463	}
464
465	fn poll_accept_bi(
466		&mut self,
467		cx: &mut Context<'_>,
468	) -> Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
469		let waiter = self.park.hold(cx);
470		// The borrow ends before the handles are built: constructing a send
471		// stream reaches back into the connection.
472		let accepted = self.shared.conn.borrow_mut().streams().accept(Dir::Bi);
473		if let Some(id) = accepted {
474			return Poll::Ready(Ok((
475				super::SendStream::new(self.shared.clone(), id),
476				super::RecvStream::new(self.shared.clone(), id),
477			)));
478		}
479		let mut state = self.shared.state.borrow_mut();
480		if let Some(err) = &state.closed {
481			return Poll::Ready(Err(err.clone()));
482		}
483		waiter.register(&mut state.accept_bi_waiters);
484		Poll::Pending
485	}
486
487	fn poll_open_uni(&mut self, cx: &mut Context<'_>) -> Poll<Result<Self::SendStream, Self::Error>> {
488		let waiter = self.park.hold(cx);
489		if let Some(err) = self.shared.closed() {
490			return Poll::Ready(Err(err));
491		}
492		// The peer's MAX_STREAMS credit is what gates us: `open` hands back
493		// nothing while it is spent.
494		let opened = self.shared.conn.borrow_mut().streams().open(Dir::Uni);
495		match opened {
496			Some(id) => Poll::Ready(Ok(super::SendStream::new(self.shared.clone(), id))),
497			None => {
498				let mut state = self.shared.state.borrow_mut();
499				waiter.register(&mut state.open_waiters);
500				Poll::Pending
501			}
502		}
503	}
504
505	fn poll_open_bi(
506		&mut self,
507		cx: &mut Context<'_>,
508	) -> Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
509		let waiter = self.park.hold(cx);
510		if let Some(err) = self.shared.closed() {
511			return Poll::Ready(Err(err));
512		}
513		let opened = self.shared.conn.borrow_mut().streams().open(Dir::Bi);
514		match opened {
515			Some(id) => Poll::Ready(Ok((
516				super::SendStream::new(self.shared.clone(), id),
517				super::RecvStream::new(self.shared.clone(), id),
518			))),
519			None => {
520				let mut state = self.shared.state.borrow_mut();
521				waiter.register(&mut state.open_waiters);
522				Poll::Pending
523			}
524		}
525	}
526
527	fn poll_send_datagram(&mut self, cx: &mut Context<'_>, payload: &[u8]) -> Poll<Result<(), Self::Error>> {
528		let waiter = self.park.hold(cx);
529		if let Some(err) = self.shared.closed() {
530			return Poll::Ready(Err(err));
531		}
532		let payload = Bytes::copy_from_slice(payload);
533		match self.shared.conn.borrow_mut().datagrams().send(payload, false) {
534			Ok(()) => {
535				self.shared.kick();
536				Poll::Ready(Ok(()))
537			}
538			// The send queue is full; a flush frees space.
539			Err(moq_noq_proto::SendDatagramError::Blocked(_)) => {
540				let mut state = self.shared.state.borrow_mut();
541				waiter.register(&mut state.datagram_send_waiters);
542				Poll::Pending
543			}
544			Err(err) => Poll::Ready(Err(Error::Quic(err.to_string()))),
545		}
546	}
547
548	fn poll_recv_datagram(&mut self, cx: &mut Context<'_>) -> Poll<Result<Bytes, Self::Error>> {
549		let waiter = self.park.hold(cx);
550		if let Some(datagram) = self.shared.conn.borrow_mut().datagrams().recv() {
551			return Poll::Ready(Ok(datagram));
552		}
553		let mut state = self.shared.state.borrow_mut();
554		if let Some(err) = &state.closed {
555			return Poll::Ready(Err(err.clone()));
556		}
557		waiter.register(&mut state.datagram_recv_waiters);
558		Poll::Pending
559	}
560
561	fn max_datagram_size(&self) -> usize {
562		self.shared.conn.borrow_mut().datagrams().max_size().unwrap_or(0)
563	}
564
565	fn protocol(&self) -> Option<&str> {
566		self.alpn.as_deref()
567	}
568
569	fn close(&mut self, code: u32, reason: &str) {
570		self.shared.close_code(u64::from(code), reason);
571	}
572
573	fn poll_closed(&mut self, cx: &mut Context<'_>) -> Poll<Self::Error> {
574		let waiter = self.park.hold(cx);
575		let mut state = self.shared.state.borrow_mut();
576		if let Some(err) = &state.closed {
577			return Poll::Ready(err.clone());
578		}
579		waiter.register(&mut state.closed_waiters);
580		Poll::Pending
581	}
582
583	fn stats(&self) -> impl web_transport_trait::Stats {
584		let (stats, path) = {
585			let mut conn = self.shared.conn.borrow_mut();
586			let stats = conn.stats();
587			let path = conn.path_stats(moq_noq_proto::PathId::ZERO).unwrap_or_default();
588			(stats, path)
589		};
590		Stats {
591			bytes_sent: stats.udp_tx.bytes,
592			bytes_received: stats.udp_rx.bytes,
593			bytes_lost: stats.lost_bytes,
594			packets_sent: stats.udp_tx.datagrams,
595			packets_received: stats.udp_rx.datagrams,
596			packets_lost: stats.lost_packets,
597			rtt: path.rtt,
598		}
599	}
600}
601
602impl std::fmt::Debug for Connection {
603	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604		f.debug_struct("Connection").field("alpn", &self.alpn).finish()
605	}
606}
607
608/// A snapshot of noq-proto's counters in [`web_transport_trait::Stats`]
609/// shape.
610struct Stats {
611	bytes_sent: u64,
612	bytes_received: u64,
613	bytes_lost: u64,
614	packets_sent: u64,
615	packets_received: u64,
616	packets_lost: u64,
617	rtt: std::time::Duration,
618}
619
620impl web_transport_trait::Stats for Stats {
621	fn bytes_sent(&self) -> Option<u64> {
622		Some(self.bytes_sent)
623	}
624
625	fn bytes_received(&self) -> Option<u64> {
626		Some(self.bytes_received)
627	}
628
629	fn bytes_lost(&self) -> Option<u64> {
630		Some(self.bytes_lost)
631	}
632
633	fn packets_sent(&self) -> Option<u64> {
634		Some(self.packets_sent)
635	}
636
637	fn packets_received(&self) -> Option<u64> {
638		Some(self.packets_received)
639	}
640
641	fn packets_lost(&self) -> Option<u64> {
642		Some(self.packets_lost)
643	}
644
645	fn rtt(&self) -> Option<std::time::Duration> {
646		Some(self.rtt)
647	}
648
649	/// Nothing, because noq-proto exposes no delivery or pacing rate.
650	///
651	/// The congestion window over the RTT is not a stand-in for one: BBR
652	/// deliberately holds about twice the bandwidth-delay product (nearly
653	/// three times it while starting up), so that number is a multiple of
654	/// what the path will carry, and moq-net feeds this straight into its
655	/// bandwidth allocator and PROBE. A missing sample is a state the model
656	/// already handles (`stats.estimated_send_rate` is an `Option`, and no
657	/// bandwidth producer is created without one); an inflated one is a rate
658	/// an encoder will chase.
659	fn estimated_send_rate(&self) -> Option<u64> {
660		None
661	}
662}
663
664/// The per-connection task: endpoint events, application events, timers, and
665/// packets out. Packets in come from the endpoint's demux task, which feeds
666/// noq-proto directly and kicks this driver.
667struct Driver {
668	shared: Shared,
669	socket: Rc<udp::Socket>,
670	/// The endpoint this connection belongs to, for the events the two of
671	/// them trade (fresh connection ids, retirements, and the drain that
672	/// frees the slot). Weak, because the endpoint owns us.
673	endpoint: Weak<endpoint::Inner>,
674	key: ConnectionHandle,
675	deadline: crate::Timer,
676	/// Egress staging: noq-proto writes into a `Vec`, so a train is built
677	/// here and copied into the socket's registered buffer.
678	scratch: Vec<u8>,
679	/// The last flush found the transmit pool drained, so nothing it owed the
680	/// peer has reached the wire yet.
681	blocked: bool,
682}
683
684impl Driver {
685	fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
686		{
687			let mut state = self.shared.state.borrow_mut();
688			// The endpoint fails the connection from outside when its socket
689			// dies; there is nothing left to drain it through.
690			if state.dead {
691				return Poll::Ready(());
692			}
693			// Register the kick first: a handle mutating the connection after
694			// this turn's sweeps still re-polls us.
695			waiter.register(&mut state.driver);
696		}
697
698		loop {
699			self.endpoint_events();
700			self.sweep();
701
702			if self.shared.state.borrow().dead {
703				return Poll::Ready(());
704			}
705			// A closed connection still owes the peer its CONNECTION_CLOSE
706			// (and a retransmit for each packet that arrives after), so the
707			// driver runs until noq says the drain is over.
708			if self.shared.conn.borrow().is_drained() {
709				return Poll::Ready(());
710			}
711
712			if let Poll::Ready(err) = self.flush(waiter) {
713				self.shared.state.borrow_mut().fail(err);
714				return Poll::Ready(());
715			}
716			self.publish_close();
717
718			// Arm, *then* poll: the poll is what registers the waiter, so
719			// polling before the set would leave the firing to wake nobody
720			// (fatal on a dial nobody answers, where no ingress ever re-polls
721			// us).
722			self.deadline.set(self.shared.conn.borrow_mut().poll_timeout());
723			if self.deadline.poll(waiter).is_pending() {
724				return Poll::Pending;
725			}
726			self.shared.conn.borrow_mut().handle_timeout(Instant::now());
727		}
728	}
729
730	/// Trade events with the endpoint: connection ids to issue and retire, and
731	/// the drain that frees our slot in its table.
732	///
733	/// The endpoint's borrow is never held across the connection's, and the
734	/// demux task borrows them in the same order, so the two cannot deadlock.
735	fn endpoint_events(&mut self) {
736		let Some(endpoint) = self.endpoint.upgrade() else {
737			return;
738		};
739		loop {
740			let event = self.shared.conn.borrow_mut().poll_endpoint_events();
741			let Some(event) = event else {
742				return;
743			};
744			if let Some(event) = endpoint.on_connection_event(self.key, event) {
745				self.shared.conn.borrow_mut().handle_event(event);
746			}
747		}
748	}
749
750	/// Everything event-shaped: establishment, new and readable streams,
751	/// writability, finished sends, received datagrams, and the end.
752	fn sweep(&mut self) {
753		loop {
754			let event = self.shared.conn.borrow_mut().poll();
755			let Some(event) = event else {
756				return;
757			};
758
759			let mut state = self.shared.state.borrow_mut();
760			match event {
761				moq_noq_proto::Event::Connected => {
762					state.established = true;
763					state.establish_waiters.wake();
764				}
765				moq_noq_proto::Event::ConnectionLost { reason } => state.fail(reason.into()),
766				moq_noq_proto::Event::DatagramReceived => state.datagram_recv_waiters.wake(),
767				moq_noq_proto::Event::DatagramsUnblocked => state.datagram_send_waiters.wake(),
768				moq_noq_proto::Event::HandshakeDataReady => {}
769				moq_noq_proto::Event::Stream(event) => sweep_stream(&mut state, event),
770				moq_noq_proto::Event::HandshakeConfirmed
771				| moq_noq_proto::Event::Path(_)
772				| moq_noq_proto::Event::NatTraversal(_) => {}
773			}
774		}
775	}
776
777	/// Publish the terminal error for a close this side asked for.
778	///
779	/// noq raises no event for it, so the driver is what reports it, and
780	/// only once the flush above has staged the CONNECTION_CLOSE, since an
781	/// application is free to stop driving the worker the moment
782	/// `poll_closed` resolves. Staged is not delivered: the send is
783	/// fire-and-forget, so a worker torn down in the same breath can still
784	/// take the packet with it and leave the peer to idle out.
785	fn publish_close(&mut self) {
786		if self.blocked || !self.shared.conn.borrow().is_closed() {
787			return;
788		}
789		let mut state = self.shared.state.borrow_mut();
790		let Some((code, reason)) = state.local_close.take() else {
791			return;
792		};
793		state.fail(Error::App { code, reason });
794	}
795
796	/// Stage one GSO train, then yield so another connection sharing the
797	/// socket gets a chance at the transmit pool.
798	fn flush(&mut self, waiter: &kio::Waiter) -> Poll<Error> {
799		match self.flush_one(waiter) {
800			Poll::Ready(Ok(())) => {}
801			Poll::Ready(Err(err)) => return Poll::Ready(err),
802			// Backpressure, or nothing left to stage.
803			Poll::Pending => return Poll::Pending,
804		}
805		// Requeue behind the other ready tasks. If noq is drained, the next
806		// poll costs one empty acquire and then parks normally.
807		waiter.waker().wake_by_ref();
808		Poll::Pending
809	}
810
811	/// Fill one transmit buffer and stage it. Ignores noq's pacing hint;
812	/// the congestion controller still bounds each train.
813	fn flush_one(&mut self, waiter: &kio::Waiter) -> Poll<Result<(), Error>> {
814		let mut tx = match self.socket.poll_acquire(waiter) {
815			Poll::Ready(Ok(tx)) => tx,
816			Poll::Ready(Err(err)) => return Poll::Ready(Err(Error::Io(err.to_string()))),
817			// Backpressure: a completed send re-polls us.
818			Poll::Pending => {
819				self.blocked = true;
820				return Poll::Pending;
821			}
822		};
823		self.blocked = false;
824
825		let segments = (tx.len() / SEGMENT).min(TRAIN_SEGMENTS);
826		if segments == 0 {
827			return Poll::Ready(Err(Error::Io(format!(
828				"transmit buffer of {} bytes holds no {SEGMENT} byte segment",
829				tx.len()
830			))));
831		}
832		let segments = std::num::NonZeroUsize::new(segments).expect("segments was checked above");
833
834		self.scratch.clear();
835		let transmit = match self
836			.shared
837			.conn
838			.borrow_mut()
839			.poll_transmit(Instant::now(), segments, &mut self.scratch)
840		{
841			Some(transmit) => transmit,
842			// Nothing to send; the buffer returns to the pool on drop.
843			None => return Poll::Pending,
844		};
845
846		tx[..transmit.size].copy_from_slice(&self.scratch[..transmit.size]);
847		// A lone datagram is its own segment size, and the socket's GSO
848		// stride has to match what noq actually packed.
849		let transmit = udp::Transmit {
850			to: transmit.destination,
851			len: transmit.size,
852			segment: transmit.segment_size.unwrap_or(transmit.size),
853			ecn: transmit.ecn.map(super::ecn_from_noq),
854		};
855		if let Err(err) = tx.send(transmit) {
856			return Poll::Ready(Err(Error::Io(err.to_string())));
857		}
858		// A flush frees datagram-send queue space.
859		self.shared.state.borrow_mut().datagram_send_waiters.wake();
860		Poll::Ready(Ok(()))
861	}
862}
863
864/// How a send stream ended, once the driver has seen it happen.
865#[derive(Clone, Copy, Debug)]
866pub(crate) enum End {
867	/// The peer acknowledged the FIN.
868	Delivered,
869	/// The peer sent `STOP_SENDING` with this code.
870	Stopped(u64),
871}
872
873/// Record how a send stream ended, and wake whoever is watching it.
874///
875/// A stream whose handle has already dropped records nothing: nobody is left
876/// to read the verdict, and the entry would outlive every use for it.
877fn end(state: &mut State, id: StreamId, end: End) {
878	if let Some(slot) = state.sends.get_mut(&id) {
879		*slot = Some(end);
880	}
881	if let Some(mut waiters) = state.finishing.remove(&id) {
882		waiters.wake();
883	}
884}
885
886/// Apply one stream event to the parking tables.
887fn sweep_stream(state: &mut State, event: moq_noq_proto::StreamEvent) {
888	// Waking removes the entry: a still-interested poller re-registers on its
889	// next poll, so the maps only hold streams somebody is parked on.
890	match event {
891		moq_noq_proto::StreamEvent::Opened { dir: Dir::Bi } => state.accept_bi_waiters.wake(),
892		moq_noq_proto::StreamEvent::Opened { dir: Dir::Uni } => state.accept_uni_waiters.wake(),
893		moq_noq_proto::StreamEvent::Available { .. } => state.open_waiters.wake(),
894		moq_noq_proto::StreamEvent::Readable { id } => {
895			if let Some(mut waiters) = state.readable.remove(&id) {
896				waiters.wake();
897			}
898		}
899		moq_noq_proto::StreamEvent::Writable { id } => {
900			if let Some(mut waiters) = state.writable.remove(&id) {
901				waiters.wake();
902			}
903		}
904		moq_noq_proto::StreamEvent::Finished { id } => end(state, id, End::Delivered),
905		moq_noq_proto::StreamEvent::Stopped { id, error_code } => {
906			end(state, id, End::Stopped(error_code.into_inner()));
907			// A writer blocked on capacity has to learn it will never come.
908			if let Some(mut waiters) = state.writable.remove(&id) {
909				waiters.wake();
910			}
911		}
912	}
913}
914
915#[cfg(test)]
916mod tests {
917	use moq_noq_proto::Side;
918
919	use super::*;
920
921	/// A dropped handle takes its parking with it.
922	///
923	/// Nothing else can: a stream reset or stopped on the way out is never
924	/// reported writable or readable again, so an entry left behind sits
925	/// there for the life of the connection, one per cancelled stream.
926	#[test]
927	fn forgetting_a_handle_clears_its_parking() {
928		let mut state = State::new();
929		let id = StreamId::new(Side::Client, Dir::Bi, 0);
930		state.writable.entry(id).or_default();
931		state.readable.entry(id).or_default();
932		state.finishing.entry(id).or_default();
933		state.sends.insert(id, None);
934
935		state.forget_send(id);
936		assert!(state.writable.is_empty(), "the write half's parking");
937		assert!(state.finishing.is_empty(), "the finish watch");
938		assert!(state.sends.is_empty(), "the end bookkeeping");
939		// The read half is a separate handle, which may still be parked.
940		assert!(!state.readable.is_empty(), "the read half's parking survives");
941
942		state.forget_recv(id);
943		assert!(state.readable.is_empty(), "the read half's parking");
944	}
945}