Skip to main content

datum_net/
quic.rs

1//! QUIC endpoints, connections, and bidirectional byte streams.
2//!
3//! [`TokioQuic`] builds on `quinn` and exposes QUIC's reliable, ordered,
4//! flow-controlled bidirectional streams as Datum byte flows. Callers provide
5//! Quinn client/server configs, typically built from rustls configs through
6//! [`crypto::rustls::QuicClientConfig`] and
7//! [`crypto::rustls::QuicServerConfig`].
8
9use crate::async_carrier::{self, AsyncCommandSender, DemandBatcher};
10use datum::{Flow, Keep, NotUsed, Sink, Source, StreamCompletion, StreamError, StreamResult};
11pub use quinn::{self, crypto, rustls};
12use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
13use std::sync::{Arc, Mutex, mpsc as std_mpsc};
14use tokio::net::ToSocketAddrs;
15use tokio::runtime::Handle;
16use tokio::sync::{mpsc, watch};
17use tokio::task::JoinHandle;
18
19/// Default maximum bytes emitted per QUIC byte-source chunk.
20pub const DEFAULT_CHUNK_SIZE: usize = 8192;
21
22const DEFAULT_RECEIVE_BUFFER: usize = 64;
23
24/// Snapshot of QUIC listener/stream-accept resilience counters.
25#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
26pub struct QuicCarrierDiagnosticCounts {
27    pub handshake_failures: u64,
28    pub accept_bi_failures: u64,
29}
30
31/// Shared collector for QUIC listener/stream-accept resilience counters.
32#[derive(Clone, Default)]
33pub struct QuicCarrierDiagnostics {
34    counts: Arc<Mutex<QuicCarrierDiagnosticCounts>>,
35}
36
37impl QuicCarrierDiagnostics {
38    #[must_use]
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    #[must_use]
44    pub fn snapshot(&self) -> QuicCarrierDiagnosticCounts {
45        *self
46            .counts
47            .lock()
48            .unwrap_or_else(|poison| poison.into_inner())
49    }
50
51    fn record_handshake_failure(&self) {
52        let mut counts = self
53            .counts
54            .lock()
55            .unwrap_or_else(|poison| poison.into_inner());
56        counts.handshake_failures = counts.handshake_failures.saturating_add(1);
57    }
58
59    fn record_accept_bi_failure(&self) {
60        let mut counts = self
61            .counts
62            .lock()
63            .unwrap_or_else(|poison| poison.into_inner());
64        counts.accept_bi_failures = counts.accept_bi_failures.saturating_add(1);
65    }
66}
67
68/// QUIC byte source used by accepted and opened bidirectional streams.
69///
70/// The source emits `Vec<u8>` chunks and backpressures the Quinn receive stream
71/// with a bounded demand window, preserving QUIC's reliable flow-control
72/// semantics without a blocking Tokio receive seam.
73pub type QuicByteSource = Source<Vec<u8>, NotUsed>;
74
75/// QUIC byte sink used by accepted and opened bidirectional streams.
76///
77/// The sink writes one upstream chunk at a time and sends a QUIC stream FIN from
78/// its resource close hook when upstream completes.
79pub type QuicByteSink = Sink<Vec<u8>, StreamCompletion<NotUsed>>;
80
81enum DemandResponse<T> {
82    Item(T),
83    Complete,
84    Error(StreamError),
85}
86
87struct ReadResource {
88    receiver: std_mpsc::Receiver<DemandResponse<Vec<u8>>>,
89    carrier: QuicCarrier,
90    demand: DemandBatcher,
91    pending: Option<DemandResponse<Vec<u8>>>,
92}
93
94impl Drop for ReadResource {
95    fn drop(&mut self) {
96        self.carrier.close_read();
97    }
98}
99
100enum QuicCarrierCommand {
101    Demand(usize),
102    SendOne(Vec<u8>),
103    SendBatch(Vec<Vec<u8>>),
104    CloseRead,
105    CloseWrite {
106        ack: std_mpsc::Sender<StreamResult<()>>,
107    },
108}
109
110#[derive(Clone)]
111struct QuicCarrier {
112    inner: Arc<QuicCarrierInner>,
113}
114
115struct QuicCarrierInner {
116    commands: AsyncCommandSender<QuicCarrierCommand>,
117    send_errors: Mutex<std_mpsc::Receiver<StreamError>>,
118    task: Mutex<Option<JoinHandle<()>>>,
119}
120
121impl Drop for QuicCarrierInner {
122    fn drop(&mut self) {
123        if let Some(task) = self
124            .task
125            .lock()
126            .unwrap_or_else(|poison| poison.into_inner())
127            .take()
128        {
129            task.abort();
130        }
131    }
132}
133
134impl QuicCarrier {
135    fn close_read(&self) {
136        let _ = self.inner.commands.try_send(QuicCarrierCommand::CloseRead);
137    }
138
139    fn request_demand(&self, demand: usize) -> StreamResult<()> {
140        self.inner
141            .commands
142            .send_or_blocking(QuicCarrierCommand::Demand(demand))
143    }
144
145    fn send_items(&self, items: Vec<Vec<u8>>) -> StreamResult<()> {
146        self.check_send_error()?;
147        self.inner
148            .commands
149            .send_or_blocking(QuicCarrierCommand::SendBatch(items))
150            .map_err(|error| StreamError::Failed(format!("QUIC send batch failed: {error:?}")))
151    }
152
153    fn send_one(&self, item: Vec<u8>) -> StreamResult<()> {
154        self.check_send_error()?;
155        self.inner
156            .commands
157            .send_or_blocking(QuicCarrierCommand::SendOne(item))
158            .map_err(|error| StreamError::Failed(format!("QUIC send failed: {error:?}")))
159    }
160
161    fn close_write(&self) -> StreamResult<()> {
162        self.check_send_error()?;
163        let (ack_sender, ack_receiver) = std_mpsc::channel();
164        if self
165            .inner
166            .commands
167            .send_or_blocking(QuicCarrierCommand::CloseWrite { ack: ack_sender })
168            .is_err()
169        {
170            return Ok(());
171        }
172        match ack_receiver.recv() {
173            Ok(result) => result,
174            Err(_) => Err(abrupt_termination()),
175        }?;
176        self.check_send_error()
177    }
178
179    fn check_send_error(&self) -> StreamResult<()> {
180        match self
181            .inner
182            .send_errors
183            .lock()
184            .expect("QUIC carrier send error receiver poisoned")
185            .try_recv()
186        {
187            Ok(error) => Err(error),
188            Err(std_mpsc::TryRecvError::Empty) | Err(std_mpsc::TryRecvError::Disconnected) => {
189                Ok(())
190            }
191        }
192    }
193}
194
195struct SendResource {
196    carrier: QuicCarrier,
197    pending: Vec<Vec<u8>>,
198    batch_size: usize,
199}
200
201#[derive(Clone, Copy)]
202struct QuicReadConfig {
203    chunk_size: usize,
204    emit_available: bool,
205}
206
207struct BindResource {
208    demands: mpsc::Sender<std_mpsc::Sender<DemandResponse<QuicIncomingConnection>>>,
209    cancel: watch::Sender<bool>,
210    task: JoinHandle<()>,
211}
212
213impl Drop for BindResource {
214    fn drop(&mut self) {
215        let _ = self.cancel.send(true);
216        self.task.abort();
217    }
218}
219
220struct AcceptBiResource {
221    demands: mpsc::Sender<std_mpsc::Sender<DemandResponse<QuicBidirectionalStream>>>,
222    cancel: watch::Sender<bool>,
223    task: JoinHandle<()>,
224}
225
226impl Drop for AcceptBiResource {
227    fn drop(&mut self) {
228        let _ = self.cancel.send(true);
229        self.task.abort();
230    }
231}
232
233fn quic_error(error: impl std::fmt::Display) -> StreamError {
234    StreamError::Failed(error.to_string())
235}
236
237fn io_error(error: std::io::Error) -> StreamError {
238    StreamError::Failed(error.to_string())
239}
240
241fn abrupt_termination() -> StreamError {
242    StreamError::AbruptTermination
243}
244
245fn close_code() -> quinn::VarInt {
246    quinn::VarInt::from_u32(0)
247}
248
249/// A materialized QUIC listener binding.
250#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub struct QuicBinding {
252    pub local_addr: SocketAddr,
253}
254
255impl QuicBinding {
256    /// Returns the local UDP address the QUIC endpoint is bound to.
257    #[must_use]
258    pub fn local_addr(&self) -> SocketAddr {
259        self.local_addr
260    }
261}
262
263/// Metadata for a materialized QUIC stream.
264#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265pub struct QuicStream {
266    pub id: quinn::StreamId,
267}
268
269impl QuicStream {
270    /// Returns Quinn's stream identifier.
271    #[must_use]
272    pub fn id(&self) -> quinn::StreamId {
273        self.id
274    }
275}
276
277/// A materialized QUIC connection.
278#[derive(Debug, Clone)]
279pub struct QuicConnection {
280    endpoint: quinn::Endpoint,
281    connection: quinn::Connection,
282    handle: Handle,
283    local_addr: SocketAddr,
284    remote_addr: SocketAddr,
285    chunk_size: usize,
286}
287
288impl QuicConnection {
289    /// Returns the local UDP endpoint address for this connection.
290    #[must_use]
291    pub fn local_addr(&self) -> SocketAddr {
292        self.local_addr
293    }
294
295    /// Returns the peer UDP endpoint address for this connection.
296    #[must_use]
297    pub fn remote_addr(&self) -> SocketAddr {
298        self.remote_addr
299    }
300
301    /// Returns the default chunk size used by stream helpers on this connection.
302    #[must_use]
303    pub fn chunk_size(&self) -> usize {
304        self.chunk_size
305    }
306
307    /// Returns the underlying Quinn connection handle.
308    #[must_use]
309    pub fn quinn_connection(&self) -> &quinn::Connection {
310        &self.connection
311    }
312
313    /// Returns the underlying Quinn endpoint handle that owns the UDP socket.
314    #[must_use]
315    pub fn quinn_endpoint(&self) -> &quinn::Endpoint {
316        &self.endpoint
317    }
318
319    /// Opens a bidirectional QUIC stream as a Datum byte flow.
320    ///
321    /// Opening and the stream-id allocation happen when this flow is
322    /// materialized. Quinn only exposes the stream to the peer after the
323    /// initiating side writes data, so a peer-side `accept_bi` will not complete
324    /// until the first write or FIN is sent.
325    #[must_use]
326    pub fn open_bi(
327        &self,
328        chunk_size: usize,
329    ) -> Flow<Vec<u8>, Vec<u8>, StreamCompletion<QuicStream>> {
330        assert!(chunk_size > 0, "chunk size must be greater than zero");
331        let connection = self.connection.clone();
332        let handle = self.handle.clone();
333        Flow::future_flow(move || {
334            let connection = connection.clone();
335            let handle = handle.clone();
336            async move {
337                let (send, recv) = connection.open_bi().await.map_err(quic_error)?;
338                Ok(quic_bi_stream_from_halves(send, recv, handle, chunk_size, false).into_flow())
339            }
340        })
341    }
342
343    /// Opens a bidirectional stream using the connection's default chunk size.
344    #[must_use]
345    pub fn open_bi_default(&self) -> Flow<Vec<u8>, Vec<u8>, StreamCompletion<QuicStream>> {
346        self.open_bi(self.chunk_size)
347    }
348
349    /// Opens a bidirectional QUIC stream and emits the split stream object.
350    ///
351    /// This is the object-shaped counterpart to [`QuicConnection::open_bi`],
352    /// used by protocol carriers that need to drive the byte source and sink
353    /// independently.
354    #[must_use]
355    pub fn open_bi_stream(
356        &self,
357        chunk_size: usize,
358    ) -> Source<QuicBidirectionalStream, StreamCompletion<QuicStream>> {
359        assert!(chunk_size > 0, "chunk size must be greater than zero");
360        let connection = self.connection.clone();
361        let handle = self.handle.clone();
362        Source::lazy_future_source(move || {
363            let connection = connection.clone();
364            let handle = handle.clone();
365            async move {
366                let (send, recv) = connection.open_bi().await.map_err(quic_error)?;
367                let stream = quic_bi_stream_from_halves(send, recv, handle, chunk_size, false);
368                let metadata = stream.stream();
369                let stream = Arc::new(Mutex::new(Some(stream)));
370                Ok(Source::unfold_resource(
371                    {
372                        let stream = Arc::clone(&stream);
373                        move || {
374                            stream
375                                .lock()
376                                .expect("single-use QUIC bidi stream poisoned")
377                                .take()
378                                .map(Some)
379                                .ok_or_else(|| {
380                                    StreamError::Failed(
381                                        "QUIC bidi stream already materialized".into(),
382                                    )
383                                })
384                        }
385                    },
386                    |stream| Ok(stream.take()),
387                    |_stream| Ok(()),
388                )
389                .map_materialized_value(move |_| metadata))
390            }
391        })
392    }
393
394    /// Opens a split bidirectional stream using the connection's default chunk size.
395    #[must_use]
396    pub fn open_bi_stream_default(
397        &self,
398    ) -> Source<QuicBidirectionalStream, StreamCompletion<QuicStream>> {
399        self.open_bi_stream(self.chunk_size)
400    }
401
402    /// Opens a split bidirectional stream with emit-available read mode.
403    ///
404    /// Like [`open_bi_stream`](QuicConnection::open_bi_stream) but the byte source emits chunks as soon as
405    /// any bytes arrive rather than waiting to fill `chunk_size`. Use for
406    /// interactive protocols (e.g. StreamRefs) where small frames must flow
407    /// without accumulating in the read buffer.
408    #[must_use]
409    pub fn open_bi_stream_available(
410        &self,
411        chunk_size: usize,
412    ) -> Source<QuicBidirectionalStream, StreamCompletion<QuicStream>> {
413        assert!(chunk_size > 0, "chunk size must be greater than zero");
414        let connection = self.connection.clone();
415        let handle = self.handle.clone();
416        Source::lazy_future_source(move || {
417            let connection = connection.clone();
418            let handle = handle.clone();
419            async move {
420                let (send, recv) = connection.open_bi().await.map_err(quic_error)?;
421                let stream = quic_bi_stream_from_halves(send, recv, handle, chunk_size, true);
422                let metadata = stream.stream();
423                let stream = Arc::new(Mutex::new(Some(stream)));
424                Ok(Source::unfold_resource(
425                    {
426                        let stream = Arc::clone(&stream);
427                        move || {
428                            stream
429                                .lock()
430                                .expect("single-use QUIC bidi stream poisoned")
431                                .take()
432                                .map(Some)
433                                .ok_or_else(|| {
434                                    StreamError::Failed(
435                                        "QUIC bidi stream already materialized".into(),
436                                    )
437                                })
438                        }
439                    },
440                    |stream| Ok(stream.take()),
441                    |_stream| Ok(()),
442                )
443                .map_materialized_value(move |_| metadata))
444            }
445        })
446    }
447
448    /// Accepts incoming bidirectional QUIC streams.
449    ///
450    /// Each downstream pull accepts one stream. Accepted streams are emitted as
451    /// [`QuicBidirectionalStream`] values that can be split or converted into a
452    /// Datum byte flow.
453    #[must_use]
454    pub fn accept_bi(&self, chunk_size: usize) -> Source<QuicBidirectionalStream, QuicConnection> {
455        self.accept_bi_optional_diagnostics(chunk_size, false, None)
456    }
457
458    /// Accepts incoming bidirectional QUIC streams and records accept failures.
459    #[must_use]
460    pub fn accept_bi_with_diagnostics(
461        &self,
462        chunk_size: usize,
463        diagnostics: QuicCarrierDiagnostics,
464    ) -> Source<QuicBidirectionalStream, QuicConnection> {
465        self.accept_bi_optional_diagnostics(chunk_size, false, Some(diagnostics))
466    }
467
468    fn accept_bi_optional_diagnostics(
469        &self,
470        chunk_size: usize,
471        emit_available: bool,
472        diagnostics: Option<QuicCarrierDiagnostics>,
473    ) -> Source<QuicBidirectionalStream, QuicConnection> {
474        assert!(chunk_size > 0, "chunk size must be greater than zero");
475        let connection = self.clone();
476        Source::unfold_resource(
477            {
478                let connection = connection.clone();
479                let diagnostics = diagnostics.clone();
480                move || {
481                    let handle = connection.handle.clone();
482                    let (demand_sender, demand_receiver) = mpsc::channel(1);
483                    let (cancel_sender, cancel_receiver) = watch::channel(false);
484                    let task = handle.spawn(run_accept_bi_task(
485                        connection.connection.clone(),
486                        chunk_size,
487                        emit_available,
488                        handle.clone(),
489                        diagnostics.clone(),
490                        demand_receiver,
491                        cancel_receiver,
492                    ));
493                    Ok(AcceptBiResource {
494                        demands: demand_sender,
495                        cancel: cancel_sender,
496                        task,
497                    })
498                }
499            },
500            receive_demand_response,
501            close_accept_bi_resource,
502        )
503        .map_materialized_value(move |_| connection.clone())
504    }
505
506    /// Accepts incoming bidirectional streams using the connection's default
507    /// chunk size.
508    #[must_use]
509    pub fn accept_bi_default(&self) -> Source<QuicBidirectionalStream, QuicConnection> {
510        self.accept_bi(self.chunk_size)
511    }
512
513    /// Accepts incoming bidirectional streams with emit-available read mode.
514    ///
515    /// Like [`accept_bi`](QuicConnection::accept_bi) but the byte source emits chunks as soon as any
516    /// bytes arrive rather than waiting to fill `chunk_size`.
517    #[must_use]
518    pub fn accept_bi_available(
519        &self,
520        chunk_size: usize,
521    ) -> Source<QuicBidirectionalStream, QuicConnection> {
522        self.accept_bi_optional_diagnostics(chunk_size, true, None)
523    }
524
525    /// Accepts incoming bidirectional streams in emit-available mode and records failures.
526    #[must_use]
527    pub fn accept_bi_available_with_diagnostics(
528        &self,
529        chunk_size: usize,
530        diagnostics: QuicCarrierDiagnostics,
531    ) -> Source<QuicBidirectionalStream, QuicConnection> {
532        self.accept_bi_optional_diagnostics(chunk_size, true, Some(diagnostics))
533    }
534
535    /// Closes the QUIC connection with an application close code of `0`.
536    pub fn close(&self, reason: &[u8]) {
537        self.connection.close(close_code(), reason);
538    }
539}
540
541/// A QUIC connection accepted by [`TokioQuic::bind`].
542#[derive(Debug, Clone)]
543pub struct QuicIncomingConnection {
544    connection: QuicConnection,
545}
546
547impl QuicIncomingConnection {
548    /// Returns the local UDP endpoint address for this connection.
549    #[must_use]
550    pub fn local_addr(&self) -> SocketAddr {
551        self.connection.local_addr()
552    }
553
554    /// Returns the peer UDP endpoint address for this connection.
555    #[must_use]
556    pub fn remote_addr(&self) -> SocketAddr {
557        self.connection.remote_addr()
558    }
559
560    /// Returns a clone of the materialized QUIC connection handle.
561    #[must_use]
562    pub fn connection(&self) -> QuicConnection {
563        self.connection.clone()
564    }
565
566    /// Consumes this value and returns the materialized QUIC connection.
567    #[must_use]
568    pub fn into_connection(self) -> QuicConnection {
569        self.connection
570    }
571
572    /// Opens a bidirectional stream from the accepted connection.
573    #[must_use]
574    pub fn open_bi(
575        &self,
576        chunk_size: usize,
577    ) -> Flow<Vec<u8>, Vec<u8>, StreamCompletion<QuicStream>> {
578        self.connection.open_bi(chunk_size)
579    }
580
581    /// Opens a bidirectional stream using the connection's default chunk size.
582    #[must_use]
583    pub fn open_bi_default(&self) -> Flow<Vec<u8>, Vec<u8>, StreamCompletion<QuicStream>> {
584        self.connection.open_bi_default()
585    }
586
587    /// Opens a split bidirectional stream from the accepted connection.
588    #[must_use]
589    pub fn open_bi_stream(
590        &self,
591        chunk_size: usize,
592    ) -> Source<QuicBidirectionalStream, StreamCompletion<QuicStream>> {
593        self.connection.open_bi_stream(chunk_size)
594    }
595
596    /// Opens a split bidirectional stream using the default chunk size.
597    #[must_use]
598    pub fn open_bi_stream_default(
599        &self,
600    ) -> Source<QuicBidirectionalStream, StreamCompletion<QuicStream>> {
601        self.connection.open_bi_stream_default()
602    }
603
604    /// Opens a split bidirectional stream with emit-available read mode.
605    #[must_use]
606    pub fn open_bi_stream_available(
607        &self,
608        chunk_size: usize,
609    ) -> Source<QuicBidirectionalStream, StreamCompletion<QuicStream>> {
610        self.connection.open_bi_stream_available(chunk_size)
611    }
612
613    /// Accepts incoming bidirectional streams on this connection.
614    #[must_use]
615    pub fn accept_bi(&self, chunk_size: usize) -> Source<QuicBidirectionalStream, QuicConnection> {
616        self.connection.accept_bi(chunk_size)
617    }
618
619    /// Accepts incoming bidirectional streams and records accept failures.
620    #[must_use]
621    pub fn accept_bi_with_diagnostics(
622        &self,
623        chunk_size: usize,
624        diagnostics: QuicCarrierDiagnostics,
625    ) -> Source<QuicBidirectionalStream, QuicConnection> {
626        self.connection
627            .accept_bi_with_diagnostics(chunk_size, diagnostics)
628    }
629
630    /// Accepts incoming bidirectional streams using the default chunk size.
631    #[must_use]
632    pub fn accept_bi_default(&self) -> Source<QuicBidirectionalStream, QuicConnection> {
633        self.connection.accept_bi_default()
634    }
635
636    /// Accepts incoming bidirectional streams with emit-available read mode.
637    #[must_use]
638    pub fn accept_bi_available(
639        &self,
640        chunk_size: usize,
641    ) -> Source<QuicBidirectionalStream, QuicConnection> {
642        self.connection.accept_bi_available(chunk_size)
643    }
644
645    /// Accepts incoming bidirectional streams in emit-available mode and records failures.
646    #[must_use]
647    pub fn accept_bi_available_with_diagnostics(
648        &self,
649        chunk_size: usize,
650        diagnostics: QuicCarrierDiagnostics,
651    ) -> Source<QuicBidirectionalStream, QuicConnection> {
652        self.connection
653            .accept_bi_available_with_diagnostics(chunk_size, diagnostics)
654    }
655}
656
657/// An accepted or opened QUIC bidirectional stream.
658pub struct QuicBidirectionalStream {
659    stream: QuicStream,
660    send: quinn::SendStream,
661    recv: quinn::RecvStream,
662    handle: Handle,
663    chunk_size: usize,
664    emit_available: bool,
665}
666
667impl QuicBidirectionalStream {
668    /// Returns stream metadata.
669    #[must_use]
670    pub fn stream(&self) -> QuicStream {
671        self.stream
672    }
673
674    /// Splits the stream into receive and send byte halves.
675    #[must_use]
676    pub fn into_parts(self) -> (QuicByteSource, QuicByteSink) {
677        let Self {
678            send,
679            recv,
680            handle,
681            chunk_size,
682            emit_available,
683            ..
684        } = self;
685        single_use_quic_halves(send, recv, handle, chunk_size, emit_available)
686    }
687
688    /// Converts this QUIC stream into a Datum byte flow.
689    #[must_use]
690    pub fn into_flow(self) -> Flow<Vec<u8>, Vec<u8>, QuicStream> {
691        let stream = self.stream;
692        let (source, sink) = self.into_parts();
693        Flow::from_sink_and_source(sink, source).map_materialized_value(move |_| stream)
694    }
695
696    pub(crate) fn into_stream_ref_parts(
697        self,
698    ) -> (quinn::RecvStream, quinn::SendStream, Handle, usize, bool) {
699        (
700            self.recv,
701            self.send,
702            self.handle,
703            self.chunk_size,
704            self.emit_available,
705        )
706    }
707}
708
709/// QUIC endpoint entry points backed by Quinn.
710pub struct TokioQuic;
711
712/// Alias for [`TokioQuic`].
713pub type Quic = TokioQuic;
714
715impl TokioQuic {
716    /// Binds a QUIC server endpoint and emits accepted connections.
717    ///
718    /// The UDP socket and Quinn endpoint bind when the source is materialized.
719    /// Each downstream pull accepts one connection attempt and drives the QUIC
720    /// handshake. Per-connection handshake failures are logged and skipped.
721    #[must_use]
722    pub fn bind<A>(
723        addr: A,
724        server_config: quinn::ServerConfig,
725        chunk_size: usize,
726    ) -> Source<QuicIncomingConnection, StreamCompletion<QuicBinding>>
727    where
728        A: ToSocketAddrs + Clone + Send + Sync + 'static,
729    {
730        Self::bind_optional_diagnostics(addr, server_config, chunk_size, None)
731    }
732
733    /// Binds a QUIC server endpoint and records skipped handshake failures.
734    #[must_use]
735    pub fn bind_with_diagnostics<A>(
736        addr: A,
737        server_config: quinn::ServerConfig,
738        chunk_size: usize,
739        diagnostics: QuicCarrierDiagnostics,
740    ) -> Source<QuicIncomingConnection, StreamCompletion<QuicBinding>>
741    where
742        A: ToSocketAddrs + Clone + Send + Sync + 'static,
743    {
744        Self::bind_optional_diagnostics(addr, server_config, chunk_size, Some(diagnostics))
745    }
746
747    fn bind_optional_diagnostics<A>(
748        addr: A,
749        server_config: quinn::ServerConfig,
750        chunk_size: usize,
751        diagnostics: Option<QuicCarrierDiagnostics>,
752    ) -> Source<QuicIncomingConnection, StreamCompletion<QuicBinding>>
753    where
754        A: ToSocketAddrs + Clone + Send + Sync + 'static,
755    {
756        assert!(chunk_size > 0, "chunk size must be greater than zero");
757        Source::lazy_future_source(move || {
758            let addr = addr.clone();
759            let server_config = server_config.clone();
760            let diagnostics = diagnostics.clone();
761            async move {
762                let handle = Handle::current();
763                let addr = resolve_addr(addr).await?;
764                let endpoint = quinn::Endpoint::server(server_config, addr).map_err(io_error)?;
765                let local_addr = endpoint.local_addr().map_err(io_error)?;
766                Ok(quic_bind_source(
767                    endpoint,
768                    local_addr,
769                    handle,
770                    chunk_size,
771                    diagnostics,
772                ))
773            }
774        })
775    }
776
777    /// Binds a QUIC server endpoint using the default 8 KiB stream chunk size.
778    #[must_use]
779    pub fn bind_default<A>(
780        addr: A,
781        server_config: quinn::ServerConfig,
782    ) -> Source<QuicIncomingConnection, StreamCompletion<QuicBinding>>
783    where
784        A: ToSocketAddrs + Clone + Send + Sync + 'static,
785    {
786        Self::bind(addr, server_config, DEFAULT_CHUNK_SIZE)
787    }
788
789    /// Opens a QUIC client endpoint and emits one materialized connection.
790    ///
791    /// The local endpoint binds to an OS-assigned UDP port matching the remote
792    /// address family. The Quinn client config controls rustls trust policy,
793    /// ALPN, transport settings, and certificate verification.
794    #[must_use]
795    pub fn connect<A>(
796        addr: A,
797        server_name: impl Into<String>,
798        client_config: quinn::ClientConfig,
799        chunk_size: usize,
800    ) -> Source<QuicConnection, StreamCompletion<QuicConnection>>
801    where
802        A: ToSocketAddrs + Clone + Send + Sync + 'static,
803    {
804        assert!(chunk_size > 0, "chunk size must be greater than zero");
805        let server_name = server_name.into();
806        Source::lazy_future_source(move || {
807            let addr = addr.clone();
808            let server_name = server_name.clone();
809            let client_config = client_config.clone();
810            async move {
811                let remote_addr = resolve_addr(addr).await?;
812                let local_addr = client_bind_addr(remote_addr);
813                let mut endpoint = quinn::Endpoint::client(local_addr).map_err(io_error)?;
814                endpoint.set_default_client_config(client_config);
815                let connecting = endpoint
816                    .connect(remote_addr, &server_name)
817                    .map_err(quic_error)?;
818                let connection = connecting.await.map_err(quic_error)?;
819                let endpoint_local_addr = endpoint.local_addr().map_err(io_error)?;
820                let connection = QuicConnection {
821                    local_addr: connection_local_addr(
822                        &connection,
823                        endpoint_local_addr,
824                        remote_addr.ip(),
825                    ),
826                    remote_addr: connection.remote_address(),
827                    endpoint,
828                    connection,
829                    handle: Handle::current(),
830                    chunk_size,
831                };
832                let materialized = connection.clone();
833                Ok(
834                    Source::single(connection)
835                        .map_materialized_value(move |_| materialized.clone()),
836                )
837            }
838        })
839    }
840
841    /// Opens a QUIC client endpoint using the default 8 KiB stream chunk size.
842    #[must_use]
843    pub fn connect_default<A>(
844        addr: A,
845        server_name: impl Into<String>,
846        client_config: quinn::ClientConfig,
847    ) -> Source<QuicConnection, StreamCompletion<QuicConnection>>
848    where
849        A: ToSocketAddrs + Clone + Send + Sync + 'static,
850    {
851        Self::connect(addr, server_name, client_config, DEFAULT_CHUNK_SIZE)
852    }
853}
854
855async fn resolve_addr<A>(addr: A) -> StreamResult<SocketAddr>
856where
857    A: ToSocketAddrs,
858{
859    let mut addrs = tokio::net::lookup_host(addr).await.map_err(io_error)?;
860    addrs
861        .next()
862        .ok_or_else(|| StreamError::Failed("address resolved to no socket addresses".into()))
863}
864
865fn client_bind_addr(remote_addr: SocketAddr) -> SocketAddr {
866    if remote_addr.is_ipv6() {
867        SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0)
868    } else {
869        SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
870    }
871}
872
873fn connection_local_addr(
874    connection: &quinn::Connection,
875    endpoint_addr: SocketAddr,
876    fallback_ip: IpAddr,
877) -> SocketAddr {
878    connection
879        .local_ip()
880        .map(|ip| SocketAddr::new(ip, endpoint_addr.port()))
881        .or_else(|| {
882            endpoint_addr
883                .ip()
884                .is_unspecified()
885                .then(|| SocketAddr::new(fallback_ip, endpoint_addr.port()))
886        })
887        .unwrap_or(endpoint_addr)
888}
889
890fn quic_bi_stream_from_halves(
891    send: quinn::SendStream,
892    recv: quinn::RecvStream,
893    handle: Handle,
894    chunk_size: usize,
895    emit_available: bool,
896) -> QuicBidirectionalStream {
897    let stream = QuicStream { id: send.id() };
898    QuicBidirectionalStream {
899        stream,
900        send,
901        recv,
902        handle,
903        chunk_size,
904        emit_available,
905    }
906}
907
908fn single_use_quic_halves(
909    send: quinn::SendStream,
910    recv: quinn::RecvStream,
911    handle: Handle,
912    chunk_size: usize,
913    emit_available: bool,
914) -> (QuicByteSource, QuicByteSink) {
915    let (carrier, receiver) = start_quic_carrier(
916        send,
917        recv,
918        handle,
919        chunk_size,
920        emit_available,
921        DEFAULT_RECEIVE_BUFFER,
922    );
923    let source =
924        single_use_quic_read_source_from_carrier(carrier.clone(), receiver, DEFAULT_RECEIVE_BUFFER);
925    let sink = single_use_quic_write_sink_from_carrier(carrier, 1);
926    (source, sink)
927}
928
929fn single_use_quic_read_source_from_carrier(
930    carrier: QuicCarrier,
931    receiver: std_mpsc::Receiver<DemandResponse<Vec<u8>>>,
932    receive_buffer: usize,
933) -> QuicByteSource {
934    let receiver = Arc::new(Mutex::new(Some(receiver)));
935    Source::unfold_resource(
936        {
937            let receiver = Arc::clone(&receiver);
938            move || {
939                let receiver = receiver
940                    .lock()
941                    .expect("single-use QUIC receiver poisoned")
942                    .take()
943                    .ok_or_else(|| {
944                        StreamError::Failed("QUIC source already materialized".into())
945                    })?;
946                let demand = DemandBatcher::new(receive_buffer);
947                let pending = match carrier.request_demand(demand.initial()) {
948                    Ok(()) => None,
949                    Err(error) => match receiver.try_recv() {
950                        Ok(response) => Some(response),
951                        Err(std_mpsc::TryRecvError::Empty) => return Err(error),
952                        Err(std_mpsc::TryRecvError::Disconnected) => {
953                            return Err(abrupt_termination());
954                        }
955                    },
956                };
957                Ok(ReadResource {
958                    receiver,
959                    carrier: carrier.clone(),
960                    demand,
961                    pending,
962                })
963            }
964        },
965        read_next_quic_chunk,
966        close_read_resource,
967    )
968}
969
970fn read_next_quic_chunk(resource: &mut ReadResource) -> StreamResult<Option<Vec<u8>>> {
971    let response = match resource.pending.take() {
972        Some(response) => response,
973        None => resource.receiver.recv().map_err(|_| abrupt_termination())?,
974    };
975    match response {
976        DemandResponse::Item(chunk) => {
977            if let Some(demand) = resource.demand.record_consumed() {
978                let _ = resource.carrier.request_demand(demand);
979            }
980            Ok(Some(chunk))
981        }
982        DemandResponse::Complete => Ok(None),
983        DemandResponse::Error(error) => Err(error),
984    }
985}
986
987fn close_read_resource(resource: ReadResource) -> StreamResult<()> {
988    resource.carrier.close_read();
989    Ok(())
990}
991
992fn start_quic_carrier(
993    send: quinn::SendStream,
994    recv: quinn::RecvStream,
995    handle: Handle,
996    chunk_size: usize,
997    emit_available: bool,
998    receive_buffer: usize,
999) -> (QuicCarrier, std_mpsc::Receiver<DemandResponse<Vec<u8>>>) {
1000    let command_capacity = async_carrier::DEFAULT_COMMAND_BUFFER.max(receive_buffer);
1001    let (commands, command_receiver) = async_carrier::command_channel(command_capacity, "QUIC");
1002    let (send_error_sender, send_error_receiver) = std_mpsc::channel();
1003    let (receive_sender, receive_receiver) =
1004        std_mpsc::sync_channel(receive_buffer.saturating_add(1));
1005    let command_keepalive = commands.clone();
1006    let read_config = QuicReadConfig {
1007        chunk_size,
1008        emit_available,
1009    };
1010    let task = handle.spawn(run_quic_carrier_task(
1011        send,
1012        recv,
1013        read_config,
1014        receive_sender,
1015        send_error_sender,
1016        command_keepalive,
1017        command_receiver,
1018    ));
1019    (
1020        QuicCarrier {
1021            inner: Arc::new(QuicCarrierInner {
1022                commands,
1023                send_errors: Mutex::new(send_error_receiver),
1024                task: Mutex::new(Some(task)),
1025            }),
1026        },
1027        receive_receiver,
1028    )
1029}
1030
1031async fn run_quic_carrier_task(
1032    mut send: quinn::SendStream,
1033    mut recv: quinn::RecvStream,
1034    read_config: QuicReadConfig,
1035    receive_sender: std_mpsc::SyncSender<DemandResponse<Vec<u8>>>,
1036    send_error_sender: std_mpsc::Sender<StreamError>,
1037    _command_keepalive: AsyncCommandSender<QuicCarrierCommand>,
1038    mut commands: mpsc::Receiver<QuicCarrierCommand>,
1039) {
1040    let mut buffer = vec![0_u8; read_config.chunk_size];
1041    let mut pending_tail = Vec::with_capacity(read_config.chunk_size);
1042    let mut requested = 0_usize;
1043    let mut read_open = true;
1044    let mut write_open = true;
1045
1046    loop {
1047        if !read_open && !write_open {
1048            return;
1049        }
1050
1051        if read_open && requested > 0 {
1052            tokio::select! {
1053                biased;
1054                command = commands.recv() => {
1055                    let Some(command) = command else {
1056                        return;
1057                    };
1058                    if !handle_quic_carrier_command(
1059                        &mut send,
1060                        command,
1061                        &send_error_sender,
1062                        &mut read_open,
1063                        &mut write_open,
1064                        &mut requested,
1065                    ).await {
1066                        return;
1067                    }
1068                }
1069                read = recv.read(&mut buffer) => {
1070                    match read {
1071                        Ok(Some(read)) => {
1072                            match queue_quic_read_chunks(
1073                                &receive_sender,
1074                                &send_error_sender,
1075                                read_config.chunk_size,
1076                                &mut pending_tail,
1077                                &buffer[..read],
1078                                read_config.emit_available,
1079                            ) {
1080                                QuicReadQueueResult::Queued(queued) => {
1081                                    requested = requested.saturating_sub(queued);
1082                                }
1083                                QuicReadQueueResult::Closed => {
1084                                    read_open = false;
1085                                }
1086                                QuicReadQueueResult::Failed => {
1087                                    return;
1088                                }
1089                            }
1090                        }
1091                        Ok(None) => {
1092                            if !pending_tail.is_empty() {
1093                                match try_send_quic_read_response(
1094                                    &receive_sender,
1095                                    DemandResponse::Item(std::mem::take(&mut pending_tail)),
1096                                ) {
1097                                    QuicQueueOutcome::Queued => {
1098                                        requested = requested.saturating_sub(1);
1099                                    }
1100                                    QuicQueueOutcome::Closed => {
1101                                        read_open = false;
1102                                        continue;
1103                                    }
1104                                    QuicQueueOutcome::Full => {
1105                                        report_quic_read_error(
1106                                            &receive_sender,
1107                                            &send_error_sender,
1108                                            quic_receive_buffer_overflow(),
1109                                        );
1110                                        return;
1111                                    }
1112                                }
1113                            }
1114                            match try_send_quic_read_response(
1115                                &receive_sender,
1116                                DemandResponse::Complete,
1117                            ) {
1118                                QuicQueueOutcome::Queued | QuicQueueOutcome::Closed => {
1119                                    read_open = false;
1120                                }
1121                                QuicQueueOutcome::Full => {
1122                                    report_quic_read_error(
1123                                        &receive_sender,
1124                                        &send_error_sender,
1125                                        quic_receive_buffer_overflow(),
1126                                    );
1127                                    return;
1128                                }
1129                            }
1130                        }
1131                        Err(error) => {
1132                            report_quic_read_error(
1133                                &receive_sender,
1134                                &send_error_sender,
1135                                quic_error(error),
1136                            );
1137                            return;
1138                        }
1139                    }
1140                }
1141            }
1142        } else {
1143            let Some(command) = commands.recv().await else {
1144                return;
1145            };
1146            if !handle_quic_carrier_command(
1147                &mut send,
1148                command,
1149                &send_error_sender,
1150                &mut read_open,
1151                &mut write_open,
1152                &mut requested,
1153            )
1154            .await
1155            {
1156                return;
1157            }
1158        }
1159    }
1160}
1161
1162async fn handle_quic_carrier_command(
1163    send: &mut quinn::SendStream,
1164    command: QuicCarrierCommand,
1165    send_error_sender: &std_mpsc::Sender<StreamError>,
1166    read_open: &mut bool,
1167    write_open: &mut bool,
1168    requested: &mut usize,
1169) -> bool {
1170    match command {
1171        QuicCarrierCommand::Demand(demand) => {
1172            *requested = requested.saturating_add(demand);
1173            true
1174        }
1175        QuicCarrierCommand::SendOne(chunk) => {
1176            if !*write_open {
1177                report_quic_write_error(
1178                    send_error_sender,
1179                    StreamError::Failed("QUIC write side is closed".to_owned()),
1180                );
1181                return *read_open;
1182            }
1183            if write_one_quic_chunk(send, send_error_sender, &chunk).await {
1184                true
1185            } else {
1186                *write_open = false;
1187                *read_open
1188            }
1189        }
1190        QuicCarrierCommand::SendBatch(chunks) => {
1191            if !*write_open {
1192                report_quic_write_error(
1193                    send_error_sender,
1194                    StreamError::Failed("QUIC write side is closed".to_owned()),
1195                );
1196                return *read_open;
1197            }
1198            for chunk in &chunks {
1199                if let Err(error) = send.write_all(chunk).await.map_err(quic_error) {
1200                    report_quic_write_error(send_error_sender, error);
1201                    *write_open = false;
1202                    return *read_open;
1203                }
1204            }
1205            true
1206        }
1207        QuicCarrierCommand::CloseRead => {
1208            *read_open = false;
1209            true
1210        }
1211        QuicCarrierCommand::CloseWrite { ack } => {
1212            *write_open = false;
1213            let result = close_quic_writer(send).await;
1214            match result {
1215                Ok(()) => {
1216                    let _ = ack.send(Ok(()));
1217                    true
1218                }
1219                Err(error) => {
1220                    report_quic_write_error(send_error_sender, error.clone());
1221                    let _ = ack.send(Err(error));
1222                    *read_open
1223                }
1224            }
1225        }
1226    }
1227}
1228
1229async fn write_one_quic_chunk(
1230    send: &mut quinn::SendStream,
1231    send_error_sender: &std_mpsc::Sender<StreamError>,
1232    chunk: &[u8],
1233) -> bool {
1234    if let Err(error) = send.write_all(chunk).await.map_err(quic_error) {
1235        report_quic_write_error(send_error_sender, error);
1236        return false;
1237    }
1238    true
1239}
1240
1241async fn close_quic_writer(send: &mut quinn::SendStream) -> StreamResult<()> {
1242    send.write_all(&[]).await.map_err(quic_error)?;
1243    send.finish().map_err(quic_error)
1244}
1245
1246enum QuicReadQueueResult {
1247    Queued(usize),
1248    Closed,
1249    Failed,
1250}
1251
1252enum QuicQueueOutcome {
1253    Queued,
1254    Full,
1255    Closed,
1256}
1257
1258fn queue_quic_read_chunks(
1259    sender: &std_mpsc::SyncSender<DemandResponse<Vec<u8>>>,
1260    send_error_sender: &std_mpsc::Sender<StreamError>,
1261    chunk_size: usize,
1262    pending_tail: &mut Vec<u8>,
1263    read_buffer: &[u8],
1264    emit_available: bool,
1265) -> QuicReadQueueResult {
1266    let mut offset = 0;
1267    let mut queued = 0_usize;
1268    if !pending_tail.is_empty() {
1269        let needed = chunk_size - pending_tail.len();
1270        let take = needed.min(read_buffer.len());
1271        pending_tail.extend_from_slice(&read_buffer[..take]);
1272        offset += take;
1273        if pending_tail.len() == chunk_size {
1274            match try_send_quic_read_response(
1275                sender,
1276                DemandResponse::Item(std::mem::take(pending_tail)),
1277            ) {
1278                QuicQueueOutcome::Queued => queued += 1,
1279                QuicQueueOutcome::Closed => return QuicReadQueueResult::Closed,
1280                QuicQueueOutcome::Full => {
1281                    report_quic_read_error(
1282                        sender,
1283                        send_error_sender,
1284                        quic_receive_buffer_overflow(),
1285                    );
1286                    return QuicReadQueueResult::Failed;
1287                }
1288            }
1289        }
1290    }
1291
1292    while offset + chunk_size <= read_buffer.len() {
1293        let next = offset + chunk_size;
1294        match try_send_quic_read_response(
1295            sender,
1296            DemandResponse::Item(read_buffer[offset..next].to_vec()),
1297        ) {
1298            QuicQueueOutcome::Queued => queued += 1,
1299            QuicQueueOutcome::Closed => return QuicReadQueueResult::Closed,
1300            QuicQueueOutcome::Full => {
1301                report_quic_read_error(sender, send_error_sender, quic_receive_buffer_overflow());
1302                return QuicReadQueueResult::Failed;
1303            }
1304        }
1305        offset = next;
1306    }
1307
1308    if offset < read_buffer.len() {
1309        pending_tail.extend_from_slice(&read_buffer[offset..]);
1310    }
1311    if emit_available && !pending_tail.is_empty() {
1312        match try_send_quic_read_response(
1313            sender,
1314            DemandResponse::Item(std::mem::take(pending_tail)),
1315        ) {
1316            QuicQueueOutcome::Queued => queued += 1,
1317            QuicQueueOutcome::Closed => return QuicReadQueueResult::Closed,
1318            QuicQueueOutcome::Full => {
1319                report_quic_read_error(sender, send_error_sender, quic_receive_buffer_overflow());
1320                return QuicReadQueueResult::Failed;
1321            }
1322        }
1323    }
1324    QuicReadQueueResult::Queued(queued)
1325}
1326
1327fn try_send_quic_read_response(
1328    sender: &std_mpsc::SyncSender<DemandResponse<Vec<u8>>>,
1329    item: DemandResponse<Vec<u8>>,
1330) -> QuicQueueOutcome {
1331    match sender.try_send(item) {
1332        Ok(()) => QuicQueueOutcome::Queued,
1333        Err(std_mpsc::TrySendError::Full(_)) => QuicQueueOutcome::Full,
1334        Err(std_mpsc::TrySendError::Disconnected(_)) => QuicQueueOutcome::Closed,
1335    }
1336}
1337
1338fn report_quic_read_error(
1339    receive_sender: &std_mpsc::SyncSender<DemandResponse<Vec<u8>>>,
1340    send_error_sender: &std_mpsc::Sender<StreamError>,
1341    error: StreamError,
1342) {
1343    let _ = send_error_sender.send(error.clone());
1344    let _ = receive_sender.try_send(DemandResponse::Error(error));
1345}
1346
1347fn report_quic_write_error(send_error_sender: &std_mpsc::Sender<StreamError>, error: StreamError) {
1348    let _ = send_error_sender.send(error);
1349}
1350
1351fn quic_receive_buffer_overflow() -> StreamError {
1352    StreamError::Failed("QUIC receive buffer filled without downstream demand".to_owned())
1353}
1354
1355fn single_use_quic_write_sink_from_carrier(
1356    carrier: QuicCarrier,
1357    batch_size: usize,
1358) -> QuicByteSink {
1359    let carrier = Arc::new(Mutex::new(Some(carrier)));
1360    Flow::<Vec<u8>, Vec<u8>>::identity()
1361        .map_with_resource(
1362            {
1363                let carrier = Arc::clone(&carrier);
1364                move || {
1365                    let carrier = carrier
1366                        .lock()
1367                        .expect("single-use QUIC carrier poisoned")
1368                        .take()
1369                        .ok_or_else(|| {
1370                            StreamError::Failed("QUIC sink already materialized".into())
1371                        })?;
1372                    Ok(SendResource {
1373                        carrier,
1374                        pending: Vec::with_capacity(batch_size),
1375                        batch_size,
1376                    })
1377                }
1378            },
1379            |resource, chunk| {
1380                send_quic_chunk(resource, chunk)?;
1381                Ok(NotUsed)
1382            },
1383            close_quic_send_resource,
1384        )
1385        .to_mat(Sink::ignore(), Keep::right)
1386}
1387
1388fn close_quic_send_resource(mut resource: SendResource) -> StreamResult<Option<NotUsed>> {
1389    flush_quic_send_resource(&mut resource)?;
1390    resource.carrier.close_write()?;
1391    Ok(None)
1392}
1393
1394fn send_quic_chunk(resource: &mut SendResource, chunk: Vec<u8>) -> StreamResult<()> {
1395    if resource.batch_size <= 1 {
1396        return resource.carrier.send_one(chunk);
1397    }
1398    resource.pending.push(chunk);
1399    if resource.pending.len() >= resource.batch_size {
1400        flush_quic_send_resource(resource)?;
1401    }
1402    Ok(())
1403}
1404
1405fn flush_quic_send_resource(resource: &mut SendResource) -> StreamResult<()> {
1406    if resource.pending.is_empty() {
1407        return resource.carrier.check_send_error();
1408    }
1409    let pending = std::mem::take(&mut resource.pending);
1410    resource.carrier.send_items(pending)
1411}
1412
1413fn quic_bind_source(
1414    endpoint: quinn::Endpoint,
1415    local_addr: SocketAddr,
1416    handle: Handle,
1417    chunk_size: usize,
1418    diagnostics: Option<QuicCarrierDiagnostics>,
1419) -> Source<QuicIncomingConnection, QuicBinding> {
1420    let endpoint = Arc::new(Mutex::new(Some(endpoint)));
1421    Source::unfold_resource(
1422        {
1423            let endpoint = Arc::clone(&endpoint);
1424            let handle = handle.clone();
1425            move || {
1426                let endpoint = endpoint
1427                    .lock()
1428                    .expect("single-use QUIC endpoint poisoned")
1429                    .take()
1430                    .ok_or_else(|| {
1431                        StreamError::Failed("QUIC endpoint already materialized".into())
1432                    })?;
1433                let (demand_sender, demand_receiver) = mpsc::channel(1);
1434                let (cancel_sender, cancel_receiver) = watch::channel(false);
1435                let task = handle.spawn(run_quic_bind_task(
1436                    endpoint,
1437                    local_addr,
1438                    chunk_size,
1439                    handle.clone(),
1440                    diagnostics.clone(),
1441                    demand_receiver,
1442                    cancel_receiver,
1443                ));
1444                Ok(BindResource {
1445                    demands: demand_sender,
1446                    cancel: cancel_sender,
1447                    task,
1448                })
1449            }
1450        },
1451        receive_demand_response,
1452        close_bind_resource,
1453    )
1454    .map_materialized_value(move |_| QuicBinding { local_addr })
1455}
1456
1457fn receive_demand_response<T>(resource: &mut impl DemandResource<T>) -> StreamResult<Option<T>>
1458where
1459    T: Send + 'static,
1460{
1461    let (reply_sender, reply_receiver) = std_mpsc::channel();
1462    resource
1463        .demands()
1464        .blocking_send(reply_sender)
1465        .map_err(|_| abrupt_termination())?;
1466    match reply_receiver.recv() {
1467        Ok(DemandResponse::Item(item)) => Ok(Some(item)),
1468        Ok(DemandResponse::Complete) => Ok(None),
1469        Ok(DemandResponse::Error(error)) => Err(error),
1470        Err(_) => Err(abrupt_termination()),
1471    }
1472}
1473
1474trait DemandResource<T>
1475where
1476    T: Send + 'static,
1477{
1478    fn demands(&self) -> &mpsc::Sender<std_mpsc::Sender<DemandResponse<T>>>;
1479}
1480
1481impl DemandResource<QuicIncomingConnection> for BindResource {
1482    fn demands(&self) -> &mpsc::Sender<std_mpsc::Sender<DemandResponse<QuicIncomingConnection>>> {
1483        &self.demands
1484    }
1485}
1486
1487impl DemandResource<QuicBidirectionalStream> for AcceptBiResource {
1488    fn demands(&self) -> &mpsc::Sender<std_mpsc::Sender<DemandResponse<QuicBidirectionalStream>>> {
1489        &self.demands
1490    }
1491}
1492
1493fn close_bind_resource(resource: BindResource) -> StreamResult<()> {
1494    let _ = resource.cancel.send(true);
1495    resource.task.abort();
1496    Ok(())
1497}
1498
1499fn close_accept_bi_resource(resource: AcceptBiResource) -> StreamResult<()> {
1500    let _ = resource.cancel.send(true);
1501    resource.task.abort();
1502    Ok(())
1503}
1504
1505async fn run_quic_bind_task(
1506    endpoint: quinn::Endpoint,
1507    local_addr: SocketAddr,
1508    chunk_size: usize,
1509    handle: Handle,
1510    diagnostics: Option<QuicCarrierDiagnostics>,
1511    mut demands: mpsc::Receiver<std_mpsc::Sender<DemandResponse<QuicIncomingConnection>>>,
1512    mut cancel: watch::Receiver<bool>,
1513) {
1514    loop {
1515        let reply = tokio::select! {
1516            demand = demands.recv() => match demand {
1517                Some(reply) => reply,
1518                None => return,
1519            },
1520            changed = cancel.changed() => {
1521                let _ = changed;
1522                return;
1523            }
1524        };
1525
1526        loop {
1527            let incoming = tokio::select! {
1528                incoming = endpoint.accept() => incoming,
1529                changed = cancel.changed() => {
1530                    let _ = changed;
1531                    return;
1532                }
1533            };
1534
1535            let Some(incoming) = incoming else {
1536                let _ = reply.send(DemandResponse::Complete);
1537                return;
1538            };
1539
1540            let connected = tokio::select! {
1541                connected = incoming => connected,
1542                changed = cancel.changed() => {
1543                    let _ = changed;
1544                    return;
1545                }
1546            };
1547
1548            match connected {
1549                Ok(connection) => {
1550                    let incoming = QuicIncomingConnection {
1551                        connection: QuicConnection {
1552                            endpoint: endpoint.clone(),
1553                            local_addr: connection_local_addr(
1554                                &connection,
1555                                local_addr,
1556                                local_addr.ip(),
1557                            ),
1558                            remote_addr: connection.remote_address(),
1559                            connection,
1560                            handle: handle.clone(),
1561                            chunk_size,
1562                        },
1563                    };
1564                    if reply.send(DemandResponse::Item(incoming)).is_err() {
1565                        return;
1566                    }
1567                    break;
1568                }
1569                Err(error) => {
1570                    if let Some(diagnostics) = &diagnostics {
1571                        diagnostics.record_handshake_failure();
1572                    }
1573                    tracing::warn!(
1574                        local_addr = %local_addr,
1575                        error = %error,
1576                        "QUIC connection handshake failed; continuing listener"
1577                    );
1578                    continue;
1579                }
1580            }
1581        }
1582    }
1583}
1584
1585async fn run_accept_bi_task(
1586    connection: quinn::Connection,
1587    chunk_size: usize,
1588    emit_available: bool,
1589    handle: Handle,
1590    diagnostics: Option<QuicCarrierDiagnostics>,
1591    mut demands: mpsc::Receiver<std_mpsc::Sender<DemandResponse<QuicBidirectionalStream>>>,
1592    mut cancel: watch::Receiver<bool>,
1593) {
1594    loop {
1595        let reply = tokio::select! {
1596            demand = demands.recv() => match demand {
1597                Some(reply) => reply,
1598                None => return,
1599            },
1600            changed = cancel.changed() => {
1601                let _ = changed;
1602                return;
1603            }
1604        };
1605
1606        let accepted = tokio::select! {
1607            accepted = connection.accept_bi() => accepted,
1608            changed = cancel.changed() => {
1609                let _ = changed;
1610                return;
1611            }
1612        };
1613
1614        match accepted {
1615            Ok((send, recv)) => {
1616                let stream = quic_bi_stream_from_halves(
1617                    send,
1618                    recv,
1619                    handle.clone(),
1620                    chunk_size,
1621                    emit_available,
1622                );
1623                if reply.send(DemandResponse::Item(stream)).is_err() {
1624                    return;
1625                }
1626            }
1627            Err(error) => {
1628                if let Some(diagnostics) = &diagnostics {
1629                    diagnostics.record_accept_bi_failure();
1630                }
1631                tracing::warn!(
1632                    remote_addr = %connection.remote_address(),
1633                    error = %error,
1634                    "QUIC accept_bi failed; completing stream accept source"
1635                );
1636                let _ = reply.send(DemandResponse::Complete);
1637                return;
1638            }
1639        }
1640    }
1641}