Skip to content

Commit b035fc8

Browse files
feat: report changes in supported protocols to ConnectionHandler
With this patch, implementations of `ConnectionHandler` (which are typically composed in a tree) can exchange information about the supported protocols of a remote with each other via `ConnectionHandlerEvent::ReportRemoteProtocols`. The provided `ProtocolSupport` enum can describe either additions or removals of the remote peer's protocols. This information is aggregated in the connection and passed down to the `ConnectionHandler` via `ConnectionEvent::RemoteProtocolsChange`. Similarly, if the listen protocols of a connection change, all `ConnectionHandler`s on the connection will be notified via `ConnectionEvent::LocalProtocolsChange`. This will allow us to eventually remove `PollParameters` from `NetworkBehaviour`. This pattern allows protocols on a connection to communicate with each other. For example, protocols like identify can share the list of (supposedly) supported protocols by the remote with all other handlers. A protocol like kademlia can accurately add and remove a remote from its routing table as a result. Resolves: #2680. Related: #3124. Pull-Request: #3651.
1 parent b8a7684 commit b035fc8

File tree

30 files changed

+844
-243
lines changed

30 files changed

+844
-243
lines changed

protocols/dcutr/src/handler/direct.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ impl ConnectionHandler for Handler {
9191
| ConnectionEvent::FullyNegotiatedOutbound(_)
9292
| ConnectionEvent::DialUpgradeError(_)
9393
| ConnectionEvent::ListenUpgradeError(_)
94-
| ConnectionEvent::AddressChange(_) => {}
94+
| ConnectionEvent::AddressChange(_)
95+
| ConnectionEvent::LocalProtocolsChange(_)
96+
| ConnectionEvent::RemoteProtocolsChange(_) => {}
9597
}
9698
}
9799
}

protocols/dcutr/src/handler/relayed.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,9 @@ impl ConnectionHandler for Handler {
379379
ConnectionEvent::DialUpgradeError(dial_upgrade_error) => {
380380
self.on_dial_upgrade_error(dial_upgrade_error)
381381
}
382-
ConnectionEvent::AddressChange(_) => {}
382+
ConnectionEvent::AddressChange(_)
383+
| ConnectionEvent::LocalProtocolsChange(_)
384+
| ConnectionEvent::RemoteProtocolsChange(_) => {}
383385
}
384386
}
385387
}

protocols/gossipsub/src/handler.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,10 @@ impl ConnectionHandler for Handler {
553553
}) => {
554554
log::debug!("Protocol negotiation failed: {e}")
555555
}
556-
ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {}
556+
ConnectionEvent::AddressChange(_)
557+
| ConnectionEvent::ListenUpgradeError(_)
558+
| ConnectionEvent::LocalProtocolsChange(_)
559+
| ConnectionEvent::RemoteProtocolsChange(_) => {}
557560
}
558561
}
559562
Handler::Disabled(_) => {}

protocols/identify/src/behaviour.rs

Lines changed: 51 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919
// DEALINGS IN THE SOFTWARE.
2020

2121
use crate::handler::{self, Handler, InEvent};
22-
use crate::protocol::{Info, Protocol, UpgradeError};
22+
use crate::protocol::{Info, UpgradeError};
2323
use libp2p_core::{multiaddr, ConnectedPoint, Endpoint, Multiaddr};
2424
use libp2p_identity::PeerId;
2525
use libp2p_identity::PublicKey;
2626
use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm};
2727
use libp2p_swarm::{
2828
AddressScore, ConnectionDenied, DialError, ExternalAddresses, ListenAddresses,
29-
NetworkBehaviour, NotifyHandler, PollParameters, StreamProtocol, StreamUpgradeError,
30-
THandlerInEvent, ToSwarm,
29+
NetworkBehaviour, NotifyHandler, PollParameters, StreamUpgradeError, THandlerInEvent, ToSwarm,
3130
};
3231
use libp2p_swarm::{ConnectionId, THandler, THandlerOutEvent};
3332
use lru::LruCache;
@@ -50,10 +49,6 @@ pub struct Behaviour {
5049
config: Config,
5150
/// For each peer we're connected to, the observed address to send back to it.
5251
connected: HashMap<PeerId, HashMap<ConnectionId, Multiaddr>>,
53-
/// Pending requests to be fulfilled, either `Handler` requests for `Behaviour` info
54-
/// to address identification requests, or push requests to peers
55-
/// with current information about the local peer.
56-
requests: Vec<Request>,
5752
/// Pending events to be emitted when polled.
5853
events: VecDeque<ToSwarm<Event, InEvent>>,
5954
/// The addresses of all peers that we have discovered.
@@ -63,15 +58,6 @@ pub struct Behaviour {
6358
external_addresses: ExternalAddresses,
6459
}
6560

66-
/// A `Behaviour` request to be fulfilled, either `Handler` requests for `Behaviour` info
67-
/// to address identification requests, or push requests to peers
68-
/// with current information about the local peer.
69-
#[derive(Debug, PartialEq, Eq)]
70-
struct Request {
71-
peer_id: PeerId,
72-
protocol: Protocol,
73-
}
74-
7561
/// Configuration for the [`identify::Behaviour`](Behaviour).
7662
#[non_exhaustive]
7763
#[derive(Debug, Clone)]
@@ -184,7 +170,6 @@ impl Behaviour {
184170
Self {
185171
config,
186172
connected: HashMap::new(),
187-
requests: Vec::new(),
188173
events: VecDeque::new(),
189174
discovered_peers,
190175
listen_addresses: Default::default(),
@@ -203,13 +188,11 @@ impl Behaviour {
203188
continue;
204189
}
205190

206-
let request = Request {
191+
self.events.push_back(ToSwarm::NotifyHandler {
207192
peer_id: p,
208-
protocol: Protocol::Push,
209-
};
210-
if !self.requests.contains(&request) {
211-
self.requests.push(request);
212-
}
193+
handler: NotifyHandler::Any,
194+
event: InEvent::Push,
195+
});
213196
}
214197
}
215198

@@ -239,6 +222,14 @@ impl Behaviour {
239222
}
240223
}
241224
}
225+
226+
fn all_addresses(&self) -> HashSet<Multiaddr> {
227+
self.listen_addresses
228+
.iter()
229+
.chain(self.external_addresses.iter())
230+
.cloned()
231+
.collect()
232+
}
242233
}
243234

