Skip to content

Commit

Permalink
Move platform specific stuff around and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sfackler committed May 28, 2015
1 parent 04b4083 commit 97040aa
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 96 deletions.
40 changes: 40 additions & 0 deletions src/libstd/net/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
37 changes: 37 additions & 0 deletions src/libstd/net/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
106 changes: 10 additions & 96 deletions src/libstd/sys/common/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use time::Duration;
// sockaddr and misc bindings
////////////////////////////////////////////////////////////////////////////////

fn setsockopt<T>(sock: &Socket, opt: c_int, val: c_int,
pub fn setsockopt<T>(sock: &Socket, opt: c_int, val: c_int,
payload: T) -> io::Result<()> {
unsafe {
let payload = &payload as *const T as *const c_void;
Expand All @@ -36,7 +36,7 @@ fn setsockopt<T>(sock: &Socket, opt: c_int, val: c_int,
}
}

fn getsockopt<T: Copy>(sock: &Socket, opt: c_int,
pub fn getsockopt<T: Copy>(sock: &Socket, opt: c_int,
val: c_int) -> io::Result<T> {
unsafe {
let mut slot: T = mem::zeroed();
Expand Down Expand Up @@ -163,92 +163,6 @@ pub fn lookup_addr(addr: &IpAddr) -> io::Result<String> {
}
}

////////////////////////////////////////////////////////////////////////////////
// Timeouts
////////////////////////////////////////////////////////////////////////////////

#[cfg(target_os = "windows")]
fn set_timeout(socket: &Socket, dur: Option<Duration>, 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<Duration>, 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<Option<Duration>> {
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<Option<Duration>> {
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
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -307,19 +221,19 @@ impl TcpStream {
}

pub fn set_read_timeout(&self, dur: Option<Duration>) -> 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<Duration>) -> 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<Option<Duration>> {
timeout(&self.inner, libc::SO_RCVTIMEO)
self.inner.timeout(libc::SO_RCVTIMEO)
}

pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
timeout(&self.inner, libc::SO_SNDTIMEO)
self.inner.timeout(libc::SO_SNDTIMEO)
}

pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
Expand Down Expand Up @@ -575,19 +489,19 @@ impl UdpSocket {
}

pub fn set_read_timeout(&self, dur: Option<Duration>) -> 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<Duration>) -> 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<Option<Duration>> {
timeout(&self.inner, libc::SO_RCVTIMEO)
self.inner.timeout(libc::SO_RCVTIMEO)
}

pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
timeout(&self.inner, libc::SO_SNDTIMEO)
self.inner.timeout(libc::SO_SNDTIMEO)
}
}

Expand Down
45 changes: 45 additions & 0 deletions src/libstd/sys/unix/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -73,6 +75,49 @@ impl Socket {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}

pub fn set_timeout(&self, dur: Option<Duration>, 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<Option<Duration>> {
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<c_int> for Socket {
Expand Down
29 changes: 29 additions & 0 deletions src/libstd/sys/windows/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -127,6 +130,32 @@ impl Socket {
}
}
}

pub fn set_timeout(&self, dur: Option<Duration>, 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<Option<Duration>> {
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 {
Expand Down

0 comments on commit 97040aa

Please sign in to comment.