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
This commit is contained in:
Sean McArthur
2015-06-16 11:02:36 -07:00
parent 421422b620
commit 7d1f154cb7
11 changed files with 311 additions and 50 deletions

View File

@@ -59,13 +59,16 @@ use std::default::Default;
use std::io::{self, copy, Read};
use std::iter::Extend;
#[cfg(feature = "timeouts")]
use std::time::Duration;
use url::UrlParser;
use url::ParseError as UrlError;
use header::{Headers, Header, HeaderFormat};
use header::{ContentLength, Location};
use method::Method;
use net::{NetworkConnector, NetworkStream};
use net::{NetworkConnector, NetworkStream, Fresh};
use {Url};
use Error;
@@ -87,7 +90,9 @@ pub struct Client {
protocol: Box<Protocol + Send + Sync>,
redirect_policy: RedirectPolicy,
#[cfg(feature = "timeouts")]
read_timeout: Option<Duration>
read_timeout: Option<Duration>,
#[cfg(feature = "timeouts")]
write_timeout: Option<Duration>,
}
impl Client {
@@ -108,11 +113,23 @@ impl Client {
Client::with_protocol(Http11Protocol::with_connector(connector))
}
#[cfg(not(feature = "timeouts"))]
/// Create a new client with a specific `Protocol`.
pub fn with_protocol<P: Protocol + Send + Sync + 'static>(protocol: P) -> Client {
Client {
protocol: Box::new(protocol),
redirect_policy: Default::default()
redirect_policy: Default::default(),
}
}
#[cfg(feature = "timeouts")]
/// Create a new client with a specific `Protocol`.
pub fn with_protocol<P: Protocol + Send + Sync + 'static>(protocol: P) -> Client {
Client {
protocol: Box::new(protocol),
redirect_policy: Default::default(),
read_timeout: None,
write_timeout: None,
}
}
@@ -127,6 +144,12 @@ impl Client {
self.read_timeout = dur;
}
/// Set the write timeout value for all requests.
#[cfg(feature = "timeouts")]
pub fn set_write_timeout(&mut self, dur: Option<Duration>) {
self.write_timeout = dur;
}
/// Build a Get request.
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
self.request(Method::Get, url)
@@ -236,6 +259,20 @@ impl<'a, U: IntoUrl> RequestBuilder<'a, U> {
let mut req = try!(Request::with_message(method.clone(), url.clone(), message));
headers.as_ref().map(|headers| req.headers_mut().extend(headers.iter()));
#[cfg(not(feature = "timeouts"))]
fn set_timeouts(_req: &mut Request<Fresh>, _client: &Client) -> ::Result<()> {
Ok(())
}
#[cfg(feature = "timeouts")]
fn set_timeouts(req: &mut Request<Fresh>, client: &Client) -> ::Result<()> {
try!(req.set_write_timeout(client.write_timeout));
try!(req.set_read_timeout(client.read_timeout));
Ok(())
}
try!(set_timeouts(&mut req, &client));
match (can_have_body, body.as_ref()) {
(true, Some(body)) => match body.size() {
Some(size) => req.headers_mut().set(ContentLength(size)),