- Add 7 registry tests (handler_registry_*) to registry.rs - Add 4 dispatch tests (build_auth_context_*, dispatch_decision_logic_*) to dispatch.rs - Add 6 endpoint tests to endpoint.rs: - debug_for_alknet_endpoint_is_implemented_without_panicking - endpoint_constructs_with_iroh_raw_key_identity (adapted for new API) - iroh_endpoint_runs_accept_loop_and_shutdown (adapted for new API) - with_iroh_sets_field (replaces has_iroh_identity_true_for_raw_key) - without_iroh_field_is_none (replaces has_iroh_identity_false_for_x509) - endpoint_works_without_iroh (replaces has_iroh_identity_false_when_no_identity) - Add async-trait as dev-dependency - All 17 tests pass with iroh feature - All 8 tests pass without features - Workspace tests all pass (no breakage) - Mark tasks 1-7 as completed
154 lines
4.3 KiB
Rust
154 lines
4.3 KiB
Rust
//! `HandlerRegistry` — maps ALPN byte strings to `ProtocolHandler` instances.
|
|
//!
|
|
//! Registered statically at startup by the assembly layer; the endpoint
|
|
//! dispatches by looking up the negotiated ALPN.
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
|
|
use alknet_core::types::ProtocolHandler;
|
|
|
|
pub struct HandlerRegistry {
|
|
handlers: HashMap<&'static [u8], Arc<dyn ProtocolHandler>>,
|
|
}
|
|
|
|
impl HandlerRegistry {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
handlers: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub fn register(&mut self, handler: Arc<dyn ProtocolHandler>) {
|
|
let alpn = handler.alpn();
|
|
if self.handlers.contains_key(alpn) {
|
|
panic!(
|
|
"HandlerRegistry: ALPN already registered: {:?}",
|
|
String::from_utf8_lossy(alpn)
|
|
);
|
|
}
|
|
self.handlers.insert(alpn, handler);
|
|
}
|
|
|
|
pub fn get(&self, alpn: &[u8]) -> Option<&Arc<dyn ProtocolHandler>> {
|
|
self.handlers.get(alpn)
|
|
}
|
|
|
|
pub fn alpn_strings(&self) -> Vec<Vec<u8>> {
|
|
self.handlers.keys().map(|k| k.to_vec()).collect()
|
|
}
|
|
}
|
|
|
|
impl Default for HandlerRegistry {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for HandlerRegistry {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("HandlerRegistry")
|
|
.field(
|
|
"alpns",
|
|
&self
|
|
.handlers
|
|
.keys()
|
|
.map(|k| String::from_utf8_lossy(k).to_string())
|
|
.collect::<Vec<_>>(),
|
|
)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use alknet_core::auth::AuthContext;
|
|
use alknet_core::types::{Connection, HandlerError};
|
|
use async_trait::async_trait;
|
|
|
|
struct DummyHandler {
|
|
alpn: &'static [u8],
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ProtocolHandler for DummyHandler {
|
|
fn alpn(&self) -> &'static [u8] {
|
|
self.alpn
|
|
}
|
|
async fn handle(
|
|
&self,
|
|
_connection: Connection,
|
|
_auth: &AuthContext,
|
|
) -> Result<(), HandlerError> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn make_handler(alpn: &'static [u8]) -> Arc<dyn ProtocolHandler> {
|
|
Arc::new(DummyHandler { alpn })
|
|
}
|
|
|
|
#[test]
|
|
fn handler_registry_new_is_empty() {
|
|
let reg = HandlerRegistry::new();
|
|
assert!(reg.alpn_strings().is_empty());
|
|
assert!(reg.get(b"alknet/test").is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn handler_registry_register_then_get() {
|
|
let mut reg = HandlerRegistry::new();
|
|
reg.register(make_handler(b"alknet/test"));
|
|
assert_eq!(reg.alpn_strings(), vec![b"alknet/test".to_vec()]);
|
|
assert!(reg.get(b"alknet/test").is_some());
|
|
assert!(reg.get(b"alknet/other").is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn handler_registry_multiple_alpns() {
|
|
let mut reg = HandlerRegistry::new();
|
|
reg.register(make_handler(b"alknet/ssh"));
|
|
reg.register(make_handler(b"alknet/call"));
|
|
let mut alpns = reg
|
|
.alpn_strings()
|
|
.into_iter()
|
|
.map(|a| String::from_utf8(a).unwrap())
|
|
.collect::<Vec<_>>();
|
|
alpns.sort();
|
|
assert_eq!(alpns, vec!["alknet/call", "alknet/ssh"]);
|
|
assert!(reg.get(b"alknet/ssh").is_some());
|
|
assert!(reg.get(b"alknet/call").is_some());
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "ALPN already registered")]
|
|
fn handler_registry_register_panics_on_duplicate() {
|
|
let mut reg = HandlerRegistry::new();
|
|
reg.register(make_handler(b"alknet/test"));
|
|
reg.register(make_handler(b"alknet/test"));
|
|
}
|
|
|
|
#[test]
|
|
fn handler_registry_debug_lists_alpns() {
|
|
let mut reg = HandlerRegistry::new();
|
|
reg.register(make_handler(b"alknet/test"));
|
|
let s = format!("{:?}", reg);
|
|
assert!(s.contains("alknet/test"));
|
|
}
|
|
|
|
#[test]
|
|
fn handler_registry_default_is_empty() {
|
|
let reg = HandlerRegistry::default();
|
|
assert!(reg.alpn_strings().is_empty());
|
|
assert!(reg.get(b"alknet/test").is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn handler_registry_debug_lists_alpns_via_default() {
|
|
let reg = HandlerRegistry::default();
|
|
let s = format!("{reg:?}");
|
|
assert!(s.contains("HandlerRegistry"));
|
|
}
|
|
}
|