diff --git a/http/src/v0/swarm.rs b/http/src/v0/swarm.rs index 6d245c51..c0a3774a 100644 --- a/http/src/v0/swarm.rs +++ b/http/src/v0/swarm.rs @@ -1,5 +1,5 @@ use super::support::{with_ipfs, StringError}; -use ipfs::{Ipfs, IpfsTypes, Multiaddr}; +use ipfs::{p2p::ConnectionTarget, Ipfs, IpfsTypes, Multiaddr}; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::collections::BTreeMap; @@ -7,14 +7,18 @@ use warp::{query, Filter}; #[derive(Debug, Deserialize)] struct ConnectQuery { - arg: Multiaddr, + arg: String, } async fn connect_query( ipfs: Ipfs, query: ConnectQuery, ) -> Result { - ipfs.connect(query.arg) + let target = query + .arg + .parse::() + .map_err(|e| warp::reject::custom(StringError::from(e)))?; + ipfs.connect(target) .await .map_err(|e| warp::reject::custom(StringError::from(e)))?; let response: &[&str] = &[]; diff --git a/src/p2p/swarm.rs b/src/p2p/swarm.rs index 4d8b4fa2..907ecd45 100644 --- a/src/p2p/swarm.rs +++ b/src/p2p/swarm.rs @@ -1,4 +1,5 @@ use crate::subscription::{SubscriptionFuture, SubscriptionRegistry}; +use anyhow::anyhow; use core::task::{Context, Poll}; use libp2p::core::{connection::ConnectionId, ConnectedPoint, Multiaddr, PeerId}; use libp2p::swarm::protocols_handler::{ @@ -6,6 +7,7 @@ use libp2p::swarm::protocols_handler::{ }; use libp2p::swarm::{self, DialPeerCondition, NetworkBehaviour, PollParameters, Swarm}; use std::collections::{HashMap, HashSet, VecDeque}; +use std::str::FromStr; use std::time::Duration; /// A description of currently active connection. @@ -23,6 +25,7 @@ pub struct Connection { pub enum ConnectionTarget { Addr(Multiaddr), PeerId(PeerId), + PeerIdWithAddr(PeerId, Multiaddr), } impl From for ConnectionTarget { @@ -37,6 +40,33 @@ impl From for ConnectionTarget { } } +impl FromStr for ConnectionTarget { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + if s.contains("/p2p/") { + let mut iter = s.split("/p2p/"); + + let addr = iter + .next() + .ok_or_else(|| anyhow!("missing Multiaddr part of the address"))? + .parse::()?; + let peer_id = iter + .next() + .ok_or_else(|| anyhow!("missing PeerId part of the address"))? + .parse::()?; + + Ok(ConnectionTarget::PeerIdWithAddr(peer_id, addr)) + } else if s.contains('/') { + let addr = s.parse::()?; + Ok(ConnectionTarget::Addr(addr)) + } else { + let peer_id = s.parse::()?; + Ok(ConnectionTarget::PeerId(peer_id)) + } + } +} + /// Disconnected will use banning to disconnect a node. Disconnecting a single peer connection is /// not supported at the moment. pub struct Disconnector { @@ -105,6 +135,9 @@ impl SwarmApi { let will_attempt_connection = match target { ConnectionTarget::PeerId(ref id) => self.connected_peers.get(id).is_none(), ConnectionTarget::Addr(ref addr) => !self.connections.contains_key(addr), + ConnectionTarget::PeerIdWithAddr(ref id, ref addr) => { + self.connected_peers.get(id).is_none() || !self.connections.contains_key(addr) + } }; if !will_attempt_connection { @@ -113,6 +146,14 @@ impl SwarmApi { trace!("Connecting to {:?}", target); + // convert the ConnectionTarget to the actual dial method so that it's easier + // to handle the subscription we'll be creating. + let target = if let ConnectionTarget::PeerIdWithAddr(_id, addr) = target { + ConnectionTarget::Addr(addr) + } else { + target + }; + let subscription = self .connect_registry .create_subscription(target.clone().into(), None); @@ -123,6 +164,7 @@ impl SwarmApi { peer_id, condition: DialPeerCondition::Disconnected, }, + _ => unreachable!(), }); Some(subscription) @@ -277,6 +319,26 @@ mod tests { use libp2p::identity::Keypair; use libp2p::swarm::Swarm; + #[test] + fn connection_targets() { + let peer_id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"; + let multiaddr = "/ip4/104.131.131.82/tcp/4001"; + let both = format!("{}/p2p/{}", multiaddr, peer_id); + + assert!(matches!( + peer_id.parse().unwrap(), + ConnectionTarget::PeerId(_) + )); + assert!(matches!( + multiaddr.parse().unwrap(), + ConnectionTarget::Addr(_) + )); + assert!(matches!( + both.parse().unwrap(), + ConnectionTarget::PeerIdWithAddr(..) + )); + } + #[async_std::test] async fn swarm_api() { let (peer1_id, trans) = mk_transport();