Skip to content

Commit ab5e7d1

Browse files
authored
Merge pull request #311 from clue-labs/cancel-happy
Fix cancelling happy eyeballs when IPv6 resolution is pending
2 parents 0cd247c + 3f4a3c8 commit ab5e7d1

File tree

2 files changed

+14
-11
lines changed

2 files changed

+14
-11
lines changed

src/HappyEyeBallsConnectionBuilder.php

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,8 @@ public function __construct(LoopInterface $loop, ConnectorInterface $connector,
6565

6666
public function connect()
6767
{
68-
$timer = null;
6968
$that = $this;
70-
return new Promise\Promise(function ($resolve, $reject) use ($that, &$timer) {
69+
return new Promise\Promise(function ($resolve, $reject) use ($that) {
7170
$lookupResolve = function ($type) use ($that, $resolve, $reject) {
7271
return function (array $ips) use ($that, $type, $resolve, $reject) {
7372
unset($that->resolverPromises[$type]);
@@ -83,36 +82,36 @@ public function connect()
8382
};
8483

8584
$that->resolverPromises[Message::TYPE_AAAA] = $that->resolve(Message::TYPE_AAAA, $reject)->then($lookupResolve(Message::TYPE_AAAA));
86-
$that->resolverPromises[Message::TYPE_A] = $that->resolve(Message::TYPE_A, $reject)->then(function (array $ips) use ($that, &$timer) {
85+
$that->resolverPromises[Message::TYPE_A] = $that->resolve(Message::TYPE_A, $reject)->then(function (array $ips) use ($that) {
8786
// happy path: IPv6 has resolved already (or could not resolve), continue with IPv4 addresses
8887
if ($that->resolved[Message::TYPE_AAAA] === true || !$ips) {
8988
return $ips;
9089
}
9190

9291
// Otherwise delay processing IPv4 lookup until short timer passes or IPv6 resolves in the meantime
93-
$deferred = new Promise\Deferred();
92+
$deferred = new Promise\Deferred(function () use (&$ips) {
93+
// discard all IPv4 addresses if cancelled
94+
$ips = array();
95+
});
9496
$timer = $that->loop->addTimer($that::RESOLUTION_DELAY, function () use ($deferred, $ips) {
9597
$deferred->resolve($ips);
9698
});
9799

98-
$that->resolverPromises[Message::TYPE_AAAA]->then(function () use ($that, $timer, $deferred, $ips) {
100+
$that->resolverPromises[Message::TYPE_AAAA]->then(function () use ($that, $timer, $deferred, &$ips) {
99101
$that->loop->cancelTimer($timer);
100102
$deferred->resolve($ips);
101103
});
102104

103105
return $deferred->promise();
104106
})->then($lookupResolve(Message::TYPE_A));
105-
}, function ($_, $reject) use ($that, &$timer) {
107+
}, function ($_, $reject) use ($that) {
106108
$reject(new \RuntimeException(
107109
'Connection to ' . $that->uri . ' cancelled' . (!$that->connectionPromises ? ' during DNS lookup' : '') . ' (ECONNABORTED)',
108110
\defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
109111
));
110112
$_ = $reject = null;
111113

112114
$that->cleanUp();
113-
if ($timer instanceof TimerInterface) {
114-
$that->loop->cancelTimer($timer);
115-
}
116115
});
117116
}
118117

@@ -247,13 +246,15 @@ public function cleanUp()
247246
// clear list of outstanding IPs to avoid creating new connections
248247
$this->connectQueue = array();
249248

249+
// cancel pending connection attempts
250250
foreach ($this->connectionPromises as $connectionPromise) {
251251
if ($connectionPromise instanceof PromiseInterface && \method_exists($connectionPromise, 'cancel')) {
252252
$connectionPromise->cancel();
253253
}
254254
}
255255

256-
foreach ($this->resolverPromises as $resolverPromise) {
256+
// cancel pending DNS resolution (cancel IPv4 first in case it is awaiting IPv6 resolution delay)
257+
foreach (\array_reverse($this->resolverPromises) as $resolverPromise) {
257258
if ($resolverPromise instanceof PromiseInterface && \method_exists($resolverPromise, 'cancel')) {
258259
$resolverPromise->cancel();
259260
}

tests/HappyEyeBallsConnectionBuilderTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,9 @@ public function testCancelConnectWillRejectPromiseAndCancelPendingIpv6LookupAndC
695695
array('reactphp.org', Message::TYPE_AAAA),
696696
array('reactphp.org', Message::TYPE_A)
697697
)->willReturnOnConsecutiveCalls(
698-
new Promise(function () { }, $this->expectCallableOnce()),
698+
new Promise(function () { }, function () {
699+
throw new \RuntimeException('DNS cancelled');
700+
}),
699701
\React\Promise\resolve(array('127.0.0.1'))
700702
);
701703

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy