From d97bfb22f8c4958909c5669c256c50f124c17d58 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Sun, 19 Oct 2014 23:02:21 +0400 Subject: [PATCH 1/7] 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. --- src/libstd/io/net/ip.rs | 151 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/src/libstd/io/net/ip.rs b/src/libstd/io/net/ip.rs index f4f3be13f6675..1a9ba07b1ca1d 100644 --- a/src/libstd/io/net/ip.rs +++ b/src/libstd/io/net/ip.rs @@ -17,10 +17,14 @@ use fmt; use from_str::FromStr; +use io::{mod, IoResult, IoError}; +use io::net; use iter::Iterator; use option::{Option, None, Some}; +use result::{Ok, Err}; use str::StrSlice; use slice::{MutableCloneableSlice, MutableSlice, ImmutableSlice}; +use vec::Vec; pub type Port = u16; @@ -348,6 +352,109 @@ impl FromStr for SocketAddr { } } +pub trait ToSocketAddr { + fn to_socket_addr(&self) -> IoResult { + self.to_socket_addr_all() + .and_then(|v| v.into_iter().next().ok_or_else(|| IoError { + kind: io::InvalidInput, + desc: "no address available", + detail: None + })) + } + + #[inline] + fn to_socket_addr_all(&self) -> IoResult> { + self.to_socket_addr().map(|a| vec![a]) + } +} + +impl ToSocketAddr for SocketAddr { + #[inline] + fn to_socket_addr(&self) -> IoResult { Ok(*self) } +} + +impl ToSocketAddr for (IpAddr, u16) { + #[inline] + fn to_socket_addr(&self) -> IoResult { + let (ip, port) = *self; + Ok(SocketAddr { ip: ip, port: port }) + } +} + +fn resolve_socket_addr(s: &str, p: u16) -> IoResult> { + net::get_host_addresses(s) + .map(|v| v.into_iter().map(|a| SocketAddr { ip: a, port: p }).collect()) +} + +fn parse_and_resolve_socket_addr(s: &str) -> IoResult> { + macro_rules! try_opt( + ($e:expr, $msg:expr) => ( + match $e { + Some(r) => r, + None => return Err(IoError { + kind: io::InvalidInput, + desc: $msg, + detail: None + }) + } + ) + ) + + // split the string by ':' and convert the second part to u16 + let mut parts_iter = s.rsplitn(2, ':'); + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); + let host = try_opt!(parts_iter.next(), "invalid socket address"); + let port: u16 = try_opt!(FromStr::from_str(port_str), "invalid port value"); + resolve_socket_addr(host, port) +} + +impl<'a> ToSocketAddr for (&'a str, u16) { + fn to_socket_addr_all(&self) -> IoResult> { + let (host, port) = *self; + + // try to parse the host as a regular IpAddr first + match FromStr::from_str(host) { + Some(addr) => return Ok(vec![SocketAddr { + ip: addr, + port: port + }]), + None => {} + } + + resolve_socket_addr(host, port) + } +} + +// accepts strings like 'localhost:12345' +impl<'a> ToSocketAddr for &'a str { + fn to_socket_addr(&self) -> IoResult { + // try to parse as a regular SocketAddr first + match FromStr::from_str(*self) { + Some(addr) => return Ok(addr), + None => {} + } + + parse_and_resolve_socket_addr(*self) + .and_then(|v| v.into_iter().next() + .ok_or_else(|| IoError { + kind: io::InvalidInput, + desc: "no address available", + detail: None + }) + ) + } + + fn to_socket_addr_all(&self) -> IoResult> { + // try to parse as a regular SocketAddr first + match FromStr::from_str(*self) { + Some(addr) => return Ok(vec![addr]), + None => {} + } + + parse_and_resolve_socket_addr(*self) + } +} + #[cfg(test)] mod test { @@ -457,4 +564,48 @@ mod test { assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f".to_string()); } + + #[test] + fn to_socket_addr_socketaddr() { + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 12345 }; + assert_eq!(Ok(a), a.to_socket_addr()); + assert_eq!(Ok(vec![a]), a.to_socket_addr_all()); + } + + #[test] + fn to_socket_addr_ipaddr_u16() { + let a = Ipv4Addr(77, 88, 21, 11); + let p = 12345u16; + let e = SocketAddr { ip: a, port: p }; + assert_eq!(Ok(e), (a, p).to_socket_addr()); + assert_eq!(Ok(vec![e]), (a, p).to_socket_addr_all()); + } + + #[test] + fn to_socket_addr_str_u16() { + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; + assert_eq!(Ok(a), ("77.88.21.11", 24352u16).to_socket_addr()); + assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352u16).to_socket_addr_all()); + + let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; + assert_eq!(Ok(a), ("2a02:6b8:0:1::1", 53).to_socket_addr()); + assert_eq!(Ok(vec![a]), ("2a02:6b8:0:1::1", 53).to_socket_addr_all()); + + let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; + assert!(("localhost", 23924u16).to_socket_addr_all().unwrap().contains(&a)); + } + + #[test] + fn to_socket_addr_str() { + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; + assert_eq!(Ok(a), "77.88.21.11:24352".to_socket_addr()); + assert_eq!(Ok(vec![a]), "77.88.21.11:24352".to_socket_addr_all()); + + let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; + assert_eq!(Ok(a), "[2a02:6b8:0:1::1]:53".to_socket_addr()); + assert_eq!(Ok(vec![a]), "[2a02:6b8:0:1::1]:53".to_socket_addr_all()); + + let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; + assert!("localhost:23924".to_socket_addr_all().unwrap().contains(&a)); + } } From ac846749f0abbd0b6107406ba2f97886605e1ad4 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Wed, 22 Oct 2014 23:00:47 +0400 Subject: [PATCH 2/7] Switched io::net::tcp to use ToSocketAddr TcpListener and TcpStream are converted to use ToSocketAddr trait in their constructor methods. [breaking-change] --- src/libstd/io/net/mod.rs | 27 +++- src/libstd/io/net/tcp.rs | 330 ++++++++++++--------------------------- 2 files changed, 130 insertions(+), 227 deletions(-) diff --git a/src/libstd/io/net/mod.rs b/src/libstd/io/net/mod.rs index 4bd998d5ed96d..68577e192d2eb 100644 --- a/src/libstd/io/net/mod.rs +++ b/src/libstd/io/net/mod.rs @@ -10,8 +10,11 @@ //! Networking I/O +use io::{IoError, InvalidInput}; +use option::None; +use result::{Result, Ok, Err}; use rt::rtio; -use self::ip::{Ipv4Addr, Ipv6Addr, IpAddr}; +use self::ip::{Ipv4Addr, Ipv6Addr, IpAddr, ToSocketAddr}; pub use self::addrinfo::get_host_addresses; @@ -38,3 +41,25 @@ fn from_rtio(ip: rtio::IpAddr) -> IpAddr { } } } + +fn with_addresses( + addr: A, + action: |&mut rtio::IoFactory, rtio::SocketAddr| -> Result +) -> Result { + const DEFAULT_ERROR: IoError = IoError { + kind: InvalidInput, + desc: "no addresses found for hostname", + detail: None + }; + + let addresses = try!(addr.to_socket_addr_all()); + let mut err = DEFAULT_ERROR; + for addr in addresses.into_iter() { + let addr = rtio::SocketAddr { ip: to_rtio(addr.ip), port: addr.port }; + match rtio::LocalIo::maybe_raise(|io| action(io, addr)) { + Ok(r) => return Ok(r), + Err(e) => err = IoError::from_rtio_error(e) + } + } + Err(err) +} diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 09804ef570313..5dffc45bcd85c 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -23,7 +23,7 @@ use iter::Iterator; use slice::ImmutableSlice; use result::{Ok,Err}; use io::net::addrinfo::get_host_addresses; -use io::net::ip::SocketAddr; +use io::net::ip::{SocketAddr, ToSocketAddr}; use io::{IoError, ConnectionFailed, InvalidInput}; use io::{Reader, Writer, Listener, Acceptor}; use io::{standard_error, TimedOut}; @@ -45,7 +45,7 @@ use time::Duration; /// # #![allow(unused_must_use)] /// use std::io::TcpStream; /// -/// let mut stream = TcpStream::connect("127.0.0.1", 34254); +/// let mut stream = TcpStream::connect("127.0.0.1:34254"); /// /// stream.write([1]); /// let mut buf = [0]; @@ -61,35 +61,13 @@ impl TcpStream { TcpStream { obj: s } } - /// Open a TCP connection to a remote host by hostname or IP address. + /// Open a TCP connection to a remote host. /// - /// `host` can be a hostname or IP address string. If no error is - /// encountered, then `Ok(stream)` is returned. - pub fn connect(host: &str, port: u16) -> IoResult { - let addresses = match FromStr::from_str(host) { - Some(addr) => vec!(addr), - None => try!(get_host_addresses(host)) - }; - let mut err = IoError { - kind: ConnectionFailed, - desc: "no addresses found for hostname", - detail: None - }; - for addr in addresses.iter() { - let addr = rtio::SocketAddr{ ip: super::to_rtio(*addr), port: port }; - let result = LocalIo::maybe_raise(|io| { - io.tcp_connect(addr, None).map(TcpStream::new) - }); - match result { - Ok(stream) => { - return Ok(stream) - } - Err(connect_err) => { - err = IoError::from_rtio_error(connect_err) - } - } - } - Err(err) + /// `addr` is an address of the remote host. Anything which implements `ToSocketAddr` + /// trait can be supplied for the address; see this trait documentation for + /// concrete examples. + pub fn connect(addr: A) -> IoResult { + super::with_addresses(addr, |io, addr| io.tcp_connect(addr, None).map(TcpStream::new)) } /// Creates a TCP connection to a remote socket address, timing out after @@ -99,23 +77,21 @@ impl TcpStream { /// specified elapses before a connection is made an error will be /// returned. The error's kind will be `TimedOut`. /// - /// Note that the `addr` argument may one day be split into a separate host - /// and port, similar to the API seen in `connect`. + /// Same as the `connect` method, `addr` argument type can vary as defined + /// by `ToSocketAddr` trait. /// /// If a `timeout` with zero or negative duration is specified then /// the function returns `Err`, with the error kind set to `TimedOut`. #[experimental = "the timeout argument may eventually change types"] - pub fn connect_timeout(addr: SocketAddr, - timeout: Duration) -> IoResult { + pub fn connect_timeout(addr: A, + timeout: Duration) -> IoResult { if timeout <= Duration::milliseconds(0) { return Err(standard_error(TimedOut)); } - let SocketAddr { ip, port } = addr; - let addr = rtio::SocketAddr { ip: super::to_rtio(ip), port: port }; - LocalIo::maybe_raise(|io| { + super::with_addresses(addr, |io, addr| io.tcp_connect(addr, Some(timeout.num_milliseconds() as u64)).map(TcpStream::new) - }).map_err(IoError::from_rtio_error) + ) } /// Returns the socket address of the remote peer of this TCP connection. @@ -174,7 +150,7 @@ impl TcpStream { /// use std::io::TcpStream; /// use std::time::Duration; /// - /// let mut stream = TcpStream::connect("127.0.0.1", 34254).unwrap(); + /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); /// let stream2 = stream.clone(); /// /// spawn(proc() { @@ -311,7 +287,7 @@ impl Writer for TcpStream { /// use std::io::{TcpListener, TcpStream}; /// use std::io::{Acceptor, Listener}; /// -/// let listener = TcpListener::bind("127.0.0.1", 80); +/// let listener = TcpListener::bind("127.0.0.1:80"); /// /// // bind the listener to the specified address /// let mut acceptor = listener.listen(); @@ -340,32 +316,15 @@ pub struct TcpListener { } impl TcpListener { - /// Creates a new `TcpListener` which will be bound to the specified IP - /// and port. This listener is not ready for accepting connections, - /// `listen` must be called on it before that's possible. + /// Creates a new `TcpListener` which will be bound to the specified address. + /// This listener is not ready for accepting connections, `listen` must be called + /// on it before that's possible. /// /// Binding with a port number of 0 will request that the OS assigns a port /// to this listener. The port allocated can be queried via the /// `socket_name` function. - pub fn bind(addr: &str, port: u16) -> IoResult { - match FromStr::from_str(addr) { - Some(ip) => { - let addr = rtio::SocketAddr{ - ip: super::to_rtio(ip), - port: port, - }; - LocalIo::maybe_raise(|io| { - io.tcp_bind(addr).map(|l| TcpListener { obj: l }) - }).map_err(IoError::from_rtio_error) - } - None => { - Err(IoError{ - kind: InvalidInput, - desc: "invalid IP address specified", - detail: None - }) - } - } + pub fn bind(addr: A) -> IoResult { + super::with_addresses(addr, |io, addr| io.tcp_bind(addr).map(|l| TcpListener { obj: l })) } /// Returns the local socket address of this listener. @@ -419,7 +378,7 @@ impl TcpAcceptor { /// use std::io::TcpListener; /// use std::io::{Listener, Acceptor, TimedOut}; /// - /// let mut a = TcpListener::bind("127.0.0.1", 8482).listen().unwrap(); + /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); /// /// // After 100ms have passed, all accepts will fail /// a.set_timeout(Some(100)); @@ -462,7 +421,7 @@ impl TcpAcceptor { /// # #![allow(experimental)] /// use std::io::{TcpListener, Listener, Acceptor, EndOfFile}; /// - /// let mut a = TcpListener::bind("127.0.0.1", 8482).listen().unwrap(); + /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); /// let a2 = a.clone(); /// /// spawn(proc() { @@ -531,7 +490,7 @@ mod test { #[cfg_attr(any(windows, target_os = "android"), ignore)] #[test] fn bind_error() { - match TcpListener::bind("0.0.0.0", 1) { + match TcpListener::bind("0.0.0.0:1") { Ok(..) => panic!(), Err(e) => assert_eq!(e.kind, PermissionDenied), } @@ -539,7 +498,7 @@ mod test { #[test] fn connect_error() { - match TcpStream::connect("0.0.0.0", 1) { + match TcpStream::connect("0.0.0.0:1") { Ok(..) => panic!(), Err(e) => assert_eq!(e.kind, ConnectionRefused), } @@ -548,13 +507,11 @@ mod test { #[test] fn listen_ip4_localhost() { let socket_addr = next_test_ip4(); - let ip_str = socket_addr.ip.to_string(); - let port = socket_addr.port; - let listener = TcpListener::bind(ip_str.as_slice(), port); + let listener = TcpListener::bind(socket_addr); let mut acceptor = listener.listen(); spawn(proc() { - let mut stream = TcpStream::connect("localhost", port); + let mut stream = TcpStream::connect(("localhost", socket_addr.port)); stream.write([144]).unwrap(); }); @@ -567,12 +524,10 @@ mod test { #[test] fn connect_localhost() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let mut stream = TcpStream::connect("localhost", addr.port); + let mut stream = TcpStream::connect(("localhost", addr.port)); stream.write([64]).unwrap(); }); @@ -585,12 +540,10 @@ mod test { #[test] fn connect_ip4_loopback() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let mut stream = TcpStream::connect("127.0.0.1", addr.port); + let mut stream = TcpStream::connect(("127.0.0.1", addr.port)); stream.write([44]).unwrap(); }); @@ -603,12 +556,10 @@ mod test { #[test] fn connect_ip6_loopback() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let mut stream = TcpStream::connect("::1", addr.port); + let mut stream = TcpStream::connect(("::1", addr.port)); stream.write([66]).unwrap(); }); @@ -621,12 +572,10 @@ mod test { #[test] fn smoke_test_ip4() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let mut stream = TcpStream::connect(ip_str.as_slice(), port); + let mut stream = TcpStream::connect(addr); stream.write([99]).unwrap(); }); @@ -639,12 +588,10 @@ mod test { #[test] fn smoke_test_ip6() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let mut stream = TcpStream::connect(ip_str.as_slice(), port); + let mut stream = TcpStream::connect(addr); stream.write([99]).unwrap(); }); @@ -657,12 +604,10 @@ mod test { #[test] fn read_eof_ip4() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let _stream = TcpStream::connect(ip_str.as_slice(), port); + let _stream = TcpStream::connect(addr); // Close }); @@ -675,12 +620,10 @@ mod test { #[test] fn read_eof_ip6() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let _stream = TcpStream::connect(ip_str.as_slice(), port); + let _stream = TcpStream::connect(addr); // Close }); @@ -693,12 +636,10 @@ mod test { #[test] fn read_eof_twice_ip4() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let _stream = TcpStream::connect(ip_str.as_slice(), port); + let _stream = TcpStream::connect(addr); // Close }); @@ -719,12 +660,10 @@ mod test { #[test] fn read_eof_twice_ip6() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let _stream = TcpStream::connect(ip_str.as_slice(), port); + let _stream = TcpStream::connect(addr); // Close }); @@ -745,12 +684,10 @@ mod test { #[test] fn write_close_ip4() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let _stream = TcpStream::connect(ip_str.as_slice(), port); + let _stream = TcpStream::connect(addr); // Close }); @@ -773,12 +710,10 @@ mod test { #[test] fn write_close_ip6() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let _stream = TcpStream::connect(ip_str.as_slice(), port); + let _stream = TcpStream::connect(addr); // Close }); @@ -801,14 +736,12 @@ mod test { #[test] fn multiple_connect_serial_ip4() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; let max = 10u; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { for _ in range(0, max) { - let mut stream = TcpStream::connect(ip_str.as_slice(), port); + let mut stream = TcpStream::connect(addr); stream.write([99]).unwrap(); } }); @@ -823,14 +756,12 @@ mod test { #[test] fn multiple_connect_serial_ip6() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; let max = 10u; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { for _ in range(0, max) { - let mut stream = TcpStream::connect(ip_str.as_slice(), port); + let mut stream = TcpStream::connect(addr); stream.write([99]).unwrap(); } }); @@ -845,10 +776,8 @@ mod test { #[test] fn multiple_connect_interleaved_greedy_schedule_ip4() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; static MAX: int = 10; - let acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let acceptor = TcpListener::bind(addr).listen(); spawn(proc() { let mut acceptor = acceptor; @@ -867,13 +796,11 @@ mod test { connect(0, addr); fn connect(i: int, addr: SocketAddr) { - let ip_str = addr.ip.to_string(); - let port = addr.port; if i == MAX { return } spawn(proc() { debug!("connecting"); - let mut stream = TcpStream::connect(ip_str.as_slice(), port); + let mut stream = TcpStream::connect(addr); // Connect again before writing connect(i + 1, addr); debug!("writing"); @@ -885,10 +812,8 @@ mod test { #[test] fn multiple_connect_interleaved_greedy_schedule_ip6() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; static MAX: int = 10; - let acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let acceptor = TcpListener::bind(addr).listen(); spawn(proc() { let mut acceptor = acceptor; @@ -907,13 +832,11 @@ mod test { connect(0, addr); fn connect(i: int, addr: SocketAddr) { - let ip_str = addr.ip.to_string(); - let port = addr.port; if i == MAX { return } spawn(proc() { debug!("connecting"); - let mut stream = TcpStream::connect(ip_str.as_slice(), port); + let mut stream = TcpStream::connect(addr); // Connect again before writing connect(i + 1, addr); debug!("writing"); @@ -926,9 +849,7 @@ mod test { fn multiple_connect_interleaved_lazy_schedule_ip4() { static MAX: int = 10; let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let acceptor = TcpListener::bind(addr).listen(); spawn(proc() { let mut acceptor = acceptor; @@ -947,13 +868,11 @@ mod test { connect(0, addr); fn connect(i: int, addr: SocketAddr) { - let ip_str = addr.ip.to_string(); - let port = addr.port; if i == MAX { return } spawn(proc() { debug!("connecting"); - let mut stream = TcpStream::connect(ip_str.as_slice(), port); + let mut stream = TcpStream::connect(addr); // Connect again before writing connect(i + 1, addr); debug!("writing"); @@ -966,9 +885,7 @@ mod test { fn multiple_connect_interleaved_lazy_schedule_ip6() { static MAX: int = 10; let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let acceptor = TcpListener::bind(addr).listen(); spawn(proc() { let mut acceptor = acceptor; @@ -987,13 +904,11 @@ mod test { connect(0, addr); fn connect(i: int, addr: SocketAddr) { - let ip_str = addr.ip.to_string(); - let port = addr.port; if i == MAX { return } spawn(proc() { debug!("connecting"); - let mut stream = TcpStream::connect(ip_str.as_slice(), port); + let mut stream = TcpStream::connect(addr); // Connect again before writing connect(i + 1, addr); debug!("writing"); @@ -1003,9 +918,7 @@ mod test { } pub fn socket_name(addr: SocketAddr) { - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut listener = TcpListener::bind(ip_str.as_slice(), port).unwrap(); + let mut listener = TcpListener::bind(addr).unwrap(); // Make sure socket_name gives // us the socket we binded to. @@ -1015,15 +928,13 @@ mod test { } pub fn peer_name(addr: SocketAddr) { - let ip_str = addr.ip.to_string(); - let port = addr.port; - let acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let acceptor = TcpListener::bind(addr).listen(); spawn(proc() { let mut acceptor = acceptor; acceptor.accept().unwrap(); }); - let stream = TcpStream::connect(ip_str.as_slice(), port); + let stream = TcpStream::connect(addr); assert!(stream.is_ok()); let mut stream = stream.unwrap(); @@ -1052,11 +963,9 @@ mod test { #[test] fn partial_read() { let addr = next_test_ip4(); - let port = addr.port; let (tx, rx) = channel(); spawn(proc() { - let ip_str = addr.ip.to_string(); - let mut srv = TcpListener::bind(ip_str.as_slice(), port).listen().unwrap(); + let mut srv = TcpListener::bind(addr).listen().unwrap(); tx.send(()); let mut cl = srv.accept().unwrap(); cl.write([10]).unwrap(); @@ -1066,8 +975,7 @@ mod test { }); rx.recv(); - let ip_str = addr.ip.to_string(); - let mut c = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let mut c = TcpStream::connect(addr).unwrap(); let mut b = [0, ..10]; assert_eq!(c.read(b), Ok(1)); c.write([1]).unwrap(); @@ -1077,11 +985,9 @@ mod test { #[test] fn double_bind() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let listener = TcpListener::bind(ip_str.as_slice(), port).unwrap().listen(); + let listener = TcpListener::bind(addr).unwrap().listen(); assert!(listener.is_ok()); - match TcpListener::bind(ip_str.as_slice(), port).listen() { + match TcpListener::bind(addr).listen() { Ok(..) => panic!(), Err(e) => { assert!(e.kind == ConnectionRefused || e.kind == OtherIoError, @@ -1093,20 +999,17 @@ mod test { #[test] fn fast_rebind() { let addr = next_test_ip4(); - let port = addr.port; let (tx, rx) = channel(); spawn(proc() { - let ip_str = addr.ip.to_string(); rx.recv(); - let _stream = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let _stream = TcpStream::connect(addr).unwrap(); // Close rx.recv(); }); { - let ip_str = addr.ip.to_string(); - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); tx.send(()); { let _stream = acceptor.accept().unwrap(); @@ -1115,18 +1018,16 @@ mod test { } // Close listener } - let _listener = TcpListener::bind(addr.ip.to_string().as_slice(), port); + let _listener = TcpListener::bind(addr); } #[test] fn tcp_clone_smoke() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let mut s = TcpStream::connect(ip_str.as_slice(), port); + let mut s = TcpStream::connect(addr); let mut buf = [0, 0]; assert_eq!(s.read(buf), Ok(1)); assert_eq!(buf[0], 1); @@ -1153,14 +1054,12 @@ mod test { #[test] fn tcp_clone_two_read() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); let (tx1, rx) = channel(); let tx2 = tx1.clone(); spawn(proc() { - let mut s = TcpStream::connect(ip_str.as_slice(), port); + let mut s = TcpStream::connect(addr); s.write([1]).unwrap(); rx.recv(); s.write([2]).unwrap(); @@ -1188,12 +1087,10 @@ mod test { #[test] fn tcp_clone_two_write() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut acceptor = TcpListener::bind(ip_str.as_slice(), port).listen(); + let mut acceptor = TcpListener::bind(addr).listen(); spawn(proc() { - let mut s = TcpStream::connect(ip_str.as_slice(), port); + let mut s = TcpStream::connect(addr); let mut buf = [0, 1]; s.read(buf).unwrap(); s.read(buf).unwrap(); @@ -1218,9 +1115,7 @@ mod test { use rt::rtio::RtioTcpStream; let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let a = TcpListener::bind(ip_str.as_slice(), port).unwrap().listen(); + let a = TcpListener::bind(addr).unwrap().listen(); spawn(proc() { let mut a = a; let mut c = a.accept().unwrap(); @@ -1228,7 +1123,7 @@ mod test { c.write([1]).unwrap(); }); - let mut s = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let mut s = TcpStream::connect(addr).unwrap(); assert!(s.obj.close_write().is_ok()); assert!(s.write([1]).is_err()); assert_eq!(s.read_to_end(), Ok(vec!(1))); @@ -1237,9 +1132,7 @@ mod test { #[test] fn accept_timeout() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut a = TcpListener::bind(ip_str.as_slice(), port).unwrap().listen().unwrap(); + let mut a = TcpListener::bind(addr).unwrap().listen().unwrap(); a.set_timeout(Some(10)); @@ -1258,8 +1151,7 @@ mod test { if !cfg!(target_os = "freebsd") { let (tx, rx) = channel(); spawn(proc() { - tx.send(TcpStream::connect(addr.ip.to_string().as_slice(), - port).unwrap()); + tx.send(TcpStream::connect(addr).unwrap()); }); let _l = rx.recv(); for i in range(0i, 1001) { @@ -1276,8 +1168,7 @@ mod test { // Unset the timeout and make sure that this always blocks. a.set_timeout(None); spawn(proc() { - drop(TcpStream::connect(addr.ip.to_string().as_slice(), - port).unwrap()); + drop(TcpStream::connect(addr).unwrap()); }); a.accept().unwrap(); } @@ -1285,9 +1176,7 @@ mod test { #[test] fn close_readwrite_smoke() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let a = TcpListener::bind(ip_str.as_slice(), port).listen().unwrap(); + let a = TcpListener::bind(addr).listen().unwrap(); let (_tx, rx) = channel::<()>(); spawn(proc() { let mut a = a; @@ -1296,7 +1185,7 @@ mod test { }); let mut b = [0]; - let mut s = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let mut s = TcpStream::connect(addr).unwrap(); let mut s2 = s.clone(); // closing should prevent reads/writes @@ -1324,9 +1213,7 @@ mod test { #[test] fn close_read_wakes_up() { let addr = next_test_ip4(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let a = TcpListener::bind(ip_str.as_slice(), port).listen().unwrap(); + let a = TcpListener::bind(addr).listen().unwrap(); let (_tx, rx) = channel::<()>(); spawn(proc() { let mut a = a; @@ -1334,7 +1221,7 @@ mod test { let _ = rx.recv_opt(); }); - let mut s = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let mut s = TcpStream::connect(addr).unwrap(); let s2 = s.clone(); let (tx, rx) = channel(); spawn(proc() { @@ -1352,12 +1239,10 @@ mod test { #[test] fn readwrite_timeouts() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut a = TcpListener::bind(ip_str.as_slice(), port).listen().unwrap(); + let mut a = TcpListener::bind(addr).listen().unwrap(); let (tx, rx) = channel::<()>(); spawn(proc() { - let mut s = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let mut s = TcpStream::connect(addr).unwrap(); rx.recv(); assert!(s.write([0]).is_ok()); let _ = rx.recv_opt(); @@ -1387,12 +1272,10 @@ mod test { #[test] fn read_timeouts() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut a = TcpListener::bind(ip_str.as_slice(), port).listen().unwrap(); + let mut a = TcpListener::bind(addr).listen().unwrap(); let (tx, rx) = channel::<()>(); spawn(proc() { - let mut s = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let mut s = TcpStream::connect(addr).unwrap(); rx.recv(); let mut amt = 0; while amt < 100 * 128 * 1024 { @@ -1418,12 +1301,10 @@ mod test { #[test] fn write_timeouts() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut a = TcpListener::bind(ip_str.as_slice(), port).listen().unwrap(); + let mut a = TcpListener::bind(addr).listen().unwrap(); let (tx, rx) = channel::<()>(); spawn(proc() { - let mut s = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let mut s = TcpStream::connect(addr).unwrap(); rx.recv(); assert!(s.write([0]).is_ok()); let _ = rx.recv_opt(); @@ -1448,12 +1329,10 @@ mod test { #[test] fn timeout_concurrent_read() { let addr = next_test_ip6(); - let ip_str = addr.ip.to_string(); - let port = addr.port; - let mut a = TcpListener::bind(ip_str.as_slice(), port).listen().unwrap(); + let mut a = TcpListener::bind(addr).listen().unwrap(); let (tx, rx) = channel::<()>(); spawn(proc() { - let mut s = TcpStream::connect(ip_str.as_slice(), port).unwrap(); + let mut s = TcpStream::connect(addr).unwrap(); rx.recv(); assert_eq!(s.write([0]), Ok(())); let _ = rx.recv_opt(); @@ -1478,7 +1357,7 @@ mod test { #[test] fn clone_while_reading() { let addr = next_test_ip6(); - let listen = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let listen = TcpListener::bind(addr); let mut accept = listen.listen().unwrap(); // Enqueue a task to write to a socket @@ -1486,8 +1365,7 @@ mod test { let (txdone, rxdone) = channel(); let txdone2 = txdone.clone(); spawn(proc() { - let mut tcp = TcpStream::connect(addr.ip.to_string().as_slice(), - addr.port).unwrap(); + let mut tcp = TcpStream::connect(addr).unwrap(); rx.recv(); tcp.write_u8(0).unwrap(); txdone2.send(()); @@ -1519,15 +1397,15 @@ mod test { #[test] fn clone_accept_smoke() { let addr = next_test_ip4(); - let l = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let l = TcpListener::bind(addr); let mut a = l.listen().unwrap(); let mut a2 = a.clone(); spawn(proc() { - let _ = TcpStream::connect(addr.ip.to_string().as_slice(), addr.port); + let _ = TcpStream::connect(addr); }); spawn(proc() { - let _ = TcpStream::connect(addr.ip.to_string().as_slice(), addr.port); + let _ = TcpStream::connect(addr); }); assert!(a.accept().is_ok()); @@ -1537,7 +1415,7 @@ mod test { #[test] fn clone_accept_concurrent() { let addr = next_test_ip4(); - let l = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let l = TcpListener::bind(addr); let a = l.listen().unwrap(); let a2 = a.clone(); @@ -1548,10 +1426,10 @@ mod test { spawn(proc() { let mut a = a2; tx2.send(a.accept()) }); spawn(proc() { - let _ = TcpStream::connect(addr.ip.to_string().as_slice(), addr.port); + let _ = TcpStream::connect(addr); }); spawn(proc() { - let _ = TcpStream::connect(addr.ip.to_string().as_slice(), addr.port); + let _ = TcpStream::connect(addr); }); assert!(rx.recv().is_ok()); @@ -1561,7 +1439,7 @@ mod test { #[test] fn close_accept_smoke() { let addr = next_test_ip4(); - let l = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let l = TcpListener::bind(addr); let mut a = l.listen().unwrap(); a.close_accept().unwrap(); @@ -1571,7 +1449,7 @@ mod test { #[test] fn close_accept_concurrent() { let addr = next_test_ip4(); - let l = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let l = TcpListener::bind(addr); let a = l.listen().unwrap(); let mut a2 = a.clone(); From 7e3344b17f777f5bca0d3eaa9278fa2d628ca064 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Wed, 29 Oct 2014 22:59:43 +0300 Subject: [PATCH 3/7] Migrated io::net::udp over to ToSocketAddr UdpSocket constructor methods now use ToSocketAddr trait instead of SocketAddr. [breaking-change] --- src/libstd/io/net/mod.rs | 25 ++++++++++++++++++++++--- src/libstd/io/net/tcp.rs | 6 +++--- src/libstd/io/net/udp.rs | 23 +++++++++-------------- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/libstd/io/net/mod.rs b/src/libstd/io/net/mod.rs index 68577e192d2eb..61cefa6f20821 100644 --- a/src/libstd/io/net/mod.rs +++ b/src/libstd/io/net/mod.rs @@ -10,11 +10,11 @@ //! Networking I/O -use io::{IoError, InvalidInput}; +use io::{IoError, IoResult, InvalidInput}; use option::None; use result::{Result, Ok, Err}; use rt::rtio; -use self::ip::{Ipv4Addr, Ipv6Addr, IpAddr, ToSocketAddr}; +use self::ip::{Ipv4Addr, Ipv6Addr, IpAddr, SocketAddr, ToSocketAddr}; pub use self::addrinfo::get_host_addresses; @@ -42,7 +42,7 @@ fn from_rtio(ip: rtio::IpAddr) -> IpAddr { } } -fn with_addresses( +fn with_addresses_io( addr: A, action: |&mut rtio::IoFactory, rtio::SocketAddr| -> Result ) -> Result { @@ -63,3 +63,22 @@ fn with_addresses( } Err(err) } + +fn with_addresses(addr: A, action: |SocketAddr| -> IoResult) + -> IoResult { + const DEFAULT_ERROR: IoError = IoError { + kind: InvalidInput, + desc: "no addresses found for hostname", + detail: None + }; + + let addresses = try!(addr.to_socket_addr_all()); + let mut err = DEFAULT_ERROR; + for addr in addresses.into_iter() { + match action(addr) { + Ok(r) => return Ok(r), + Err(e) => err = e + } + } + Err(err) +} diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 5dffc45bcd85c..ee5f56469adf6 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -67,7 +67,7 @@ impl TcpStream { /// trait can be supplied for the address; see this trait documentation for /// concrete examples. pub fn connect(addr: A) -> IoResult { - super::with_addresses(addr, |io, addr| io.tcp_connect(addr, None).map(TcpStream::new)) + super::with_addresses_io(addr, |io, addr| io.tcp_connect(addr, None).map(TcpStream::new)) } /// Creates a TCP connection to a remote socket address, timing out after @@ -89,7 +89,7 @@ impl TcpStream { return Err(standard_error(TimedOut)); } - super::with_addresses(addr, |io, addr| + super::with_addresses_io(addr, |io, addr| io.tcp_connect(addr, Some(timeout.num_milliseconds() as u64)).map(TcpStream::new) ) } @@ -324,7 +324,7 @@ impl TcpListener { /// to this listener. The port allocated can be queried via the /// `socket_name` function. pub fn bind(addr: A) -> IoResult { - super::with_addresses(addr, |io, addr| io.tcp_bind(addr).map(|l| TcpListener { obj: l })) + super::with_addresses_io(addr, |io, addr| io.tcp_bind(addr).map(|l| TcpListener { obj: l })) } /// Returns the local socket address of this listener. diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index ad9ed090a5ba6..62eeba7cbd263 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -16,7 +16,7 @@ //! datagram protocol. use clone::Clone; -use io::net::ip::{SocketAddr, IpAddr}; +use io::net::ip::{SocketAddr, IpAddr, ToSocketAddr}; use io::{Reader, Writer, IoResult, IoError}; use kinds::Send; use boxed::Box; @@ -65,18 +65,13 @@ pub struct UdpSocket { impl UdpSocket { /// Creates a UDP socket from the given socket address. - pub fn bind(addr: SocketAddr) -> IoResult { - let SocketAddr { ip, port } = addr; - LocalIo::maybe_raise(|io| { - let addr = rtio::SocketAddr { ip: super::to_rtio(ip), port: port }; - io.udp_bind(addr).map(|s| UdpSocket { obj: s }) - }).map_err(IoError::from_rtio_error) + pub fn bind(addr: A) -> IoResult { + super::with_addresses_io(addr, |io, addr| io.udp_bind(addr).map(|s| UdpSocket { obj: s })) } /// Receives data from the socket. On success, returns the number of bytes /// read and the address from whence the data came. - pub fn recv_from(&mut self, buf: &mut [u8]) - -> IoResult<(uint, SocketAddr)> { + pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)> { match self.obj.recv_from(buf) { Ok((amt, rtio::SocketAddr { ip, port })) => { Ok((amt, SocketAddr { ip: super::from_rtio(ip), port: port })) @@ -87,11 +82,11 @@ impl UdpSocket { /// Sends data on the socket to the given address. Returns nothing on /// success. - pub fn send_to(&mut self, buf: &[u8], dst: SocketAddr) -> IoResult<()> { - self.obj.send_to(buf, rtio::SocketAddr { - ip: super::to_rtio(dst.ip), - port: dst.port, - }).map_err(IoError::from_rtio_error) + pub fn send_to(&mut self, buf: &[u8], addr: A) -> IoResult<()> { + super::with_addresses(addr, |addr| self.obj.send_to(buf, rtio::SocketAddr { + ip: super::to_rtio(addr.ip), + port: addr.port, + }).map_err(IoError::from_rtio_error)) } /// Creates a `UdpStream`, which allows use of the `Reader` and `Writer` From 7d379fa78f78a3b88245d73a6d9aa1d2725c304c Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 30 Oct 2014 23:22:40 +0300 Subject: [PATCH 4/7] Fixed other tests to pass make check --- src/compiletest/runtest.rs | 2 +- src/libstd/io/mod.rs | 4 ++-- src/libstd/io/net/tcp.rs | 7 ++----- src/libstd/io/net/udp.rs | 2 +- src/test/run-pass/tcp-accept-stress.rs | 5 ++--- src/test/run-pass/tcp-connect-timeouts.rs | 8 ++------ src/test/run-pass/tcp-stress.rs | 6 ++---- 7 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 55247792921a0..b082cffd66818 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -444,7 +444,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { //waiting 1 second for gdbserver start timer::sleep(Duration::milliseconds(1000)); let result = task::try(proc() { - tcp::TcpStream::connect("127.0.0.1", 5039).unwrap(); + tcp::TcpStream::connect("127.0.0.1:5039").unwrap(); }); if result.is_err() { continue; diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 3dcd8d792a4d8..ebf541a63dafa 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -91,7 +91,7 @@ Some examples of obvious things you might want to do # // locally, we still want to be type checking this code, so lets # // just stop it running (#11576) # if false { - let mut socket = TcpStream::connect("127.0.0.1", 8080).unwrap(); + let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap(); socket.write(b"GET / HTTP/1.0\n\n"); let response = socket.read_to_end(); # } @@ -106,7 +106,7 @@ Some examples of obvious things you might want to do use std::io::{TcpListener, TcpStream}; use std::io::{Acceptor, Listener}; - let listener = TcpListener::bind("127.0.0.1", 80); + let listener = TcpListener::bind("127.0.0.1:80"); // bind the listener to the specified address let mut acceptor = listener.listen(); diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index ee5f56469adf6..6464a180b68fb 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -20,18 +20,15 @@ use clone::Clone; use io::IoResult; use iter::Iterator; -use slice::ImmutableSlice; use result::{Ok,Err}; -use io::net::addrinfo::get_host_addresses; use io::net::ip::{SocketAddr, ToSocketAddr}; -use io::{IoError, ConnectionFailed, InvalidInput}; +use io::IoError; use io::{Reader, Writer, Listener, Acceptor}; use io::{standard_error, TimedOut}; -use from_str::FromStr; use kinds::Send; use option::{None, Some, Option}; use boxed::Box; -use rt::rtio::{IoFactory, LocalIo, RtioSocket, RtioTcpListener}; +use rt::rtio::{IoFactory, RtioSocket, RtioTcpListener}; use rt::rtio::{RtioTcpAcceptor, RtioTcpStream}; use rt::rtio; use time::Duration; diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index 62eeba7cbd263..63303ef9c897a 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -22,7 +22,7 @@ use kinds::Send; use boxed::Box; use option::Option; use result::{Ok, Err}; -use rt::rtio::{RtioSocket, RtioUdpSocket, IoFactory, LocalIo}; +use rt::rtio::{RtioSocket, RtioUdpSocket, IoFactory}; use rt::rtio; /// A User Datagram Protocol socket. diff --git a/src/test/run-pass/tcp-accept-stress.rs b/src/test/run-pass/tcp-accept-stress.rs index bf6fe5a9d26d9..1ed2efa4b3203 100644 --- a/src/test/run-pass/tcp-accept-stress.rs +++ b/src/test/run-pass/tcp-accept-stress.rs @@ -24,7 +24,7 @@ fn main() { } fn test() { - let mut l = TcpListener::bind("127.0.0.1", 0).unwrap(); + let mut l = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = l.socket_name().unwrap(); let mut a = l.listen().unwrap(); let cnt = Arc::new(atomic::AtomicUint::new(0)); @@ -56,8 +56,7 @@ fn test() { let cli_tx = cli_tx.clone(); spawn(proc() { for _ in range(0, M) { - let _s = TcpStream::connect(addr.ip.to_string().as_slice(), - addr.port).unwrap(); + let _s = TcpStream::connect(addr).unwrap(); } cli_tx.send(()); }); diff --git a/src/test/run-pass/tcp-connect-timeouts.rs b/src/test/run-pass/tcp-connect-timeouts.rs index 07fc1212d7899..7dcbccdb17af0 100644 --- a/src/test/run-pass/tcp-connect-timeouts.rs +++ b/src/test/run-pass/tcp-connect-timeouts.rs @@ -31,13 +31,11 @@ use std::time::Duration; #[cfg_attr(target_os = "freebsd", ignore)] fn eventual_timeout() { let addr = next_test_ip4(); - let host = addr.ip.to_string(); - let port = addr.port; let (tx1, rx1) = channel(); let (_tx2, rx2) = channel::<()>(); std::task::spawn(proc() { - let _l = TcpListener::bind(host.as_slice(), port).unwrap().listen(); + let _l = TcpListener::bind(addr).unwrap().listen(); tx1.send(()); let _ = rx2.recv_opt(); }); @@ -56,9 +54,7 @@ fn eventual_timeout() { fn timeout_success() { let addr = next_test_ip4(); - let host = addr.ip.to_string(); - let port = addr.port; - let _l = TcpListener::bind(host.as_slice(), port).unwrap().listen(); + let _l = TcpListener::bind(addr).unwrap().listen(); assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(1000)).is_ok()); } diff --git a/src/test/run-pass/tcp-stress.rs b/src/test/run-pass/tcp-stress.rs index 8d2a8a6ccfe20..676f7e91857f0 100644 --- a/src/test/run-pass/tcp-stress.rs +++ b/src/test/run-pass/tcp-stress.rs @@ -33,7 +33,7 @@ fn main() { let (tx, rx) = channel(); spawn(proc() { - let mut listener = TcpListener::bind("127.0.0.1", 0).unwrap(); + let mut listener = TcpListener::bind("127.0.0.1:0").unwrap(); tx.send(listener.socket_name().unwrap()); let mut acceptor = listener.listen(); loop { @@ -54,9 +54,7 @@ fn main() { for _ in range(0u, 1000) { let tx = tx.clone(); TaskBuilder::new().stack_size(64 * 1024).spawn(proc() { - let host = addr.ip.to_string(); - let port = addr.port; - match TcpStream::connect(host.as_slice(), port) { + match TcpStream::connect(addr) { Ok(stream) => { let mut stream = stream; stream.write([1]); From 7af0cb8af79ea6abd683c61d77e41b14a1242d23 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 31 Oct 2014 11:09:09 +0300 Subject: [PATCH 5/7] Fixed tidy errors --- src/libstd/io/net/mod.rs | 2 +- src/libstd/io/net/tcp.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstd/io/net/mod.rs b/src/libstd/io/net/mod.rs index 61cefa6f20821..b9b50a55a10f2 100644 --- a/src/libstd/io/net/mod.rs +++ b/src/libstd/io/net/mod.rs @@ -43,7 +43,7 @@ fn from_rtio(ip: rtio::IpAddr) -> IpAddr { } fn with_addresses_io( - addr: A, + addr: A, action: |&mut rtio::IoFactory, rtio::SocketAddr| -> Result ) -> Result { const DEFAULT_ERROR: IoError = IoError { diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 6464a180b68fb..6fb31d52dabeb 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -313,8 +313,8 @@ pub struct TcpListener { } impl TcpListener { - /// Creates a new `TcpListener` which will be bound to the specified address. - /// This listener is not ready for accepting connections, `listen` must be called + /// Creates a new `TcpListener` which will be bound to the specified address. + /// This listener is not ready for accepting connections, `listen` must be called /// on it before that's possible. /// /// Binding with a port number of 0 will request that the OS assigns a port From d1ec703329614af5e892605c47be08e7d0e15b8f Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 4 Nov 2014 13:05:12 +0300 Subject: [PATCH 6/7] Added more documentation on ToSocketAddr trait --- src/libstd/io/net/ip.rs | 80 ++++++++++++++++++++++++++++++++++++++++ src/libstd/io/net/tcp.rs | 7 +++- src/libstd/io/net/udp.rs | 8 +++- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/src/libstd/io/net/ip.rs b/src/libstd/io/net/ip.rs index 1a9ba07b1ca1d..4289ce5bea382 100644 --- a/src/libstd/io/net/ip.rs +++ b/src/libstd/io/net/ip.rs @@ -352,7 +352,80 @@ impl FromStr for SocketAddr { } } +/// A trait for objects which can be converted or resolved to one or more `SocketAddr` values. +/// +/// Implementing types minimally have to implement either `to_socket_addr` or `to_socket_addr_all` +/// method, and its trivial counterpart will be available automatically. +/// +/// This trait is used for generic address resolution when constructing network objects. +/// By default it is implemented for the following types: +/// +/// * `SocketAddr` - `to_socket_addr` is identity function. +/// +/// * `(IpAddr, u16)` - `to_socket_addr` constructs `SocketAddr` trivially. +/// +/// * `(&str, u16)` - the string should be either a string representation of an IP address +/// expected by `FromStr` implementation for `IpAddr` or a host name. +/// +/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding +/// to that IP address joined with the given port. +/// +/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses +/// for the host name, each joined with the given port. +/// +/// * `&str` - the string should be either a string representation of a `SocketAddr` as +/// expected by its `FromStr` implementation or a string like `:` pair +/// where `` is a `u16` value. +/// +/// For the former, `to_socker_addr_all` returns a vector with a single element corresponding +/// to that socker address. +/// +/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses +/// for the host name, each joined with the port. +/// +/// +/// This trait allows constructing network objects like `TcpStream` or `UdpSocket` easily with +/// values of various types for the bind/connection address. It is needed because sometimes +/// one type is more appropriate than the other: for simple uses a string like `"localhost:12345"` +/// is much nicer than manual construction of the corresponding `SocketAddr`, but sometimes +/// `SocketAddr` value is *the* main source of the address, and converting it to some other type +/// (e.g. a string) just for it to be converted back to `SocketAddr` in constructor methods +/// is pointless. +/// +/// Some examples: +/// +/// ```rust,no_run +/// # #![allow(unused_must_use)] +/// +/// use std::io::{TcpStream, TcpListener}; +/// use std::io::net::udp::UdpSocket; +/// use std::io::net::ip::{ToSocketAddr, Ipv4Addr, SocketAddr}; +/// +/// fn main() { +/// // The following lines are equivalent modulo possible "localhost" name resolution +/// // differences +/// let tcp_s = TcpStream::connect(SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 12345 }); +/// let tcp_s = TcpStream::connect((Ipv4Addr(127, 0, 0, 1), 12345u16)); +/// let tcp_s = TcpStream::connect(("127.0.0.1", 12345u16)); +/// let tcp_s = TcpStream::connect(("localhost", 12345u16)); +/// let tcp_s = TcpStream::connect("127.0.0.1:12345"); +/// let tcp_s = TcpStream::connect("localhost:12345"); +/// +/// // TcpListener::bind(), UdpSocket::bind() and UdpSocket::send_to() behave similarly +/// let tcp_l = TcpListener::bind("localhost:12345"); +/// +/// let udp_s = UdpSocket::bind(("127.0.0.1", 23451u16)); +/// udp_s.send_to([7u8, 7u8, 7u8].as_slice(), (Ipv4Addr(127, 0, 0, 1), 23451u16)); +/// } +/// ``` pub trait ToSocketAddr { + /// Converts this object to single socket address value. + /// + /// If more than one value is available, this method returns the first one. If no + /// values are available, this method returns an `IoError`. + /// + /// By default this method delegates to `to_socket_addr_all` method, taking the first + /// item from its result. fn to_socket_addr(&self) -> IoResult { self.to_socket_addr_all() .and_then(|v| v.into_iter().next().ok_or_else(|| IoError { @@ -362,6 +435,13 @@ pub trait ToSocketAddr { })) } + /// Converts this object to all available socket address values. + /// + /// Some values like host name string naturally corrrespond to multiple IP addresses. + /// This method tries to return all available addresses corresponding to this object. + /// + /// By default this method delegates to `to_socket_addr` method, creating a singleton + /// vector from its result. #[inline] fn to_socket_addr_all(&self) -> IoResult> { self.to_socket_addr().map(|a| vec![a]) diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 6fb31d52dabeb..928c858673963 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -74,8 +74,8 @@ impl TcpStream { /// specified elapses before a connection is made an error will be /// returned. The error's kind will be `TimedOut`. /// - /// Same as the `connect` method, `addr` argument type can vary as defined - /// by `ToSocketAddr` trait. + /// Same as the `connect` method, `addr` argument type can be anything which + /// implements `ToSocketAddr` trait. /// /// If a `timeout` with zero or negative duration is specified then /// the function returns `Err`, with the error kind set to `TimedOut`. @@ -320,6 +320,9 @@ impl TcpListener { /// Binding with a port number of 0 will request that the OS assigns a port /// to this listener. The port allocated can be queried via the /// `socket_name` function. + /// + /// The address type can be any implementor of `ToSocketAddr` trait. See its + /// documentation for concrete examples. pub fn bind(addr: A) -> IoResult { super::with_addresses_io(addr, |io, addr| io.tcp_bind(addr).map(|l| TcpListener { obj: l })) } diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index 63303ef9c897a..4ae054beadb96 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -64,7 +64,10 @@ pub struct UdpSocket { } impl UdpSocket { - /// Creates a UDP socket from the given socket address. + /// Creates a UDP socket from the given address. + /// + /// Address type can be any implementor of `ToSocketAddr` trait. See its + /// documentation for concrete examples. pub fn bind(addr: A) -> IoResult { super::with_addresses_io(addr, |io, addr| io.udp_bind(addr).map(|s| UdpSocket { obj: s })) } @@ -82,6 +85,9 @@ impl UdpSocket { /// Sends data on the socket to the given address. Returns nothing on /// success. + /// + /// Address type can be any implementor of `ToSocketAddr` trait. See its + /// documentation for concrete examples. pub fn send_to(&mut self, buf: &[u8], addr: A) -> IoResult<()> { super::with_addresses(addr, |addr| self.obj.send_to(buf, rtio::SocketAddr { ip: super::to_rtio(addr.ip), From 0f610f3c14f25bb51a865d04a42a4cbb62b8815f Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Wed, 5 Nov 2014 19:18:30 +0300 Subject: [PATCH 7/7] Fixed not compiling code in docstring --- src/libstd/io/net/ip.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/io/net/ip.rs b/src/libstd/io/net/ip.rs index 4289ce5bea382..b4e9f7502e47c 100644 --- a/src/libstd/io/net/ip.rs +++ b/src/libstd/io/net/ip.rs @@ -399,7 +399,7 @@ impl FromStr for SocketAddr { /// /// use std::io::{TcpStream, TcpListener}; /// use std::io::net::udp::UdpSocket; -/// use std::io::net::ip::{ToSocketAddr, Ipv4Addr, SocketAddr}; +/// use std::io::net::ip::{Ipv4Addr, SocketAddr}; /// /// fn main() { /// // The following lines are equivalent modulo possible "localhost" name resolution @@ -414,7 +414,7 @@ impl FromStr for SocketAddr { /// // TcpListener::bind(), UdpSocket::bind() and UdpSocket::send_to() behave similarly /// let tcp_l = TcpListener::bind("localhost:12345"); /// -/// let udp_s = UdpSocket::bind(("127.0.0.1", 23451u16)); +/// let mut udp_s = UdpSocket::bind(("127.0.0.1", 23451u16)).unwrap(); /// udp_s.send_to([7u8, 7u8, 7u8].as_slice(), (Ipv4Addr(127, 0, 0, 1), 23451u16)); /// } /// ```