1use crate::origin;
2#[cfg(test)]
3use crate::runtime::Timers;
4use crate::time::{Clock, Instant};
5use crate::{
6 ALPN_14, ALPN_15, ALPN_16, ALPN_17, ALPN_18, ALPN_19, ALPN_20, ALPN_21, ALPN_22, ALPN_LITE, ALPN_LITE_03,
7 ALPN_LITE_04, ALPN_LITE_05, ALPN_LITE_06, ALPN_LITE_07_WIP, Consume, Error, NEGOTIATED, Session, Version, Versions,
8 coding::{self, Decode, Encode, Stream},
9 ietf, lite, setup, stats,
10};
11
12#[derive(Default, Clone)]
14pub struct Client {
15 publish: Option<origin::Consumer>,
16 subscribe: Option<origin::Producer>,
17 stats: stats::Session,
18 versions: Versions,
19 setup_path: Option<String>,
20 setup_authority: Option<String>,
21 cost: Option<u64>,
22 peer_hop: Option<crate::Hop>,
23}
24
25impl Client {
26 pub fn new() -> Self {
28 Default::default()
29 }
30
31 pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
35 self.publish = Some(publish.consume());
36 self
37 }
38
39 pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
42 self.subscribe = Some(subscribe);
43 self
44 }
45
46 pub fn with_stats(mut self, stats: stats::Session) -> Self {
51 self.stats = stats;
52 self
53 }
54
55 pub fn with_origin(self, origin: origin::Producer) -> Self {
60 self.with_publisher(&origin).with_subscriber(origin)
61 }
62
63 pub fn with_versions(mut self, versions: Versions) -> Self {
66 self.versions = versions;
67 self
68 }
69
70 pub fn with_path(mut self, path: impl Into<String>) -> Self {
82 self.setup_path = Some(path.into());
83 self
84 }
85
86 pub fn with_authority(mut self, authority: impl Into<String>) -> Self {
88 self.setup_authority = Some(authority.into());
89 self
90 }
91
92 pub fn with_cost(mut self, cost: u64) -> Self {
107 self.cost = Some(cost);
108 self
109 }
110
111 pub fn with_peer_hop(mut self, hop: crate::Hop) -> Self {
128 self.peer_hop = Some(hop);
129 self
130 }
131
132 fn assigned_hop(&self) -> crate::Hop {
138 self.peer_hop.unwrap_or_else(crate::Hop::random)
139 }
140
141 fn origins(&self, peer_hop: crate::Hop) -> (Option<origin::Consumer>, Option<origin::Producer>) {
152 if self.publish.is_none() && self.subscribe.is_none() {
153 tracing::warn!("not publishing or consuming anything");
154 }
155 let publish = self.publish.clone().map(|origin| origin.with_stats(self.stats.clone()));
156 let subscribe = self
157 .subscribe
158 .clone()
159 .map(|origin| origin.with_stats(self.stats.clone()));
160 let publish = publish.map(|origin| origin.excluding(peer_hop));
161 (publish, subscribe)
162 }
163
164 fn start_lite<S>(
167 &self,
168 runtime: Clock,
169 session: S,
170 version: lite::Version,
171 peer_hop: crate::Hop,
172 ) -> Result<(Session, crate::Driver<S>), Error>
173 where
174 S: crate::transport::poll::Session,
175 {
176 let (publish, subscribe) = self.origins(peer_hop);
177
178 let our_setup = if version.has_setup_stream() {
184 lite::Setup {
185 probe: lite::ProbeLevel::detect(&session),
186 path: self.setup_path.clone(),
187 role: lite::Role::from_origins(self.publish.is_some(), self.subscribe.is_some()),
188 cost: self.cost,
189 hop: None,
191 }
192 } else {
193 lite::Setup::default()
194 };
195
196 let start = lite::start(lite::Config {
197 runtime: runtime.clone(),
198 session: session.clone(),
199 setup_stream: None,
200 publish,
201 subscribe,
202 peer_hop: Some(peer_hop),
203 version,
204 our_setup,
205 peer_setup: None,
206 })?;
207
208 Ok(Session::new(
209 runtime,
210 session,
211 version.into(),
212 start.recv_bandwidth,
213 crate::driver::Protocol::Lite(Box::new(start.driver)),
214 start.goaway,
215 Some(start.estimate),
216 ))
217 }
218
219 pub async fn connect_lite<S>(&self, now: Instant, session: S) -> Result<(Session, crate::Driver<S>), Error>
229 where
230 S: crate::transport::poll::Session,
231 {
232 let runtime = Clock::new(now);
233 let version = match session.protocol() {
234 Some(ALPN_LITE_07_WIP) => lite::Version::Lite07,
235 Some(ALPN_LITE_06) => lite::Version::Lite06,
236 Some(ALPN_LITE_05) => lite::Version::Lite05,
237 Some(ALPN_LITE_04) => lite::Version::Lite04,
238 Some(ALPN_LITE_03) => lite::Version::Lite03,
239 _ => return Err(Error::Version),
240 };
241 self.versions.select(Version::Lite(version)).ok_or(Error::Version)?;
242 self.start_lite(runtime, session, version, self.assigned_hop())
243 }
244
245 pub async fn connect<S>(&self, now: Instant, mut session: S) -> Result<(Session, crate::Driver<S>), Error>
249 where
250 S: crate::transport::poll::Boxable,
251 {
252 let runtime = Clock::new(now);
253 let peer_hop = self.assigned_hop();
254 let (publish, subscribe) = self.origins(peer_hop);
255
256 let (encoding, supported) = match session.protocol() {
259 Some(alpn @ (ALPN_22 | ALPN_21 | ALPN_20 | ALPN_19 | ALPN_18 | ALPN_17)) => {
260 let draft = match alpn {
261 ALPN_22 => ietf::Version::Draft22,
262 ALPN_21 => ietf::Version::Draft21,
263 ALPN_20 => ietf::Version::Draft20,
264 ALPN_19 => ietf::Version::Draft19,
265 ALPN_18 => ietf::Version::Draft18,
266 _ => ietf::Version::Draft17,
267 };
268
269 let v = self.versions.select(Version::Ietf(draft)).ok_or(Error::Version)?;
270
271 let (protocol, goaway) = ietf::start(ietf::Config {
274 runtime: runtime.clone(),
275 session: session.clone(),
276 setup: None,
277 request_id_max: None,
278 client: true,
279 publish: publish.clone(),
280 subscribe: subscribe.clone(),
281 peer_hop: Some(peer_hop),
282 cost: self.cost,
283 version: draft,
284 path: self.setup_path.clone(),
285 authority: self.setup_authority.clone(),
286 peer_setup_stream: None,
287 peer_declared: None,
288 early_unis: Vec::new(),
289 })?;
290
291 tracing::debug!(version = ?v, "connected");
292 return Ok(Session::new(
293 runtime,
294 session,
295 v,
296 None,
297 crate::driver::Protocol::Ietf(protocol),
298 goaway,
299 None,
300 ));
301 }
302 Some(ALPN_16) => {
303 let v = self
304 .versions
305 .select(Version::Ietf(ietf::Version::Draft16))
306 .ok_or(Error::Version)?;
307 (v, v.into())
308 }
309 Some(ALPN_15) => {
310 let v = self
311 .versions
312 .select(Version::Ietf(ietf::Version::Draft15))
313 .ok_or(Error::Version)?;
314 (v, v.into())
315 }
316 Some(ALPN_14) => {
317 let v = self
318 .versions
319 .select(Version::Ietf(ietf::Version::Draft14))
320 .ok_or(Error::Version)?;
321 (v, v.into())
322 }
323 Some(alpn @ (ALPN_LITE_05 | ALPN_LITE_06 | ALPN_LITE_07_WIP)) => {
324 let version = match alpn {
325 ALPN_LITE_07_WIP => lite::Version::Lite07,
326 ALPN_LITE_06 => lite::Version::Lite06,
327 _ => lite::Version::Lite05,
328 };
329 self.versions.select(Version::Lite(version)).ok_or(Error::Version)?;
330 return self.start_lite(runtime, session, version, peer_hop);
331 }
332 Some(ALPN_LITE_04) => {
333 self.versions
334 .select(Version::Lite(lite::Version::Lite04))
335 .ok_or(Error::Version)?;
336 return self.start_lite(runtime, session, lite::Version::Lite04, peer_hop);
337 }
338 Some(ALPN_LITE_03) => {
339 self.versions
340 .select(Version::Lite(lite::Version::Lite03))
341 .ok_or(Error::Version)?;
342 return self.start_lite(runtime, session, lite::Version::Lite03, peer_hop);
343 }
344 Some(ALPN_LITE) | None => {
345 let supported = self.versions.filter(&NEGOTIATED.into()).ok_or(Error::Version)?;
346 (Version::Ietf(ietf::Version::Draft14), supported)
347 }
348 Some(p) => return Err(Error::UnknownAlpn(p.to_string())),
349 };
350
351 let mut stream = Stream::open(&mut session, encoding).await?;
352
353 let ietf_encoding = ietf::Version::try_from(encoding).map_err(|_| Error::Version)?;
355
356 let mut parameters = ietf::Parameters::default();
357 parameters.set_varint(ietf::ParameterVarInt::MaxRequestId, u32::MAX as u64);
358 parameters.set_bytes(ietf::ParameterBytes::Implementation, b"moq-lite-rs".to_vec());
359 if let Some(path) = &self.setup_path {
361 parameters.set_bytes(ietf::ParameterBytes::Path, path.clone().into_bytes());
362 }
363 if let Some(authority) = &self.setup_authority {
364 parameters.set_bytes(ietf::ParameterBytes::Authority, authority.clone().into_bytes());
365 }
366 ietf::solicit::into_setup(&mut parameters, ietf_encoding);
367 ietf::hidden::into_setup(&mut parameters, ietf_encoding);
368 let parameters = parameters.encode_bytes(ietf_encoding)?;
369
370 let client = setup::Client {
371 versions: supported.clone().into(),
372 parameters,
373 };
374
375 stream.writer.encode(&client).await?;
376
377 let mut server: setup::Server = stream.reader.decode().await?;
378
379 let version = supported
380 .iter()
381 .find(|v| coding::Version::from(**v) == server.version)
382 .copied()
383 .ok_or(Error::Version)?;
384
385 let (recv_bw, protocol, goaway, estimate) = match version {
386 Version::Lite(v) => {
387 let stream = stream.with_version(v);
388 let start = lite::start(lite::Config {
389 runtime: runtime.clone(),
390 session: session.clone(),
391 setup_stream: Some(stream),
392 publish: publish.clone(),
393 subscribe: subscribe.clone(),
394 peer_hop: Some(peer_hop),
395 version: v,
396 our_setup: lite::Setup::default(),
399 peer_setup: None,
400 })?;
401
402 (
403 start.recv_bandwidth,
404 crate::driver::Protocol::Lite(Box::new(start.driver)),
405 start.goaway,
406 Some(start.estimate),
407 )
408 }
409 Version::Ietf(v) => {
410 let parameters = ietf::Parameters::decode(&mut server.parameters, v)?;
413 let request_id_max = parameters
414 .get_varint(ietf::ParameterVarInt::MaxRequestId)
415 .map(ietf::RequestId);
416 let peer_declared = ietf::peer::Peer {
417 solicit: ietf::solicit::from_setup(¶meters, v)?,
418 hidden: ietf::hidden::from_setup(¶meters, v),
419 ..Default::default()
420 };
421
422 let stream = stream.with_version(v);
423 let (protocol, goaway) = ietf::start(ietf::Config {
425 runtime: runtime.clone(),
426 session: session.clone(),
427 setup: Some(stream),
428 request_id_max,
429 client: true,
430 publish: publish.clone(),
431 subscribe: subscribe.clone(),
432 peer_hop: Some(peer_hop),
433 cost: self.cost,
434 version: v,
435 path: None,
436 authority: None,
437 peer_setup_stream: None,
438 peer_declared: Some(peer_declared),
439 early_unis: Vec::new(),
440 })?;
441 (None, crate::driver::Protocol::Ietf(protocol), goaway, None)
442 }
443 };
444
445 Ok(Session::new(
446 runtime, session, version, recv_bw, protocol, goaway, estimate,
447 ))
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454 use crate::model::ProduceTest;
455 use std::{
456 collections::VecDeque,
457 sync::{Arc, Mutex},
458 };
459
460 use std::task::{Context, Poll};
461
462 use crate::SessionError;
463 use crate::coding::{Decode, Encode};
464 use bytes::{BufMut, Bytes};
465
466 #[derive(Debug, Clone, Default)]
467 struct FakeError;
468
469 impl std::fmt::Display for FakeError {
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 write!(f, "fake transport error")
472 }
473 }
474
475 impl std::error::Error for FakeError {}
476
477 impl web_transport_trait::Error for FakeError {
478 fn session_error(&self) -> Option<(u32, String)> {
479 Some((0, "closed".to_string()))
480 }
481 }
482
483 #[derive(Clone, Default)]
484 struct FakeSession {
485 state: Arc<FakeSessionState>,
486 park: kio::Park,
488 }
489
490 #[derive(Default)]
491 struct FakeSessionState {
492 protocol: Option<&'static str>,
493 control_stream: Mutex<Option<(FakeSendStream, FakeRecvStream)>>,
494 close_events: Mutex<Vec<(u32, String)>>,
495 closed: kio::Fan,
496 control_writes: Arc<Mutex<Vec<u8>>>,
497 send_rate: Mutex<Option<u64>>,
498 bytes_sent: Mutex<Option<u64>>,
499 }
500
501 impl FakeSession {
502 fn new(protocol: Option<&'static str>, server_control_bytes: Vec<u8>) -> Self {
503 let writes = Arc::new(Mutex::new(Vec::new()));
504 let send = FakeSendStream { writes: writes.clone() };
505 let recv = FakeRecvStream {
506 data: VecDeque::from(server_control_bytes),
507 };
508 let state = FakeSessionState {
509 protocol,
510 control_stream: Mutex::new(Some((send, recv))),
511 close_events: Mutex::new(Vec::new()),
512 closed: kio::Fan::default(),
513 control_writes: writes,
514 send_rate: Mutex::new(None),
515 bytes_sent: Mutex::new(None),
516 };
517 Self {
518 state: Arc::new(state),
519 park: kio::Park::default(),
520 }
521 }
522
523 fn set_send_rate(&self, rate: Option<u64>) {
524 *self.state.send_rate.lock().unwrap() = rate;
525 }
526
527 fn set_bytes_sent(&self, bytes: Option<u64>) {
528 *self.state.bytes_sent.lock().unwrap() = bytes;
529 }
530
531 fn control_writes(&self) -> Vec<u8> {
532 self.state.control_writes.lock().unwrap().clone()
533 }
534
535 async fn wait_for_first_close(&self) -> (u32, String) {
536 kio::wait(|waiter| {
537 self.state.closed.register(waiter);
538 match self.state.close_events.lock().unwrap().first().cloned() {
539 Some(close) => std::task::Poll::Ready(close),
540 None => std::task::Poll::Pending,
541 }
542 })
543 .await
544 }
545 }
546
547 impl web_transport_trait::poll::Session for FakeSession {
548 type SendStream = FakeSendStream;
549 type RecvStream = FakeRecvStream;
550 type Error = FakeError;
551
552 fn poll_accept_uni(&mut self, _cx: &mut Context<'_>) -> Poll<Result<Self::RecvStream, Self::Error>> {
553 Poll::Pending
554 }
555
556 fn poll_accept_bi(
557 &mut self,
558 _cx: &mut Context<'_>,
559 ) -> Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
560 Poll::Pending
561 }
562
563 fn poll_open_bi(
564 &mut self,
565 _cx: &mut Context<'_>,
566 ) -> Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
567 Poll::Ready(self.state.control_stream.lock().unwrap().take().ok_or(FakeError))
568 }
569
570 fn poll_open_uni(&mut self, _cx: &mut Context<'_>) -> Poll<Result<Self::SendStream, Self::Error>> {
571 Poll::Pending
572 }
573
574 fn poll_send_datagram(&mut self, _cx: &mut Context<'_>, _payload: &[u8]) -> Poll<Result<(), Self::Error>> {
575 Poll::Ready(Ok(()))
576 }
577
578 fn poll_recv_datagram(&mut self, _cx: &mut Context<'_>) -> Poll<Result<Bytes, Self::Error>> {
579 Poll::Pending
580 }
581
582 fn max_datagram_size(&self) -> usize {
583 1200
584 }
585
586 fn protocol(&self) -> Option<&str> {
587 self.state.protocol
588 }
589
590 fn close(&mut self, code: u32, reason: &str) {
591 self.state.close_events.lock().unwrap().push((code, reason.to_string()));
592 self.state.closed.wake();
593 }
594
595 fn poll_closed(&mut self, cx: &mut Context<'_>) -> Poll<Self::Error> {
596 self.state.closed.register(self.park.hold(cx));
598 match self.state.close_events.lock().unwrap().is_empty() {
599 false => Poll::Ready(FakeError),
600 true => Poll::Pending,
601 }
602 }
603
604 fn stats(&self) -> impl web_transport_trait::Stats {
605 FakeStats {
606 send_rate: *self.state.send_rate.lock().unwrap(),
607 bytes_sent: *self.state.bytes_sent.lock().unwrap(),
608 }
609 }
610 }
611
612 struct FakeStats {
613 send_rate: Option<u64>,
614 bytes_sent: Option<u64>,
615 }
616
617 impl web_transport_trait::Stats for FakeStats {
618 fn estimated_send_rate(&self) -> Option<u64> {
619 self.send_rate
620 }
621
622 fn bytes_sent(&self) -> Option<u64> {
623 self.bytes_sent
624 }
625 }
626
627 #[derive(Clone, Default)]
628 struct FakeSendStream {
629 writes: Arc<Mutex<Vec<u8>>>,
630 }
631
632 impl web_transport_trait::poll::SendStream for FakeSendStream {
633 type Error = FakeError;
634
635 fn poll_write(&mut self, _cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize, Self::Error>> {
636 self.writes.lock().unwrap().put_slice(buf);
637 Poll::Ready(Ok(buf.len()))
638 }
639
640 fn set_priority(&mut self, _order: u8) {}
641
642 fn finish(&mut self) -> Result<(), Self::Error> {
643 Ok(())
644 }
645
646 fn reset(&mut self, _code: u32) {}
647
648 fn poll_closed(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
649 Poll::Ready(Ok(()))
650 }
651 }
652
653 struct FakeRecvStream {
654 data: VecDeque<u8>,
655 }
656
657 impl web_transport_trait::poll::RecvStream for FakeRecvStream {
658 type Error = FakeError;
659
660 fn poll_read(&mut self, _cx: &mut Context<'_>, dst: &mut [u8]) -> Poll<Result<Option<usize>, Self::Error>> {
661 if self.data.is_empty() {
662 return Poll::Ready(Ok(None));
663 }
664
665 let size = dst.len().min(self.data.len());
666 for slot in dst.iter_mut().take(size) {
667 *slot = self.data.pop_front().unwrap();
668 }
669 Poll::Ready(Ok(Some(size)))
670 }
671
672 fn stop(&mut self, _code: u32) {}
673
674 fn poll_closed(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
675 Poll::Ready(Ok(()))
676 }
677 }
678
679 fn mock_server_setup(negotiated: Version) -> Vec<u8> {
680 let mut encoded = Vec::new();
681 let server = setup::Server {
682 version: negotiated.into(),
683 parameters: Bytes::new(),
684 };
685 server
686 .encode(&mut encoded, Version::Ietf(ietf::Version::Draft14))
687 .unwrap();
688
689 let info = lite::SessionInfo { bitrate: Some(1) };
691 let lite_v = lite::Version::try_from(negotiated).unwrap();
692 info.encode(&mut encoded, lite_v).unwrap();
693
694 encoded
695 }
696
697 async fn run_alpn_lite_fallback_case(protocol: Option<&'static str>) {
698 let fake = FakeSession::new(protocol, mock_server_setup(Version::Lite(lite::Version::Lite01)));
699 let client = Client::new().with_versions(
700 [
701 Version::Lite(lite::Version::Lite03),
702 Version::Lite(lite::Version::Lite02),
703 Version::Lite(lite::Version::Lite01),
704 Version::Ietf(ietf::Version::Draft14),
705 ]
706 .into(),
707 );
708
709 let (_session, driver) = client
711 .connect(tokio::time::Instant::now().into_std(), fake.clone())
712 .await
713 .unwrap();
714 tokio::spawn(crate::time::run(driver));
715
716 let mut setup_bytes = Bytes::from(fake.control_writes());
718 let setup = setup::Client::decode(&mut setup_bytes, Version::Ietf(ietf::Version::Draft14)).unwrap();
719 let advertised: Vec<Version> = setup.versions.iter().map(|v| Version::try_from(*v).unwrap()).collect();
720 assert_eq!(
721 advertised,
722 vec![
723 Version::Lite(lite::Version::Lite02),
724 Version::Lite(lite::Version::Lite01),
725 Version::Ietf(ietf::Version::Draft14),
726 ]
727 );
728
729 let (code, _) = fake.wait_for_first_close().await;
736 assert_ne!(code, SessionError::Version.to_code(), "SessionInfo failed to decode");
738 }
739
740 #[tokio::test(start_paused = true)]
746 async fn connect_does_not_wait_for_the_peer_to_announce() {
747 let gate = kio::Producer::new(true);
749 let transport = crate::lite::test_transport::SinkSession::gated_bi(gate.consume())
750 .with_protocol(crate::version::ALPN_LITE_05);
751
752 let origin = crate::origin::Config::new(crate::Hop::new(1).unwrap()).produce();
754 let client = Client::new()
755 .with_versions([Version::Lite(lite::Version::Lite05)].into())
756 .with_subscriber(origin);
757
758 let (_session, _driver) = tokio::time::timeout(
761 std::time::Duration::from_secs(30),
762 client.connect(tokio::time::Instant::now().into_std(), transport),
763 )
764 .await
765 .expect("connect waited on a peer that never announced")
766 .expect("connect failed");
767 }
768
769 #[tokio::test(start_paused = true)]
772 async fn draft14_setup_carries_the_authority() {
773 let fake = FakeSession::new(Some(ALPN_LITE), mock_server_setup(Version::Lite(lite::Version::Lite01)));
774 let client = Client::new()
775 .with_versions(
776 [
777 Version::Lite(lite::Version::Lite01),
778 Version::Ietf(ietf::Version::Draft14),
779 ]
780 .into(),
781 )
782 .with_path("/anon")
783 .with_authority("relay.example.com:4443");
784
785 let (_session, driver) = client
786 .connect(tokio::time::Instant::now().into_std(), fake.clone())
787 .await
788 .unwrap();
789 tokio::spawn(crate::time::run(driver));
790
791 let mut setup_bytes = Bytes::from(fake.control_writes());
792 let setup = setup::Client::decode(&mut setup_bytes, Version::Ietf(ietf::Version::Draft14)).unwrap();
793 let mut parameters = setup.parameters;
794 let parameters = ietf::Parameters::decode(&mut parameters, ietf::Version::Draft14).unwrap();
795 assert_eq!(
796 parameters.get_bytes(ietf::ParameterBytes::Authority),
797 Some(b"relay.example.com:4443".as_ref())
798 );
799 assert_eq!(
800 parameters.get_bytes(ietf::ParameterBytes::Path),
801 Some(b"/anon".as_ref())
802 );
803 }
804
805 #[tokio::test(start_paused = true)]
806 async fn alpn_lite_falls_back_to_draft14_and_switches_version_post_setup() {
807 run_alpn_lite_fallback_case(Some(ALPN_LITE)).await;
808 }
809
810 #[tokio::test(start_paused = true)]
811 async fn no_alpn_falls_back_to_draft14_and_switches_version_post_setup() {
812 run_alpn_lite_fallback_case(None).await;
813 }
814
815 #[test]
818 fn driver_is_caller_polled_and_holds_no_session() {
819 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
820 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
821
822 let runtime = crate::runtime::Test::new();
823 let (session, mut driver) = futures::executor::block_on(client.connect(runtime.now(), fake.clone())).unwrap();
824 assert_eq!(session.version(), Version::Lite(lite::Version::Lite04));
825
826 assert!(driver.poll(runtime.now(), &kio::Waiter::noop()).is_ok());
828
829 drop(session);
832 assert!(fake.state.close_events.lock().unwrap().is_empty());
833 let _ = driver.poll(runtime.now(), &kio::Waiter::noop());
834 assert_eq!(
835 fake.state.close_events.lock().unwrap()[0].0,
836 SessionError::Cancel.to_code()
837 );
838 }
839
840 #[test]
844 fn session_clones_share_the_close() {
845 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
846 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
847
848 let runtime = crate::runtime::Test::new();
849 let (session, mut driver) = futures::executor::block_on(client.connect(runtime.now(), fake.clone())).unwrap();
850 let clone = session.clone();
851
852 drop(session);
854 assert!(fake.state.close_events.lock().unwrap().is_empty());
855 let _ = driver.poll(runtime.now(), &kio::Waiter::noop());
856 assert!(fake.state.close_events.lock().unwrap().is_empty());
857
858 clone.abort(Error::Cancel);
859 let _ = driver.poll(runtime.now(), &kio::Waiter::noop());
860 assert_eq!(
861 fake.state.close_events.lock().unwrap()[0].0,
862 SessionError::Cancel.to_code()
863 );
864
865 let _ = driver.poll(runtime.now(), &kio::Waiter::noop());
868 futures::executor::block_on(clone.closed());
869
870 let closes = fake.state.close_events.lock().unwrap().len();
872 drop(clone);
873 let _ = driver.poll(runtime.now(), &kio::Waiter::noop());
874 assert_eq!(fake.state.close_events.lock().unwrap().len(), closes);
875 }
876
877 #[test]
881 fn dropped_driver_resolves_closed() {
882 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
883 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
884
885 let runtime = crate::runtime::Test::new();
886 let (session, driver) = futures::executor::block_on(client.connect(runtime.now(), fake.clone())).unwrap();
887
888 drop(driver);
889 assert!(matches!(futures::executor::block_on(session.closed()), Error::Cancel));
890 }
891
892 #[derive(Clone)]
896 struct LocalSession {
897 inner: FakeSession,
898 _local: std::rc::Rc<()>,
899 }
900
901 struct LocalSend {
902 inner: FakeSendStream,
903 _local: std::rc::Rc<()>,
904 }
905
906 struct LocalRecv {
907 inner: FakeRecvStream,
908 _local: std::rc::Rc<()>,
909 }
910
911 impl web_transport_trait::poll::Session for LocalSession {
912 type SendStream = LocalSend;
913 type RecvStream = LocalRecv;
914 type Error = FakeError;
915
916 fn poll_accept_uni(&mut self, cx: &mut Context<'_>) -> Poll<Result<Self::RecvStream, Self::Error>> {
917 self.inner.poll_accept_uni(cx).map_ok(|stream| LocalRecv {
918 inner: stream,
919 _local: self._local.clone(),
920 })
921 }
922
923 fn poll_accept_bi(
924 &mut self,
925 cx: &mut Context<'_>,
926 ) -> Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
927 self.inner.poll_accept_bi(cx).map_ok(|(send, recv)| {
928 (
929 LocalSend {
930 inner: send,
931 _local: self._local.clone(),
932 },
933 LocalRecv {
934 inner: recv,
935 _local: self._local.clone(),
936 },
937 )
938 })
939 }
940
941 fn poll_open_bi(
942 &mut self,
943 cx: &mut Context<'_>,
944 ) -> Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
945 self.inner.poll_open_bi(cx).map_ok(|(send, recv)| {
946 (
947 LocalSend {
948 inner: send,
949 _local: self._local.clone(),
950 },
951 LocalRecv {
952 inner: recv,
953 _local: self._local.clone(),
954 },
955 )
956 })
957 }
958
959 fn poll_open_uni(&mut self, cx: &mut Context<'_>) -> Poll<Result<Self::SendStream, Self::Error>> {
960 self.inner.poll_open_uni(cx).map_ok(|stream| LocalSend {
961 inner: stream,
962 _local: self._local.clone(),
963 })
964 }
965
966 fn poll_send_datagram(&mut self, cx: &mut Context<'_>, payload: &[u8]) -> Poll<Result<(), Self::Error>> {
967 self.inner.poll_send_datagram(cx, payload)
968 }
969
970 fn poll_recv_datagram(&mut self, cx: &mut Context<'_>) -> Poll<Result<Bytes, Self::Error>> {
971 self.inner.poll_recv_datagram(cx)
972 }
973
974 fn max_datagram_size(&self) -> usize {
975 self.inner.max_datagram_size()
976 }
977
978 fn protocol(&self) -> Option<&str> {
979 self.inner.protocol()
980 }
981
982 fn close(&mut self, code: u32, reason: &str) {
983 self.inner.close(code, reason);
984 }
985
986 fn poll_closed(&mut self, cx: &mut Context<'_>) -> Poll<Self::Error> {
987 self.inner.poll_closed(cx)
988 }
989
990 fn stats(&self) -> impl web_transport_trait::Stats {
991 self.inner.stats()
992 }
993 }
994
995 impl web_transport_trait::poll::SendStream for LocalSend {
996 type Error = FakeError;
997
998 fn poll_write(&mut self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize, Self::Error>> {
999 self.inner.poll_write(cx, buf)
1000 }
1001
1002 fn set_priority(&mut self, order: u8) {
1003 self.inner.set_priority(order);
1004 }
1005
1006 fn finish(&mut self) -> Result<(), Self::Error> {
1007 self.inner.finish()
1008 }
1009
1010 fn reset(&mut self, code: u32) {
1011 self.inner.reset(code);
1012 }
1013
1014 fn poll_closed(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1015 web_transport_trait::poll::SendStream::poll_closed(&mut self.inner, cx)
1016 }
1017 }
1018
1019 impl web_transport_trait::poll::RecvStream for LocalRecv {
1020 type Error = FakeError;
1021
1022 fn poll_read(&mut self, cx: &mut Context<'_>, dst: &mut [u8]) -> Poll<Result<Option<usize>, Self::Error>> {
1023 self.inner.poll_read(cx, dst)
1024 }
1025
1026 fn stop(&mut self, code: u32) {
1027 self.inner.stop(code);
1028 }
1029
1030 fn poll_closed(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1031 web_transport_trait::poll::RecvStream::poll_closed(&mut self.inner, cx)
1032 }
1033 }
1034
1035 #[test]
1040 fn connect_lite_over_a_send_less_transport() {
1041 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
1042 let local = LocalSession {
1043 inner: fake.clone(),
1044 _local: std::rc::Rc::new(()),
1045 };
1046 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
1047
1048 let runtime = crate::runtime::Test::new();
1049 let (session, mut driver) = futures::executor::block_on(client.connect_lite(runtime.now(), local)).unwrap();
1050 assert!(driver.poll(runtime.now(), &kio::Waiter::noop()).is_ok());
1051
1052 fn assert_send_sync<T: Send + Sync>(_: &T) {}
1053 assert_send_sync(&session);
1054
1055 session.abort(Error::Cancel);
1056 let _ = driver.poll(runtime.now(), &kio::Waiter::noop());
1057 assert_eq!(
1058 fake.state.close_events.lock().unwrap()[0].0,
1059 SessionError::Cancel.to_code()
1060 );
1061 }
1062
1063 #[test]
1066 fn accept_lite_over_a_send_less_transport() {
1067 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
1068 let local = LocalSession {
1069 inner: fake.clone(),
1070 _local: std::rc::Rc::new(()),
1071 };
1072 let server = crate::Server::new().with_versions(Version::Lite(lite::Version::Lite04).into());
1073
1074 let runtime = crate::runtime::Test::new();
1075 let (session, mut driver) = futures::executor::block_on(server.accept_lite(runtime.now(), local)).unwrap();
1076 assert_eq!(session.version(), Version::Lite(lite::Version::Lite04));
1077 assert!(driver.poll(runtime.now(), &kio::Waiter::noop()).is_ok());
1078
1079 drop(session);
1080 assert!(fake.state.close_events.lock().unwrap().is_empty());
1081 let _ = driver.poll(runtime.now(), &kio::Waiter::noop());
1082 assert_eq!(
1083 fake.state.close_events.lock().unwrap()[0].0,
1084 SessionError::Cancel.to_code()
1085 );
1086 }
1087
1088 #[test]
1091 fn connect_lite_refuses_ietf_alpns() {
1092 let fake = FakeSession::new(Some(ALPN_19), Vec::new());
1093 let local = LocalSession {
1094 inner: fake,
1095 _local: std::rc::Rc::new(()),
1096 };
1097 let client = Client::new();
1098 let runtime = crate::runtime::Test::new();
1099 let result = futures::executor::block_on(client.connect_lite(runtime.now(), local));
1100 assert!(matches!(result, Err(Error::Version)));
1101 }
1102
1103 #[tokio::test(start_paused = true)]
1107 async fn stats_reads_prime_the_sampler() {
1108 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
1109 fake.set_send_rate(Some(1_000_000));
1110
1111 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
1112 let (session, driver) = client
1113 .connect(tokio::time::Instant::now().into_std(), fake.clone())
1114 .await
1115 .unwrap();
1116 tokio::spawn(crate::time::run(driver));
1117
1118 assert_eq!(
1120 session.stats().estimated_send_rate,
1121 Some(crate::bandwidth::Rate::from_bps(1_000_000))
1122 );
1123
1124 fake.set_send_rate(Some(2_000_000));
1127 while session.stats().estimated_send_rate != Some(crate::bandwidth::Rate::from_bps(2_000_000)) {
1128 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
1129 }
1130 }
1131
1132 #[tokio::test(start_paused = true)]
1139 async fn stats_capture_the_final_counters() {
1140 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
1141 fake.set_send_rate(None);
1142 fake.set_bytes_sent(Some(0));
1143
1144 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
1145 let (session, driver) = client
1146 .connect(tokio::time::Instant::now().into_std(), fake.clone())
1147 .await
1148 .unwrap();
1149 tokio::spawn(crate::time::run(driver));
1150 assert!(
1151 session.send_bandwidth().is_none(),
1152 "no send-rate estimate, so nothing samples on its own"
1153 );
1154
1155 fake.set_bytes_sent(Some(4242));
1156
1157 session.abort(Error::Cancel);
1158 session.closed().await;
1159
1160 assert_eq!(
1161 session.stats().bytes_sent,
1162 Some(4242),
1163 "the closing snapshot must carry the session's final counters"
1164 );
1165 }
1166
1167 #[tokio::test(start_paused = true)]
1171 async fn send_bandwidth_samples_while_the_driver_runs() {
1172 let fake = FakeSession::new(Some(ALPN_LITE_04), Vec::new());
1173 fake.set_send_rate(Some(1_000_000));
1174
1175 let client = Client::new().with_versions(Version::Lite(lite::Version::Lite04).into());
1176 let (session, driver) = client
1177 .connect(tokio::time::Instant::now().into_std(), fake.clone())
1178 .await
1179 .unwrap();
1180 tokio::spawn(crate::time::run(driver));
1181
1182 let mut bandwidth = session.send_bandwidth().expect("backend reports an estimate");
1183 assert_eq!(
1184 bandwidth.changed().await.unwrap(),
1185 Some(crate::bandwidth::Rate::from_bps(1_000_000))
1186 );
1187
1188 fake.set_send_rate(Some(2_000_000));
1190 assert_eq!(
1191 bandwidth.changed().await.unwrap(),
1192 Some(crate::bandwidth::Rate::from_bps(2_000_000))
1193 );
1194 }
1195}