Skip to content

Commit

Permalink
src/lib: Add Multiaddr::protocol_stack (#60)
Browse files Browse the repository at this point in the history
A convenience for cases where _which_ protocols is of concern
but the specific addresses are not, for example
libp2p/rust-libp2p#2758
  • Loading branch information
John-LittleBearLabs authored Oct 24, 2022
1 parent 82a171e commit 1cfb923
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
target
Cargo.lock
*.rs.bk
*.rs.bk
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.16.0 [unreleased]

- Create `protocol_stack` for Multiaddr. See [PR 60]

# 0.15.0 [2022-10-20]

- Add `WebRTC` instance for `Multiaddr`. See [PR 59].
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ keywords = ["multiaddr", "ipfs"]
license = "MIT"
name = "multiaddr"
readme = "README.md"
version = "0.15.0"
version = "0.16.0"

[features]
default = ["url"]
Expand Down
19 changes: 19 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ impl Multiaddr {
}
self.bytes[(n - m)..] == other.bytes[..]
}

/// Returns &str identifiers for the protocol names themselves.
/// This omits specific info like addresses, ports, peer IDs, and the like.
/// Example: `"/ip4/127.0.0.1/tcp/5001"` would return `["ip4", "tcp"]`
pub fn protocol_stack(&self) -> ProtoStackIter {
ProtoStackIter { parts: self.iter() }
}
}

impl fmt::Debug for Multiaddr {
Expand Down Expand Up @@ -293,6 +300,18 @@ impl<'a> Iterator for Iter<'a> {
}
}

/// Iterator over the string idtenfiers of the protocols (not addrs) in a multiaddr
pub struct ProtoStackIter<'a> {
parts: Iter<'a>,
}

impl<'a> Iterator for ProtoStackIter<'a> {
type Item = &'static str;
fn next(&mut self) -> Option<Self::Item> {
self.parts.next().as_ref().map(Protocol::tag)
}
}

impl<'a> From<Protocol<'a>> for Multiaddr {
fn from(p: Protocol<'a>) -> Multiaddr {
let mut w = Vec::new();
Expand Down
100 changes: 61 additions & 39 deletions src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,66 +510,88 @@ impl<'a> Protocol<'a> {
Wss(cow) => Wss(Cow::Owned(cow.into_owned())),
}
}

pub fn tag(&self) -> &'static str {
use self::Protocol::*;
match self {
Dccp(_) => "dccp",
Dns(_) => "dns",
Dns4(_) => "dns4",
Dns6(_) => "dns6",
Dnsaddr(_) => "dnsaddr",
Http => "http",
Https => "https",
Ip4(_) => "ip4",
Ip6(_) => "ip6",
P2pWebRtcDirect => "p2p-webrtc-direct",
P2pWebRtcStar => "p2p-webrtc-star",
WebRTC => "webrtc",
Certhash(_) => "certhash",
P2pWebSocketStar => "p2p-websocket-star",
Memory(_) => "memory",
Onion(_, _) => "onion",
Onion3(_) => "onion3",
P2p(_) => "p2p",
P2pCircuit => "p2p-circuit",
Quic => "quic",
Sctp(_) => "sctp",
Tcp(_) => "tcp",
Tls => "tls",
Noise => "noise",
Udp(_) => "udp",
Udt => "udt",
Unix(_) => "unix",
Utp => "utp",
Ws(ref s) if s == "/" => "ws",
Ws(_) => "x-parity-ws",
Wss(ref s) if s == "/" => "wss",
Wss(_) => "x-parity-wss",
}
}
}

