Skip to content

Commit 04b4083

Browse files
committed
Implement RFC 1047 - socket timeouts
Closes #25619
1 parent 1a3cffb commit 04b4083

File tree

3 files changed

+248
-5
lines changed

3 files changed

+248
-5
lines changed

src/libstd/net/tcp.rs

+63
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use io;
1919
use net::{ToSocketAddrs, SocketAddr, Shutdown};
2020
use sys_common::net as net_imp;
2121
use sys_common::{AsInner, FromInner};
22+
use time::Duration;
2223

2324
/// A structure which represents a TCP stream between a local socket and a
2425
/// remote socket.
@@ -139,6 +140,42 @@ impl TcpStream {
139140
pub fn set_keepalive(&self, seconds: Option<u32>) -> io::Result<()> {
140141
self.0.set_keepalive(seconds)
141142
}
143+
144+
/// Sets the read timeout to the timeout specified.
145+
///
146+
/// If the value specified is `None`, then `read` calls will block
147+
/// indefinitely. It is an error to pass the zero `Duration` to this
148+
/// method.
149+
#[unstable(feature = "socket_timeout", reason = "RFC 1047 - recently added")]
150+
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
151+
self.0.set_read_timeout(dur)
152+
}
153+
154+
/// Sets the write timeout to the timeout specified.
155+
///
156+
/// If the value specified is `None`, then `write` calls will block
157+
/// indefinitely. It is an error to pass the zero `Duration` to this
158+
/// method.
159+
#[unstable(feature = "socket_timeout", reason = "RFC 1047 - recently added")]
160+
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
161+
self.0.set_write_timeout(dur)
162+
}
163+
164+
/// Returns the read timeout of this socket.
165+
///
166+
/// If the timeout is `None`, then `read` calls will block indefinitely.
167+
#[unstable(feature = "socket_timeout", reason = "RFC 1047 - recently added")]
168+
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
169+
self.0.read_timeout()
170+
}
171+
172+
/// Returns the write timeout of this socket.
173+
///
174+
/// If the timeout is `None`, then `write` calls will block indefinitely.
175+
#[unstable(feature = "socket_timeout", reason = "RFC 1047 - recently added")]
176+
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
177+
self.0.write_timeout()
178+
}
142179
}
143180

144181
#[stable(feature = "rust1", since = "1.0.0")]
@@ -262,6 +299,7 @@ mod tests {
262299
use net::test::{next_test_ip4, next_test_ip6};
263300
use sync::mpsc::channel;
264301
use sys_common::AsInner;
302+
use time::Duration;
265303
use thread;
266304

267305
fn each_ip(f: &mut FnMut(SocketAddr)) {
@@ -855,4 +893,29 @@ mod tests {
855893
stream_inner);
856894
assert_eq!(format!("{:?}", stream), compare);
857895
}
896+
897+
#[test]
898+
fn timeouts() {
899+
let addr = next_test_ip4();
900+
let listener = t!(TcpListener::bind(&addr));
901+
902+
let stream = t!(TcpStream::connect(&("localhost", addr.port())));
903+
let dur = Duration::new(15410, 0);
904+
905+
assert_eq!(None, t!(stream.read_timeout()));
906+
907+
t!(stream.set_read_timeout(Some(dur)));
908+
assert_eq!(Some(dur), t!(stream.read_timeout()));
909+
910+
assert_eq!(None, t!(stream.write_timeout()));
911+
912+
t!(stream.set_write_timeout(Some(dur)));
913+
assert_eq!(Some(dur), t!(stream.write_timeout()));
914+
915+
t!(stream.set_read_timeout(None));
916+
assert_eq!(None, t!(stream.read_timeout()));
917+
918+
t!(stream.set_write_timeout(None));
919+
assert_eq!(None, t!(stream.write_timeout()));
920+
}
858921
}

src/libstd/net/udp.rs

+62
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use io::{self, Error, ErrorKind};
1818
use net::{ToSocketAddrs, SocketAddr, IpAddr};
1919
use sys_common::net as net_imp;
2020
use sys_common::{AsInner, FromInner};
21+
use time::Duration;
2122

