Skip to content

Commit 4265044

Browse files
committed
Support cancellation for RetryExecutor
1 parent bbdde98 commit 4265044

2 files changed

Lines changed: 49 additions & 15 deletions

File tree

src/Query/RetryExecutor.php

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,28 @@ public function __construct(ExecutorInterface $executor, $retries = 2)
1717

1818
public function query($nameserver, Query $query)
1919
{
20-
$deferred = new Deferred();
21-
22-
$this->tryQuery($nameserver, $query, $this->retries, $deferred);
23-
24-
return $deferred->promise();
20+
return $this->tryQuery($nameserver, $query, $this->retries);
2521
}
2622

27-
public function tryQuery($nameserver, Query $query, $retries, $deferred)
23+
public function tryQuery($nameserver, Query $query, $retries)
2824
{
2925
$that = $this;
30-
$errorback = function ($error) use ($nameserver, $query, $retries, $deferred, $that) {
26+
$errorback = function ($error) use ($nameserver, $query, $retries, $that) {
3127
if (!$error instanceof TimeoutException) {
32-
$deferred->reject($error);
33-
return;
28+
throw $error;
3429
}
3530
if (0 >= $retries) {
36-
$error = new \RuntimeException(
31+
throw new \RuntimeException(
3732
sprintf("DNS query for %s failed: too many retries", $query->name),
3833
0,
3934
$error
4035
);
41-
$deferred->reject($error);
42-
return;
4336
}
44-
$that->tryQuery($nameserver, $query, $retries-1, $deferred);
37+
return $that->tryQuery($nameserver, $query, $retries-1);
4538
};
4639

47-
$this->executor
40+
return $this->executor
4841
->query($nameserver, $query)
49-
->then(array($deferred, 'resolve'), $errorback);
42+
->then(null, $errorback);
5043
}
5144
}

tests/Query/RetryExecutorTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use React\Dns\Query\TimeoutException;
1010
use React\Dns\Model\Record;
1111
use React\Promise;
12+
use React\Promise\Deferred;
13+
use React\Dns\Query\CancellationException;
1214

1315
class RetryExecutorTest extends TestCase
1416
{
@@ -125,6 +127,45 @@ public function queryShouldForwardNonTimeoutErrors()
125127
$retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback);
126128
}
127129

130+
/**
131+
* @covers React\Dns\Query\RetryExecutor
132+
* @test
133+
*/
134+
public function queryShouldCancelQueryOnCancel()
135+
{
136+
if (!interface_exists('React\Promise\CancellablePromiseInterface')) {
137+
$this->markTestSkipped('Skipped missing CancellablePromiseInterface');
138+
}
139+
140+
$cancelled = 0;
141+
142+
$executor = $this->createExecutorMock();
143+
$executor
144+
->expects($this->once())
145+
->method('query')
146+
->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query'))
147+
->will($this->returnCallback(function ($domain, $query) use (&$cancelled) {
148+
$deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) {
149+
++$cancelled;
150+
$reject(new CancellationException('Cancelled'));
151+
});
152+
153+
return $deferred->promise();
154+
})
155+
);
156+
157+
$retryExecutor = new RetryExecutor($executor, 2);
158+
159+
$query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
160+
$promise = $retryExecutor->query('8.8.8.8', $query);
161+
162+
$promise->then($this->expectCallableNever(), $this->expectCallableOnce());
163+
164+
$this->assertEquals(0, $cancelled);
165+
$promise->cancel();
166+
$this->assertEquals(1, $cancelled);
167+
}
168+
128169
protected function expectCallableOnce()
129170
{
130171
$mock = $this->createCallableMock();

0 commit comments

Comments
 (0)