1use crate::coding::*;
5
6use super::{Message, Parameters, Version};
7
8const PARAM_PROBE: u64 = 0x1;
10const PARAM_PATH: u64 = 0x2;
12const PARAM_ROLE: u64 = 0x3;
14const PARAM_COST: u64 = 0x4;
16const PARAM_HOP: u64 = 0x5;
18
19pub const DEFAULT_COST: u64 = 1;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
33pub enum ProbeLevel {
34 #[default]
36 None,
37 Report,
41 Increase,
43}
44
45impl ProbeLevel {
46 pub fn detect<S: crate::transport::poll::Session>(session: &S) -> Self {
58 use web_transport_trait::Stats as _;
59 let stats = session.stats();
60 match stats.estimated_send_rate().is_some() || stats.rtt().is_some() {
61 true => Self::Report,
62 false => Self::None,
63 }
64 }
65
66 fn from_code(code: u64) -> Self {
68 match code {
69 0 => Self::None,
70 1 => Self::Report,
71 _ => Self::Increase,
72 }
73 }
74
75 fn to_code(self) -> u64 {
77 match self {
78 Self::None => 0,
79 Self::Report => 1,
80 Self::Increase => 2,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97#[non_exhaustive]
98pub enum Role {
99 Publisher,
101 Subscriber,
103}
104
105impl Role {
106 fn from_code(code: u64) -> Option<Self> {
111 match code {
112 1 => Some(Role::Publisher),
113 2 => Some(Role::Subscriber),
114 _ => None,
115 }
116 }
117
118 fn to_code(self) -> u64 {
120 match self {
121 Role::Publisher => 1,
122 Role::Subscriber => 2,
123 }
124 }
125
126 pub(crate) fn from_origins(publishes: bool, consumes: bool) -> Option<Self> {
131 match (publishes, consumes) {
132 (true, false) => Some(Role::Publisher),
133 (false, true) => Some(Role::Subscriber),
134 _ => None,
135 }
136 }
137
138 pub fn as_str(self) -> &'static str {
140 match self {
141 Role::Publisher => "publisher",
142 Role::Subscriber => "subscriber",
143 }
144 }
145}
146
147impl std::fmt::Display for Role {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.write_str(self.as_str())
150 }
151}
152
153#[derive(Debug, Clone, Default, PartialEq, Eq)]
159pub struct Setup {
160 pub probe: ProbeLevel,
162 pub path: Option<String>,
169 pub role: Option<Role>,
173 pub cost: Option<u64>,
179 pub hop: Option<crate::Hop>,
185}
186
187impl Message for Setup {
188 const CONTROL: Option<crate::stats::Control> = Some(crate::stats::Control::Session);
189 const MAX_SIZE: usize = crate::setup::MAX_SETUP_SIZE;
190
191 fn decode_msg<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
192 if !version.has_setup_stream() {
193 return Err(DecodeError::Version);
194 }
195
196 let params = Parameters::decode(r, version)?;
197 let probe = params
198 .get_varint(PARAM_PROBE, version)?
199 .map(ProbeLevel::from_code)
200 .unwrap_or_default();
201 let path = match params.get_bytes(PARAM_PATH) {
202 Some(bytes) => Some(
203 std::str::from_utf8(bytes)
204 .map_err(|_| DecodeError::InvalidValue)?
205 .to_string(),
206 ),
207 None => None,
208 };
209 let role = params.get_varint(PARAM_ROLE, version)?.and_then(Role::from_code);
210 let cost = params.get_varint(PARAM_COST, version)?;
211 let hop = params
214 .get_varint(PARAM_HOP, version)?
215 .and_then(|id| crate::Hop::new(id).ok());
216
217 Ok(Self {
218 probe,
219 path,
220 role,
221 cost,
222 hop,
223 })
224 }
225
226 fn encode_msg<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
227 if !version.has_setup_stream() {
228 return Err(EncodeError::Version);
229 }
230
231 let mut params = Parameters::default();
232 if self.probe != ProbeLevel::None {
234 params.set_varint(PARAM_PROBE, self.probe.to_code(), version)?;
235 }
236 if let Some(path) = &self.path {
237 params.set_bytes(PARAM_PATH, path.as_bytes().to_vec());
238 }
239 if let Some(role) = self.role {
242 params.set_varint(PARAM_ROLE, role.to_code(), version)?;
243 }
244 if let Some(cost) = self.cost {
245 params.set_varint(PARAM_COST, cost, version)?;
246 }
247 if let Some(hop) = self.hop {
248 params.set_varint(PARAM_HOP, hop.id(), version)?;
249 }
250
251 params.encode(w, version)
252 }
253}
254
255#[derive(Clone, Default)]
261pub(crate) struct PeerSetup(kio::Shared<PeerSetupState>);
262
263#[derive(Default)]
264struct PeerSetupState {
265 seen: bool,
266 setup: Option<Setup>,
267}
268
269impl PeerSetup {
270 pub fn set(&self, setup: Setup) {
272 let mut state = self.0.lock();
273 state.seen = true;
274 state.setup = Some(setup);
275 }
276
277 pub fn claim(&self) -> Result<(), crate::Error> {
279 let mut state = self.0.lock();
280 if state.seen {
281 return Err(crate::Error::ProtocolViolation);
282 }
283 state.seen = true;
284 Ok(())
285 }
286
287 pub fn poll_probe_level(&self, waiter: &kio::Waiter) -> std::task::Poll<ProbeLevel> {
289 self.poll_get(waiter, |setup| setup.probe)
290 }
291
292 pub fn poll_cost(&self, waiter: &kio::Waiter) -> std::task::Poll<Option<u64>> {
295 self.poll_get(waiter, |setup| setup.cost)
296 }
297
298 pub fn poll_hop(&self, waiter: &kio::Waiter) -> std::task::Poll<Option<crate::Hop>> {
301 self.poll_get(waiter, |setup| setup.hop)
302 }
303
304 fn poll_get<T>(&self, waiter: &kio::Waiter, f: impl FnOnce(&Setup) -> T) -> std::task::Poll<T> {
310 let slot = std::task::ready!(self.0.poll(waiter, |setup| {
311 if setup.setup.is_some() {
312 std::task::Poll::Ready(())
313 } else {
314 std::task::Poll::Pending
315 }
316 }));
317 std::task::Poll::Ready(f(slot.setup.as_ref().expect("waited for Some")))
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 fn round_trip(msg: &Setup) -> Setup {
326 let mut buf = bytes::BytesMut::new();
327 msg.encode(&mut buf, Version::Lite05).unwrap();
328 let mut slice = &buf[..];
329 let got = Setup::decode(&mut slice, Version::Lite05).unwrap();
330 assert!(bytes::Buf::remaining(&slice) == 0, "trailing bytes after decode");
331 got
332 }
333
334 #[test]
335 fn empty_round_trip() {
336 let msg = Setup::default();
337 assert_eq!(round_trip(&msg), msg);
338 }
339
340 #[test]
344 fn detect_reports_nothing_without_stats() {
345 use crate::lite::test_transport::{SinkSession, SinkStats};
346 let session = SinkSession::new(Default::default()).with_stats(SinkStats::default());
347 assert_eq!(ProbeLevel::detect(&session), ProbeLevel::None);
348 }
349
350 #[test]
353 fn detect_reports_with_either_metric() {
354 use crate::lite::test_transport::{SinkSession, SinkStats};
355
356 let rtt_only = SinkStats::default().with_rtt(std::time::Duration::from_millis(40));
357 let session = SinkSession::new(Default::default()).with_stats(rtt_only);
358 assert_eq!(ProbeLevel::detect(&session), ProbeLevel::Report);
359
360 let rate_only = SinkStats::default().with_send_rate(1_000_000);
361 let session = SinkSession::new(Default::default()).with_stats(rate_only);
362 assert_eq!(ProbeLevel::detect(&session), ProbeLevel::Report);
363 }
364
365 #[test]
366 fn probe_levels_round_trip() {
367 for probe in [ProbeLevel::None, ProbeLevel::Report, ProbeLevel::Increase] {
368 let msg = Setup {
369 probe,
370 ..Default::default()
371 };
372 assert_eq!(round_trip(&msg), msg);
373 }
374 }
375
376 #[test]
377 fn cost_round_trip() {
378 for cost in [None, Some(0), Some(1), Some(7)] {
381 let msg = Setup {
382 cost,
383 ..Default::default()
384 };
385 assert_eq!(round_trip(&msg), msg);
386 }
387 }
388
389 #[test]
392 fn parameter_values_use_the_version_codec() {
393 let msg = Setup {
394 cost: Some(100),
395 ..Default::default()
396 };
397 for (version, wire) in [
398 (Version::Lite06, &[0x05, 0x01, 0x04, 0x02, 0x40, 0x64][..]),
399 (Version::Lite07, &[0x04, 0x01, 0x04, 0x01, 0x64][..]),
400 ] {
401 let mut buf = Vec::new();
402 msg.encode(&mut buf, version).unwrap();
403 assert_eq!(buf, wire, "{version}");
404 assert_eq!(Setup::decode(&mut &buf[..], version).unwrap(), msg, "{version}");
405 }
406 }
407
408 #[test]
410 fn encode_enforces_the_setup_limit() {
411 let at_limit = crate::setup::MAX_SETUP_SIZE - 6;
413 let msg = Setup {
414 path: Some("a".repeat(at_limit)),
415 ..Default::default()
416 };
417 assert_eq!(round_trip(&msg), msg);
418
419 let msg = Setup {
420 path: Some("a".repeat(at_limit + 1)),
421 ..Default::default()
422 };
423 let mut buf = bytes::BytesMut::new();
424 assert!(matches!(
425 msg.encode(&mut buf, Version::Lite05),
426 Err(EncodeError::TooLarge)
427 ));
428 assert!(buf.is_empty());
429 }
430
431 #[test]
432 fn path_round_trip() {
433 let msg = Setup {
434 probe: ProbeLevel::Report,
435 path: Some("/room/123".to_string()),
436 ..Default::default()
437 };
438 assert_eq!(round_trip(&msg), msg);
439 }
440
441 #[test]
442 fn hop_round_trip() {
443 let msg = Setup {
444 hop: Some(crate::Hop::new(42).unwrap()),
445 ..Default::default()
446 };
447 assert_eq!(round_trip(&msg), msg);
448 }
449
450 #[test]
453 fn hop_zero_decodes_as_none() {
454 use crate::coding::Encode;
455
456 let version = Version::Lite05;
457 let mut params = Parameters::default();
458 params.set_varint(super::PARAM_HOP, 0, version).unwrap();
459 let mut body = bytes::BytesMut::new();
460 params.encode(&mut body, version).unwrap();
461 let mut buf = bytes::BytesMut::new();
463 (body.len() as u64).encode(&mut buf, version).unwrap();
464 buf.extend_from_slice(&body);
465 let mut slice = &buf[..];
466 let got = Setup::decode(&mut slice, version).unwrap();
467 assert_eq!(got.hop, None);
468 }
469
470 #[test]
471 fn empty_path_round_trips() {
472 let msg = Setup {
475 path: Some(String::new()),
476 ..Default::default()
477 };
478 assert_eq!(round_trip(&msg), msg);
479 }
480
481 #[test]
482 fn roles_round_trip() {
483 for role in [Some(Role::Publisher), Some(Role::Subscriber), None] {
484 let msg = Setup {
485 path: Some("/room/123".to_string()),
486 role,
487 ..Default::default()
488 };
489 assert_eq!(round_trip(&msg), msg);
490 }
491 }
492
493 #[test]
494 fn unknown_probe_level_saturates_to_increase() {
495 let mut params = Parameters::default();
498 params.set_varint(PARAM_PROBE, 99, Version::Lite05).unwrap();
499 let mut body = Vec::new();
500 params.encode(&mut body, Version::Lite05).unwrap();
501
502 let mut buf = bytes::BytesMut::new();
503 body.len().encode(&mut buf, Version::Lite05).unwrap();
504 buf.extend_from_slice(&body);
505
506 let mut slice = &buf[..];
507 let got = Setup::decode(&mut slice, Version::Lite05).unwrap();
508 assert_eq!(got.probe, ProbeLevel::Increase);
509 }
510
511 #[test]
512 fn role_wire_codes() {
513 for (role, code) in [(Role::Publisher, 1u64), (Role::Subscriber, 2)] {
516 assert_eq!(role.to_code(), code);
517 assert_eq!(Role::from_code(code), Some(role));
518 }
519 }
520
521 #[test]
522 fn unknown_role_decodes_as_bidirectional() {
523 for code in [0u64, 9, 250] {
527 let mut params = Parameters::default();
528 params.set_varint(PARAM_ROLE, code, Version::Lite05).unwrap();
529 let mut body = Vec::new();
530 params.encode(&mut body, Version::Lite05).unwrap();
531
532 let mut buf = bytes::BytesMut::new();
533 body.len().encode(&mut buf, Version::Lite05).unwrap();
534 buf.extend_from_slice(&body);
535
536 let mut slice = &buf[..];
537 let got = Setup::decode(&mut slice, Version::Lite05).unwrap();
538 assert_eq!(got.role, None, "role code {code} should decode as bidirectional");
539 }
540 }
541
542 #[test]
543 fn rejects_before_lite05() {
544 let msg = Setup::default();
545 let mut buf = bytes::BytesMut::new();
546 assert!(matches!(
547 msg.encode(&mut buf, Version::Lite04),
548 Err(EncodeError::Version)
549 ));
550 }
551
552 #[test]
553 fn ignores_unknown_parameters() {
554 let mut params = Parameters::default();
556 params.set_bytes(PARAM_PATH, b"/foo".to_vec());
557 params.set_bytes(0xbeef, b"whatever".to_vec());
558
559 let mut body = Vec::new();
560 params.encode(&mut body, Version::Lite05).unwrap();
561
562 let mut buf = bytes::BytesMut::new();
564 body.len().encode(&mut buf, Version::Lite05).unwrap();
565 buf.extend_from_slice(&body);
566
567 let mut slice = &buf[..];
568 let got = Setup::decode(&mut slice, Version::Lite05).unwrap();
569 assert_eq!(got.path.as_deref(), Some("/foo"));
570 }
571}