Skip to content

Commit 7d1f154

Browse files
committed
feat(net): add socket timeouts to Server and Client
While these methods are marked unstable in libstd, this is behind a feature flag, `timeouts`. The Client and Server both have `set_read_timeout` and `set_write_timeout` methods, that will affect all connections with that entity. BREAKING CHANGE: Any custom implementation of NetworkStream must now implement `set_read_timeout` and `set_write_timeout`, so those will break. Most users who only use the provided streams should work with no changes needed. Closes #315
1 parent 421422b commit 7d1f154

File tree

11 files changed

+310
-49
lines changed

11 files changed

+310
-49
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,5 @@ env_logger = "*"
4747
default = ["ssl"]
4848
ssl = ["openssl", "cookie/secure"]
4949
serde-serialization = ["serde"]
50-
nightly = []
51-
50+
timeouts = []
51+
nightly = ["timeouts"]

src/client/mod.rs

+40-3
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,16 @@ use std::default::Default;
5959
use std::io::{self, copy, Read};
6060
use std::iter::Extend;
6161

62+
#[cfg(feature = "timeouts")]
63+
use std::time::Duration;
64+
6265
use url::UrlParser;
6366
use url::ParseError as UrlError;
6467

6568
use header::{Headers, Header, HeaderFormat};
6669
use header::{ContentLength, Location};
6770
use method::Method;
68-
use net::{NetworkConnector, NetworkStream};
71+
use net::{NetworkConnector, NetworkStream, Fresh};
6972
use {Url};
7073
use Error;
7174

@@ -87,7 +90,9 @@ pub struct Client {
8790
protocol: Box<Protocol + Send + Sync>,
8891
redirect_policy: RedirectPolicy,
8992
#[cfg(feature = "timeouts")]
90-
read_timeout: Option<Duration>
93+
read_timeout: Option<Duration>,
94+
#[cfg(feature = "timeouts")]
95+
write_timeout: Option<Duration>,
9196
}
9297

9398
impl Client {
@@ -108,11 +113,23 @@ impl Client {
108113
Client::with_protocol(Http11Protocol::with_connector(connector))
109114
}
110115

116+
#[cfg(not(feature = "timeouts"))]
117+
/// Create a new client with a specific `Protocol`.
118+
pub fn with_protocol<P: Protocol + Send + Sync + 'static>(protocol: P) -> Client {
119+
Client {
120+
protocol: Box::new(protocol),
121+
redirect_policy: Default::default(),
122+
}
123+
}
124+
125+
#[cfg(feature = "timeouts")]
111126
/// Create a new client with a specific `Protocol`.
112127
pub fn with_protocol<P: Protocol + Send + Sync + 'static>(protocol: P) -> Client {
113128
Client {
114129
protocol: Box::new(protocol),
115-
redirect_policy: Default::default()
130+
redirect_policy: Default::default(),
131+
read_timeout: None,
132+
write_timeout: None,
116133
}
117134
}
118135

@@ -127,6 +144,12 @@ impl Client {
127144
self.read_timeout = dur;
128145
}
129146

