fix(client): detect connection closes as pool tries to use

Currently, if the remote closes the connection at the same time that the
pool selects it to use for a new request, the connection may actually
hang. This fix will now more allow the keep-alive read to check the
socket even when the `Conn` think it's busy.

If the connection was closed before the request write happened, returns
back an `Error::Cancel`, letting the user know they could safely retry
it.

Closes #1439
This commit is contained in:
Sean McArthur
2018-02-12 13:27:50 -08:00
parent a9413d7367
commit dc619a8fa0
6 changed files with 167 additions and 94 deletions

View File

@@ -60,9 +60,9 @@ pub enum Error {
}
impl Error {
pub(crate) fn new_canceled() -> Error {
pub(crate) fn new_canceled(cause: Option<Error>) -> Error {
Error::Cancel(Canceled {
_inner: (),
cause: cause.map(Box::new),
})
}
}
@@ -73,10 +73,9 @@ impl Error {
/// as the related connection gets closed by the remote. In that case,
/// when the connection drops, the pending response future will be
/// fulfilled with this error, signaling the `Request` was never started.
#[derive(Debug)]
pub struct Canceled {
// maybe in the future this contains an optional value of
// what was canceled?
_inner: (),
cause: Option<Box<Error>>,
}
impl Canceled {
@@ -85,13 +84,6 @@ impl Canceled {
}
}
impl fmt::Debug for Canceled {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Canceled")
.finish()
}
}
impl fmt::Display for Canceled {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(self.description())
@@ -142,6 +134,7 @@ impl StdError for Error {
Io(ref error) => Some(error),
Uri(ref error) => Some(error),
Utf8(ref error) => Some(error),
Cancel(ref e) => e.cause.as_ref().map(|e| &**e as &StdError),
Error::__Nonexhaustive(..) => unreachable!(),
_ => None,
}