2223
/// A User Datagram Protocol socket.
2324
///
@@ -127,6 +128,42 @@ impl UdpSocket {
127128
pub fn set_time_to_live(&self, ttl: i32) -> io::Result<()> {
128129
self.0.time_to_live(ttl)
129130
}
131+
132+
/// Sets the read timeout to the timeout specified.
133+
///
134+
/// If the value specified is `None`, then `read` calls will block
135+
/// indefinitely. It is an error to pass the zero `Duration` to this
136+
/// method.
137+
#[unstable(feature = "socket_timeout", reason = "RFC 1047 - recently added")]
138+
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
139+
self.0.set_read_timeout(dur)
140+
}
141+
142+
/// Sets the write timeout to the timeout specified.
143+
///
144+
/// If the value specified is `None`, then `write` calls will block
145+
/// indefinitely. It is an error to pass the zero `Duration` to this
146+
/// method.
147+
#[unstable(feature = "socket_timeout", reason = "RFC 1047 - recently added")]
148+
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
149+
self.0.set_write_timeout(dur)
150+
}
151+
152+
/// Returns the read timeout of this socket.
153+
///
154+
/// If the timeout is `None`, then `read` calls will block indefinitely.
155+
#[unstable(feature = "socket_timeout", reason = "RFC 1047 - recently added")]
156+
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
157+
self.0.read_timeout()
158+
}
159+
160+
/// Returns the write timeout of this socket.
161+
///
162+
/// If the timeout is `None`, then `write` calls will block indefinitely.
163+
#[unstable(feature = "socket_timeout", reason = "RFC 1047 - recently added")]
164+
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
165+
self.0.write_timeout()
166+
}
130167
}
131168

132169
impl AsInner<net_imp::UdpSocket> for UdpSocket {
@@ -152,6 +189,7 @@ mod tests {
152189
use net::test::{next_test_ip4, next_test_ip6};
153190
use sync::mpsc::channel;
154191
use sys_common::AsInner;
192+
use time::Duration;
155193
use thread;
156194

157195
fn each_ip(f: &mut FnMut(SocketAddr, SocketAddr)) {
@@ -321,4 +359,28 @@ mod tests {
321359
socket_addr, name, udpsock_inner);
322360
assert_eq!(format!("{:?}", udpsock), compare);
323361
}
362+
363+
#[test]
364+
fn timeouts() {
365+
let addr = next_test_ip4();
366+
367+
let stream = t!(UdpSocket::bind(&addr));
368+
let dur = Duration::new(15410, 0);
369+
370+
assert_eq!(None, t!(stream.read_timeout()));
371+
372+
t!(stream.set_read_timeout(Some(dur)));
373+
assert_eq!(Some(dur), t!(stream.read_timeout()));
374+
375+
assert_eq!(None, t!(stream.write_timeout()));
376+
377+
t!(stream.set_write_timeout(Some(dur)));
378+
assert_eq!(Some(dur), t!(stream.write_timeout()));
379+
380+
t!(stream.set_read_timeout(None));
381+
assert_eq!(None, t!(stream.read_timeout()));
382+
383+
t!(stream.set_write_timeout(None));
384+
assert_eq!(None, t!(stream.write_timeout()));
385+
}
324386
}

src/libstd/sys/common/net.rs

+123-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use str::from_utf8;
2020
use sys::c;
2121
use sys::net::{cvt, cvt_r, cvt_gai, Socket, init, wrlen_t};
2222
use sys_common::{AsInner, FromInner, IntoInner};
23+
use time::Duration;
2324

2425
////////////////////////////////////////////////////////////////////////////////
2526
// sockaddr and misc bindings
@@ -35,16 +36,15 @@ fn setsockopt<T>(sock: &Socket, opt: c_int, val: c_int,
3536
}
3637
}
3738

38-
#[allow(dead_code)]
3939
fn getsockopt<T: Copy>(sock: &Socket, opt: c_int,
4040
val: c_int) -> io::Result<T> {
4141
unsafe {
4242
let mut slot: T = mem::zeroed();
4343
let mut len = mem::size_of::<T>() as socklen_t;
44-
let ret = try!(cvt(c::getsockopt(*sock.as_inner(), opt, val,
45-
&mut slot as *mut _ as *mut _,
46-
&mut len)));
47-
assert_eq!(ret as usize, mem::size_of::<T>());
44+
try!(cvt(c::getsockopt(*sock.as_inner(), opt, val,
45+
&mut slot as *mut _ as *mut _,
46+
&mut len)));
47+
assert_eq!(len as usize, mem::size_of::<T>());
4848
Ok(slot)
4949
}
5050
}
@@ -163,6 +163,92 @@ pub fn lookup_addr(addr: &IpAddr) -> io::Result<String> {
163163
}
164164
}
165165

