Skip to main content

moqtap_session/
request_id.rs

1use moqtap_codec::varint::VarInt;
2
3/// Role of the endpoint (determines request ID parity).
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Role {
6    /// Client uses even request IDs: 0, 2, 4, ...
7    Client,
8    /// Server uses odd request IDs: 1, 3, 5, ...
9    Server,
10}
11
12#[derive(Debug, thiserror::Error, PartialEq, Eq)]
13pub enum RequestIdError {
14    #[error("request ID {0} exceeds max {1}")]
15    ExceedsMax(u64, u64),
16    #[error("request ID {0} has wrong parity for {1:?}")]
17    WrongParity(u64, Role),
18    #[error("max request ID can only increase: was {0}, got {1}")]
19    Decreased(u64, u64),
20    #[error("no request IDs available (blocked)")]
21    Blocked,
22}
23
24/// Allocates and validates request IDs per the MoQT spec.
25///
26/// - Client: even IDs (0, 2, 4, ...)
27/// - Server: odd IDs (1, 3, 5, ...)
28/// - Default MAX_REQUEST_ID: 0 (no requests until increased)
29/// - MAX_REQUEST_ID can only increase
30pub struct RequestIdAllocator {
31    role: Role,
32    next_id: u64,
33    max_id: u64,
34}
35
36impl RequestIdAllocator {
37    pub fn new(role: Role) -> Self {
38        let next_id = match role {
39            Role::Client => 0,
40            Role::Server => 1,
41        };
42        Self { role, next_id, max_id: 0 }
43    }
44
45    /// Allocate the next request ID.
46    pub fn allocate(&mut self) -> Result<VarInt, RequestIdError> {
47        if self.max_id == 0 || self.next_id > self.max_id {
48            return Err(RequestIdError::Blocked);
49        }
50        let id = VarInt::from_u64(self.next_id).unwrap();
51        self.next_id += 2;
52        Ok(id)
53    }
54
55    /// Update the maximum allowed request ID (can only increase).
56    pub fn update_max(&mut self, new_max: u64) -> Result<(), RequestIdError> {
57        if new_max <= self.max_id {
58            return Err(RequestIdError::Decreased(self.max_id, new_max));
59        }
60        self.max_id = new_max;
61        Ok(())
62    }
63
64    /// Validate a request ID received from the peer.
65    pub fn validate_peer_id(&self, id: u64) -> Result<(), RequestIdError> {
66        // Peer has opposite parity
67        let expected_even = match self.role {
68            Role::Client => false, // peer is Server, expects odd
69            Role::Server => true,  // peer is Client, expects even
70        };
71        let is_even = id % 2 == 0;
72        if is_even != expected_even {
73            let peer_role = match self.role {
74                Role::Client => Role::Server,
75                Role::Server => Role::Client,
76            };
77            return Err(RequestIdError::WrongParity(id, peer_role));
78        }
79        if id > self.max_id {
80            return Err(RequestIdError::ExceedsMax(id, self.max_id));
81        }
82        Ok(())
83    }
84
85    /// Check if we are blocked (max_id is 0 or next_id > max_id).
86    pub fn is_blocked(&self) -> bool {
87        self.max_id == 0 || self.next_id > self.max_id
88    }
89
90    /// Get the current maximum request ID.
91    pub fn max_id(&self) -> u64 {
92        self.max_id
93    }
94}