pub struct ProtocolEndpoint<P: Protocol> { /* private fields */ }Expand description
Per-protocol reflector endpoint.
Owns the client pool, the per-module stream cache, and the
authorizer used to admit LINK attempts for one reflector protocol.
Supports all three D-STAR reflector protocols (DExtra, DPlus,
DCS); the endpoint’s default reflector module is used as the
fallback for DPlus sessions (which don’t carry a module on the
wire) and as the seed for DExtra/DCS sessions before the LINK
packet overwrites it.
Implementations§
Source§impl<P: Protocol> ProtocolEndpoint<P>
impl<P: Protocol> ProtocolEndpoint<P>
Sourcepub fn new(
protocol: ProtocolKind,
default_reflector_module: Module,
authorizer: Arc<dyn ClientAuthorizer>,
) -> Self
pub fn new( protocol: ProtocolKind, default_reflector_module: Module, authorizer: Arc<dyn ClientAuthorizer>, ) -> Self
Construct a new endpoint for the given protocol with the supplied authorizer.
default_reflector_module is passed to every
[ServerSessionCore] created on this endpoint; DExtra and
DCS sessions overwrite their client_module from the LINK
packet but DPlus sessions keep the default because the
DPlus LINK2 wire packet doesn’t carry a module.
The authorizer is consulted on every inbound LINK attempt;
rejected attempts never materialize a ClientHandle and
instead produce a protocol-appropriate NAK plus a
[ServerEvent::ClientRejected] event.
Sourcepub fn new_with_voice_bus(
protocol: ProtocolKind,
default_reflector_module: Module,
authorizer: Arc<dyn ClientAuthorizer>,
voice_bus: Option<Sender<CrossProtocolEvent>>,
) -> Self
pub fn new_with_voice_bus( protocol: ProtocolKind, default_reflector_module: Module, authorizer: Arc<dyn ClientAuthorizer>, voice_bus: Option<Sender<CrossProtocolEvent>>, ) -> Self
Construct a new endpoint with an optional cross-protocol voice bus.
Identical to Self::new except the caller supplies a
[broadcast::Sender<CrossProtocolEvent>] clone; when Some,
the endpoint publishes inbound voice events to the bus so
other protocols’ endpoints can transcode and re-broadcast.
Used by crate::reflector::Reflector when its config has
cross_protocol_forwarding = true. Pass None to disable
cross-protocol participation on this endpoint.
Sourcepub const fn protocol_kind(&self) -> ProtocolKind
pub const fn protocol_kind(&self) -> ProtocolKind
Runtime protocol discriminator for this endpoint.
Sourcepub const fn clients(&self) -> &ClientPool<P>
pub const fn clients(&self) -> &ClientPool<P>
Access the endpoint’s client pool (primarily for tests).
Sourcepub async fn handle_inbound(
&self,
bytes: &[u8],
peer: SocketAddr,
now: Instant,
) -> Result<EndpointOutcome<P>, ShellError>
pub async fn handle_inbound( &self, bytes: &[u8], peer: SocketAddr, now: Instant, ) -> Result<EndpointOutcome<P>, ShellError>
Feed one inbound datagram into the endpoint.
Dispatches to the protocol-specific handler based on
Self::protocol_kind. Each handler pre-decodes the inbound
packet, consults the authorizer on LINK attempts, gates
voice-stream ingress on AccessPolicy, drives the core via
the private drive_core helper, then updates the per-module
stream cache and drains pending background events into the
outcome.
§Errors
Returns ShellError::Core if the core rejects the input
(parse failure, wrong-state, etc.). Returns
ShellError::Protocol if the endpoint was constructed with
a [ProtocolKind] the shell does not recognize.
§Cancellation safety
This method is not cancel-safe. It takes multiple
ClientPool locks in sequence (contains → insert →
set_module → record_last_heard) and cancellation between
any two awaits can leave the pool in a half-updated state where
a session has been created but not yet attached to its module
in the reverse index. The reflector’s run loop is the only
expected caller and it never cancels this future except via
shutdown.
Sourcepub async fn run(
self: Arc<Self>,
socket: Arc<UdpSocket>,
shutdown: Receiver<bool>,
) -> Result<(), ShellError>
pub async fn run( self: Arc<Self>, socket: Arc<UdpSocket>, shutdown: Receiver<bool>, ) -> Result<(), ShellError>
Bind-less run loop that owns a pre-bound [UdpSocket].
Each iteration reads one datagram, feeds it to
Self::handle_inbound, writes outbound responses back to
their destination peer, and finally fans voice frames out to
every other peer on the same module.
Returns when shutdown transitions to true, or when an
unrecoverable I/O error occurs.
§Errors
Returns ShellError::Io if the socket errors during a
recv_from. Send-side failures are logged and the offending
peer is marked unhealthy; they do not terminate the loop.
§Cancellation safety
Dropping this future is the intended shutdown mechanism for an
endpoint task — the enclosing [tokio::task::JoinSet] in
crate::reflector::Reflector::run will abort the task when the
shutdown watch channel fires, which drops the run future
cleanly. Any in-progress handle_inbound call for a single
datagram will be abandoned mid-lock-sequence, which is
acceptable during shutdown because the entire ClientPool
is about to be dropped with it. Do not race run() against
another future with tokio::select! while the endpoint is
expected to remain operational.