Skip to content

Commit 9916052

Browse files
committed
extend the handling of custom protocols to be graceful when handling binary<>string format round trips
1 parent 2904bdf commit 9916052

3 files changed

Lines changed: 92 additions & 10 deletions

File tree

src/custom.rs

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,18 +159,17 @@ impl Registry {
159159
) -> Result<(Protocol<'a>, &'a [u8])> {
160160
let n_input = input;
161161
let id_res = unsigned_varint::decode::u32(n_input);
162-
if let Ok((id, _rest)) = id_res {
163-
if !self.by_code.contains_key(&id) {
164-
return Err(Error::UnknownProtocolId(id));
165-
}
166-
}
167162

168163
if let Ok(res) = Protocol::from_bytes(input) {
169-
return Ok(res);
164+
let is_unknown = match &res.0 {
165+
Protocol::Unknown(_, _) => true,
166+
_ => false,
167+
};
168+
if !is_unknown {
169+
return Ok(res);
170+
}
170171
}
171172

172-
let n_input = input;
173-
let id_res = unsigned_varint::decode::u32(n_input);
174173
if let Ok((id, rest)) = id_res {
175174
if let Some(def) = self.get_by_code(id) {
176175
let (data, out_rest) = if def.size == 0 {
@@ -193,6 +192,11 @@ impl Registry {
193192
};
194193
return Ok((Protocol::Custom { def, data }, out_rest));
195194
}
195+
196+
return Ok((
197+
Protocol::Unknown(id, std::borrow::Cow::Borrowed(rest)),
198+
[].as_ref(),
199+
));
196200
}
197201

198202
Err(Error::UnknownProtocolId(
@@ -279,6 +283,15 @@ impl Registry {
279283
}
280284
Ok(addr)
281285
}
286+
287+
/// Format a Multiaddr into a string using this registry
288+
pub fn to_string(&self, addr: &Multiaddr) -> String {
289+
let mut s = String::new();
290+
for p in self.iter(addr) {
291+
s.push_str(&p.to_string());
292+
}
293+
s
294+
}
282295
}
283296

284297
#[cfg(test)]
@@ -356,8 +369,47 @@ mod tests {
356369
// Assert tcp fails now
357370
assert!(registry.try_from_str("/ip4/127.0.0.1/tcp/80").is_err());
358371

359-
// And similarly from bytes
372+
// And similarly from bytes, it will now parse as Tcp since we natively fallback to standard protocols
360373
let vec = addr.to_vec();
361-
assert!(registry.try_from_bytes(&vec).is_err());
374+
let parsed_unknown = registry.try_from_bytes(&vec).unwrap();
375+
let mut parsed_iter = registry.iter(&parsed_unknown);
376+
assert!(matches!(parsed_iter.next(), Some(Protocol::Ip4(_))));
377+
assert!(matches!(parsed_iter.next(), Some(Protocol::Tcp(80))));
378+
}
379+
380+
#[test]
381+
fn test_custom_protocol_registry_printing() {
382+
let mut registry = Registry::new();
383+
registry.register(CustomProtocolDef {
384+
name: "my-custom",
385+
code: 999,
386+
size: -1,
387+
path: false,
388+
transcoder: Some(Box::new(SimpleTranscoder)),
389+
});
390+
391+
// Parsed string multi addr with a custom protocol
392+
let addr = registry
393+
.try_from_str("/ip4/127.0.0.1/my-custom/helloworld")
394+
.unwrap();
395+
396+
// 1. Printing with Registry works as expected, displaying the registered custom format
397+
let registry_printed = registry.to_string(&addr);
398+
assert_eq!(registry_printed, "/ip4/127.0.0.1/my-custom/helloworld");
399+
400+
// 2. Native Multiaddr printing gracefully falls back to unknown without panicking
401+
let native_printed = addr.to_string();
402+
// Native printing uses base58 for the rest of the bytes (the length varint and data).
403+
// For size=-1, the length varint `10` followed by "helloworld" becomes '3ah4EQvnau95Y8K'
404+
assert_eq!(native_printed, "/ip4/127.0.0.1/unknown-999/3ah4EQvnau95Y8K");
405+
406+
// 3. Confirm that the final 'unknown-999' round-trips on parse back to the same multiaddr
407+
let parsed_back = native_printed
408+
.parse::<Multiaddr>()
409+
.expect("Should parse unknown protocol formatting natively");
410+
assert_eq!(
411+
parsed_back, addr,
412+
"Round-trip multiaddr bytes must match the original instance precisely"
413+
);
362414
}
363415
}

src/protocol.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,22 @@ impl<'a> Protocol<'a> {
333333
let decoded = percent_encoding::percent_decode(s.as_bytes()).decode_utf8()?;
334334
Ok(Protocol::HttpPath(decoded))
335335
}
336+
#[cfg(feature = "custom")]
337+
unknown if unknown.starts_with("unknown-") => {
338+
let id_str = &unknown["unknown-".len()..];
339+
let id: u32 = id_str
340+
.parse()
341+
.map_err(|_| Error::UnknownProtocolString(unknown.to_string()))?;
342+
let data = match iter.next() {
343+
Some(s) if s.is_empty() => vec![],
344+
Some(s) => match multibase::Base::Base58Btc.decode(s) {
345+
Ok(d) => d,
346+
Err(_) => return Err(Error::InvalidProtocolString),
347+
},
348+
None => vec![],
349+
};
350+
Ok(Protocol::Unknown(id, std::borrow::Cow::Owned(data)))
351+
}
336352
unknown => Err(Error::UnknownProtocolString(unknown.to_string())),
337353
}
338354
}
@@ -522,6 +538,12 @@ impl<'a> Protocol<'a> {
522538
rest,
523539
))
524540
}
541+
#[cfg(feature = "custom")]
542+
_ => Ok((
543+
Protocol::Unknown(id, std::borrow::Cow::Borrowed(input)),
544+
[].as_ref(),
545+
)),
546+
#[cfg(not(feature = "custom"))]
525547
_ => Err(Error::UnknownProtocolId(id)),
526548
}
527549
}

tests/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,15 @@ fn to_multiaddr() {
598598
#[test]
599599
fn from_bytes_fail() {
600600
let bytes = vec![1, 2, 3, 4];
601+
#[cfg(not(feature = "custom"))]
601602
assert!(Multiaddr::try_from(bytes).is_err());
603+
#[cfg(feature = "custom")]
604+
{
605+
let multiaddr = Multiaddr::try_from(bytes)
606+
.expect("Should parse as Unknown protocol when custom feature is on");
607+
let mut iter = multiaddr.iter();
608+
assert_eq!("unknown", iter.next().unwrap().tag());
609+
}
602610
}
603611

604612
#[test]

0 commit comments

Comments
 (0)