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:
		| @@ -10,8 +10,9 @@ license = "MIT/Apache-2.0" | |||||||
| categories = ["web-programming::http-client"] | categories = ["web-programming::http-client"] | ||||||
|  |  | ||||||
| [dependencies] | [dependencies] | ||||||
| hyper = "0.10.2" | hyper = "0.10.12" | ||||||
| hyper-native-tls = "0.2.3" | hyper-native-tls = "0.2.4" | ||||||
|  | libc = "0.2" | ||||||
| log = "0.3" | log = "0.3" | ||||||
| serde = "1.0" | serde = "1.0" | ||||||
| serde_json = "1.0" | serde_json = "1.0" | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
|  | use std::net::TcpStream; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| use std::time::Duration; | use std::time::Duration; | ||||||
|  |  | ||||||
| @@ -9,7 +10,7 @@ use hyper::method::Method; | |||||||
| use hyper::status::StatusCode; | use hyper::status::StatusCode; | ||||||
| use hyper::Url; | use hyper::Url; | ||||||
|  |  | ||||||
| use hyper_native_tls::{NativeTlsClient, native_tls}; | use hyper_native_tls::{NativeTlsClient, TlsStream, native_tls}; | ||||||
|  |  | ||||||
| use body; | use body; | ||||||
| use redirect::{self, RedirectPolicy, check_redirect, remove_sensitive_headers}; | use redirect::{self, RedirectPolicy, check_redirect, remove_sensitive_headers}; | ||||||
| @@ -153,14 +154,8 @@ impl ClientBuilder { | |||||||
|             tls_client.danger_disable_hostname_verification(true); |             tls_client.danger_disable_hostname_verification(true); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let mut hyper_client = ::hyper::Client::with_connector( |         let mut hyper_client = create_hyper_client(tls_client); | ||||||
|             ::hyper::client::Pool::with_connector( |  | ||||||
|                 Default::default(), |  | ||||||
|                 ::hyper::net::HttpsConnector::new(tls_client), |  | ||||||
|             ) |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         hyper_client.set_redirect_policy(::hyper::client::RedirectPolicy::FollowNone); |  | ||||||
|         hyper_client.set_read_timeout(config.timeout); |         hyper_client.set_read_timeout(config.timeout); | ||||||
|         hyper_client.set_write_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 { | impl Client { | ||||||
|     /// Constructs a new `Client`. |     /// Constructs a new `Client`. | ||||||
|     /// |     /// | ||||||
|   | |||||||
| @@ -123,6 +123,7 @@ extern crate hyper; | |||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| extern crate log; | extern crate log; | ||||||
|  | extern crate libc; | ||||||
| extern crate libflate; | extern crate libflate; | ||||||
| extern crate hyper_native_tls; | extern crate hyper_native_tls; | ||||||
| extern crate serde; | extern crate serde; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user