@@ -22,12 +22,16 @@ pub trait Transcoder: Send + Sync {
2222/// A custom protocol definition.
2323#[ derive( Clone ) ]
2424pub 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