244235
impl NetworkBehaviour for Behaviour {
@@ -261,6 +252,7 @@ impl NetworkBehaviour for Behaviour {
261252
self.config.protocol_version.clone(),
262253
self.config.agent_version.clone(),
263254
remote_addr.clone(),
255+
self.all_addresses(),
264256
))
265257
}
266258

@@ -280,13 +272,14 @@ impl NetworkBehaviour for Behaviour {
280272
self.config.protocol_version.clone(),
281273
self.config.agent_version.clone(),
282274
addr.clone(), // TODO: This is weird? That is the public address we dialed, shouldn't need to tell the other party?
275+
self.all_addresses(),
283276
))
284277
}
285278

286279
fn on_connection_handler_event(
287280
&mut self,
288281
peer_id: PeerId,
289-
connection_id: ConnectionId,
282+
_: ConnectionId,
290283
event: THandlerOutEvent<Self>,
291284
) {
292285
match event {
@@ -315,12 +308,6 @@ impl NetworkBehaviour for Behaviour {
315308
self.events
316309
.push_back(ToSwarm::GenerateEvent(Event::Pushed { peer_id }));
317310
}
318-
handler::Event::Identify => {
319-
self.requests.push(Request {
320-
peer_id,
321-
protocol: Protocol::Identify(connection_id),
322-
});
323-
}
324311
handler::Event::IdentificationError(error) => {
325312
self.events
326313
.push_back(ToSwarm::GenerateEvent(Event::Error { peer_id, error }));
@@ -331,50 +318,13 @@ impl NetworkBehaviour for Behaviour {
331318
fn poll(
332319
&mut self,
333320
_cx: &mut Context<'_>,
334-
params: &mut impl PollParameters,
321+
_: &mut impl PollParameters,
335322
) -> Poll<ToSwarm<Self::OutEvent, THandlerInEvent<Self>>> {
336323
if let Some(event) = self.events.pop_front() {
337324
return Poll::Ready(event);
338325
}
339326

340-
// Check for pending requests.
341-
match self.requests.pop() {
342-
Some(Request {
343-
peer_id,
344-
protocol: Protocol::Push,
345-
}) => Poll::Ready(ToSwarm::NotifyHandler {
346-
peer_id,
347-
handler: NotifyHandler::Any,
348-
event: InEvent {
349-
listen_addrs: self
350-
.listen_addresses
351-
.iter()
352-
.chain(self.external_addresses.iter())
353-
.cloned()
354-
.collect(),
355-
supported_protocols: supported_protocols(params),
356-
protocol: Protocol::Push,
357-
},
358-
}),
359-
Some(Request {
360-
peer_id,
361-
protocol: Protocol::Identify(connection_id),
362-
}) => Poll::Ready(ToSwarm::NotifyHandler {
363-
peer_id,
364-
handler: NotifyHandler::One(connection_id),
365-
event: InEvent {
366-
listen_addrs: self
367-
.listen_addresses
368-
.iter()
369-
.chain(self.external_addresses.iter())
370-
.cloned()
371-
.collect(),
372-
supported_protocols: supported_protocols(params),
373-
protocol: Protocol::Identify(connection_id),
374-
},
375-
}),
376-
None => Poll::Pending,
377-
}
327+
Poll::Pending
378328
}
379329

380330
fn handle_pending_outbound_connection(
@@ -393,8 +343,35 @@ impl NetworkBehaviour for Behaviour {
393343
}
394344

395345
fn on_swarm_event(&mut self, event: FromSwarm<Self::ConnectionHandler>) {
396-
self.listen_addresses.on_swarm_event(&event);
397-
self.external_addresses.on_swarm_event(&event);
346+
let listen_addr_changed = self.listen_addresses.on_swarm_event(&event);
347+
let external_addr_changed = self.external_addresses.on_swarm_event(&event);
348+
349+
if listen_addr_changed || external_addr_changed {
350+
// notify all connected handlers about our changed addresses
351+
let change_events = self
352+
.connected
353+
.iter()
354+
.flat_map(|(peer, map)| map.keys().map(|id| (*peer, id)))
355+
.map(|(peer_id, connection_id)| ToSwarm::NotifyHandler {
356+
peer_id,
357+
handler: NotifyHandler::One(*connection_id),
358+
event: InEvent::AddressesChanged(self.all_addresses()),
359+
})
360+
.collect::<Vec<_>>();
361+
362+
self.events.extend(change_events)
363+
}
364+
365+
if listen_addr_changed && self.config.push_listen_addr_updates {
366+
// trigger an identify push for all connected peers
367+
let push_events = self.connected.keys().map(|peer| ToSwarm::NotifyHandler {
368+
peer_id: *peer,
369+
handler: NotifyHandler::Any,
370+
event: InEvent::Push,
371+
});
372+
373+
self.events.extend(push_events);
374+
}
398375

399376
match event {
400377
FromSwarm::ConnectionEstablished(connection_established) => {
@@ -408,30 +385,11 @@ impl NetworkBehaviour for Behaviour {
408385
}) => {
409386
if remaining_established == 0 {
410387
self.connected.remove(&peer_id);
411-
self.requests.retain(|request| {
412-
request
413-
!= &Request {
414-
peer_id,
415-
protocol: Protocol::Push,
416-
}
417-
});
418388
} else if let Some(addrs) = self.connected.get_mut(&peer_id) {
419389
addrs.remove(&connection_id);
420390
}
421391
}
422392
FromSwarm::DialFailure(DialFailure { peer_id, error, .. }) => {
423-
if let Some(peer_id) = peer_id {
424-
if !self.connected.contains_key(&peer_id) {
425-
self.requests.retain(|request| {
426-
request
427-
!= &Request {
428-
peer_id,
429-
protocol: Protocol::Push,
430-
}
431-
});
432-
}
433-
}
434-
435393
if let Some(entry) = peer_id.and_then(|id| self.discovered_peers.get_mut(&id)) {
436394
if let DialError::Transport(errors) = error {
437395
for (addr, _error) in errors {
@@ -440,20 +398,9 @@ impl NetworkBehaviour for Behaviour {
440398
}
441399
}
442400
}
443-
FromSwarm::NewListenAddr(_) | FromSwarm::ExpiredListenAddr(_) => {
444-
if self.config.push_listen_addr_updates {
445-
for p in self.connected.keys() {
446-
let request = Request {
447-
peer_id: *p,
448-
protocol: Protocol::Push,
449-
};
450-
if !self.requests.contains(&request) {
451-
self.requests.push(request);
452-
}
453-
}
454-
}
455-
}
456-
FromSwarm::AddressChange(_)
401+
FromSwarm::NewListenAddr(_)
402+
| FromSwarm::ExpiredListenAddr(_)
403+
| FromSwarm::AddressChange(_)
457404
| FromSwarm::ListenFailure(_)
458405
| FromSwarm::NewListener(_)
459406
| FromSwarm::ListenerError(_)
@@ -496,17 +443,6 @@ pub enum Event {
496443
},
497444
}
498445

499-
fn supported_protocols(params: &impl PollParameters) -> Vec<StreamProtocol> {
500-
// The protocol names can be bytes, but the identify protocol except UTF-8 strings.
501-
// There's not much we can do to solve this conflict except strip non-UTF-8 characters.
502-
params
503-
.supported_protocols()
504-
.filter_map(|p| {
505-
StreamProtocol::try_from_owned(String::from_utf8_lossy(&p).to_string()).ok()
506-
})
507-
.collect()
508-
}
509-
510446
/// If there is a given peer_id in the multiaddr, make sure it is the same as
511447
/// the given peer_id. If there is no peer_id for the peer in the mutiaddr, this returns true.
512448
fn multiaddr_matches_peer_id(addr: &Multiaddr, peer_id: &PeerId) -> bool {

0 commit comments

Comments
 (0)