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