@@ -46,6 +46,9 @@ use libp2p_swarm::{
4646/// contain a [`ConnectionDenied`] type that can be downcast to [`Exceeded`] error if (and only if)
4747/// **this** behaviour denied the connection.
4848///
49+ /// You can also set Peer IDs that bypass the said limit. Connections that
50+ /// match the bypass rules will not be checked against or counted for limits.
51+ ///
4952/// If you employ multiple [`NetworkBehaviour`]s that manage connections,
5053/// it may also be a different error.
5154///
@@ -67,6 +70,8 @@ use libp2p_swarm::{
6770/// ```
6871pub struct Behaviour {
6972 limits : ConnectionLimits ,
73+ /// Peer IDs that bypass limit check, regardless of inbound or outbound.
74+ bypass_peer_id : HashSet < PeerId > ,
7075
7176 pending_inbound_connections : HashSet < ConnectionId > ,
7277 pending_outbound_connections : HashSet < ConnectionId > ,
@@ -79,6 +84,7 @@ impl Behaviour {
7984 pub fn new ( limits : ConnectionLimits ) -> Self {
8085 Self {
8186 limits,
87+ bypass_peer_id : Default :: default ( ) ,
8288 pending_inbound_connections : Default :: default ( ) ,
8389 pending_outbound_connections : Default :: default ( ) ,
8490 established_inbound_connections : Default :: default ( ) ,
@@ -92,6 +98,19 @@ impl Behaviour {
9298 pub fn limits_mut ( & mut self ) -> & mut ConnectionLimits {
9399 & mut self . limits
94100 }
101+
102+ /// Add the peer to bypass list.
103+ pub fn bypass_peer_id ( & mut self , peer_id : & PeerId ) {
104+ self . bypass_peer_id . insert ( * peer_id) ;
105+ }
106+ /// Remove the peer from bypass list.
107+ pub fn remove_peer_id ( & mut self , peer_id : & PeerId ) {
108+ self . bypass_peer_id . remove ( peer_id) ;
109+ }
110+ /// Whether the connection is bypassed.
111+ pub fn is_bypassed ( & self , remote_peer : & PeerId ) -> bool {
112+ self . bypass_peer_id . contains ( remote_peer)
113+ }
95114}
96115
97116fn check_limit ( limit : Option < u32 > , current : usize , kind : Kind ) -> Result < ( ) , ConnectionDenied > {
@@ -238,6 +257,9 @@ impl NetworkBehaviour for Behaviour {
238257 ) -> Result < THandler < Self > , ConnectionDenied > {
239258 self . pending_inbound_connections . remove ( & connection_id) ;
240259
260+ if self . is_bypassed ( & peer) {
261+ return Ok ( dummy:: ConnectionHandler ) ;
262+ }
241263 check_limit (
242264 self . limits . max_established_incoming ,
243265 self . established_inbound_connections . len ( ) ,
@@ -264,10 +286,13 @@ impl NetworkBehaviour for Behaviour {
264286 fn handle_pending_outbound_connection (
265287 & mut self ,
266288 connection_id : ConnectionId ,
267- _ : Option < PeerId > ,
289+ maybe_peer : Option < PeerId > ,
268290 _: & [ Multiaddr ] ,
269291 _: Endpoint ,
270292 ) -> Result < Vec < Multiaddr > , ConnectionDenied > {
293+ if maybe_peer. is_some_and ( |peer| self . is_bypassed ( & peer) ) {
294+ return Ok ( vec ! [ ] ) ;
295+ }
271296 check_limit (
272297 self . limits . max_pending_outgoing ,
273298 self . pending_outbound_connections . len ( ) ,
@@ -288,6 +313,9 @@ impl NetworkBehaviour for Behaviour {
288313 _: PortUse ,
289314 ) -> Result < THandler < Self > , ConnectionDenied > {
290315 self . pending_outbound_connections . remove ( & connection_id) ;
316+ if self . is_bypassed ( & peer) {
317+ return Ok ( dummy:: ConnectionHandler ) ;
318+ }
291319
292320 check_limit (
293321 self . limits . max_established_outgoing ,
@@ -385,8 +413,7 @@ mod tests {
385413
386414 use super :: * ;
387415
388- #[ test]
389- fn max_outgoing ( ) {
416+ fn fill_outgoing ( ) -> ( Swarm < Behaviour > , Multiaddr , u32 ) {
390417 use rand:: Rng ;
391418
392419 let outgoing_limit = rand:: thread_rng ( ) . gen_range ( 1 ..10 ) ;
@@ -411,10 +438,15 @@ mod tests {
411438 )
412439 . expect ( "Unexpected connection limit." ) ;
413440 }
441+ ( network, addr, outgoing_limit)
442+ }
414443
444+ #[ test]
445+ fn max_outgoing ( ) {
446+ let ( mut network, addr, outgoing_limit) = fill_outgoing ( ) ;
415447 match network
416448 . dial (
417- DialOpts :: peer_id ( target )
449+ DialOpts :: peer_id ( PeerId :: random ( ) )
418450 . condition ( PeerCondition :: Always )
419451 . addresses ( vec ! [ addr] )
420452 . build ( ) ,
@@ -439,6 +471,47 @@ mod tests {
439471 ) ;
440472 }
441473
474+ #[ test]
475+ fn outgoing_limit_bypass ( ) {
476+ let ( mut network, addr, _) = fill_outgoing ( ) ;
477+ let bypassed_peer = PeerId :: random ( ) ;
478+ network
479+ . behaviour_mut ( )
480+ . limits
481+ . bypass_peer_id ( & bypassed_peer) ;
482+ assert ! ( network. behaviour( ) . limits. is_bypassed( & bypassed_peer) ) ;
483+ if let Err ( DialError :: Denied { cause } ) = network. dial (
484+ DialOpts :: peer_id ( bypassed_peer)
485+ . addresses ( vec ! [ addr. clone( ) ] )
486+ . build ( ) ,
487+ ) {
488+ cause
489+ . downcast :: < Exceeded > ( )
490+ . expect_err ( "Unexpected connection denied because of limit" ) ;
491+ }
492+ let not_bypassed_peer = loop {
493+ let new_peer = PeerId :: random ( ) ;
494+ if new_peer != bypassed_peer {
495+ break new_peer;
496+ }
497+ } ;
498+ match network
499+ . dial (
500+ DialOpts :: peer_id ( not_bypassed_peer)
501+ . addresses ( vec ! [ addr] )
502+ . build ( ) ,
503+ )
504+ . expect_err ( "Unexpected dialing success." )
505+ {
506+ DialError :: Denied { cause } => {
507+ cause
508+ . downcast :: < Exceeded > ( )
509+ . expect ( "connection denied because of limit" ) ;
510+ }
511+ e => panic ! ( "Unexpected error: {e:?}" ) ,
512+ }
513+ }
514+
442515 #[ test]
443516 fn max_established_incoming ( ) {
444517 fn prop ( Limit ( limit) : Limit ) {
@@ -479,13 +552,65 @@ mod tests {
479552 } ) ;
480553 }
481554
482- # [ derive ( Debug , Clone ) ]
483- struct Limit ( u32 ) ;
555+ quickcheck ( prop as fn ( _ ) ) ;
556+ }
484557
485- impl Arbitrary for Limit {
486- fn arbitrary ( g : & mut Gen ) -> Self {
487- Self ( g. gen_range ( 1 ..10 ) )
488- }
558+ #[ test]
559+ fn bypass_established_incoming ( ) {
560+ fn prop ( Limit ( limit) : Limit ) {
561+ let mut swarm1 = Swarm :: new_ephemeral ( |_| {
562+ Behaviour :: new (
563+ ConnectionLimits :: default ( ) . with_max_established_incoming ( Some ( limit) ) ,
564+ )
565+ } ) ;
566+ let mut swarm2 = Swarm :: new_ephemeral ( |_| {
567+ Behaviour :: new (
568+ ConnectionLimits :: default ( ) . with_max_established_incoming ( Some ( limit) ) ,
569+ )
570+ } ) ;
571+ let mut swarm3 = Swarm :: new_ephemeral ( |_| {
572+ Behaviour :: new (
573+ ConnectionLimits :: default ( ) . with_max_established_incoming ( Some ( limit) ) ,
574+ )
575+ } ) ;
576+
577+ let rt = Runtime :: new ( ) . unwrap ( ) ;
578+ let bypassed_peer_id = * swarm3. local_peer_id ( ) ;
579+ swarm1
580+ . behaviour_mut ( )
581+ . limits
582+ . bypass_peer_id ( & bypassed_peer_id) ;
583+
584+ rt. block_on ( async {
585+ let ( listen_addr, _) = swarm1. listen ( ) . with_memory_addr_external ( ) . await ;
586+
587+ for _ in 0 ..limit {
588+ swarm2. connect ( & mut swarm1) . await ;
589+ }
590+
591+ swarm3. dial ( listen_addr. clone ( ) ) . unwrap ( ) ;
592+
593+ tokio:: spawn ( swarm2. loop_on_next ( ) ) ;
594+ tokio:: spawn ( swarm3. loop_on_next ( ) ) ;
595+
596+ swarm1
597+ . wait ( |event| match event {
598+ SwarmEvent :: ConnectionEstablished { peer_id, .. } => {
599+ ( peer_id == bypassed_peer_id) . then_some ( ( ) )
600+ }
601+ SwarmEvent :: IncomingConnectionError {
602+ error : ListenError :: Denied { cause } ,
603+ ..
604+ } => {
605+ cause
606+ . downcast :: < Exceeded > ( )
607+ . expect_err ( "Unexpected connection denied because of limit" ) ;
608+ None
609+ }
610+ _ => None ,
611+ } )
612+ . await ;
613+ } ) ;
489614 }
490615
491616 quickcheck ( prop as fn ( _) ) ;
@@ -609,4 +734,13 @@ mod tests {
609734 Poll :: Pending
610735 }
611736 }
737+
738+ #[ derive( Debug , Clone ) ]
739+ struct Limit ( u32 ) ;
740+
741+ impl Arbitrary for Limit {
742+ fn arbitrary ( g : & mut Gen ) -> Self {
743+ Self ( g. gen_range ( 1 ..10 ) )
744+ }
745+ }
612746}
0 commit comments