Skip to content

Commit b1fc7bd

Browse files
committed
address reviwer comments
1 parent 394c1c5 commit b1fc7bd

1 file changed

Lines changed: 124 additions & 78 deletions

File tree

src/custom.rs

Lines changed: 124 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ pub trait Transcoder: Send + Sync {
2222
/// A custom protocol definition.
2323
#[derive(Clone)]
2424
pub struct CustomProtocolDef {
25+
/// The string identifier for the protocol (e.g. `tcp`, `http`, or `my-custom`).
2526
pub name: &'static str,
27+
/// The unique unsigned integer code for the protocol.
2628
pub code: u32,
2729
/// The length of the binary payload.
28-
/// `0` means no data. `> 0` means a fixed data length. `-1` denotes a length-prefixed protocol.
30+
/// `0` means no data. `> 0` means a fixed data length. `-1` denotes a length-prefixed protocol or custom encoding.
2931
pub size: i32,
32+
/// Whether the protocol's string representation is a file path (e.g., `/unix/tmp/socket` instead of a base-encoded payload).
3033
pub path: bool,
34+
/// An optional transcoder used to encode and decode the protocol's data between binary and human-readable string formats.
3135
pub transcoder: Option<Arc<dyn Transcoder>>,
3236
}
3337

@@ -94,7 +98,7 @@ impl Registry {
9498
Self::default()
9599
}
96100

97-
/// Add all built-in standard protocols to the registry
101+
/// Add all built-in standard protocols to the registry.
98102
fn register_builtins(&mut self) {
99103
for &(name, code, size, path) in crate::protocol::BUILT_IN_PROTOCOLS.iter() {
100104
self.register(CustomProtocolDef {
@@ -143,7 +147,7 @@ impl Registry {
143147
}
144148

145149
/// Iterate over the protocols in a `Multiaddr` using this registry.
146-
pub fn iter<'a>(&'a self, ma: &'a Multiaddr) -> RegistryIter<'a> {
150+
pub fn parse_addr<'a>(&'a self, ma: &'a Multiaddr) -> RegistryIter<'a> {
147151
RegistryIter {
148152
registry: self,
149153
data: ma.as_ref(),
@@ -187,56 +191,68 @@ impl Registry {
187191
}
188192
}
189193

190-
if let Ok((id, rest)) = id_res {
191-
if let Some(def) = self.get_by_code(id) {
192-
let (data, out_rest) = if def.size == 0 {
193-
(std::borrow::Cow::Borrowed(&rest[..0]), rest)
194-
} else if def.size > 0 {
195-
let fixed = def.size as usize;
196-
if rest.len() < fixed {
197-
return Err(Error::DataLessThanLen);
198-
}
199-
let (d, r) = rest.split_at(fixed);
200-
(std::borrow::Cow::Borrowed(d), r)
201-
} else {
202-
let (len, r) =
203-
unsigned_varint::decode::usize(rest).map_err(|_| Error::DataLessThanLen)?;
204-
if r.len() < len {
205-
return Err(Error::DataLessThanLen);
206-
}
207-
let (d, r2) = r.split_at(len);
208-
(std::borrow::Cow::Borrowed(d), r2)
209-
};
194+
let (id, rest) = match id_res {
195+
Ok((id, rest)) => (id, rest),
196+
Err(_) => return Err(Error::UnknownProtocolId(0)),
197+
};
198+
199+
let def = match self.get_by_code(id) {
200+
Some(def) => def,
201+
None => {
202+
// If the protocol isn't registered, we just return it as Unknown
203+
// so that we can gracefully iterate over or reserialize it later.
210204
return Ok((
211-
Protocol::Custom {
212-
def: Arc::new(def),
213-
data,
214-
},
215-
out_rest,
205+
Protocol::Unknown(id, std::borrow::Cow::Borrowed(rest)),
206+
[].as_ref(),
216207
));
217208
}
209+
};
218210

219-
return Ok((
220-
Protocol::Unknown(id, std::borrow::Cow::Borrowed(rest)),
221-
[].as_ref(),
222-
));
223-
}
211+
// Extract the protocol data based on the registered size definition
212+
let (data, out_rest) = if def.size == 0 {
213+
// Protocol has no data payload expected
214+
(std::borrow::Cow::Borrowed(&rest[..0]), rest)
215+
} else if def.size > 0 {
216+
// Protocol has a fixed-length data payload
217+
let fixed = def.size as usize;
218+
if rest.len() < fixed {
219+
return Err(Error::DataLessThanLen);
220+
}
221+
let (d, r) = rest.split_at(fixed);
222+
(std::borrow::Cow::Borrowed(d), r)
223+
} else {
224+
// Protocol size is -1, meaning it is length-prefixed.
225+
// Decode the varint representing the length of the upcoming data.
226+
let (len, r) =
227+
unsigned_varint::decode::usize(rest).map_err(|_| Error::DataLessThanLen)?;
228+
if r.len() < len {
229+
return Err(Error::DataLessThanLen);
230+
}
231+
let (d, r2) = r.split_at(len);
232+
(std::borrow::Cow::Borrowed(d), r2)
233+
};
224234

225-
Err(Error::UnknownProtocolId(
226-
id_res.map(|(i, _)| i).unwrap_or(0),
235+
Ok((
236+
Protocol::Custom {
237+
def: Arc::new(def),
238+
data,
239+
},
240+
out_rest,
227241
))
228242
}
229243

230244
/// Try parsing a single Protocol from string parts using the registry.
231-
pub fn parse_protocol_from_str_parts<'a, I>(&self, iter: &mut I) -> Result<Protocol<'a>>
245+
pub fn parse_protocol_from_str_parts<'a, I>(
246+
&self,
247+
iter: &mut std::iter::Peekable<I>,
248+
) -> Result<Protocol<'a>>
232249
where
233250
I: Iterator<Item = &'a str> + Clone,
234251
{
235-
let mut peek_iter = iter.clone();
236-
if let Some(tag) = peek_iter.next() {
237-
if !self.by_name.contains_key(tag) {
238-
return Err(Error::UnknownProtocolString(tag.to_string()));
239-
}
252+
let &tag = iter.peek().ok_or(Error::InvalidProtocolString)?;
253+
254+
if !self.by_name.contains_key(tag) {
255+
return Err(Error::UnknownProtocolString(tag.to_string()));
240256
}
241257

242258
let mut native_iter = iter.clone();
@@ -245,50 +261,39 @@ impl Registry {
245261
return Ok(p);
246262
}
247263

248-
let mut peek_iter = iter.clone();
249-
if let Some(tag) = peek_iter.next() {
250-
if let Some(def) = self.get_by_name(tag) {
251-
iter.next(); // consume the tag
252-
let data = if def.size == 0 {
253-
vec![]
254-
} else if let Some(t) = &def.transcoder {
255-
let part = iter.next().ok_or(Error::InvalidProtocolString)?;
256-
t.string_to_bytes(part)
257-
.map_err(|_| Error::InvalidProtocolString)?
258-
} else if def.path {
259-
let part = iter.next().ok_or(Error::InvalidProtocolString)?;
260-
percent_encoding::percent_decode(part.as_bytes()).collect::<Vec<u8>>()
261-
} else {
262-
let part = iter.next().ok_or(Error::InvalidProtocolString)?;
263-
multibase::Base::Base58Btc
264-
.decode(part)
265-
.map_err(|_| Error::InvalidProtocolString)?
266-
};
267-
return Ok(Protocol::Custom {
268-
def: Arc::new(def),
269-
data: std::borrow::Cow::Owned(data),
270-
});
271-
}
272-
}
273-
274-
let mut final_try = iter.clone();
275-
if let Some(tag) = final_try.next() {
276-
Err(Error::UnknownProtocolString(tag.to_string()))
264+
let def = self.get_by_name(tag).unwrap();
265+
iter.next(); // consume the tag
266+
let data = if def.size == 0 {
267+
vec![]
268+
} else if let Some(t) = &def.transcoder {
269+
let part = iter.next().ok_or(Error::InvalidProtocolString)?;
270+
t.string_to_bytes(part)
271+
.map_err(|_| Error::InvalidProtocolString)?
272+
} else if def.path {
273+
let part = iter.next().ok_or(Error::InvalidProtocolString)?;
274+
percent_encoding::percent_decode(part.as_bytes()).collect::<Vec<u8>>()
277275
} else {
278-
Err(Error::InvalidProtocolString)
279-
}
276+
let part = iter.next().ok_or(Error::InvalidProtocolString)?;
277+
multibase::Base::Base58Btc
278+
.decode(part)
279+
.map_err(|_| Error::InvalidProtocolString)?
280+
};
281+
Ok(Protocol::Custom {
282+
def: Arc::new(def),
283+
data: std::borrow::Cow::Owned(data),
284+
})
280285
}
281286

282287
/// Parse a Multiaddr string using this registry
283288
pub fn try_from_str(&self, input: &str) -> Result<Multiaddr> {
284289
let mut addr = Multiaddr::empty();
285-
let mut parts = input.split('/');
290+
let mut parts = input.split('/').peekable();
286291

287292
if Some("") != parts.next() {
288293
return Err(Error::InvalidMultiaddr);
289294
}
290295

291-
while parts.clone().peekable().peek().is_some() {
296+
while parts.peek().is_some() {
292297
let p = self.parse_protocol_from_str_parts(&mut parts)?;
293298
addr = addr.with(p);
294299
}
@@ -310,7 +315,7 @@ impl Registry {
310315
/// Format a Multiaddr into a string using this registry
311316
pub fn to_string(&self, addr: &Multiaddr) -> String {
312317
let mut s = String::new();
313-
for p in self.iter(addr) {
318+
for p in self.parse_addr(addr) {
314319
s.push_str(&p.to_string());
315320
}
316321
s
@@ -360,7 +365,7 @@ mod tests {
360365
// Parse back from vec
361366
let parsed = registry.try_from_bytes(&vec).unwrap();
362367

363-
let mut iter = registry.iter(&parsed);
368+
let mut iter = registry.parse_addr(&parsed);
364369
if let Some(Protocol::Ip4(ip)) = iter.next() {
365370
assert_eq!(ip, std::net::Ipv4Addr::new(127, 0, 0, 1));
366371
} else {
@@ -382,7 +387,7 @@ mod tests {
382387

383388
// Assert tcp works
384389
let addr = registry.try_from_str("/ip4/127.0.0.1/tcp/80").unwrap();
385-
let mut iter = registry.iter(&addr);
390+
let mut iter = registry.parse_addr(&addr);
386391
assert!(matches!(iter.next(), Some(Protocol::Ip4(_))));
387392
assert!(matches!(iter.next(), Some(Protocol::Tcp(80))));
388393

@@ -395,7 +400,7 @@ mod tests {
395400
// And similarly from bytes, it will now parse as Tcp since we natively fallback to standard protocols
396401
let vec = addr.to_vec();
397402
let parsed_unknown = registry.try_from_bytes(&vec).unwrap();
398-
let mut parsed_iter = registry.iter(&parsed_unknown);
403+
let mut parsed_iter = registry.parse_addr(&parsed_unknown);
399404
assert!(matches!(parsed_iter.next(), Some(Protocol::Ip4(_))));
400405
assert!(matches!(parsed_iter.next(), Some(Protocol::Tcp(80))));
401406
}
@@ -435,4 +440,45 @@ mod tests {
435440
"Round-trip multiaddr bytes must match the original instance precisely"
436441
);
437442
}
443+
444+
#[test]
445+
fn test_custom_protocol_size_zero() {
446+
let mut registry = Registry::new();
447+
// Register a custom protocol with size 0 (no data payload expected)
448+
registry.register(CustomProtocolDef::new(
449+
"my-empty",
450+
1000,
451+
0,
452+
false,
453+
None::<SimpleTranscoder>,
454+
));
455+
456+
// It should parse without needing an additional value
457+
let addr = registry.try_from_str("/ip4/127.0.0.1/my-empty").unwrap();
458+
459+
let vec = addr.to_vec();
460+
let parsed = registry.try_from_bytes(&vec).unwrap();
461+
462+
let mut iter = registry.parse_addr(&parsed);
463+
if let Some(Protocol::Ip4(ip)) = iter.next() {
464+
assert_eq!(ip, std::net::Ipv4Addr::new(127, 0, 0, 1));
465+
} else {
466+
panic!("expected ip4");
467+
}
468+
469+
if let Some(Protocol::Custom { def, data }) = iter.next() {
470+
assert_eq!(def.code, 1000);
471+
assert_eq!(def.name, "my-empty");
472+
assert!(data.is_empty());
473+
} else {
474+
panic!("expected custom protocol");
475+
}
476+
477+
// Ensure that a subsequent protocol is parsed correctly, not consumed as data
478+
let addr2 = registry.try_from_str("/my-empty/tcp/80").unwrap();
479+
let mut iter2 = registry.parse_addr(&addr2);
480+
481+
assert!(matches!(iter2.next(), Some(Protocol::Custom { def, .. }) if def.name == "my-empty"));
482+
assert!(matches!(iter2.next(), Some(Protocol::Tcp(80))));
483+
}
438484
}

0 commit comments

Comments
 (0)