Skip to content

Commit 155ccfe

Browse files
authored
fix(request-response): don't fail requests on DialPeerConditionFalse
If a dial fails because of an unmet peer condition, it just means that there is already an ongoing dial. We shouldn't fail the pending outbound requests to the peer in that case. Fixes #5996. Pull-Request: #6000.
1 parent 9016cbc commit 155ccfe

File tree

3 files changed

+130
-2
lines changed

3 files changed

+130
-2
lines changed

protocols/request-response/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
- feat: add `Behaviour::send_request_with_addresses()`
77
See [PR 5938](https://github.com/libp2p/rust-libp2p/issues/5938).
88

9+
- fix: don't fail outbound request on `DialError::DialPeerConditionFalse`.
10+
See [PR 6000](https://github.com/libp2p/rust-libp2p/pull/6000)
11+
912
## 0.28.0
1013

1114
- Deprecate `void` crate.

protocols/request-response/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ use libp2p_identity::PeerId;
9090
use libp2p_swarm::{
9191
behaviour::{AddressChange, ConnectionClosed, DialFailure, FromSwarm},
9292
dial_opts::DialOpts,
93-
ConnectionDenied, ConnectionHandler, ConnectionId, NetworkBehaviour, NotifyHandler,
93+
ConnectionDenied, ConnectionHandler, ConnectionId, DialError, NetworkBehaviour, NotifyHandler,
9494
PeerAddresses, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm,
9595
};
9696
use smallvec::SmallVec;
@@ -706,9 +706,13 @@ where
706706
DialFailure {
707707
peer_id,
708708
connection_id,
709-
..
709+
error,
710710
}: DialFailure,
711711
) {
712+
if let DialError::DialPeerConditionFalse(_) = error {
713+
// Dial-condition fails because there is already another ongoing dial.
714+
return;
715+
}
712716
if let Some(peer) = peer_id {
713717
// If there are pending outgoing requests when a dial failure occurs,
714718
// it is implied that we are not connected to the peer, since pending

protocols/request-response/tests/ping.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,127 @@ async fn emits_inbound_connection_closed_if_channel_is_dropped() {
420420
));
421421
}
422422

423+
/// Send multiple requests concurrently.
424+
#[async_std::test]
425+
#[cfg(feature = "cbor")]
426+
async fn concurrent_ping_protocol() {
427+
use std::{collections::HashMap, num::NonZero};
428+
429+
use libp2p_core::ConnectedPoint;
430+
use libp2p_swarm::{dial_opts::PeerCondition, DialError};
431+
432+
let protocols = iter::once((StreamProtocol::new("/ping/1"), ProtocolSupport::Full));
433+
let cfg = request_response::Config::default();
434+
435+
let mut swarm1 = Swarm::new_ephemeral(|_| {
436+
request_response::cbor::Behaviour::<Ping, Pong>::new(protocols.clone(), cfg.clone())
437+
});
438+
let peer1_id = *swarm1.local_peer_id();
439+
let mut swarm2 = Swarm::new_ephemeral(|_| {
440+
request_response::cbor::Behaviour::<Ping, Pong>::new(protocols, cfg)
441+
});
442+
let peer2_id = *swarm2.local_peer_id();
443+
444+
let (peer1_listen_addr, _) = swarm1.listen().with_memory_addr_external().await;
445+
swarm2.add_peer_address(peer1_id, peer1_listen_addr);
446+
447+
let peer1 = async move {
448+
loop {
449+
match swarm1.next_swarm_event().await.try_into_behaviour_event() {
450+
Ok(request_response::Event::Message {
451+
peer,
452+
message:
453+
request_response::Message::Request {
454+
request, channel, ..
455+
},
456+
..
457+
}) => {
458+
assert_eq!(&peer, &peer2_id);
459+
swarm1
460+
.behaviour_mut()
461+
.send_response(channel, Pong(request.0))
462+
.unwrap();
463+
}
464+
Ok(request_response::Event::ResponseSent { peer, .. }) => {
465+
assert_eq!(&peer, &peer2_id);
466+
}
467+
Ok(e) => {
468+
panic!("Peer1: Unexpected event: {e:?}")
469+
}
470+
Err(..) => {}
471+
}
472+
}
473+
};
474+
475+
let peer2 = async {
476+
let mut count = 0;
477+
let num_pings: u8 = rand::thread_rng().gen_range(1..100);
478+
let mut expected_pongs = HashMap::new();
479+
for i in 0..num_pings {
480+
let ping_bytes = vec![i];
481+
let req_id = swarm2
482+
.behaviour_mut()
483+
.send_request(&peer1_id, Ping(ping_bytes.clone()));
484+
assert!(swarm2.behaviour().is_pending_outbound(&peer1_id, &req_id));
485+
assert!(expected_pongs.insert(req_id, ping_bytes).is_none());
486+
}
487+
488+
let mut started_dialing = false;
489+
let mut is_connected = false;
490+
491+
loop {
492+
match swarm2.next_swarm_event().await {
493+
SwarmEvent::Behaviour(request_response::Event::Message {
494+
peer,
495+
message:
496+
request_response::Message::Response {
497+
request_id,
498+
response,
499+
},
500+
..
501+
}) => {
502+
count += 1;
503+
let expected_pong = expected_pongs.remove(&request_id).unwrap();
504+
assert_eq!(response, Pong(expected_pong));
505+
assert_eq!(&peer, &peer1_id);
506+
if count >= num_pings {
507+
break;
508+
}
509+
}
510+
SwarmEvent::Dialing { peer_id, .. } => {
511+
assert_eq!(&peer_id.unwrap(), &peer1_id);
512+
assert!(!started_dialing);
513+
started_dialing = true;
514+
}
515+
SwarmEvent::ConnectionEstablished {
516+
peer_id,
517+
endpoint: ConnectedPoint::Dialer { .. },
518+
num_established,
519+
..
520+
} => {
521+
assert_eq!(&peer_id, &peer1_id);
522+
assert_eq!(num_established, NonZero::new(1).unwrap());
523+
assert!(!is_connected);
524+
is_connected = true;
525+
}
526+
SwarmEvent::OutgoingConnectionError { peer_id, error, .. } if started_dialing => {
527+
assert_eq!(&peer_id.unwrap(), &peer1_id);
528+
assert!(started_dialing);
529+
assert!(matches!(
530+
error,
531+
DialError::DialPeerConditionFalse(PeerCondition::DisconnectedAndNotDialing)
532+
));
533+
}
534+
e => panic!("Peer2: Unexpected event: {e:?}"),
535+
}
536+
}
537+
assert_eq!(count, num_pings);
538+
};
539+
540+
async_std::task::spawn(Box::pin(peer1));
541+
peer2.await;
542+
}
543+
423544
// Simple Ping-Pong Protocol
424545
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
425546
struct Ping(Vec<u8>);

0 commit comments

Comments
 (0)