pub struct RequestIdAllocator {
role: Role,
next_id: u64,
max_id: u64,
peer_next_id: u64,
}Expand description
Allocates this endpoint’s request IDs and checks the parity of the peer’s.
Draft-16 Section 9.1: “The client’s Request ID starts at 0 and are even and the server’s Request ID starts at 1 and are odd. The Request ID increments by 2 with each FETCH, SUBSCRIBE, REQUEST_UPDATE, SUBSCRIBE_NAMESPACE, PUBLISH, PUBLISH_NAMESPACE or TRACK_STATUS request.” The step of two is what keeps the two endpoints’ ID spaces disjoint, so it is not a spacing convention an implementation may tighten: one that steps by one starts issuing the IDs reserved for its peer on its second request, and the same section makes that a session close with INVALID_REQUEST_ID.
The ceiling is exclusive. Section 9.5 describes the MAX_REQUEST_ID message’s Max Request ID field as “The new Maximum Request ID for the session plus 1”, and closes the session with TOO_MANY_REQUESTS on a request ID “equal to or larger than this”. Section 9.3.1.3 gives the setup parameter of the same name a default of 0 and reads that as “the peer MUST NOT send requests”, which only holds if a ceiling of 0 forbids the ID 0 as well.
Fields§
§role: Role§next_id: u64§max_id: u64§peer_next_id: u64Implementations§
Source§impl RequestIdAllocator
impl RequestIdAllocator
Sourcepub fn new(role: Role) -> Self
pub fn new(role: Role) -> Self
Create an allocator for the given role, starting at ID 0 or 1 with a ceiling of 0 - blocked until the peer raises it.
Sourcepub fn allocate(&mut self) -> Result<VarInt, RequestIdError>
pub fn allocate(&mut self) -> Result<VarInt, RequestIdError>
Allocate the next request ID.
§Errors
RequestIdError::Blocked when the next ID would reach the ceiling.
Sourcepub fn update_max(&mut self, new_max: u64) -> Result<(), RequestIdError>
pub fn update_max(&mut self, new_max: u64) -> Result<(), RequestIdError>
Update the maximum allowed request ID (can only increase).
§Errors
RequestIdError::Decreased if the new value is not strictly greater
than the current one, which Section 9.5 calls a protocol
violation.
Sourcepub fn validate_peer_id(&self, id: u64) -> Result<(), RequestIdError>
pub fn validate_peer_id(&self, id: u64) -> Result<(), RequestIdError>
Check the parity of a request ID the peer put on the wire.
The parity checked here is the peer’s, the opposite of this allocator’s own, so a client accepts only odd IDs.
This deliberately does not check the ID against a ceiling. The ceiling
that applies to a peer’s request ID is the MAX_REQUEST_ID this
endpoint advertised, which the allocator does not hold - max_id here
is the budget the peer granted us, a different number that may be
larger or smaller. The endpoint owns that check.
Parity is the half that needs no history. The sequence half is
Self::record_peer_id, which does.
§Errors
RequestIdError::WrongParity carrying the ID and the peer’s
role, so the message names the endpoint that broke the rule rather than
the one that caught it.
Sourcepub fn is_blocked(&self) -> bool
pub fn is_blocked(&self) -> bool
Whether the next allocation would reach the ceiling.
A ceiling of 0 needs no special case: the client’s first ID is 0 and the server’s is 1, and neither is below 0.
Sourcepub fn peer_next_id(&self) -> u64
pub fn peer_next_id(&self) -> u64
The Request ID the peer’s next new request must carry.
Section 9.1 starts each endpoint’s sequence at 0 or 1 by role and steps it by 2 per request, so the whole sequence is fixed from the outset and the next value is a number rather than a guess.
Sourcepub fn record_peer_id(&mut self, id: u64) -> Result<(), RequestIdError>
pub fn record_peer_id(&mut self, id: u64) -> Result<(), RequestIdError>
Take the Request ID of a new request the peer sent, holding it to the sequence Section 9.1 fixes.
“If an endpoint receives a Request ID that is not valid for the peer, or a new request with a Request ID that is not the next in sequence or exceeds the received MAX_REQUEST_ID, it MUST close the session with INVALID_REQUEST_ID.”
One counter answers both halves of that. A repeat is below the next value and a skip is above it, and neither is the value the peer’s own step of two produces, so nothing further has to be remembered: the set of IDs already spent is every value of this parity below the counter.
Only a new request advances this. A message that names a request already open - a response, a cancellation, an update that carries the original’s ID rather than one of its own - reuses an ID on purpose, and passing one here would refuse it.
§Errors
RequestIdError::OutOfSequence carrying the ID that arrived and the
one the sequence called for.