Skip to content

Commit d97bfb2

Browse files
committed
Added ToSocketAddr trait
This commit adds ToSocketAddr trait to std::io::net::ip module. This trait is used for generic conversion from different types (strings, (string, u16) tuples, etc.) into a SocketAddr instance. It supports multiple output SocketAddresses when it is appropriate (e.g. DNS name resolution). This trait is going to be used by TcpStream, TcpListener and UdpSocket structures.
1 parent 4375b32 commit d97bfb2

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed

src/libstd/io/net/ip.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717

1818
use fmt;
1919
use from_str::FromStr;
20+
use io::{mod, IoResult, IoError};
21+
use io::net;
2022
use iter::Iterator;
2123
use option::{Option, None, Some};
24+
use result::{Ok, Err};
2225
use str::StrSlice;
2326
use slice::{MutableCloneableSlice, MutableSlice, ImmutableSlice};
27+
use vec::Vec;
2428

2529
pub type Port = u16;
2630

@@ -348,6 +352,109 @@ impl FromStr for SocketAddr {
348352
}
349353
}
350354

355+
pub trait ToSocketAddr {
356+
fn to_socket_addr(&self) -> IoResult<SocketAddr> {
357+
self.to_socket_addr_all()
358+
.and_then(|v| v.into_iter().next().ok_or_else(|| IoError {
359+
kind: io::InvalidInput,
360+
desc: "no address available",
361+
detail: None
362+
}))
363+
}
364+
365+
#[inline]
366+
fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
367+
self.to_socket_addr().map(|a| vec![a])
368+
}
369+
}
370+
371+
impl ToSocketAddr for SocketAddr {
372+
#[inline]
373+
fn to_socket_addr(&self) -> IoResult<SocketAddr> { Ok(*self) }
374+
}
375+
376+
impl ToSocketAddr for (IpAddr, u16) {
377+
#[inline]
378+
fn to_socket_addr(&self) -> IoResult<SocketAddr> {
379+
let (ip, port) = *self;
380+
Ok(SocketAddr { ip: ip, port: port })
381+
}
382+
}
383+
384+
fn resolve_socket_addr(s: &str, p: u16) -> IoResult<Vec<SocketAddr>> {
385+
net::get_host_addresses(s)
386+
.map(|v| v.into_iter().map(|a| SocketAddr { ip: a, port: p }).collect())
387+
}
388+
389+
fn parse_and_resolve_socket_addr(s: &str) -> IoResult<Vec<SocketAddr>> {
390+
macro_rules! try_opt(
391+
($e:expr, $msg:expr) => (
392+
match $e {
393+
Some(r) => r,
394+
None => return Err(IoError {
395+
kind: io::InvalidInput,
396+
desc: $msg,
397+
detail: None
398+
})
399+
}
400+
)
401+
)
402+
403+
// split the string by ':' and convert the second part to u16
404+
let mut parts_iter = s.rsplitn(2, ':');
405+
let port_str = try_opt!(parts_iter.next(), "invalid socket address");
406+
let host = try_opt!(parts_iter.next(), "invalid socket address");
407+
let port: u16 = try_opt!(FromStr::from_str(port_str), "invalid port value");
408+
resolve_socket_addr(host, port)
409+
}
410+
411+
impl<'a> ToSocketAddr for (&'a str, u16) {
412+
fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
413+
let (host, port) = *self;
414+
415+
// try to parse the host as a regular IpAddr first
416+
match FromStr::from_str(host) {
417+
Some(addr) => return Ok(vec![SocketAddr {
418+
ip: addr,
419+
port: port
420+
}]),
421+
None => {}
422+
}
423+
424+
resolve_socket_addr(host, port)
425+
}
426+
}
427+
428+
// accepts strings like 'localhost:12345'
429+
impl<'a> ToSocketAddr for &'a str {
430+
fn to_socket_addr(&self) -> IoResult<SocketAddr> {
431+
// try to parse as a regular SocketAddr first
432+
match FromStr::from_str(*self) {
433+
Some(addr) => return Ok(addr),
434+
None => {}
435+
}
436+
437+
parse_and_resolve_socket_addr(*self)
438+
.and_then(|v| v.into_iter().next()
439+
.ok_or_else(|| IoError {
440+
kind: io::InvalidInput,
441+
desc: "no address available",
442+
detail: None
443+
})
444+
)
445+
}
446+
447+
fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
448+
// try to parse as a regular SocketAddr first
449+
match FromStr::from_str(*self) {
450+
Some(addr) => return Ok(vec![addr]),
451+
None => {}
452+
}
453+
454+
parse_and_resolve_socket_addr(*self)
455+
}
456+
}
457+
351458

352459
#[cfg(test)]
353460
mod test {
@@ -457,4 +564,48 @@ mod test {
457564
assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(),
458565
"8:9:a:b:c:d:e:f".to_string());
459566
}
567+
568+
#[test]
569+
fn to_socket_addr_socketaddr() {
570+
let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 12345 };
571+
assert_eq!(Ok(a), a.to_socket_addr());
572+
assert_eq!(Ok(vec![a]), a.to_socket_addr_all());
573+
}
574+
575+
#[test]
576+
fn to_socket_addr_ipaddr_u16() {
577+
let a = Ipv4Addr(77, 88, 21, 11);
578+
let p = 12345u16;
579+
let e = SocketAddr { ip: a, port: p };
580+
assert_eq!(Ok(e), (a, p).to_socket_addr());
581+
assert_eq!(Ok(vec![e]), (a, p).to_socket_addr_all());
582+
}
583+
584+
#[test]
585+
fn to_socket_addr_str_u16() {
586+
let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 };
587+
assert_eq!(Ok(a), ("77.88.21.11", 24352u16).to_socket_addr());
588+
assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352u16).to_socket_addr_all());
589+
590+
let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 };
591+
assert_eq!(Ok(a), ("2a02:6b8:0:1::1", 53).to_socket_addr());
592+
assert_eq!(Ok(vec![a]), ("2a02:6b8:0:1::1", 53).to_socket_addr_all());
593+
594+
let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 };
595+
assert!(("localhost", 23924u16).to_socket_addr_all().unwrap().contains(&a));
596+
}
597+
598+
#[test]
599+
fn to_socket_addr_str() {
600+
let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 };
601+
assert_eq!(Ok(a), "77.88.21.11:24352".to_socket_addr());
602+
assert_eq!(Ok(vec![a]), "77.88.21.11:24352".to_socket_addr_all());
603+
604+
let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 };
605+
assert_eq!(Ok(a), "[2a02:6b8:0:1::1]:53".to_socket_addr());
606+
assert_eq!(Ok(vec![a]), "[2a02:6b8:0:1::1]:53".to_socket_addr_all());
607+
608+
let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 };
609+
assert!("localhost:23924".to_socket_addr_all().unwrap().contains(&a));
610+
}
460611
}

0 commit comments

Comments
 (0)