166+
////////////////////////////////////////////////////////////////////////////////
167+
// Timeouts
168+
////////////////////////////////////////////////////////////////////////////////
169+
170+
#[cfg(target_os = "windows")]
171+
fn set_timeout(socket: &Socket, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
172+
let timeout = match dur {
173+
Some(dur) => {
174+
if dur.secs() == 0 && dur.extra_nanos() == 0 {
175+
return Err(io::Error::new(io::ErrorKind::InvalidInput,
176+
"cannot set a 0 duration timeout"));
177+
}
178+
179+
let mut timeout = if dur.secs() > (libc::DWORD::max_value() / 1000) as u64 {
180+
libc::DWORD::max_value()
181+
} else {
182+
(dur.secs() * 1000) as libc::DWORD
183+
};
184+
timeout = timeout.saturating_add((dur.extra_nanos() / 1000000) as libc::DWORD);
185+
if timeout == 0 {
186+
timeout = 1;
187+
}
188+
timeout
189+
}
190+
None => 0
191+
};
192+
setsockopt(socket, libc::SOL_SOCKET, kind, timeout)
193+
}
194+
195+
#[cfg(not(target_os = "windows"))]
196+
fn set_timeout(socket: &Socket, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
197+
let timeout = match dur {
198+
Some(dur) => {
199+
if dur.secs() == 0 && dur.extra_nanos() == 0 {
200+
return Err(io::Error::new(io::ErrorKind::InvalidInput,
201+
"cannot set a 0 duration timeout"));
202+
}
203+
204+
let secs = if dur.secs() > libc::time_t::max_value() as u64 {
205+
libc::time_t::max_value()
206+
} else {
207+
dur.secs() as libc::time_t
208+
};
209+
let mut timeout = libc::timeval {
210+
tv_sec: secs,
211+
tv_usec: (dur.extra_nanos() / 1000) as libc::time_t,
212+
};
213+
if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
214+
timeout.tv_usec = 1;
215+
}
216+
timeout
217+
}
218+
None => {
219+
libc::timeval {
220+
tv_sec: 0,
221+
tv_usec: 0,
222+
}
223+
}
224+
};
225+
setsockopt(socket, libc::SOL_SOCKET, kind, timeout)
226+
}
227+
228+
#[cfg(target_os = "windows")]
229+
fn timeout(socket: &Socket, kind: libc::c_int) -> io::Result<Option<Duration>> {
230+
let raw: libc::DWORD = try!(getsockopt(socket, libc::SOL_SOCKET, kind));
231+
if raw == 0 {
232+
Ok(None)
233+
} else {
234+
let secs = raw / 1000;
235+
let nsec = (raw % 1000) * 1000000;
236+
Ok(Some(Duration::new(secs as u64, nsec as u32)))
237+
}
238+
}
239+
240+
#[cfg(not(target_os = "windows"))]
241+
fn timeout(socket: &Socket, kind: libc::c_int) -> io::Result<Option<Duration>> {
242+
let raw: libc::timeval = try!(getsockopt(socket, libc::SOL_SOCKET, kind));
243+
if raw.tv_sec == 0 && raw.tv_usec == 0 {
244+
Ok(None)
245+
} else {
246+
let sec = raw.tv_sec as u64;
247+
let nsec = (raw.tv_usec as u32) * 1000;
248+
Ok(Some(Duration::new(sec, nsec)))
249+
}
250+
}
251+
166252
////////////////////////////////////////////////////////////////////////////////
167253
// TCP streams
168254
////////////////////////////////////////////////////////////////////////////////
@@ -220,6 +306,22 @@ impl TcpStream {
220306
Ok(())
221307
}
222308

309+
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
310+
set_timeout(&self.inner, dur, libc::SO_RCVTIMEO)
311+
}
312+
313+
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
314+
set_timeout(&self.inner, dur, libc::SO_SNDTIMEO)
315+
}
316+
317+
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
318+
timeout(&self.inner, libc::SO_RCVTIMEO)
319+
}
320+
321+
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
322+
timeout(&self.inner, libc::SO_SNDTIMEO)
323+
}
324+
223325
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
224326
self.inner.read(buf)
225327
}
@@ -471,6 +573,22 @@ impl UdpSocket {
471573
pub fn duplicate(&self) -> io::Result<UdpSocket> {
472574
self.inner.duplicate().map(|s| UdpSocket { inner: s })
473575
}
576+
577+
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
578+
set_timeout(&self.inner, dur, libc::SO_RCVTIMEO)
579+
}
580+
581+
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
582+
set_timeout(&self.inner, dur, libc::SO_SNDTIMEO)
583+
}
584+
585+
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
586+
timeout(&self.inner, libc::SO_RCVTIMEO)
587+
}
588+
589+
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
590+
timeout(&self.inner, libc::SO_SNDTIMEO)
591+
}
474592
}
475593

476594
impl FromInner<Socket> for UdpSocket {

0 commit comments

Comments
 (0)