Skip to content

Commit d3e9699

Browse files
authored
fix: erroneous retries on a failed request to a newly opened socket (#150)
The legacy pool client will in certain circumstances try other connections if it it notices an error just as it's trying to send a request. There is also some code that prevents retrying forever, such as if it is a new connection, since it likely won't ever work then. However, there was a bug that this fixes, where if a new connection was successfully created, but _then_ errored when trying to send the request, it would consider retrying that. This could end up in a loop if the server accepts and then errors all connections. The fix is to re-introduce tracking whether this connection has been successfully used once all the way through, "reused", and if it hasn't, abort any retrying.
1 parent 2639193 commit d3e9699

File tree

1 file changed

+12
-25
lines changed

1 file changed

+12
-25
lines changed

src/client/legacy/client.rs

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ macro_rules! e {
9292
type PoolKey = (http::uri::Scheme, http::uri::Authority);
9393

9494
enum TrySendError<B> {
95-
Retryable { error: Error, req: Request<B> },
95+
Retryable {
96+
error: Error,
97+
req: Request<B>,
98+
connection_reused: bool,
99+
},
96100
Nope(Error),
97101
}
98102

@@ -244,8 +248,12 @@ where
244248
req = match self.try_send_request(req, pool_key.clone()).await {
245249
Ok(resp) => return Ok(resp),
246250
Err(TrySendError::Nope(err)) => return Err(err),
247-
Err(TrySendError::Retryable { mut req, error }) => {
248-
if !self.config.retry_canceled_requests {
251+
Err(TrySendError::Retryable {
252+
mut req,
253+
error,
254+
connection_reused,
255+
}) => {
256+
if !self.config.retry_canceled_requests || !connection_reused {
249257
// if client disabled, don't retry
250258
// a fresh connection means we definitely can't retry
251259
return Err(error);
@@ -317,6 +325,7 @@ where
317325
Err(mut err) => {
318326
return if let Some(req) = err.take_message() {
319327
Err(TrySendError::Retryable {
328+
connection_reused: pooled.is_reused(),
320329
error: e!(Canceled, err.into_error())
321330
.with_connect_info(pooled.conn_info.clone()),
322331
req,
@@ -329,8 +338,6 @@ where
329338
}
330339
}
331340
};
332-
//.send_request_retryable(req)
333-
//.map_err(ClientError::map_with_reused(pooled.is_reused()));
334341

335342
// If the Connector included 'extra' info, add to Response...
336343
if let Some(extra) = &pooled.conn_info.extra {
@@ -803,26 +810,6 @@ impl<B: Body + 'static> PoolClient<B> {
803810
PoolTx::Http2(ref mut tx) => tx.try_send_request(req),
804811
};
805812
}
806-
807-
/*
808-
//TODO: can we re-introduce this somehow? Or must people use tower::retry?
809-
fn send_request_retryable(
810-
&mut self,
811-
req: Request<B>,
812-
) -> impl Future<Output = Result<Response<hyper::body::Incoming>, (Error, Option<Request<B>>)>>
813-
where
814-
B: Send,
815-
{
816-
match self.tx {
817-
#[cfg(not(feature = "http2"))]
818-
PoolTx::Http1(ref mut tx) => tx.send_request_retryable(req),
819-
#[cfg(feature = "http1")]
820-
PoolTx::Http1(ref mut tx) => Either::Left(tx.send_request_retryable(req)),
821-
#[cfg(feature = "http2")]
822-
PoolTx::Http2(ref mut tx) => Either::Right(tx.send_request_retryable(req)),
823-
}
824-
}
825-
*/
826813
}
827814

828815
impl<B> pool::Poolable for PoolClient<B>

0 commit comments

Comments
 (0)