@@ -69,8 +69,8 @@ pub use quinn_proto::{
6969
7070pub use self :: connection:: {
7171 Accept , Accepting , AlpnError , AuthenticationError , Connecting , ConnectingError , Connection ,
72- Incoming , IncomingZeroRttConnection , OutgoingZeroRttConnection , RemoteEndpointIdError ,
73- ZeroRttStatus ,
72+ ConnectionInfo , Incoming , IncomingZeroRttConnection , OutgoingZeroRttConnection ,
73+ RemoteEndpointIdError , ZeroRttStatus ,
7474} ;
7575
7676/// The delay to fall back to discovery when direct addresses fail.
@@ -98,7 +98,7 @@ pub enum PathSelection {
9898/// new [`EndpointId`].
9999///
100100/// To create the [`Endpoint`] call [`Builder::bind`].
101- #[ derive( Debug ) ]
101+ #[ derive( derive_more :: Debug ) ]
102102pub struct Builder {
103103 secret_key : Option < SecretKey > ,
104104 relay_mode : RelayMode ,
@@ -117,6 +117,8 @@ pub struct Builder {
117117 #[ cfg( any( test, feature = "test-utils" ) ) ]
118118 path_selection : PathSelection ,
119119 max_tls_tickets : usize ,
120+ #[ debug( "{}" , connection_monitor. as_ref( ) . map( |_| "Some(Box<dyn ConnectionMonitor>)" ) . unwrap_or( "None" ) ) ]
121+ connection_monitor : Option < Box < dyn ConnectionMonitor > > ,
120122}
121123
122124impl Builder {
@@ -158,6 +160,7 @@ impl Builder {
158160 #[ cfg( any( test, feature = "test-utils" ) ) ]
159161 path_selection : PathSelection :: default ( ) ,
160162 max_tls_tickets : DEFAULT_MAX_TLS_TICKETS ,
163+ connection_monitor : None ,
161164 }
162165 }
163166
@@ -208,6 +211,7 @@ impl Builder {
208211 #[ cfg( any( test, feature = "test-utils" ) ) ]
209212 path_selection : self . path_selection ,
210213 metrics,
214+ connection_monitor : self . connection_monitor ,
211215 } ;
212216
213217 let msock = magicsock:: MagicSock :: spawn ( msock_opts) . await ?;
@@ -432,6 +436,44 @@ impl Builder {
432436 self . max_tls_tickets = n;
433437 self
434438 }
439+
440+ // TODO docs
441+ /// Register a handler that is invoked for each connection the endpoint accepts or initiates.
442+ ///
443+ /// The [`ConnectionMonitor::on_connection`] method is invoked synchronosuly, from within a tokio
444+ /// context. So you can spawn tasks if needed.
445+ /// Make sure that whatever you do with the connection info here is non-blocking.
446+ /// Usually you'd want to send the info over a broadcast or unbounded channel,
447+ /// or insert it into some persistent datastructure.
448+ ///
449+ /// The `ConnectionInfo` internally contains a weak reference to the connection,
450+ /// so keeping the struct alive does not keep the connection alive.
451+ /// Note however that `ConnectionInfo` does keep an allocation per connection alive
452+ /// so to not leak memory you should drop the `ConnectionInfo` eventually
453+ ///
454+ /// [`ConnectionMonitor`] is implemented for `Fn(ConnectionInfo)`, so you can
455+ /// also pass a closure that takes [`ConnectionInfo`] to this function.
456+ pub fn monitor_connections ( mut self , monitor : impl ConnectionMonitor ) -> Self {
457+ self . connection_monitor = Some ( Box :: new ( monitor) ) ;
458+ self
459+ }
460+ }
461+
462+ /// Monitor each connection accepted or initiated by the endpoint.
463+ pub trait ConnectionMonitor : Send + Sync + ' static {
464+ /// Called for each new connection the endpoint accepts or initiates.
465+ ///
466+ /// This is only called when a connection is fully established.
467+ fn on_connection ( & self , connection : ConnectionInfo ) ;
468+ }
469+
470+ impl < T > ConnectionMonitor for T
471+ where
472+ T : Fn ( ConnectionInfo ) + Send + Sync + ' static ,
473+ {
474+ fn on_connection ( & self , connection : ConnectionInfo ) {
475+ ( self ) ( connection)
476+ }
435477}
436478
437479/// Configuration for a [`quinn::Endpoint`] that cannot be changed at runtime.
0 commit comments