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