Skip to main content

moqtap_client/draft18/
connection.rs

1use std::sync::Arc;
2
3use bytes::{Buf, Bytes, BytesMut};
4
5use crate::draft18::endpoint::{Endpoint, EndpointError};
6use crate::draft18::event::{ClientEvent, Direction, StreamKind};
7use crate::draft18::observer::ConnectionObserver;
8use crate::draft18::session::request_id::Role;
9use crate::transport::quic::QuicTransport;
10use crate::transport::{RecvStream, SendStream, Transport, TransportError};
11use moqtap_codec::dispatch::{
12    AnyControlMessage, AnyDatagramHeader, AnyFetchHeader, AnySubgroupHeader,
13};
14use moqtap_codec::draft18::data_stream::{FetchHeader, SubgroupObject, SubgroupObjectReader};
15use moqtap_codec::draft18::message::ControlMessage;
16use moqtap_codec::error::CodecError;
17use moqtap_codec::kvp::KeyValuePair;
18use moqtap_codec::types::*;
19use moqtap_codec::varint::VarInt;
20use moqtap_codec::version::DraftVersion;
21
22/// MoQT ALPN identifier (used by raw QUIC transport).
23pub const MOQT_ALPN: &[u8] = b"moq-00";
24
25/// Errors from the connection layer.
26#[derive(Debug, thiserror::Error)]
27pub enum ConnectionError {
28    /// Endpoint state machine error.
29    #[error("endpoint error: {0}")]
30    Endpoint(#[from] EndpointError),
31    /// Wire codec error.
32    #[error("codec error: {0}")]
33    Codec(#[from] CodecError),
34    /// Transport-level error.
35    #[error("transport error: {0}")]
36    Transport(#[from] TransportError),
37    /// Variable-length integer decoding error.
38    #[error("varint error: {0}")]
39    VarInt(#[from] moqtap_codec::varint::VarIntError),
40    /// Control stream was not opened.
41    #[error("control stream not open")]
42    NoControlStream,
43    /// Stream ended before a complete message was read.
44    #[error("unexpected end of stream")]
45    UnexpectedEnd,
46    /// Stream was finished by the peer.
47    #[error("stream finished")]
48    StreamFinished,
49    /// Invalid server address string.
50    #[error("invalid server address: {0}")]
51    InvalidAddress(String),
52    /// TLS configuration error.
53    #[error("TLS config error: {0}")]
54    TlsConfig(String),
55    /// Data stream used out of order (e.g. object before header).
56    #[error("data stream state error: {0}")]
57    DataStreamState(&'static str),
58}
59
60/// Transport type for the connection.
61#[derive(Debug, Clone)]
62pub enum TransportType {
63    /// Raw QUIC via quinn. The `addr` field should be `host:port`.
64    Quic,
65    /// WebTransport via wtransport. The `url` field is the WebTransport URL.
66    WebTransport {
67        /// The WebTransport endpoint URL (e.g., `https://host:port/path`).
68        url: String,
69    },
70}
71
72/// Configuration for a MoQT client connection.
73///
74/// Both `draft` and `transport` are required -- there is no `Default` impl.
75pub struct ClientConfig {
76    /// The MoQT draft version to use (primary, determines codec/framing).
77    pub draft: DraftVersion,
78    /// The transport type (QUIC or WebTransport).
79    pub transport: TransportType,
80    /// Whether to skip TLS certificate verification (for testing).
81    pub skip_cert_verification: bool,
82    /// Custom CA certificates to trust (DER-encoded).
83    pub ca_certs: Vec<Vec<u8>>,
84    /// Setup parameters to include in CLIENT_SETUP (e.g., auth tokens).
85    pub setup_parameters: Vec<KeyValuePair>,
86}
87
88impl ClientConfig {
89    /// Returns the ALPN protocol identifiers for the transport.
90    pub fn alpn(&self) -> Vec<Vec<u8>> {
91        match &self.transport {
92            TransportType::Quic => vec![self.draft.quic_alpn().to_vec()],
93            TransportType::WebTransport { .. } => vec![b"h3".to_vec()],
94        }
95    }
96}
97
98/// A framed writer for a send stream. Handles MoQT length-prefixed framing.
99pub struct FramedSendStream {
100    inner: SendStream,
101    draft: DraftVersion,
102    /// Stateful subgroup object writer.
103    subgroup_io: Option<SubgroupObjectReader>,
104}
105
106impl FramedSendStream {
107    /// Create a new framed send stream for the given draft version.
108    pub fn new(inner: SendStream, draft: DraftVersion) -> Self {
109        Self { inner, draft, subgroup_io: None }
110    }
111
112    /// Get the transport-level stream ID.
113    pub fn stream_id(&self) -> u64 {
114        self.inner.stream_id()
115    }
116
117    /// Write a control message to the stream with type+length framing.
118    /// Returns the raw bytes that were written (for event capture).
119    pub async fn write_control(
120        &mut self,
121        msg: &AnyControlMessage,
122    ) -> Result<Vec<u8>, ConnectionError> {
123        let mut buf = Vec::new();
124        msg.encode(&mut buf)?;
125        self.inner.write_all(&buf).await?;
126        Ok(buf)
127    }
128
129    /// Write a subgroup stream header. Also initializes the internal
130    /// delta-encoding state used by
131    /// [`FramedSendStream::write_subgroup_object`].
132    pub async fn write_subgroup_header(
133        &mut self,
134        header: &AnySubgroupHeader,
135    ) -> Result<(), ConnectionError> {
136        let mut buf = Vec::new();
137        header.encode(&mut buf);
138        self.inner.write_all(&buf).await?;
139        if let AnySubgroupHeader::Draft18(ref d17) = header {
140            self.subgroup_io = Some(SubgroupObjectReader::new(d17));
141        }
142        Ok(())
143    }
144
145    /// Write a fetch response header.
146    pub async fn write_fetch_header(
147        &mut self,
148        header: &AnyFetchHeader,
149    ) -> Result<(), ConnectionError> {
150        let mut buf = Vec::new();
151        header.encode(&mut buf);
152        self.inner.write_all(&buf).await?;
153        Ok(())
154    }
155
156    /// Append a draft-18 subgroup object to the stream using the
157    /// stateful writer seeded from
158    /// [`FramedSendStream::write_subgroup_header`].
159    pub async fn write_subgroup_object(
160        &mut self,
161        object: &SubgroupObject,
162    ) -> Result<(), ConnectionError> {
163        let writer = self
164            .subgroup_io
165            .as_mut()
166            .ok_or(ConnectionError::DataStreamState("subgroup header not written yet"))?;
167        let mut buf = Vec::new();
168        writer.write_object(object, &mut buf)?;
169        self.inner.write_all(&buf).await?;
170        Ok(())
171    }
172
173    /// Finish the stream (send FIN).
174    pub async fn finish(&mut self) -> Result<(), ConnectionError> {
175        self.inner.finish()?;
176        Ok(())
177    }
178
179    /// Returns the draft version this stream is framed for.
180    pub fn draft(&self) -> DraftVersion {
181        self.draft
182    }
183}
184
185/// A framed reader for a recv stream. Handles MoQT varint-length decoding.
186pub struct FramedRecvStream {
187    inner: RecvStream,
188    buf: BytesMut,
189    draft: DraftVersion,
190    /// Stateful subgroup object reader.
191    subgroup_io: Option<SubgroupObjectReader>,
192}
193
194impl FramedRecvStream {
195    /// Create a new framed receive stream for the given draft version.
196    pub fn new(inner: RecvStream, draft: DraftVersion) -> Self {
197        Self { inner, buf: BytesMut::with_capacity(4096), draft, subgroup_io: None }
198    }
199
200    /// Get the transport-level stream ID.
201    pub fn stream_id(&self) -> u64 {
202        self.inner.stream_id()
203    }
204
205    /// Read more data from the stream into the internal buffer.
206    async fn fill(&mut self) -> Result<bool, ConnectionError> {
207        let mut tmp = [0u8; 4096];
208        match self.inner.read(&mut tmp).await {
209            Ok(Some(n)) => {
210                self.buf.extend_from_slice(&tmp[..n]);
211                Ok(true)
212            }
213            Ok(None) => Ok(false),
214            Err(e) => Err(ConnectionError::Transport(e)),
215        }
216    }
217
218    /// Ensure at least `n` bytes are available in the buffer.
219    async fn ensure(&mut self, n: usize) -> Result<(), ConnectionError> {
220        while self.buf.len() < n {
221            if !self.fill().await? {
222                return Err(ConnectionError::UnexpectedEnd);
223            }
224        }
225        Ok(())
226    }
227
228    /// Read a control message from the stream.
229    ///
230    /// When `capture_raw` is true, the returned tuple includes a clone of the
231    /// framed wire bytes (for observer emission). When false, the second
232    /// element is `None` and the payload clone is skipped.
233    pub async fn read_control(
234        &mut self,
235        capture_raw: bool,
236    ) -> Result<(AnyControlMessage, Option<Vec<u8>>), ConnectionError> {
237        // Read type ID varint
238        self.ensure(1).await?;
239        let type_len = varint_len(self.buf[0]);
240        self.ensure(type_len).await?;
241
242        let mut cursor = &self.buf[..type_len];
243        let _type_id = VarInt::decode(&mut cursor)?;
244
245        // Draft-18: 16-bit BE payload length
246        let (payload_len, len_field_size) = if self.draft.uses_fixed_length_framing() {
247            self.ensure(type_len + 2).await?;
248            let hi = self.buf[type_len] as usize;
249            let lo = self.buf[type_len + 1] as usize;
250            ((hi << 8) | lo, 2)
251        } else {
252            self.ensure(type_len + 1).await?;
253            let payload_len_start = type_len;
254            let payload_len_varint_len = varint_len(self.buf[payload_len_start]);
255            self.ensure(type_len + payload_len_varint_len).await?;
256            let mut cursor = &self.buf[payload_len_start..type_len + payload_len_varint_len];
257            let payload_len = VarInt::decode(&mut cursor)?.into_inner() as usize;
258            (payload_len, payload_len_varint_len)
259        };
260
261        // Read full payload
262        let total = type_len + len_field_size + payload_len;
263        self.ensure(total).await?;
264
265        // Capture raw bytes only if requested (observer attached).
266        let raw = capture_raw.then(|| self.buf[..total].to_vec());
267
268        // Now decode the whole message
269        let mut frame = &self.buf[..total];
270        let msg = AnyControlMessage::decode(self.draft, &mut frame)?;
271        self.buf.advance(total);
272        Ok((msg, raw))
273    }
274
275    /// Read a subgroup stream header. Also initializes the internal
276    /// delta-decoding state.
277    pub async fn read_subgroup_header(&mut self) -> Result<AnySubgroupHeader, ConnectionError> {
278        self.ensure(1).await?;
279        loop {
280            let mut cursor = &self.buf[..];
281            match AnySubgroupHeader::decode(self.draft, &mut cursor) {
282                Ok(header) => {
283                    let consumed = self.buf.len() - cursor.remaining();
284                    self.buf.advance(consumed);
285                    if let AnySubgroupHeader::Draft18(ref d17) = header {
286                        self.subgroup_io = Some(SubgroupObjectReader::new(d17));
287                    }
288                    return Ok(header);
289                }
290                Err(CodecError::UnexpectedEnd) => {
291                    if !self.fill().await? {
292                        return Err(ConnectionError::UnexpectedEnd);
293                    }
294                }
295                Err(e) => return Err(ConnectionError::Codec(e)),
296            }
297        }
298    }
299
300    /// Read a fetch response header.
301    pub async fn read_fetch_header(&mut self) -> Result<AnyFetchHeader, ConnectionError> {
302        self.ensure(1).await?;
303        loop {
304            let mut cursor = &self.buf[..];
305            match AnyFetchHeader::decode(self.draft, &mut cursor) {
306                Ok(header) => {
307                    let consumed = self.buf.len() - cursor.remaining();
308                    self.buf.advance(consumed);
309                    return Ok(header);
310                }
311                Err(CodecError::UnexpectedEnd) => {
312                    if !self.fill().await? {
313                        return Err(ConnectionError::UnexpectedEnd);
314                    }
315                }
316                Err(e) => return Err(ConnectionError::Codec(e)),
317            }
318        }
319    }
320
321    /// Read the next draft-18 subgroup object from this stream using
322    /// the stateful reader seeded by
323    /// [`FramedRecvStream::read_subgroup_header`].
324    pub async fn read_subgroup_object(&mut self) -> Result<SubgroupObject, ConnectionError> {
325        if self.subgroup_io.is_none() {
326            return Err(ConnectionError::DataStreamState("subgroup header not read yet"));
327        }
328        loop {
329            let reader = self.subgroup_io.as_mut().unwrap();
330            let mut probe = reader.clone();
331            let mut cursor = &self.buf[..];
332            match probe.read_object(&mut cursor) {
333                Ok(obj) => {
334                    let consumed = self.buf.len() - cursor.remaining();
335                    self.buf.advance(consumed);
336                    *reader = probe;
337                    return Ok(obj);
338                }
339                Err(CodecError::UnexpectedEnd) => {
340                    if !self.fill().await? {
341                        return Err(ConnectionError::UnexpectedEnd);
342                    }
343                }
344                Err(e) => return Err(ConnectionError::Codec(e)),
345            }
346        }
347    }
348
349    /// Read the next draft-18 fetch header from this stream.
350    pub async fn read_fetch_stream_header(&mut self) -> Result<FetchHeader, ConnectionError> {
351        loop {
352            let mut cursor = &self.buf[..];
353            match FetchHeader::decode(&mut cursor) {
354                Ok(hdr) => {
355                    let consumed = self.buf.len() - cursor.remaining();
356                    self.buf.advance(consumed);
357                    return Ok(hdr);
358                }
359                Err(CodecError::UnexpectedEnd) => {
360                    if !self.fill().await? {
361                        return Err(ConnectionError::UnexpectedEnd);
362                    }
363                }
364                Err(e) => return Err(ConnectionError::Codec(e)),
365            }
366        }
367    }
368
369    /// Returns the draft version this stream is framed for.
370    pub fn draft(&self) -> DraftVersion {
371        self.draft
372    }
373}
374
375/// A live MoQT connection over QUIC or WebTransport, combining the endpoint
376/// state machine with actual network I/O.
377pub struct Connection {
378    transport: Transport,
379    endpoint: Endpoint,
380    draft: DraftVersion,
381    control_send: Option<FramedSendStream>,
382    control_recv: Option<FramedRecvStream>,
383    observer: Option<Box<dyn ConnectionObserver>>,
384    /// Setup events buffered during `connect()` and replayed when an
385    /// observer attaches via `set_observer` — without this, an observer
386    /// attached after `connect` returns would never see the handshake.
387    pending_events: Vec<ClientEvent>,
388}
389
390impl Connection {
391    /// Connect to a MoQT server as a client.
392    ///
393    /// Establishes a QUIC or WebTransport connection (based on
394    /// `config.transport`), opens a bidirectional control stream,
395    /// performs the CLIENT_SETUP / SERVER_SETUP handshake, and returns
396    /// a ready-to-use connection.
397    pub async fn connect(addr: &str, config: ClientConfig) -> Result<Self, ConnectionError> {
398        let draft = config.draft;
399        let transport = match &config.transport {
400            TransportType::Quic => Self::connect_quic(addr, &config).await?,
401            TransportType::WebTransport { url } => {
402                let url = url.clone();
403                Self::connect_webtransport(&url, &config).await?
404            }
405        };
406
407        // Open bidirectional control stream
408        let (send, recv) = transport.open_bi().await?;
409        let mut control_send = FramedSendStream::new(send, draft);
410        let mut control_recv = FramedRecvStream::new(recv, draft);
411
412        // Perform setup handshake (draft-18: no versions)
413        let mut endpoint = Endpoint::new(Role::Client);
414        endpoint.connect()?;
415        let setup_msg = endpoint.send_setup(config.setup_parameters.clone())?;
416        let any_setup = AnyControlMessage::Draft18(setup_msg);
417        let raw_setup = control_send.write_control(&any_setup).await?;
418
419        let (server_setup, raw_server_setup) = control_recv.read_control(true).await?;
420        // Unified SETUP in draft-18: server responds with the same message type.
421        match &server_setup {
422            AnyControlMessage::Draft18(ControlMessage::Setup(ref s)) => {
423                endpoint.receive_setup(s)?;
424            }
425            _ => {
426                return Err(ConnectionError::Endpoint(EndpointError::NotActive));
427            }
428        }
429
430        let mut pending_events = Vec::with_capacity(3);
431        pending_events.push(ClientEvent::ControlMessage {
432            direction: Direction::Send,
433            message: any_setup,
434            raw: Some(raw_setup),
435        });
436        pending_events.push(ClientEvent::ControlMessage {
437            direction: Direction::Receive,
438            message: server_setup,
439            raw: raw_server_setup,
440        });
441        pending_events.push(ClientEvent::SetupComplete { negotiated_version: 0xff000000 + 18 });
442
443        Ok(Self {
444            transport,
445            endpoint,
446            draft,
447            control_send: Some(control_send),
448            control_recv: Some(control_recv),
449            observer: None,
450            pending_events,
451        })
452    }
453
454    /// Establish a raw QUIC connection.
455    async fn connect_quic(addr: &str, config: &ClientConfig) -> Result<Transport, ConnectionError> {
456        let server_addr = addr.parse().map_err(|e: std::net::AddrParseError| {
457            ConnectionError::InvalidAddress(e.to_string())
458        })?;
459
460        // Build TLS config
461        let mut tls_config = if config.skip_cert_verification {
462            rustls::ClientConfig::builder()
463                .dangerous()
464                .with_custom_certificate_verifier(Arc::new(SkipVerification))
465                .with_no_client_auth()
466        } else {
467            let mut roots = rustls::RootCertStore::empty();
468            roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
469            for der in &config.ca_certs {
470                roots
471                    .add(rustls::pki_types::CertificateDer::from(der.clone()))
472                    .map_err(|e| ConnectionError::TlsConfig(format!("bad CA cert: {e}")))?;
473            }
474            rustls::ClientConfig::builder().with_root_certificates(roots).with_no_client_auth()
475        };
476
477        tls_config.alpn_protocols = config.alpn();
478
479        let quic_config: quinn::crypto::rustls::QuicClientConfig =
480            tls_config.try_into().map_err(|e| ConnectionError::TlsConfig(format!("{e}")))?;
481        let client_config = quinn::ClientConfig::new(Arc::new(quic_config));
482
483        let mut quinn_endpoint = quinn::Endpoint::client("0.0.0.0:0".parse().unwrap())
484            .map_err(|e| ConnectionError::InvalidAddress(e.to_string()))?;
485        quinn_endpoint.set_default_client_config(client_config);
486
487        let server_name = addr.split(':').next().unwrap_or("localhost").to_string();
488
489        let quic = quinn_endpoint
490            .connect(server_addr, &server_name)
491            .map_err(TransportError::from)?
492            .await
493            .map_err(TransportError::from)?;
494
495        Ok(Transport::Quic(QuicTransport::new(quic)))
496    }
497
498    /// Establish a WebTransport connection.
499    #[cfg(feature = "webtransport")]
500    async fn connect_webtransport(
501        url: &str,
502        config: &ClientConfig,
503    ) -> Result<Transport, ConnectionError> {
504        use crate::transport::webtransport::WebTransportTransport;
505
506        let wt_config = if config.skip_cert_verification {
507            wtransport::ClientConfig::builder()
508                .with_bind_default()
509                .with_no_cert_validation()
510                .build()
511        } else {
512            wtransport::ClientConfig::builder().with_bind_default().with_native_certs().build()
513        };
514
515        let endpoint = wtransport::Endpoint::client(wt_config)
516            .map_err(|e| ConnectionError::Transport(TransportError::Connect(e.to_string())))?;
517
518        let connection = endpoint
519            .connect(url)
520            .await
521            .map_err(|e| ConnectionError::Transport(TransportError::Connect(e.to_string())))?;
522
523        Ok(Transport::WebTransport(WebTransportTransport::new(connection)))
524    }
525
526    /// Stub for when the webtransport feature is not enabled.
527    #[cfg(not(feature = "webtransport"))]
528    async fn connect_webtransport(
529        _url: &str,
530        _config: &ClientConfig,
531    ) -> Result<Transport, ConnectionError> {
532        Err(ConnectionError::Transport(TransportError::Connect(
533            "webtransport feature not enabled".into(),
534        )))
535    }
536
537    // -- Observer ---------------------------------------------------
538
539    /// Attach an observer. Buffered handshake events from `connect()` are
540    /// flushed in arrival order before this returns.
541    pub fn set_observer(&mut self, observer: Box<dyn ConnectionObserver>) {
542        self.observer = Some(observer);
543        for event in self.pending_events.drain(..) {
544            if let Some(ref obs) = self.observer {
545                obs.on_event_owned(event);
546            }
547        }
548    }
549
550    /// Remove the observer.
551    pub fn clear_observer(&mut self) {
552        self.observer = None;
553    }
554
555    /// Emit an event to the observer, if one is attached.
556    fn emit(&self, event: ClientEvent) {
557        if let Some(ref obs) = self.observer {
558            obs.on_event_owned(event);
559        }
560    }
561
562    // -- Control message I/O ----------------------------------------
563
564    /// Send a control message on the control stream.
565    ///
566    /// Wraps the draft-18 message in `AnyControlMessage::Draft18` for
567    /// framing.
568    pub async fn send_control(&mut self, msg: &ControlMessage) -> Result<(), ConnectionError> {
569        let any = AnyControlMessage::Draft18(msg.clone());
570        let send = self.control_send.as_mut().ok_or(ConnectionError::NoControlStream)?;
571        let raw = send.write_control(&any).await?;
572        self.emit(ClientEvent::ControlMessage {
573            direction: Direction::Send,
574            message: any,
575            raw: Some(raw),
576        });
577        Ok(())
578    }
579
580    /// Read the next control message from the control stream.
581    ///
582    /// Returns the `AnyControlMessage` and also extracts the draft-18
583    /// `ControlMessage` for internal endpoint dispatch.
584    pub async fn recv_control(&mut self) -> Result<ControlMessage, ConnectionError> {
585        let recv = self.control_recv.as_mut().ok_or(ConnectionError::NoControlStream)?;
586        let capture_raw = self.observer.is_some();
587        let (any, raw) = recv.read_control(capture_raw).await?;
588        if capture_raw {
589            self.emit(ClientEvent::ControlMessage {
590                direction: Direction::Receive,
591                message: any.clone(),
592                raw,
593            });
594        }
595        // Unwrap to draft-18 for the endpoint
596        match any {
597            AnyControlMessage::Draft18(msg) => Ok(msg),
598            _ => Err(ConnectionError::Codec(CodecError::UnknownMessageType(0))),
599        }
600    }
601
602    /// Read and dispatch the next incoming control message through the
603    /// endpoint state machine. Returns the decoded message for inspection.
604    pub async fn recv_and_dispatch(&mut self) -> Result<ControlMessage, ConnectionError> {
605        let msg = self.recv_control().await?;
606        self.endpoint.receive_message(msg.clone())?;
607
608        // Emit draining event if this was a GoAway
609        if let ControlMessage::GoAway(ref ga) = msg {
610            self.emit(ClientEvent::Draining { new_session_uri: ga.new_session_uri.clone() });
611        }
612
613        Ok(msg)
614    }
615
616    // -- Subscribe flow ---------------------------------------------
617
618    /// Send a SUBSCRIBE and return the allocated request ID.
619    pub async fn subscribe(
620        &mut self,
621        track_namespace: TrackNamespace,
622        track_name: Vec<u8>,
623        parameters: Vec<KeyValuePair>,
624    ) -> Result<VarInt, ConnectionError> {
625        let (req_id, msg) = self.endpoint.subscribe(track_namespace, track_name, parameters)?;
626        self.send_control(&msg).await?;
627        Ok(req_id)
628    }
629
630    // Draft-18 inherits draft-17's removal of UNSUBSCRIBE: subscribers end
631    // via RequestUpdate or by waiting for PublishDone.
632
633    // -- Fetch flow -------------------------------------------------
634
635    /// Send a standalone FETCH and return the allocated request ID.
636    pub async fn fetch(
637        &mut self,
638        track_namespace: TrackNamespace,
639        track_name: Vec<u8>,
640        start_group: VarInt,
641        start_object: VarInt,
642        end_group: VarInt,
643        end_object: VarInt,
644    ) -> Result<VarInt, ConnectionError> {
645        let (req_id, msg) = self.endpoint.fetch(
646            track_namespace,
647            track_name,
648            start_group,
649            start_object,
650            end_group,
651            end_object,
652        )?;
653        self.send_control(&msg).await?;
654        Ok(req_id)
655    }
656
657    /// Send a joining FETCH and return the allocated request ID.
658    pub async fn joining_fetch(
659        &mut self,
660        joining_request_id: VarInt,
661        joining_start: VarInt,
662    ) -> Result<VarInt, ConnectionError> {
663        let (req_id, msg) = self.endpoint.joining_fetch(joining_request_id, joining_start)?;
664        self.send_control(&msg).await?;
665        Ok(req_id)
666    }
667
668    // Draft-18 inherits draft-17's removal of FETCH_CANCEL: fetchers abort
669    // via stream reset.
670
671    // -- Namespace flows --------------------------------------------
672
673    /// Send a SUBSCRIBE_NAMESPACE and return the request ID.
674    ///
675    /// Draft-18 split the draft-17 SUBSCRIBE_NAMESPACE into two messages.
676    /// This call sends the renumbered SUBSCRIBE_NAMESPACE (type 0x50), which
677    /// subscribes to NAMESPACE / NAMESPACE_DONE announcements only. To
678    /// receive PUBLISH messages for matching tracks, use
679    /// [`Self::subscribe_tracks`] instead.
680    pub async fn subscribe_namespace(
681        &mut self,
682        namespace_prefix: TrackNamespace,
683        parameters: Vec<KeyValuePair>,
684    ) -> Result<VarInt, ConnectionError> {
685        let (req_id, msg) = self.endpoint.subscribe_namespace(namespace_prefix, parameters)?;
686        self.send_control(&msg).await?;
687        Ok(req_id)
688    }
689
690    /// Send a SUBSCRIBE_TRACKS (type 0x51, new in draft-18) and return the
691    /// request ID. Causes the relay to PUBLISH matching tracks back to us.
692    pub async fn subscribe_tracks(
693        &mut self,
694        namespace_prefix: TrackNamespace,
695        parameters: Vec<KeyValuePair>,
696    ) -> Result<VarInt, ConnectionError> {
697        let (req_id, msg) = self.endpoint.subscribe_tracks(namespace_prefix, parameters)?;
698        self.send_control(&msg).await?;
699        Ok(req_id)
700    }
701
702    /// Send a PUBLISH_NAMESPACE and return the request ID.
703    pub async fn publish_namespace(
704        &mut self,
705        track_namespace: TrackNamespace,
706        parameters: Vec<KeyValuePair>,
707    ) -> Result<VarInt, ConnectionError> {
708        let (req_id, msg) = self.endpoint.publish_namespace(track_namespace, parameters)?;
709        self.send_control(&msg).await?;
710        Ok(req_id)
711    }
712
713    // -- Track Status flow ------------------------------------------
714
715    /// Send a TRACK_STATUS and return the allocated request ID.
716    pub async fn track_status(
717        &mut self,
718        track_namespace: TrackNamespace,
719        track_name: Vec<u8>,
720        parameters: Vec<KeyValuePair>,
721    ) -> Result<VarInt, ConnectionError> {
722        let (req_id, msg) = self.endpoint.track_status(track_namespace, track_name, parameters)?;
723        self.send_control(&msg).await?;
724        Ok(req_id)
725    }
726
727    // -- Publish flow (publisher side) ------------------------------
728
729    /// Send a PUBLISH and return the allocated request ID.
730    pub async fn publish(
731        &mut self,
732        track_namespace: TrackNamespace,
733        track_name: Vec<u8>,
734        track_alias: VarInt,
735        parameters: Vec<KeyValuePair>,
736        track_properties: Vec<KeyValuePair>,
737    ) -> Result<VarInt, ConnectionError> {
738        let (req_id, msg) = self.endpoint.publish(
739            track_namespace,
740            track_name,
741            track_alias,
742            parameters,
743            track_properties,
744        )?;
745        self.send_control(&msg).await?;
746        Ok(req_id)
747    }
748
749    /// Send a PUBLISH_DONE for the given request ID.
750    pub async fn publish_done(
751        &mut self,
752        request_id: VarInt,
753        status_code: VarInt,
754        stream_count: VarInt,
755        reason_phrase: Vec<u8>,
756    ) -> Result<(), ConnectionError> {
757        let msg = self.endpoint.send_publish_done(
758            request_id,
759            status_code,
760            stream_count,
761            reason_phrase,
762        )?;
763        self.send_control(&msg).await
764    }
765
766    // -- Data streams -----------------------------------------------
767
768    /// Open a new unidirectional stream for sending subgroup data.
769    pub async fn open_subgroup_stream(
770        &self,
771        header: &AnySubgroupHeader,
772    ) -> Result<FramedSendStream, ConnectionError> {
773        let send = self.transport.open_uni().await?;
774        let mut framed = FramedSendStream::new(send, self.draft);
775        let sid = framed.stream_id();
776        framed.write_subgroup_header(header).await?;
777        self.emit(ClientEvent::StreamOpened {
778            direction: Direction::Send,
779            stream_kind: StreamKind::Subgroup,
780            stream_id: sid,
781        });
782        self.emit(ClientEvent::DataStreamHeader {
783            stream_id: sid,
784            direction: Direction::Send,
785            header: header.clone(),
786        });
787        Ok(framed)
788    }
789
790    /// Accept an incoming unidirectional data stream and read its subgroup
791    /// header.
792    pub async fn accept_subgroup_stream(
793        &self,
794    ) -> Result<(AnySubgroupHeader, FramedRecvStream), ConnectionError> {
795        let recv = self.transport.accept_uni().await?;
796        let mut framed = FramedRecvStream::new(recv, self.draft);
797        let sid = framed.stream_id();
798        let header = framed.read_subgroup_header().await?;
799        self.emit(ClientEvent::StreamOpened {
800            direction: Direction::Receive,
801            stream_kind: StreamKind::Subgroup,
802            stream_id: sid,
803        });
804        self.emit(ClientEvent::DataStreamHeader {
805            stream_id: sid,
806            direction: Direction::Receive,
807            header: header.clone(),
808        });
809        Ok((header, framed))
810    }
811
812    /// Send an object via datagram.
813    pub fn send_datagram(
814        &self,
815        header: &AnyDatagramHeader,
816        payload: &[u8],
817    ) -> Result<(), ConnectionError> {
818        let mut buf = Vec::new();
819        header.encode(&mut buf);
820        buf.extend_from_slice(payload);
821        self.emit(ClientEvent::DatagramReceived {
822            direction: Direction::Send,
823            header: header.clone(),
824            payload_len: payload.len(),
825        });
826        self.transport.send_datagram(bytes::Bytes::from(buf))?;
827        Ok(())
828    }
829
830    /// Receive a datagram and decode its header.
831    pub async fn recv_datagram(&self) -> Result<(AnyDatagramHeader, Bytes), ConnectionError> {
832        let data = self.transport.recv_datagram().await?;
833        let mut cursor = &data[..];
834        let header = AnyDatagramHeader::decode(self.draft, &mut cursor)?;
835        let consumed = data.len() - cursor.len();
836        let payload = data.slice(consumed..);
837        self.emit(ClientEvent::DatagramReceived {
838            direction: Direction::Receive,
839            header: header.clone(),
840            payload_len: payload.len(),
841        });
842        Ok((header, payload))
843    }
844
845    // -- Accessors --------------------------------------------------
846
847    /// Access the underlying endpoint state machine.
848    pub fn endpoint(&self) -> &Endpoint {
849        &self.endpoint
850    }
851
852    /// Mutable access to the endpoint state machine.
853    pub fn endpoint_mut(&mut self) -> &mut Endpoint {
854        &mut self.endpoint
855    }
856
857    /// Returns the draft version this connection is using.
858    pub fn draft(&self) -> DraftVersion {
859        self.draft
860    }
861
862    /// Close the connection.
863    pub fn close(&self, code: u32, reason: &[u8]) {
864        self.emit(ClientEvent::Closed { code, reason: reason.to_vec() });
865        self.transport.close(code, reason);
866    }
867}
868
869/// Determine the encoded length of a varint from its first byte.
870fn varint_len(first_byte: u8) -> usize {
871    1 << (first_byte >> 6)
872}
873
874/// TLS certificate verifier that skips all verification (for testing only).
875#[derive(Debug)]
876struct SkipVerification;
877
878impl rustls::client::danger::ServerCertVerifier for SkipVerification {
879    fn verify_server_cert(
880        &self,
881        _end_entity: &rustls::pki_types::CertificateDer<'_>,
882        _intermediates: &[rustls::pki_types::CertificateDer<'_>],
883        _server_name: &rustls::pki_types::ServerName<'_>,
884        _ocsp_response: &[u8],
885        _now: rustls::pki_types::UnixTime,
886    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
887        Ok(rustls::client::danger::ServerCertVerified::assertion())
888    }
889
890    fn verify_tls12_signature(
891        &self,
892        _message: &[u8],
893        _cert: &rustls::pki_types::CertificateDer<'_>,
894        _dcs: &rustls::DigitallySignedStruct,
895    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
896        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
897    }
898
899    fn verify_tls13_signature(
900        &self,
901        _message: &[u8],
902        _cert: &rustls::pki_types::CertificateDer<'_>,
903        _dcs: &rustls::DigitallySignedStruct,
904    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
905        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
906    }
907
908    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
909        vec![
910            rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
911            rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
912            rustls::SignatureScheme::ED25519,
913            rustls::SignatureScheme::RSA_PSS_SHA256,
914            rustls::SignatureScheme::RSA_PSS_SHA384,
915            rustls::SignatureScheme::RSA_PSS_SHA512,
916        ]
917    }
918}
919
920#[cfg(test)]
921mod tests {
922    use super::*;
923
924    #[test]
925    fn varint_len_single_byte() {
926        assert_eq!(varint_len(0x00), 1);
927        assert_eq!(varint_len(0x3F), 1);
928    }
929
930    #[test]
931    fn varint_len_two_bytes() {
932        assert_eq!(varint_len(0x40), 2);
933        assert_eq!(varint_len(0x7F), 2);
934    }
935
936    #[test]
937    fn varint_len_four_bytes() {
938        assert_eq!(varint_len(0x80), 4);
939        assert_eq!(varint_len(0xBF), 4);
940    }
941
942    #[test]
943    fn varint_len_eight_bytes() {
944        assert_eq!(varint_len(0xC0), 8);
945        assert_eq!(varint_len(0xFF), 8);
946    }
947
948    #[test]
949    fn client_config_alpn_quic_draft18() {
950        let config = ClientConfig {
951            draft: DraftVersion::Draft18,
952            transport: TransportType::Quic,
953            skip_cert_verification: false,
954            ca_certs: Vec::new(),
955            setup_parameters: Vec::new(),
956        };
957        assert_eq!(config.alpn(), vec![b"moqt-18".to_vec()]);
958    }
959
960    #[test]
961    fn client_config_alpn_webtransport() {
962        let config = ClientConfig {
963            draft: DraftVersion::Draft18,
964            transport: TransportType::WebTransport { url: "https://example.com".to_string() },
965            skip_cert_verification: false,
966            ca_certs: Vec::new(),
967            setup_parameters: Vec::new(),
968        };
969        assert_eq!(config.alpn(), vec![b"h3".to_vec()]);
970    }
971
972    #[test]
973    fn moqt_alpn_value() {
974        assert_eq!(MOQT_ALPN, b"moq-00");
975    }
976
977    #[test]
978    fn transport_type_debug() {
979        let quic = TransportType::Quic;
980        assert!(format!("{quic:?}").contains("Quic"));
981
982        let wt = TransportType::WebTransport { url: "https://example.com".to_string() };
983        assert!(format!("{wt:?}").contains("WebTransport"));
984    }
985}