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)?;
}
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(())
}
}