147+
/// Set the write timeout value for all requests.
148+
#[cfg(feature = "timeouts")]
149+
pub fn set_write_timeout(&mut self, dur: Option<Duration>) {
150+
self.write_timeout = dur;
151+
}
152+
130153
/// Build a Get request.
131154
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
132155
self.request(Method::Get, url)
@@ -236,6 +259,20 @@ impl<'a, U: IntoUrl> RequestBuilder<'a, U> {
236259
let mut req = try!(Request::with_message(method.clone(), url.clone(), message));
237260
headers.as_ref().map(|headers| req.headers_mut().extend(headers.iter()));
238261

262+
#[cfg(not(feature = "timeouts"))]
263+
fn set_timeouts(_req: &mut Request<Fresh>, _client: &Client) -> ::Result<()> {
264+
Ok(())
265+
}
266+
267+
#[cfg(feature = "timeouts")]
268+
fn set_timeouts(req: &mut Request<Fresh>, client: &Client) -> ::Result<()> {
269+
try!(req.set_write_timeout(client.write_timeout));
270+
try!(req.set_read_timeout(client.read_timeout));
271+
Ok(())
272+
}
273+
274+
try!(set_timeouts(&mut req, &client));
275+
239276
match (can_have_body, body.as_ref()) {
240277
(true, Some(body)) => match body.size() {
241278
Some(size) => req.headers_mut().set(ContentLength(size)),

src/client/pool.rs

+15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ use std::io::{self, Read, Write};
55
use std::net::{SocketAddr, Shutdown};
66
use std::sync::{Arc, Mutex};
77

8+
#[cfg(feature = "timeouts")]
9+
use std::time::Duration;
10+
811
use net::{NetworkConnector, NetworkStream, DefaultConnector};
912

1013
/// The `NetworkConnector` that behaves as a connection pool used by hyper's `Client`.
@@ -153,6 +156,18 @@ impl<S: NetworkStream> NetworkStream for PooledStream<S> {
153156
self.inner.as_mut().unwrap().1.peer_addr()
154157
}
155158

159+
#[cfg(feature = "timeouts")]
160+
#[inline]
161+
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
162+
self.inner.as_ref().unwrap().1.set_read_timeout(dur)
163+
}
164+
165+
#[cfg(feature = "timeouts")]
166+
#[inline]
167+
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
168+
self.inner.as_ref().unwrap().1.set_write_timeout(dur)
169+
}
170+
156171
#[inline]
157172
fn close(&mut self, how: Shutdown) -> io::Result<()> {
158173
self.is_closed = true;

src/client/request.rs

+17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
use std::marker::PhantomData;
33
use std::io::{self, Write};
44

5+
#[cfg(feature = "timeouts")]
6+
use std::time::Duration;
7+
58
use url::Url;
69

710
use method::{self, Method};
@@ -39,6 +42,20 @@ impl<W> Request<W> {
3942
/// Read the Request method.
4043
#[inline]
4144
pub fn method(&self) -> method::Method { self.method.clone() }
45+
46+
/// Set the write timeout.
47+
#[cfg(feature = "timeouts")]
48+
#[inline]
49+
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
50+
self.message.set_write_timeout(dur)
51+
}
52+
53+
/// Set the read timeout.
54+
#[cfg(feature = "timeouts")]
55+
#[inline]
56+
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
57+
self.message.set_read_timeout(dur)
58+
}
4259
}
4360

4461
impl Request<Fresh> {

src/http/h1.rs

+43-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::cmp::min;
44
use std::fmt;
55
use std::io::{self, Write, BufWriter, BufRead, Read};
66
use std::net::Shutdown;
7+
#[cfg(feature = "timeouts")]
8+
use std::time::Duration;
79

810
use httparse;
911

@@ -192,6 +194,19 @@ impl HttpMessage for Http11Message {
192194
})
193195
}
194196

