2026-05-28 14:46:47 -06:00
|
|
|
mod hooks;
|
2026-05-28 11:48:46 -06:00
|
|
|
mod routing;
|
2026-05-27 11:04:22 -06:00
|
|
|
|
2026-05-28 14:46:47 -06:00
|
|
|
pub use hooks::HookID;
|
|
|
|
|
|
2026-05-28 11:48:46 -06:00
|
|
|
use alloc::{boxed::Box, vec::Vec};
|
2026-05-27 11:04:22 -06:00
|
|
|
|
2026-05-31 08:58:08 -06:00
|
|
|
use crate::protocol::{ConnectionSet, HookMap, Leaf, Packet, Path, RouteMap};
|
2026-05-27 11:04:22 -06:00
|
|
|
|
|
|
|
|
pub struct Endpoint {
|
2026-05-28 11:48:46 -06:00
|
|
|
// This endpoint's identifier
|
|
|
|
|
pub id: u32,
|
|
|
|
|
|
|
|
|
|
// A counter that creates unique hook IDs.
|
|
|
|
|
// TODO: Randomize the hooks for more obfuscation
|
2026-05-28 14:46:47 -06:00
|
|
|
pub(crate) last_hook: u16,
|
2026-05-27 11:04:22 -06:00
|
|
|
|
2026-05-28 11:48:46 -06:00
|
|
|
// Absolute path for this node. Must be set by some leaf
|
2026-05-27 11:04:22 -06:00
|
|
|
pub path: Path,
|
|
|
|
|
pub leaves: Vec<Box<dyn Leaf>>,
|
|
|
|
|
|
2026-05-28 11:48:46 -06:00
|
|
|
// Map of connections so that we can know what is connected
|
|
|
|
|
// and which endpoints are authorities
|
2026-05-27 11:04:22 -06:00
|
|
|
pub connections: ConnectionSet,
|
|
|
|
|
|
2026-05-28 11:48:46 -06:00
|
|
|
// Local list of hooks.
|
|
|
|
|
pub(crate) hooks: HookMap,
|
|
|
|
|
|
|
|
|
|
// Map of endpoints to packet queues
|
|
|
|
|
pub(crate) inbound: RouteMap,
|
|
|
|
|
pub(crate) outbound: RouteMap,
|
2026-05-27 11:04:22 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Endpoint {
|
2026-05-28 11:48:46 -06:00
|
|
|
pub fn new(id: u32, leaves: Vec<Box<dyn Leaf>>) -> Self {
|
2026-05-27 11:04:22 -06:00
|
|
|
Self {
|
2026-05-28 11:48:46 -06:00
|
|
|
id,
|
|
|
|
|
// Init the hook at 0, which will increment
|
|
|
|
|
last_hook: 0,
|
|
|
|
|
|
|
|
|
|
// Set the current path as an empty vec
|
|
|
|
|
path: Vec::new(),
|
2026-05-27 11:04:22 -06:00
|
|
|
leaves,
|
|
|
|
|
hooks: HookMap::new(),
|
|
|
|
|
connections: ConnectionSet::new(),
|
|
|
|
|
inbound: RouteMap::new(),
|
|
|
|
|
outbound: RouteMap::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 11:48:46 -06:00
|
|
|
/// Pass the endpoint state into all of the leaves
|
2026-05-27 11:04:22 -06:00
|
|
|
pub fn update(&mut self) {
|
2026-05-28 11:48:46 -06:00
|
|
|
// Grab the leaf vec temporarily so that we can iter over self
|
|
|
|
|
// Apparently this only swaps out pointers
|
|
|
|
|
let mut leaves = core::mem::take(&mut self.leaves);
|
|
|
|
|
|
|
|
|
|
for leaf in leaves.iter_mut() {
|
|
|
|
|
leaf.update(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.leaves = leaves;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Run a function over all inbound packets with some ID then clear it.
|
|
|
|
|
pub fn take_inbound_clear<F>(&mut self, path: u32, f: F)
|
|
|
|
|
where
|
|
|
|
|
F: FnMut(&Packet),
|
|
|
|
|
{
|
|
|
|
|
Self::take_clear(path, f, &mut self.inbound);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 18:17:01 -06:00
|
|
|
/// Drain inbound packets for `path` that match `predicate` and preserve the rest.
|
|
|
|
|
///
|
|
|
|
|
/// Generated leaf dispatch uses this instead of [`Self::take_inbound_clear`] so
|
|
|
|
|
/// one leaf can consume only its procedure or session packets without stealing
|
|
|
|
|
/// traffic intended for another leaf. Matching packets are passed by value because
|
|
|
|
|
/// most handlers need to move payload bytes into application state; unmatched
|
|
|
|
|
/// packets are reinserted in their original FIFO order.
|
|
|
|
|
pub fn take_inbound_matching<P, F>(&mut self, path: u32, mut predicate: P, mut f: F)
|
|
|
|
|
where
|
|
|
|
|
P: FnMut(&Packet) -> bool,
|
|
|
|
|
F: FnMut(Packet),
|
|
|
|
|
{
|
|
|
|
|
let Some(mut queue) = self.inbound.remove(&path) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut unmatched = Vec::new();
|
|
|
|
|
|
|
|
|
|
while let Some(packet) = queue.pop_front() {
|
|
|
|
|
if predicate(&packet) {
|
|
|
|
|
f(packet);
|
|
|
|
|
} else {
|
|
|
|
|
unmatched.push(packet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !unmatched.is_empty() {
|
|
|
|
|
self.inbound.entry(path).or_default().extend(unmatched);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 11:48:46 -06:00
|
|
|
/// Run a function over all outbound packets with some ID then clear it.
|
|
|
|
|
pub fn take_outbound_clear<F>(&mut self, path: u32, f: F)
|
|
|
|
|
where
|
|
|
|
|
F: FnMut(&Packet),
|
|
|
|
|
{
|
|
|
|
|
Self::take_clear(path, f, &mut self.outbound);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn take_clear<F>(path: u32, mut f: F, queue: &mut RouteMap)
|
|
|
|
|
where
|
|
|
|
|
F: FnMut(&Packet),
|
|
|
|
|
{
|
|
|
|
|
if let Some(queue) = queue.get_mut(&path) {
|
|
|
|
|
for packet in queue.iter() {
|
|
|
|
|
f(packet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queue.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-31 10:26:57 -06:00
|
|
|
|
|
|
|
|
pub fn iter_leaves<F>(&mut self) -> core::slice::IterMut<'_, Box<dyn Leaf + 'static>>
|
|
|
|
|
where
|
|
|
|
|
F: FnMut(&Packet),
|
|
|
|
|
{
|
|
|
|
|
self.leaves.iter_mut()
|
|
|
|
|
}
|
2026-05-27 11:04:22 -06:00
|
|
|
}
|