use a stale check in the pool

Check that a socket is not EOF to reduce the likelihood of using
a bad pooled connection.
This commit is contained in:
Sean McArthur
2017-06-06 13:31:28 -07:00
parent 9c258cc7e9
commit 00382f8d06
3 changed files with 89 additions and 10 deletions

View File

@@ -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"

View File

@@ -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<TlsStream<::hyper::net::HttpStream>>) -> 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`.
///

View File

@@ -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;