impl<'a> fmt::Display for Protocol<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::Protocol::*;
write!(f, "/{}", self.tag())?;
match self {
Dccp(port) => write!(f, "/dccp/{}", port),
Dns(s) => write!(f, "/dns/{}", s),
Dns4(s) => write!(f, "/dns4/{}", s),
Dns6(s) => write!(f, "/dns6/{}", s),
Dnsaddr(s) => write!(f, "/dnsaddr/{}", s),
Http => f.write_str("/http"),
Https => f.write_str("/https"),
Ip4(addr) => write!(f, "/ip4/{}", addr),
Ip6(addr) => write!(f, "/ip6/{}", addr),
P2pWebRtcDirect => f.write_str("/p2p-webrtc-direct"),
P2pWebRtcStar => f.write_str("/p2p-webrtc-star"),
WebRTC => f.write_str("/webrtc"),
Dccp(port) => write!(f, "/{}", port),
Dns(s) => write!(f, "/{}", s),
Dns4(s) => write!(f, "/{}", s),
Dns6(s) => write!(f, "/{}", s),
Dnsaddr(s) => write!(f, "/{}", s),
Ip4(addr) => write!(f, "/{}", addr),
Ip6(addr) => write!(f, "/{}", addr),
Certhash(hash) => write!(
f,
"/certhash/{}",
"/{}",
multibase::encode(multibase::Base::Base64Url, hash.to_bytes())
),
P2pWebSocketStar => f.write_str("/p2p-websocket-star"),
Memory(port) => write!(f, "/memory/{}", port),
Memory(port) => write!(f, "/{}", port),
Onion(addr, port) => {
let s = BASE32.encode(addr.as_ref());
write!(f, "/onion/{}:{}", s.to_lowercase(), port)
write!(f, "/{}:{}", s.to_lowercase(), port)
}
Onion3(addr) => {
let s = BASE32.encode(addr.hash());
write!(f, "/onion3/{}:{}", s.to_lowercase(), addr.port())
}
P2p(c) => write!(
f,
"/p2p/{}",
multibase::Base::Base58Btc.encode(c.to_bytes())
),
P2pCircuit => f.write_str("/p2p-circuit"),
Quic => f.write_str("/quic"),
Sctp(port) => write!(f, "/sctp/{}", port),
Tcp(port) => write!(f, "/tcp/{}", port),
Tls => write!(f, "/tls"),
Noise => write!(f, "/noise"),
Udp(port) => write!(f, "/udp/{}", port),
Udt => f.write_str("/udt"),
Unix(s) => write!(f, "/unix/{}", s),
Utp => f.write_str("/utp"),
Ws(ref s) if s == "/" => f.write_str("/ws"),
Ws(s) => {
write!(f, "/{}:{}", s.to_lowercase(), addr.port())
}
P2p(c) => write!(f, "/{}", multibase::Base::Base58Btc.encode(c.to_bytes())),
Sctp(port) => write!(f, "/{}", port),
Tcp(port) => write!(f, "/{}", port),
Udp(port) => write!(f, "/{}", port),
Unix(s) => write!(f, "/{}", s),
Ws(s) if s != "/" => {
let encoded =
percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
write!(f, "/x-parity-ws/{}", encoded)
write!(f, "/{}", encoded)
}
Wss(ref s) if s == "/" => f.write_str("/wss"),
Wss(s) => {
Wss(s) if s != "/" => {
let encoded =
percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
write!(f, "/x-parity-wss/{}", encoded)
write!(f, "/{}", encoded)
}
_ => Ok(()),
}
}
}
Expand Down
78 changes: 78 additions & 0 deletions tests/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
extern crate core;

use data_encoding::HEXUPPER;
use multiaddr::*;
use multihash::Multihash;
Expand Down Expand Up @@ -527,3 +529,79 @@ fn unknown_protocol_string() {
},
}
}

#[test]
fn protocol_stack() {
let addresses = [
"/ip4/0.0.0.0",
"/ip6/::1",
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
"/udp/0",
"/tcp/0",
"/sctp/0",
"/udp/1234",
"/tcp/1234",
"/sctp/1234",
"/udp/65535",
"/tcp/65535",
"/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
"/udp/1234/sctp/1234",
"/udp/1234/udt",
"/udp/1234/utp",
"/tcp/1234/http",
"/tcp/1234/tls/http",
"/tcp/1234/https",
"/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
"/ip4/127.0.0.1/udp/1234",
"/ip4/127.0.0.1/udp/0",
"/ip4/127.0.0.1/tcp/1234",
"/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
"/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
"/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
"/p2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
"/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/wss/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
"/ip4/127.0.0.1/tcp/9090/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
"/onion/aaimaq4ygg2iegci:80",
"/dnsaddr/sjc-1.bootstrap.libp2p.io",
"/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"/ip4/127.0.0.1/tcp/127/ws",
"/ip4/127.0.0.1/tcp/127/tls",
"/ip4/127.0.0.1/tcp/127/tls/ws",
"/ip4/127.0.0.1/tcp/127/noise",
"/ip4/127.0.0.1/udp/1234/webrtc",
];
let argless = std::collections::HashSet::from([
"http",
"https",
"noise",
"p2p-circuit",
"p2p-webrtc-direct",
"p2p-webrtc-star",
"p2p-websocket-star",
"quic",
"tls",
"udt",
"utp",
"webrtc",
"ws",
"wss",
]);
for addr_str in addresses {
let ma = Multiaddr::from_str(addr_str).expect("These are supposed to be valid multiaddrs");
let ps: Vec<&str> = ma.protocol_stack().collect();
let mut toks: Vec<&str> = addr_str.split('/').collect();
assert_eq!("", toks[0]);
toks.remove(0);
let mut i = 0;
while i < toks.len() {
let proto_tag = toks[i];
i += 1;
if argless.contains(proto_tag) {
//skip
} else {
toks.remove(i);
}
}
assert_eq!(ps, toks);
}
}

0 comments on commit 1cfb923

Please sign in to comment.