1use crate::origin;
2use crate::{
3 ALPN_14, ALPN_15, ALPN_16, ALPN_17, ALPN_18, ALPN_19, ALPN_LITE, ALPN_LITE_03, ALPN_LITE_04, ALPN_LITE_05,
4 ALPN_LITE_06_WIP, Consume, Driver, Error, NEGOTIATED, Session, Version, Versions,
5 coding::{self, Decode, Encode, Stream},
6 ietf, lite, setup, stats,
7};
8
9#[derive(Default, Clone)]
11pub struct Client {
12 publish: Option<origin::Consumer>,
13 subscribe: Option<origin::Producer>,
14 stats: stats::Session,
15 versions: Versions,
16 setup_path: Option<String>,
17 cost: Option<u64>,
18 peer_origin: Option<crate::Origin>,
19}
20
21impl Client {
22 pub fn new() -> Self {
24 Default::default()
25 }
26
27 pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
31 self.publish = Some(publish.consume());
32 self
33 }
34
35 pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
38 self.subscribe = Some(subscribe);
39 self
40 }
41
42 pub fn with_stats(mut self, stats: stats::Session) -> Self {
47 self.stats = stats;
48 self
49 }
50
51 pub fn with_origin(self, origin: origin::Producer) -> Self {
56 self.with_publisher(&origin).with_subscriber(origin)
57 }
58
59 pub fn with_versions(mut self, versions: Versions) -> Self {
62 self.versions = versions;
63 self
64 }
65
66 pub fn with_path(mut self, path: impl Into<String>) -> Self {
78 self.setup_path = Some(path.into());
79 self
80 }
81
82 pub fn with_cost(mut self, cost: u64) -> Self {
96 self.cost = Some(cost);
97 self
98 }
99
100 pub fn with_peer_origin(mut self, origin: crate::Origin) -> Self {
120 self.peer_origin = Some(origin);
121 self
122 }
123
124 pub async fn connect<S: web_transport_trait::Session>(&self, session: S) -> Result<(Session, Driver), Error> {
128 if self.publish.is_none() && self.subscribe.is_none() {
129 tracing::warn!("not publishing or consuming anything");
130 }
131
132 let publish = self.publish.clone().map(|origin| origin.with_stats(self.stats.clone()));
137 let subscribe = self
138 .subscribe
139 .clone()
140 .map(|origin| origin.with_stats(self.stats.clone()));
141
142 let publish = match self.peer_origin {
147 Some(peer) => publish.map(|origin| origin.excluding(peer)),
148 None => publish,
149 };
150
151 let (encoding, supported) = match session.protocol() {
154 Some(ALPN_19) => {
155 let v = self
156 .versions
157 .select(Version::Ietf(ietf::Version::Draft19))
158 .ok_or(Error::Version)?;
159
160 let protocol = ietf::start(ietf::Config {
162 session: session.clone(),
163 setup: None,
164 request_id_max: None,
165 client: true,
166 publish: publish.clone(),
167 subscribe: subscribe.clone(),
168 peer_origin: self.peer_origin,
169 cost: self.cost,
170 version: ietf::Version::Draft19,
171 path: self.setup_path.clone(),
172 peer_setup_stream: None,
173 peer_cluster: None,
174 })?;
175
176 tracing::debug!(version = ?v, "connected");
177 return Ok(Session::new(session, v, None, protocol));
178 }
179 Some(ALPN_18) => {
180 let v = self
181 .versions
182 .select(Version::Ietf(ietf::Version::Draft18))
183 .ok_or(Error::Version)?;
184
185 let protocol = ietf::start(ietf::Config {
188 session: session.clone(),
189 setup: None,
190 request_id_max: None,
191 client: true,
192 publish: publish.clone(),
193 subscribe: subscribe.clone(),
194 peer_origin: self.peer_origin,
195 cost: self.cost,
196 version: ietf::Version::Draft18,
197 path: self.setup_path.clone(),
198 peer_setup_stream: None,
199 peer_cluster: None,
200 })?;
201
202 tracing::debug!(version = ?v, "connected");
203 return Ok(Session::new(session, v, None, protocol));
204 }
205 Some(ALPN_17) => {
206 let v = self
207 .versions
208 .select(Version::Ietf(ietf::Version::Draft17))
209 .ok_or(Error::Version)?;
210
211 let protocol = ietf::start(ietf::Config {
214 session: session.clone(),
215 setup: None,
216 request_id_max: None,
217 client: true,
218 publish: publish.clone(),
219 subscribe: subscribe.clone(),
220 peer_origin: self.peer_origin,
221 cost: self.cost,
222 version: ietf::Version::Draft17,
223 path: self.setup_path.clone(),
224 peer_setup_stream: None,
225 peer_cluster: None,
226 })?;
227
228 tracing::debug!(version = ?v, "connected");
229 return Ok(Session::new(session, v, None, protocol));
230 }
231 Some(ALPN_16) => {
232 let v = self
233 .versions
234 .select(Version::Ietf(ietf::Version::Draft16))
235 .ok_or(Error::Version)?;
236 (v, v.into())
237 }
238 Some(ALPN_15) => {
239 let v = self
240 .versions
241 .select(Version::Ietf(ietf::Version::Draft15))
242 .ok_or(Error::Version)?;
243 (v, v.into())
244 }
245 Some(ALPN_14) => {
246 let v = self
247 .versions
248 .select(Version::Ietf(ietf::Version::Draft14))
249 .ok_or(Error::Version)?;
250 (v, v.into())
251 }
252 Some(alpn @ (ALPN_LITE_05 | ALPN_LITE_06_WIP)) => {
253 let version = match alpn {
254 ALPN_LITE_06_WIP => lite::Version::Lite06Wip,
255 _ => lite::Version::Lite05,
256 };
257 self.versions.select(Version::Lite(version)).ok_or(Error::Version)?;
258
259 let our_setup = lite::Setup {
264 probe: lite::ProbeLevel::Report,
265 path: self.setup_path.clone(),
266 role: lite::Role::from_origins(self.publish.is_some(), self.subscribe.is_some()),
267 cost: self.cost,
268 origin: None,
270 };
271
272 let start = lite::start(
273 session.clone(),
274 None,
275 publish.clone(),
276 subscribe.clone(),
277 self.peer_origin,
278 version,
279 our_setup,
280 None,
281 )?;
282
283 let (session, mut driver) = Session::new(session, version.into(), start.recv_bandwidth, start.driver);
287 driver.wait_ready(|waiter| start.connecting.poll_ready(waiter)).await;
288
289 return Ok((session, driver));
290 }
291 Some(ALPN_LITE_04) => {
292 self.versions
293 .select(Version::Lite(lite::Version::Lite04))
294 .ok_or(Error::Version)?;
295
296 let start = lite::start(
297 session.clone(),
298 None,
299 publish.clone(),
300 subscribe.clone(),
301 self.peer_origin,
302 lite::Version::Lite04,
303 lite::Setup::default(),
304 None,
305 )?;
306
307 let (session, mut driver) = Session::new(
309 session,
310 lite::Version::Lite04.into(),
311 start.recv_bandwidth,
312 start.driver,
313 );
314 driver.wait_ready(|waiter| start.connecting.poll_ready(waiter)).await;
315
316 return Ok((session, driver));
317 }
318 Some(ALPN_LITE_03) => {
319 self.versions
320 .select(Version::Lite(lite::Version::Lite03))
321 .ok_or(Error::Version)?;
322
323 let start = lite::start(
325 session.clone(),
326 None,
327 publish.clone(),
328 subscribe.clone(),
329 self.peer_origin,
330 lite::Version::Lite03,
331 lite::Setup::default(),
332 None,
333 )?;
334
335 let (session, mut driver) = Session::new(
337 session,
338 lite::Version::Lite03.into(),
339 start.recv_bandwidth,
340 start.driver,
341 );
342 driver.wait_ready(|waiter| start.connecting.poll_ready(waiter)).await;
343
344 return Ok((session, driver));
345 }
346 Some(ALPN_LITE) | None => {
347 let supported = self.versions.filter(&NEGOTIATED.into()).ok_or(Error::Version)?;
348 (Version::Ietf(ietf::Version::Draft14), supported)
349 }
350 Some(p) => return Err(Error::UnknownAlpn(p.to_string())),
351 };
352
353 let mut stream = Stream::open(&session, encoding).await?;
354
355 let ietf_encoding = ietf::Version::try_from(encoding).map_err(|_| Error::Version)?;
357
358 let mut parameters = ietf::Parameters::default();
359 parameters.set_varint(ietf::ParameterVarInt::MaxRequestId, u32::MAX as u64);
360 parameters.set_bytes(ietf::ParameterBytes::Implementation, b"moq-lite-rs".to_vec());
361 if let Some(path) = &self.setup_path {
363 parameters.set_bytes(ietf::ParameterBytes::Path, path.clone().into_bytes());
364 }
365 let parameters = parameters.encode_bytes(ietf_encoding)?;
366
367 let client = setup::Client {
368 versions: supported.clone().into(),
369 parameters,
370 };
371
372 stream.writer.encode(&client).await?;
373
374 let mut server: setup::Server = stream.reader.decode().await?;
375
376 let version = supported
377 .iter()
378 .find(|v| coding::Version::from(**v) == server.version)
379 .copied()
380 .ok_or(Error::Version)?;
381
382 let (recv_bw, protocol, connecting) = match version {
383 Version::Lite(v) => {
384 let stream = stream.with_version(v);
385 let start = lite::start(
386 session.clone(),
387 Some(stream),
388 publish.clone(),
389 subscribe.clone(),
390 self.peer_origin,
391 v,
392 lite::Setup::default(),
395 None,
396 )?;
397
398 (start.recv_bandwidth, start.driver, Some(start.connecting))
399 }
400 Version::Ietf(v) => {
401 let parameters = ietf::Parameters::decode(&mut server.parameters, v)?;
403 let request_id_max = parameters
404 .get_varint(ietf::ParameterVarInt::MaxRequestId)
405 .map(ietf::RequestId);
406
407 let stream = stream.with_version(v);
408 let protocol = ietf::start(ietf::Config {
410 session: session.clone(),
411 setup: Some(stream),
412 request_id_max,
413 client: true,
414 publish: publish.clone(),
415 subscribe: subscribe.clone(),
416 peer_origin: self.peer_origin,
417 cost: self.cost,
418 version: v,
419 path: None,
420 peer_setup_stream: None,
421 peer_cluster: None,
422 })?;
423 (None, protocol, None)
424 }
425 };
426
427 let (session, mut driver) = Session::new(session, version, recv_bw, protocol);
428 if let Some(connecting) = connecting {
429 driver.wait_ready(|waiter| connecting.poll_ready(waiter)).await;
432 }
433
434 Ok((session, driver))
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441 use std::{
442 collections::VecDeque,
443 sync::{Arc, Mutex},
444 };
445
446 use crate::coding::{Decode, Encode};
447 use bytes::{BufMut, Bytes};
448
449 #[derive(Debug, Clone, Default)]
450 struct FakeError;
451
452 impl std::fmt::Display for FakeError {
453 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454 write!(f, "fake transport error")
455 }
456 }
457
458 impl std::error::Error for FakeError {}
459
460 impl web_transport_trait::Error for FakeError {
461 fn session_error(&self) -> Option<(u32, String)> {
462 Some((0, "closed".to_string()))
463 }
464 }
465
466 #[derive(Clone, Default)]
467 struct FakeSession {
468 state: Arc<FakeSessionState>,
469 }
470
471 #[derive(Default)]
472 struct FakeSessionState {
473 protocol: Option<&'static str>,
474 control_stream: Mutex<Option<(FakeSendStream, FakeRecvStream)>>,
475 close_events: Mutex<Vec<(u32, String)>>,
476 close_notify: tokio::sync::Notify,
477 control_writes: Arc<Mutex<Vec<u8>>>,
478 send_rate: Mutex<Option<u64>>,
479 }
480
481 impl FakeSession {
482 fn new(protocol: Option<&'static str>, server_control_bytes: Vec<u8>) -> Self {
483 let writes = Arc::new(Mutex::new(Vec::new()));
484 let send = FakeSendStream { writes: writes.clone() };
485 let recv = FakeRecvStream {
486 data: VecDeque::from(server_control_bytes),
487 };
488 let state = FakeSessionState {
489 protocol,
490 control_stream: Mutex::new(Some((send, recv))),
491 close_events: Mutex::new(Vec::new()),
492 close_notify: tokio::sync::Notify::new(),
493 control_writes: writes,
494 send_rate: Mutex::new(None),
495 };
496 Self { state: Arc::new(state) }
497 }
498
499 fn set_send_rate(&self, rate: Option<u64>) {
500 *self.state.send_rate.lock().unwrap() = rate;
501 }
502
503 fn control_writes(&self) -> Vec<u8> {
504 self.state.control_writes.lock().unwrap().clone()
505 }
506
507 async fn wait_for_first_close(&self) -> (u32, String) {
508 loop {
509 let notified = self.state.close_notify.notified();
510 if let Some(close) = self.state.close_events.lock().unwrap().first().cloned() {
511 return close;
512 }
513 notified.await;
514 }
515 }
516 }
517
518 impl web_transport_trait::Session for FakeSession {
519 type SendStream = FakeSendStream;
520 type RecvStream = FakeRecvStream;
521 type Error = FakeError;
522
523 async fn accept_uni(&self) -> Result<Self::RecvStream, Self::Error> {
524 std::future::pending().await
525 }
526
527 async fn accept_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error> {
528 std::future::pending().await
529 }
530
531 async fn open_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error> {
532 self.state.control_stream.lock().unwrap().take().ok_or(FakeError)
533 }
534
535 async fn open_uni(&self) -> Result<Self::SendStream, Self::Error> {
536 std::future::pending().await
537 }
538
539 fn send_datagram(&self, _payload: Bytes) -> Result<(), Self::Error> {
540 Ok(())
541 }
542
543 async fn recv_datagram(&self) -> Result<Bytes, Self::Error> {
544 std::future::pending().await
545 }
546
547 fn max_datagram_size(&self) -> usize {
548 1200
549 }
550
551 fn protocol(&self) -> Option<&str> {
552 self.state.protocol
553 }
554
555 fn close(&self, code: u32, reason: &str) {
556 self.state.close_events.lock().unwrap().push((code, reason.to_string()));
557 self.state.close_notify.notify_waiters();
558 }
559
560 async fn closed(&self) -> Self::Error {
561 loop {
562 let notified = self.state.close_notify.notified();
563 if !self.state.close_events.lock().unwrap().is_empty() {
564 return FakeError;
565 }
566 notified.await;
567 }
568 }
569
570 fn stats(&self) -> impl web_transport_trait::Stats {
571 FakeStats {
572 send_rate: *self.state.send_rate.lock().unwrap(),
573 }
574 }
575 }
576
577 struct FakeStats {
578 send_rate: Option<u64>,
579 }
580
581 impl web_transport_trait::Stats for FakeStats {
582 fn estimated_send_rate(&self) -> Option<u64> {
583 self.send_rate
584 }
585 }
586
587 #[derive(Clone, Default)]
588 struct FakeSendStream {
589 writes: Arc<Mutex<Vec<u8>>>,
590 }
591
592 impl web_transport_trait::SendStream for FakeSendStream {
593 type Error = FakeError;
594
595 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
596 self.writes.lock().unwrap().put_slice(buf);
597 Ok(buf.len())
598 }
599
600 fn set_priority(&mut self, _order: u8) {}
601
602 fn finish(&mut self) -> Result<(), Self::Error> {
603 Ok(())
604 }
605
606 fn reset(&mut self, _code: u32) {}
607
608 async fn closed(&mut self) -> Result<(), Self::Error> {
609 Ok(())
610 }
611 }
612
613 struct FakeRecvStream {
614 data: VecDeque<u8>,
615 }
616
617 impl web_transport_trait::RecvStream for FakeRecvStream {
618 type Error = FakeError;
619
620 async fn read(&mut self, dst: &mut [u8]) -> Result<Option<usize>, Self::Error> {
621 if self.data.is_empty() {
622 return Ok(None);
623 }
624
625 let size = dst.len().min(self.data.len());
626 for slot in dst.iter_mut().take(size) {
627 *slot = self.data.pop_front().unwrap();
628 }
629 Ok(Some(size))
630 }
631
632 fn stop(&mut self, _code: u32) {}
633
634 async fn closed(&mut self) -> Result<(), Self::Error> {
635 Ok(())
636 }
637 }
638
639 fn mock_server_setup(negotiated: Version) -> Vec<u8> {
640 let mut encoded = Vec::new();
641 let server = setup::Server {
642 version: negotiated.into(),
643 parameters: Bytes::new(),
644 };
645 server
646 .encode(&mut encoded, Version::Ietf(ietf::Version::Draft14))
647 .unwrap();
648
649 let info = lite::SessionInfo { bitrate: Some(1) };
651 let lite_v = lite::Version::try_from(negotiated).unwrap();
652 info.encode(&mut encoded, lite_v).unwrap();
653
654 encoded
655 }
656
657 async fn run_alpn_lite_fallback_case(protocol: Option<&'static str>) {
658 let fake = FakeSession::new(protocol, mock_server_setup(Version::Lite(lite::Version::Lite01)));
659 let client = Client::new().with_versions(
660 [
661 Version::Lite(lite::Version::Lite03),
662 Version::Lite(lite::Version::Lite02),
663 Version::Lite(lite::Version::Lite01),
664 Version::Ietf(ietf::Version::Draft14),
665 ]
666 .into(),
667 );
668
669 let _connection = client.connect(fake.clone()).await.unwrap();
670
671 let mut setup_bytes = Bytes::from(fake.control_writes());
673 let setup = setup::Client::decode(&mut setup_bytes, Version::Ietf(ietf::Version::Draft14)).unwrap();
674 let advertised: Vec<Version> = setup.versions.iter().map(|v| Version::try_from(*v).unwrap()).collect();
675 assert_eq!(
676 advertised,
677 vec![
678 Version::Lite(lite::Version::Lite02),
679 Version::Lite(lite::Version::Lite01),
680 Version::Ietf(ietf::Version::Draft14),
681 ]
682 );
683
684 let (code, _) = fake.wait_for_first_close().await;
691 assert_ne!(code, Error::Version.to_code(), "SessionInfo failed to decode");
692 }
693
694 #[tokio::test(start_paused = true)]
695 async fn alpn_lite_falls_back_to_draft14_and_switches_version_post_setup() {
696 run_alpn_lite_fallback_case(Some(ALPN_LITE)).await;
697 }
698
699 #[tokio::test(start_paused = true)]
700 async fn no_alpn_falls_back_to_draft14_and_switches_version_post_setup() {
701 run_alpn_lite_fallback_case(None).await;
702 }
703
704 #[test]
712 fn driver_is_caller_polled_and_holds_no_session() {
713 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
714 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
715
716 let (session, mut driver) = futures::executor::block_on(client.connect(fake.clone())).unwrap();
717 assert_eq!(session.version(), Version::Lite(lite::Version::Lite04));
718
719 assert!(driver.poll(&kio::Waiter::noop()).is_pending());
721
722 let mut context = std::task::Context::from_waker(std::task::Waker::noop());
724 assert!(std::future::Future::poll(std::pin::Pin::new(&mut driver), &mut context).is_pending());
725
726 drop(session);
729 assert_eq!(fake.state.close_events.lock().unwrap()[0].0, Error::Cancel.to_code());
730 }
731
732 #[test]
735 fn session_clones_share_the_close() {
736 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
737 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
738
739 let (session, _driver) = futures::executor::block_on(client.connect(fake.clone())).unwrap();
740 let clone = session.clone();
741
742 drop(session);
744 assert!(fake.state.close_events.lock().unwrap().is_empty());
745
746 clone.abort(Error::Cancel);
747 assert_eq!(fake.state.close_events.lock().unwrap()[0].0, Error::Cancel.to_code());
748
749 drop(clone);
751 assert_eq!(fake.state.close_events.lock().unwrap().len(), 1);
752 }
753
754 #[tokio::test(start_paused = true)]
758 async fn send_bandwidth_samples_while_the_driver_runs() {
759 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
760 fake.set_send_rate(Some(1_000_000));
761
762 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
763 let (session, driver) = client.connect(fake.clone()).await.unwrap();
764 tokio::spawn(driver);
765
766 let mut bandwidth = session.send_bandwidth().expect("backend reports an estimate");
767 assert_eq!(bandwidth.changed().await.unwrap(), Some(1_000_000));
768
769 fake.set_send_rate(Some(2_000_000));
771 assert_eq!(bandwidth.changed().await.unwrap(), Some(2_000_000));
772 }
773}