Skip to main content

arete_server/websocket/
admission.rs

1//! Optional host-provided admission hooks for WebSocket lifecycle events.
2//!
3//! The server owns protocol and connection lifecycles, but it does not need to
4//! know how an embedder groups callers or stores quota state. Implementations
5//! may keep process-local state, delegate to a shared service, or decline to
6//! manage a context by returning no connection permit.
7
8use std::fmt::Debug;
9use std::sync::Arc;
10
11use uuid::Uuid;
12
13use super::auth::{AuthContext, AuthDeny};
14
15/// An opaque connection reservation owned by an admission provider.
16///
17/// The server retains this value for the lifetime of the connection. Dropping
18/// it must release any reservation held by the provider.
19pub trait WebSocketConnectionPermit: Debug + Send + Sync {
20    /// Stable identifier used for later lifecycle callbacks.
21    fn id(&self) -> Uuid;
22}
23
24/// Embedder-owned admission policy for lifecycle events the server controls.
25///
26/// Implementations must make `refresh_connection` transactional: an error
27/// leaves the existing permit and its policy unchanged. When it returns a new
28/// permit, dropping the previous permit must not release the new reservation.
29/// The server serializes refresh and subscription callbacks for each
30/// connection and invokes them without holding its client-registry locks.
31#[allow(clippy::result_large_err)]
32pub trait WebSocketAdmissionProvider: Debug + Send + Sync {
33    /// Reserve a newly authenticated connection.
34    ///
35    /// Returning `None` leaves this connection unmanaged by the provider.
36    fn reserve_connection(
37        &self,
38        context: &AuthContext,
39    ) -> Result<Option<Arc<dyn WebSocketConnectionPermit>>, AuthDeny>;
40
41    /// Atomically refresh admission for a live connection.
42    ///
43    /// `active_subscriptions` is the complete current set. This lets a provider
44    /// move an unmanaged connection into managed state without racing a series
45    /// of subscription callbacks.
46    fn refresh_connection(
47        &self,
48        current_permit: Option<Arc<dyn WebSocketConnectionPermit>>,
49        context: &AuthContext,
50        active_subscriptions: &[String],
51    ) -> Result<Option<Arc<dyn WebSocketConnectionPermit>>, AuthDeny>;
52
53    /// Reserve a subscription. Return `false` when the identifier is already
54    /// present for this connection.
55    fn add_subscription(&self, permit: Uuid, subscription_id: &str) -> Result<bool, AuthDeny>;
56
57    /// Release a subscription reservation.
58    fn remove_subscription(&self, permit: Uuid, subscription_id: &str);
59
60    /// Admit one inbound protocol message.
61    fn check_inbound_message(&self, permit: Uuid) -> Result<(), AuthDeny>;
62
63    /// Admit an outbound payload of `bytes` bytes.
64    fn check_egress(&self, permit: Uuid, bytes: usize) -> Result<(), AuthDeny>;
65
66    /// Periodic bounded-state maintenance.
67    fn cleanup(&self) {}
68}