liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
//! Connection and revocation owners retire the same set of keyed links.

use super::{OpenLink, OpenRefusal};
use crate::link::{CloseLinks, LinkEnd};
use crate::server::connection::supervisor::runtime::ConnectionRuntime;
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex, MutexGuard};

#[derive(Debug, Default)]
pub(super) struct Contents {
    pub(super) links: BTreeMap<u32, OpenLink>,
    closed: Option<LinkEnd>,
}

#[derive(Debug, Default)]
struct LinkSet(Mutex<Contents>);

impl CloseLinks for LinkSet {
    fn end_links(&self, reason: LinkEnd) -> Result<(), String> {
        let mut contents = self
            .0
            .lock()
            .map_err(|_| "connection link set lock poisoned")?;
        if contents.closed.is_some() {
            return Ok(());
        }
        contents.closed = Some(reason.clone());
        let mut failure = None;
        for open in std::mem::take(&mut contents.links).into_values() {
            if let Err(error) = open.life.end(reason.clone()) {
                failure = Some(error);
            }
        }
        drop(contents);
        failure.map_or(Ok(()), Err)
    }
}

impl Drop for LinkSet {
    fn drop(&mut self) {
        if let Err(error) = self.end_links(LinkEnd::PeerGone) {
            tracing::error!(%error, "connection link set drop failed");
        }
    }
}

#[derive(Debug, Default)]
pub(in crate::server::connection) struct ConnectionLinks(Arc<LinkSet>);

impl ConnectionLinks {
    pub(super) fn lock(&self) -> Result<MutexGuard<'_, Contents>, String> {
        self.0
            .0
            .lock()
            .map_err(|_| "connection link set lock poisoned".to_owned())
    }

    pub(in crate::server::connection) fn contains_key(&self, stream: u32) -> Result<bool, String> {
        Ok(self.lock()?.links.contains_key(&stream))
    }

    pub(super) fn bind(
        &self,
        pid: u64,
        runtime: &ConnectionRuntime,
        grants: Option<&crate::auth_revocation::ConnectionGrants>,
    ) -> Result<(), OpenRefusal> {
        let links: Arc<dyn CloseLinks> = self.0.clone();
        runtime
            .link_life
            .register(pid, &links)
            .map_err(OpenRefusal::Refused)?;
        if let Some(authority) = runtime.revocation() {
            authority
                .link_life
                .register(pid, &links)
                .map_err(OpenRefusal::Refused)?;
        }
        // Register before checking the fence so a racing revoke sees this set,
        // or its already-denied binding prevents the later insertion.
        if grants.is_some_and(|grants| grants.close_pending().is_some()) {
            self.0
                .end_links(LinkEnd::Revoked)
                .map_err(OpenRefusal::Refused)?;
        }
        Ok(())
    }

    pub(super) fn insert(
        &self,
        runtime: &ConnectionRuntime,
        stream: u32,
        mut open: OpenLink,
    ) -> Result<(), OpenRefusal> {
        let mut contents = self.lock().map_err(OpenRefusal::Refused)?;
        if let Some(reason) = &contents.closed {
            return Err(OpenRefusal::Refused(format!(
                "connection links ended: {reason}"
            )));
        }
        open.life = runtime
            .link_life
            .opened(*open.transport.peer())
            .map_err(OpenRefusal::Refused)?;
        if let Some(signals) = runtime.signals.get() {
            signals
                .observe_link(&open.life)
                .map_err(OpenRefusal::Refused)?;
        }
        if let Some(previous) = contents.links.insert(stream, open) {
            previous
                .life
                .end(LinkEnd::Replaced)
                .map_err(OpenRefusal::Refused)?;
        }
        drop(contents);
        Ok(())
    }
}