Skip to content

Commit 5e3b43a

Browse files
committed
fix(client): prevent a checkout loop of pooled connections that aren't ready yet
1 parent 11c92ff commit 5e3b43a

File tree

4 files changed

+30
-29
lines changed

4 files changed

+30
-29
lines changed

src/client/dispatch.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl<T, U> Sender<T, U> {
107107

108108
impl<T, U> UnboundedSender<T, U> {
109109
pub fn is_ready(&self) -> bool {
110-
self.giver.is_wanting()
110+
!self.giver.is_canceled()
111111
}
112112

113113
pub fn is_closed(&self) -> bool {

src/client/mod.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,15 @@ where C: Connect + Sync + 'static,
318318
Ok(())
319319
})
320320
);
321+
} else {
322+
// There's no body to delay, but the connection isn't
323+
// ready yet. Only re-insert when it's ready
324+
executor.execute(
325+
future::poll_fn(move || {
326+
pooled.poll_ready()
327+
})
328+
.then(|_| Ok(()))
329+
);
321330
}
322331
Ok(res)
323332
});
@@ -441,6 +450,13 @@ impl<B> PoolClient<B> {
441450
PoolTx::Http2(ref tx) => tx.is_ready(),
442451
}
443452
}
453+
454+
fn is_closed(&self) -> bool {
455+
match self.tx {
456+
PoolTx::Http1(ref tx) => tx.is_closed(),
457+
PoolTx::Http2(ref tx) => tx.is_closed(),
458+
}
459+
}
444460
}
445461

446462
impl<B: Payload + 'static> PoolClient<B> {
@@ -460,10 +476,10 @@ impl<B> Poolable for PoolClient<B>
460476
where
461477
B: 'static,
462478
{
463-
fn is_closed(&self) -> bool {
479+
fn is_open(&self) -> bool {
464480
match self.tx {
465-
PoolTx::Http1(ref tx) => tx.is_closed(),
466-
PoolTx::Http2(ref tx) => tx.is_closed(),
481+
PoolTx::Http1(ref tx) => tx.is_ready(),
482+
PoolTx::Http2(ref tx) => tx.is_ready(),
467483
}
468484
}
469485

src/client/pool.rs

+9-24
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub(super) struct Pool<T> {
2121
//
2222
// See https://github.com/hyperium/hyper/issues/1429
2323
pub(super) trait Poolable: Sized {
24-
fn is_closed(&self) -> bool;
24+
fn is_open(&self) -> bool;
2525
/// Reserve this connection.
2626
///
2727
/// Allows for HTTP/2 to return a shared reservation.
@@ -236,7 +236,7 @@ impl<'a, T: Poolable + 'a> IdlePopper<'a, T> {
236236
while let Some(entry) = self.list.pop() {
237237
// If the connection has been closed, or is older than our idle
238238
// timeout, simply drop it and keep looking...
239-
if entry.value.is_closed() {
239+
if !entry.value.is_open() {
240240
trace!("removing closed connection for {:?}", self.key);
241241
continue;
242242
}
@@ -377,7 +377,7 @@ impl<T: Poolable> PoolInner<T> {
377377

378378
self.idle.retain(|key, values| {
379379
values.retain(|entry| {
380-
if entry.value.is_closed() {
380+
if !entry.value.is_open() {
381381
trace!("idle interval evicting closed for {:?}", key);
382382
return false;
383383
}
@@ -475,7 +475,7 @@ impl<T: Poolable> DerefMut for Pooled<T> {
475475
impl<T: Poolable> Drop for Pooled<T> {
476476
fn drop(&mut self) {
477477
if let Some(value) = self.value.take() {
478-
if value.is_closed() {
478+
if !value.is_open() {
479479
// If we *already* know the connection is done here,
480480
// it shouldn't be re-inserted back into the pool.
481481
return;
@@ -519,7 +519,7 @@ impl<T: Poolable> Checkout<T> {
519519
if let Some(mut rx) = self.waiter.take() {
520520
match rx.poll() {
521521
Ok(Async::Ready(value)) => {
522-
if !value.is_closed() {
522+
if value.is_open() {
523523
Ok(Async::Ready(Some(self.pool.reuse(&self.key, value))))
524524
} else {
525525
Err(::Error::new_canceled(Some(CANCELED)))
@@ -662,30 +662,15 @@ mod tests {
662662
struct Uniq<T>(T);
663663

664664
impl<T> Poolable for Uniq<T> {
665-
fn is_closed(&self) -> bool {
666-
false
665+
fn is_open(&self) -> bool {
666+
true
667667
}
668668

669669
fn reserve(self) -> Reservation<Self> {
670670
Reservation::Unique(self)
671671
}
672672
}
673673

674-
/*
675-
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
676-
struct Share<T>(T);
677-
678-
impl<T> Poolable for Share<T> {
679-
fn is_closed(&self) -> bool {
680-
false
681-
}
682-
683-
fn reserve(self) -> Reservation<Self> {
684-
Reservation::Shared(self.clone(), self)
685-
}
686-
}
687-
*/
688-
689674
fn c<T: Poolable>(key: Key) -> Connecting<T> {
690675
Connecting {
691676
key,
@@ -817,8 +802,8 @@ mod tests {
817802
}
818803

819804
impl Poolable for CanClose {
820-
fn is_closed(&self) -> bool {
821-
self.closed
805+
fn is_open(&self) -> bool {
806+
!self.closed
822807
}
823808

824809
fn reserve(self) -> Reservation<Self> {

src/client/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ fn conn_reset_after_write() {
8888
}
8989

9090
// sleep to allow some time for the connection to return to the pool
91-
thread::sleep(Duration::from_secs(1));
91+
thread::sleep(Duration::from_millis(50));
9292

9393
let req = Request::builder()
9494
.uri("http://mock.local/a")

0 commit comments

Comments
 (0)