From 00382f8d0681a210036ecb9c0d34a3a25654c096 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Tue, 6 Jun 2017 13:31:28 -0700 Subject: [PATCH] use a stale check in the pool Check that a socket is not EOF to reduce the likelihood of using a bad pooled connection. --- Cargo.toml | 5 +-- src/client.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++----- src/lib.rs | 1 + 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9366db5..15771a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,9 @@ license = "MIT/Apache-2.0" categories = ["web-programming::http-client"] [dependencies] -hyper = "0.10.2" -hyper-native-tls = "0.2.3" +hyper = "0.10.12" +hyper-native-tls = "0.2.4" +libc = "0.2" log = "0.3" serde = "1.0" serde_json = "1.0" diff --git a/src/client.rs b/src/client.rs index 1ead2bb..c815865 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::net::TcpStream; use std::sync::Arc; use std::time::Duration; @@ -9,7 +10,7 @@ use hyper::method::Method; use hyper::status::StatusCode; use hyper::Url; -use hyper_native_tls::{NativeTlsClient, native_tls}; +use hyper_native_tls::{NativeTlsClient, TlsStream, native_tls}; use body; use redirect::{self, RedirectPolicy, check_redirect, remove_sensitive_headers}; @@ -153,14 +154,8 @@ impl ClientBuilder { tls_client.danger_disable_hostname_verification(true); } - let mut hyper_client = ::hyper::Client::with_connector( - ::hyper::client::Pool::with_connector( - Default::default(), - ::hyper::net::HttpsConnector::new(tls_client), - ) - ); + let mut hyper_client = create_hyper_client(tls_client); - hyper_client.set_redirect_policy(::hyper::client::RedirectPolicy::FollowNone); hyper_client.set_read_timeout(config.timeout); hyper_client.set_write_timeout(config.timeout); @@ -256,6 +251,88 @@ impl ClientBuilder { } } +fn create_hyper_client(tls_client: NativeTlsClient) -> ::hyper::Client { + let mut pool = ::hyper::client::Pool::with_connector( + Default::default(), + ::hyper::net::HttpsConnector::new(tls_client), + ); + // For now, while experiementing, they're constants. + // TODO: maybe make these configurable someday? + pool.set_idle_timeout(Some(Duration::from_secs(60 * 2))); + pool.set_stale_check(|mut check| { + if stream_dead(check.stream()) { + check.stale() + } else { + check.fresh() + } + }); + + let mut hyper_client = ::hyper::Client::with_connector(pool); + + hyper_client.set_redirect_policy(::hyper::client::RedirectPolicy::FollowNone); + hyper_client +} + +fn stream_dead(stream: &::hyper::net::HttpsStream>) -> bool { + match *stream { + ::hyper::net::HttpsStream::Http(ref http) => socket_is_dead(&http.0), + ::hyper::net::HttpsStream::Https(ref https) => socket_is_dead(&https.lock().get_ref().0), + } +} + +#[cfg(unix)] +fn socket_is_dead(socket: &TcpStream) -> bool { + use std::mem; + use std::os::unix::io::AsRawFd; + use std::ptr; + use libc::{FD_SET, select, timeval}; + + let ret = unsafe { + let fd = socket.as_raw_fd(); + let nfds = fd + 1; + + let mut timeout = timeval { + tv_sec: 0, + tv_usec: 0, + }; + + let mut readfs = mem::zeroed(); + let mut errfs = mem::zeroed(); + FD_SET(fd, &mut readfs); + FD_SET(fd, &mut errfs); + select(nfds, &mut readfs, ptr::null_mut(), &mut errfs, &mut timeout) + }; + + // socket was readable (eof), or an error, then it's dead + ret != 0 +} + +#[cfg(windows)] +fn socket_is_dead(socket: &TcpStream) -> bool { + use std::mem; + use std::os::windows::io::AsRawSocket; + use std::ptr; + use libc::{FD_SET, select, timeval}; + + let ret = unsafe { + let fd = socket.as_raw_socket(); + let nfds = 0; // msdn says nfds is ignored + let timeout = timeval { + tv_sec: 0, + tv_usec: 0, + }; + + let mut readfs = mem::zeroed(); + let mut errfs = mem::zeroed(); + FD_SET(fd, &mut readfs); + FD_SET(fd, &mut errfs); + select(nfds, &mut readfs, ptr::null_mut(), &mut errfs, &mut timeout) + }; + + // socket was readable (eof), or an error, then it's dead + ret != 0 +} + impl Client { /// Constructs a new `Client`. /// diff --git a/src/lib.rs b/src/lib.rs index c5c6d56..66b0e59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,6 +123,7 @@ extern crate hyper; #[macro_use] extern crate log; +extern crate libc; extern crate libflate; extern crate hyper_native_tls; extern crate serde;