Skip to main content

moqtap_client/draft15/
connection.rs

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