1use std::{
2 future::Future,
3 pin::Pin,
4 sync::Arc,
5 task::{Context, Poll},
6 time::Duration,
7};
8
9use web_transport_trait::Stats;
10
11use crate::{
12 Error, Version, bandwidth,
13 util::{MaybeBoxedExt, MaybeSendBox},
14};
15
16#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
23#[non_exhaustive]
24pub struct ConnectionStats {
25 pub rtt: Option<Duration>,
27
28 pub estimated_send_rate: Option<u64>,
30
31 pub estimated_recv_rate: Option<u64>,
35
36 pub bytes_sent: Option<u64>,
38
39 pub bytes_received: Option<u64>,
41
42 pub bytes_lost: Option<u64>,
44
45 pub packets_sent: Option<u64>,
47
48 pub packets_received: Option<u64>,
50
51 pub packets_lost: Option<u64>,
53}
54
55#[derive(Clone)]
66pub struct Session {
67 shared: Arc<SessionShared>,
68 version: Version,
69 send_bandwidth: Option<bandwidth::Consumer>,
70 recv_bandwidth: Option<bandwidth::Consumer>,
71}
72
73impl Session {
74 pub fn version(&self) -> Version {
76 self.version
77 }
78
79 pub fn send_bandwidth(&self) -> Option<bandwidth::Consumer> {
83 self.send_bandwidth.clone()
84 }
85
86 pub fn recv_bandwidth(&self) -> Option<bandwidth::Consumer> {
90 self.recv_bandwidth.clone()
91 }
92
93 pub fn stats(&self) -> ConnectionStats {
98 let mut stats = self.shared.inner.stats();
99 stats.estimated_recv_rate = self.recv_bandwidth.as_ref().and_then(bandwidth::Consumer::peek);
100 stats
101 }
102
103 pub fn abort(&self, err: Error) {
106 self.shared.close(err.to_code(), err.to_string().as_ref());
107 }
108
109 pub async fn closed(&self) -> Error {
111 Error::Transport(self.shared.inner.closed().await)
112 }
113}
114
115pub struct Driver {
128 protocol: MaybeSendBox<'static, Result<(), Error>>,
129 maintenance: Option<MaybeSendBox<'static, ()>>,
134 result: Option<Result<(), Error>>,
137 waiter: Option<kio::Waiter>,
140}
141
142impl Driver {
143 pub fn poll(&mut self, waiter: &kio::Waiter) -> Poll<Result<(), Error>> {
148 if let Some(result) = &self.result {
149 return Poll::Ready(result.clone());
150 }
151
152 if let Some(maintenance) = &mut self.maintenance
153 && waiter.poll_future(maintenance.as_mut()).is_ready()
154 {
155 self.maintenance = None;
156 }
157
158 let result = std::task::ready!(waiter.poll_future(self.protocol.as_mut()));
159 self.result = Some(result.clone());
160 self.maintenance = None;
163 Poll::Ready(result)
164 }
165
166 pub(super) async fn wait_ready(&mut self, ready: impl Future<Output = ()>) {
174 let mut ready = std::pin::pin!(ready);
175 kio::wait(|waiter| {
176 if waiter.poll_future(ready.as_mut()).is_ready() {
177 return Poll::Ready(());
178 }
179 let _ = self.poll(waiter);
180 Poll::Pending
181 })
182 .await
183 }
184}
185
186impl Future for Driver {
187 type Output = Result<(), Error>;
188
189 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
190 let this = &mut *self;
191 let waiter = kio::Waiter::new(cx.waker().clone());
194 let result = this.poll(&waiter);
195 this.waiter = Some(waiter);
196 result
197 }
198}
199
200struct SessionShared {
204 inner: Box<dyn SessionInner>,
205 closed: std::sync::atomic::AtomicBool,
206}
207
208impl SessionShared {
209 fn close(&self, code: u32, reason: &str) {
210 if !self.closed.swap(true, std::sync::atomic::Ordering::SeqCst) {
211 self.inner.close(code, reason);
212 }
213 }
214}
215
216impl Drop for SessionShared {
217 fn drop(&mut self) {
218 self.close(Error::Cancel.to_code(), "dropped");
219 }
220}
221
222impl Session {
223 pub(super) fn new<S: web_transport_trait::Session>(
224 session: S,
225 version: Version,
226 recv_bandwidth: Option<bandwidth::Consumer>,
227 protocol: MaybeSendBox<'static, Result<(), Error>>,
228 ) -> (Self, Driver) {
229 let (send_bandwidth, maintenance) = if session.stats().estimated_send_rate().is_some() {
231 let producer = bandwidth::Producer::new();
232 let consumer = producer.consume();
233
234 let mut monitor = SendBandwidth::new(session.clone(), producer);
235 let maintenance = async move { kio::wait(|waiter| monitor.poll(waiter)).await }.maybe_boxed();
236
237 (Some(consumer), Some(maintenance))
238 } else {
239 (None, None)
240 };
241
242 let session = Self {
243 shared: Arc::new(SessionShared {
244 inner: Box::new(session),
245 closed: std::sync::atomic::AtomicBool::new(false),
246 }),
247 version,
248 send_bandwidth,
249 recv_bandwidth,
250 };
251 let driver = Driver {
252 protocol,
253 maintenance,
254 result: None,
255 waiter: None,
256 };
257
258 (session, driver)
259 }
260}
261
262struct SendBandwidth<S> {
268 session: S,
269 producer: bandwidth::Producer,
270 closed: MaybeSendBox<'static, ()>,
272 mode: SendBandwidthMode,
273}
274
275enum SendBandwidthMode {
276 Idle,
278 Polling { sleep: MaybeSendBox<'static, ()> },
280}
281
282impl<S: web_transport_trait::Session> SendBandwidth<S> {
283 const POLL_INTERVAL: Duration = Duration::from_millis(100);
284
285 fn new(session: S, producer: bandwidth::Producer) -> Self {
286 let closed = {
287 let session = session.clone();
288 async move {
289 session.closed().await;
290 }
291 }
292 .maybe_boxed();
293
294 Self {
295 session,
296 producer,
297 closed,
298 mode: SendBandwidthMode::Idle,
299 }
300 }
301
302 fn sample(&mut self) -> Result<(), Error> {
305 let bitrate = self.session.stats().estimated_send_rate();
306 self.producer.set(bitrate)?;
307 self.mode = SendBandwidthMode::Polling {
308 sleep: web_async::time::sleep(Self::POLL_INTERVAL).maybe_boxed(),
309 };
310 Ok(())
311 }
312
313 fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
314 if waiter.poll_future(self.closed.as_mut()).is_ready() {
315 return Poll::Ready(());
316 }
317
318 loop {
319 match &mut self.mode {
320 SendBandwidthMode::Idle => {
321 match self.producer.poll_used(waiter) {
322 Poll::Ready(Ok(())) => {}
324 Poll::Ready(Err(_)) => return Poll::Ready(()),
325 Poll::Pending => return Poll::Pending,
326 }
327 if self.sample().is_err() {
328 return Poll::Ready(());
329 }
330 }
331 SendBandwidthMode::Polling { sleep } => {
332 match self.producer.poll_unused(waiter) {
334 Poll::Ready(Ok(())) => {
335 self.mode = SendBandwidthMode::Idle;
336 continue;
337 }
338 Poll::Ready(Err(_)) => return Poll::Ready(()),
339 Poll::Pending => {}
340 }
341
342 if waiter.poll_future(sleep.as_mut()).is_pending() {
343 return Poll::Pending;
344 }
345 if self.sample().is_err() {
346 return Poll::Ready(());
347 }
348 }
350 }
351 }
352 }
353}
354
355trait SessionInner: web_transport_trait::MaybeSend + web_transport_trait::MaybeSync {
359 fn close(&self, code: u32, reason: &str);
360 fn closed(&self) -> MaybeSendBox<'_, String>;
361 fn stats(&self) -> ConnectionStats;
362}
363
364impl<S: web_transport_trait::Session> SessionInner for S {
365 fn close(&self, code: u32, reason: &str) {
366 S::close(self, code, reason);
367 }
368
369 fn closed(&self) -> MaybeSendBox<'_, String> {
370 Box::pin(async move { S::closed(self).await.to_string() })
371 }
372
373 fn stats(&self) -> ConnectionStats {
374 let stats = S::stats(self);
377 ConnectionStats {
378 rtt: stats.rtt(),
379 estimated_send_rate: stats.estimated_send_rate(),
380 bytes_sent: stats.bytes_sent(),
381 bytes_received: stats.bytes_received(),
382 bytes_lost: stats.bytes_lost(),
383 packets_sent: stats.packets_sent(),
384 packets_received: stats.packets_received(),
385 packets_lost: stats.packets_lost(),
386 ..Default::default()
387 }
388 }
389}