197+
#[cfg(feature = "timeouts")]
198+
#[inline]
199+
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
200+
self.get_ref().set_read_timeout(dur)
201+
}
202+
203+
#[cfg(feature = "timeouts")]
204+
#[inline]
205+
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
206+
self.get_ref().set_write_timeout(dur)
207+
}
208+
209+
#[inline]
195210
fn close_connection(&mut self) -> ::Result<()> {
196211
try!(self.get_mut().close(Shutdown::Both));
197212
Ok(())
@@ -214,13 +229,27 @@ impl Http11Message {
214229

215230
/// Gets a mutable reference to the underlying `NetworkStream`, regardless of the state of the
216231
/// `Http11Message`.
217-
pub fn get_mut(&mut self) -> &mut Box<NetworkStream + Send> {
232+
pub fn get_ref(&self) -> &(NetworkStream + Send) {
218233
if self.stream.is_some() {
219-
self.stream.as_mut().unwrap()
234+
&**self.stream.as_ref().unwrap()
220235
} else if self.writer.is_some() {
221-
self.writer.as_mut().unwrap().get_mut().get_mut()
236+
&**self.writer.as_ref().unwrap().get_ref().get_ref()
222237
} else if self.reader.is_some() {
223-
self.reader.as_mut().unwrap().get_mut().get_mut()
238+
&**self.reader.as_ref().unwrap().get_ref().get_ref()
239+
} else {
240+
panic!("Http11Message lost its underlying stream somehow");
241+
}
242+
}
243+
244+
/// Gets a mutable reference to the underlying `NetworkStream`, regardless of the state of the
245+
/// `Http11Message`.
246+
pub fn get_mut(&mut self) -> &mut (NetworkStream + Send) {
247+
if self.stream.is_some() {
248+
&mut **self.stream.as_mut().unwrap()
249+
} else if self.writer.is_some() {
250+
&mut **self.writer.as_mut().unwrap().get_mut().get_mut()
251+
} else if self.reader.is_some() {
252+
&mut **self.reader.as_mut().unwrap().get_mut().get_mut()
224253
} else {
225254
panic!("Http11Message lost its underlying stream somehow");
226255
}
@@ -344,6 +373,16 @@ impl<R: Read> HttpReader<R> {
344373
}
345374
}
346375

376+
/// Gets a borrowed reference to the underlying Reader.
377+
pub fn get_ref(&self) -> &R {
378+
match *self {
379+
SizedReader(ref r, _) => r,
380+
ChunkedReader(ref r, _) => r,
381+
EofReader(ref r) => r,
382+
EmptyReader(ref r) => r,
383+
}
384+
}
385+
347386
/// Gets a mutable reference to the underlying Reader.
348387
pub fn get_mut(&mut self) -> &mut R {
349388
match *self {

src/http/h2.rs

+15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::io::{self, Write, Read, Cursor};
44
use std::net::Shutdown;
55
use std::ascii::AsciiExt;
66
use std::mem;
7+
#[cfg(feature = "timeouts")]
8+
use std::time::Duration;
79

810
use http::{
911
Protocol,
@@ -398,6 +400,19 @@ impl<S> HttpMessage for Http2Message<S> where S: CloneableStream {
398400
Ok(head)
399401
}
400402

403+
#[cfg(feature = "timeouts")]
404+
#[inline]
405+
fn set_read_timeout(&self, _dur: Option<Duration>) -> io::Result<()> {
406+
Ok(())
407+
}
408+
409+
#[cfg(feature = "timeouts")]
410+
#[inline]
411+
fn set_write_timeout(&self, _dur: Option<Duration>) -> io::Result<()> {
412+
Ok(())
413+
}
414+
415+
#[inline]
401416
fn close_connection(&mut self) -> ::Result<()> {
402417
Ok(())
403418
}

src/http/message.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
//! Defines the `HttpMessage` trait that serves to encapsulate the operations of a single
22
//! request-response cycle on any HTTP connection.
33
4-
use std::fmt::Debug;
54
use std::any::{Any, TypeId};
5+
use std::fmt::Debug;
66
use std::io::{Read, Write};
7-
87
use std::mem;
98

9+
#[cfg(feature = "timeouts")]
10+
use std::io;
11+
#[cfg(feature = "timeouts")]
12+
use std::time::Duration;
13+
1014
use typeable::Typeable;
1115

1216
use header::Headers;
@@ -62,7 +66,10 @@ pub trait HttpMessage: Write + Read + Send + Any + Typeable + Debug {
6266
fn get_incoming(&mut self) -> ::Result<ResponseHead>;
6367
/// Set the read timeout duration for this message.
6468
#[cfg(feature = "timeouts")]
65-
fn set_read_timeout(&self, dur: Option<Duration>) -> ::Result<()>;
69+
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()>;
70+
/// Set the write timeout duration for this message.
71+
#[cfg(feature = "timeouts")]
72+
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()>;
6673
/// Closes the underlying HTTP connection.
6774
fn close_connection(&mut self) -> ::Result<()>;
6875
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![cfg_attr(test, deny(missing_docs))]
33
#![cfg_attr(test, deny(warnings))]
44
#![cfg_attr(all(test, feature = "nightly"), feature(test))]
5+
#![cfg_attr(feature = "timeouts", feature(duration, socket_timeout))]
56

67
//! # Hyper
78
//!

0 commit comments

Comments
 (0)