feat(client): Client will retry requests on fresh connections

If a request sees an error on a pooled connection before ever writing
any bytes, it will now retry with a new connection.

This can be configured with `Config::retry_canceled_requests(bool)`.
This commit is contained in:
Sean McArthur
2018-02-15 12:04:58 -08:00
parent 0ea3bcf8d5
commit ee61ea9adf
6 changed files with 234 additions and 124 deletions

View File

@@ -32,7 +32,7 @@ pub struct Server<S: Service> {
}
pub struct Client<B> {
callback: Option<oneshot::Sender<::Result<::Response>>>,
callback: Option<oneshot::Sender<Result<::Response, (::Error, Option<ClientMsg<B>>)>>>,
rx: ClientRx<B>,
}
@@ -398,12 +398,13 @@ where
},
Err(err) => {
if let Some(cb) = self.callback.take() {
let _ = cb.send(Err(err));
let _ = cb.send(Err((err, None)));
Ok(())
} else if let Ok(Async::Ready(Some((_, cb)))) = self.rx.poll() {
} else if let Ok(Async::Ready(Some((req, cb)))) = self.rx.poll() {
trace!("canceling queued request with connection error: {}", err);
// in this case, the message was never even started, so it's safe to tell
// the user that the request was completely canceled
let _ = cb.send(Err(::Error::new_canceled(Some(err))));
let _ = cb.send(Err((::Error::new_canceled(Some(err)), Some(req))));
Ok(())
} else {
Err(err)

View File

@@ -105,6 +105,8 @@ impl<B> Request<B> {
/// protected by TLS.
#[inline]
pub fn set_proxy(&mut self, is_proxy: bool) { self.is_proxy = is_proxy; }
pub(crate) fn is_proxy(&self) -> bool { self.is_proxy }
}
impl Request<Body> {
@@ -165,16 +167,9 @@ impl<B> From<http::Request<B>> for Request<B> {
/// Constructs a request using a received ResponseHead and optional body
pub fn from_wire(addr: Option<SocketAddr>, incoming: RequestHead, body: Option<Body>) -> Request<Body> {
let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming;
Request {
method: method,
uri: uri,
headers: headers,
version: version,
remote_addr: addr,
body: body,
is_proxy: false,
..join((incoming, body))
}
}
@@ -192,6 +187,20 @@ pub fn split<B>(req: Request<B>) -> (RequestHead, Option<B>) {
(head, req.body)
}
pub fn join<B>((head, body): (RequestHead, Option<B>)) -> Request<B> {
let MessageHead { version, subject: RequestLine(method, uri), headers } = head;
Request {
method: method,
uri: uri,
headers: headers,
version: version,
remote_addr: None,
body: body,
is_proxy: false,
}
}
pub fn addr<B>(req: &mut Request<B>, addr: SocketAddr) {
req.remote_addr = Some(addr);
}