diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 01ae982323702..611d2932df033 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -918,4 +918,44 @@ mod tests { t!(stream.set_write_timeout(None)); assert_eq!(None, t!(stream.write_timeout())); } + + #[test] + fn test_read_timeout() { + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + t!(stream.set_read_timeout(Some(Duration::from_millis(10)))); + + let mut buf = [0; 10]; + let wait = Duration::span(|| { + assert_eq!(ErrorKind::WouldBlock, + stream.read(&mut buf).err().expect("expected error").kind()); + }); + assert!(wait > Duration::from_millis(5)); + assert!(wait < Duration::from_millis(15)); + } + + #[test] + fn test_read_with_timeout() { + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + t!(stream.set_read_timeout(Some(Duration::from_millis(10)))); + + let mut other_end = t!(listener.accept()).0; + t!(other_end.write_all(b"hello world")); + + let mut buf = [0; 11]; + t!(stream.read(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + + let wait = Duration::span(|| { + assert_eq!(ErrorKind::WouldBlock, + stream.read(&mut buf).err().expect("expected error").kind()); + }); + assert!(wait > Duration::from_millis(5)); + assert!(wait < Duration::from_millis(15)); + } } diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 71715a0031b79..d8fa1ccbd4384 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -383,4 +383,41 @@ mod tests { t!(stream.set_write_timeout(None)); assert_eq!(None, t!(stream.write_timeout())); } + + #[test] + fn test_read_timeout() { + let addr = next_test_ip4(); + + let mut stream = t!(UdpSocket::bind(&addr)); + t!(stream.set_read_timeout(Some(Duration::from_millis(10)))); + + let mut buf = [0; 10]; + let wait = Duration::span(|| { + assert_eq!(ErrorKind::WouldBlock, + stream.recv_from(&mut buf).err().expect("expected error").kind()); + }); + assert!(wait > Duration::from_millis(5)); + assert!(wait < Duration::from_millis(15)); + } + + #[test] + fn test_read_with_timeout() { + let addr = next_test_ip4(); + + let mut stream = t!(UdpSocket::bind(&addr)); + t!(stream.set_read_timeout(Some(Duration::from_millis(10)))); + + t!(stream.send_to(b"hello world", &addr)); + + let mut buf = [0; 11]; + t!(stream.recv_from(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + + let wait = Duration::span(|| { + assert_eq!(ErrorKind::WouldBlock, + stream.recv_from(&mut buf).err().expect("expected error").kind()); + }); + assert!(wait > Duration::from_millis(5)); + assert!(wait < Duration::from_millis(15)); + } } diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 2dece6107d03a..5890e6a78892c 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -26,7 +26,7 @@ use time::Duration; // sockaddr and misc bindings //////////////////////////////////////////////////////////////////////////////// -fn setsockopt(sock: &Socket, opt: c_int, val: c_int, +pub fn setsockopt(sock: &Socket, opt: c_int, val: c_int, payload: T) -> io::Result<()> { unsafe { let payload = &payload as *const T as *const c_void; @@ -36,7 +36,7 @@ fn setsockopt(sock: &Socket, opt: c_int, val: c_int, } } -fn getsockopt(sock: &Socket, opt: c_int, +pub fn getsockopt(sock: &Socket, opt: c_int, val: c_int) -> io::Result { unsafe { let mut slot: T = mem::zeroed(); @@ -163,92 +163,6 @@ pub fn lookup_addr(addr: &IpAddr) -> io::Result { } } -//////////////////////////////////////////////////////////////////////////////// -// Timeouts -//////////////////////////////////////////////////////////////////////////////// - -#[cfg(target_os = "windows")] -fn set_timeout(socket: &Socket, dur: Option, kind: libc::c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.secs() == 0 && dur.extra_nanos() == 0 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout")); - } - - let mut timeout = if dur.secs() > (libc::DWORD::max_value() / 1000) as u64 { - libc::DWORD::max_value() - } else { - (dur.secs() * 1000) as libc::DWORD - }; - timeout = timeout.saturating_add((dur.extra_nanos() / 1000000) as libc::DWORD); - if timeout == 0 { - timeout = 1; - } - timeout - } - None => 0 - }; - setsockopt(socket, libc::SOL_SOCKET, kind, timeout) -} - -#[cfg(not(target_os = "windows"))] -fn set_timeout(socket: &Socket, dur: Option, kind: libc::c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.secs() == 0 && dur.extra_nanos() == 0 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout")); - } - - let secs = if dur.secs() > libc::time_t::max_value() as u64 { - libc::time_t::max_value() - } else { - dur.secs() as libc::time_t - }; - let mut timeout = libc::timeval { - tv_sec: secs, - tv_usec: (dur.extra_nanos() / 1000) as libc::time_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => { - libc::timeval { - tv_sec: 0, - tv_usec: 0, - } - } - }; - setsockopt(socket, libc::SOL_SOCKET, kind, timeout) -} - -#[cfg(target_os = "windows")] -fn timeout(socket: &Socket, kind: libc::c_int) -> io::Result> { - let raw: libc::DWORD = try!(getsockopt(socket, libc::SOL_SOCKET, kind)); - if raw == 0 { - Ok(None) - } else { - let secs = raw / 1000; - let nsec = (raw % 1000) * 1000000; - Ok(Some(Duration::new(secs as u64, nsec as u32))) - } -} - -#[cfg(not(target_os = "windows"))] -fn timeout(socket: &Socket, kind: libc::c_int) -> io::Result> { - let raw: libc::timeval = try!(getsockopt(socket, libc::SOL_SOCKET, kind)); - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } -} - //////////////////////////////////////////////////////////////////////////////// // TCP streams //////////////////////////////////////////////////////////////////////////////// @@ -307,19 +221,19 @@ impl TcpStream { } pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - set_timeout(&self.inner, dur, libc::SO_RCVTIMEO) + self.inner.set_timeout(dur, libc::SO_RCVTIMEO) } pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - set_timeout(&self.inner, dur, libc::SO_SNDTIMEO) + self.inner.set_timeout(dur, libc::SO_SNDTIMEO) } pub fn read_timeout(&self) -> io::Result> { - timeout(&self.inner, libc::SO_RCVTIMEO) + self.inner.timeout(libc::SO_RCVTIMEO) } pub fn write_timeout(&self) -> io::Result> { - timeout(&self.inner, libc::SO_SNDTIMEO) + self.inner.timeout(libc::SO_SNDTIMEO) } pub fn read(&self, buf: &mut [u8]) -> io::Result { @@ -575,19 +489,19 @@ impl UdpSocket { } pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - set_timeout(&self.inner, dur, libc::SO_RCVTIMEO) + self.inner.set_timeout(dur, libc::SO_RCVTIMEO) } pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - set_timeout(&self.inner, dur, libc::SO_SNDTIMEO) + self.inner.set_timeout(dur, libc::SO_SNDTIMEO) } pub fn read_timeout(&self) -> io::Result> { - timeout(&self.inner, libc::SO_RCVTIMEO) + self.inner.timeout(libc::SO_RCVTIMEO) } pub fn write_timeout(&self) -> io::Result> { - timeout(&self.inner, libc::SO_SNDTIMEO) + self.inner.timeout(libc::SO_SNDTIMEO) } } diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 2e1cbb2a1e127..e316c6a986641 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -18,6 +18,8 @@ use sys::c; use net::SocketAddr; use sys::fd::FileDesc; use sys_common::{AsInner, FromInner}; +use sys_common::net::{getsockopt, setsockopt}; +use time::Duration; pub use sys::{cvt, cvt_r}; @@ -73,6 +75,49 @@ impl Socket { pub fn read(&self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } + + pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.secs() == 0 && dur.extra_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + + let secs = if dur.secs() > libc::time_t::max_value() as u64 { + libc::time_t::max_value() + } else { + dur.secs() as libc::time_t + }; + let mut timeout = libc::timeval { + tv_sec: secs, + tv_usec: (dur.extra_nanos() / 1000) as libc::time_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => { + libc::timeval { + tv_sec: 0, + tv_usec: 0, + } + } + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result> { + let raw: libc::timeval = try!(getsockopt(self, libc::SOL_SOCKET, kind)); + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } } impl AsInner for Socket { diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs index 71e064bcc6b82..0b9052672369d 100644 --- a/src/libstd/sys/windows/net.rs +++ b/src/libstd/sys/windows/net.rs @@ -19,8 +19,11 @@ use num::One; use ops::Neg; use rt; use sync::Once; +use sys; use sys::c; use sys_common::{AsInner, FromInner}; +use sys_common::net::{setsockopt, getsockopt}; +use time::Duration; pub type wrlen_t = i32; @@ -127,6 +130,32 @@ impl Socket { } } } + + pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + let timeout = sys::dur2timeout(dur); + if timeout == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + timeout + } + None => 0 + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result> { + let raw: libc::DWORD = try!(getsockopt(self, libc::SOL_SOCKET, kind)); + if raw == 0 { + Ok(None) + } else { + let secs = raw / 1000; + let nsec = (raw % 1000) * 1000000; + Ok(Some(Duration::new(secs as u64, nsec as u32))) + } + } } impl Drop for Socket {