Skip to content

Commit bbdde98

Browse files
committed
Pending query can now be cancel()'led
1 parent 62f6201 commit bbdde98

3 files changed

Lines changed: 54 additions & 1 deletion

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace React\Dns\Query;
4+
5+
class CancellationException extends \RuntimeException
6+
{
7+
}

src/Query/Executor.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,12 @@ public function doQuery($nameserver, $transport, $queryData, $name)
5353
$loop = $this->loop;
5454

5555
$response = new Message();
56-
$deferred = new Deferred();
56+
$deferred = new Deferred(function ($resolve, $reject) use (&$timer, &$conn, $name) {
57+
$reject(new CancellationException(sprintf('DNS query for %s has been cancelled', $name)));
58+
59+
$timer->cancel();
60+
$conn->close();
61+
});
5762

5863
$retryWithTcp = function () use ($that, $nameserver, $queryData, $name) {
5964
return $that->doQuery($nameserver, 'tcp', $queryData, $name);

tests/Query/ExecutorTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,47 @@ public function resolveShouldCreateTcpRequestIfRequestIsLargerThan512Bytes()
6161
$this->executor->query('8.8.8.8:53', $query, function () {}, function () {});
6262
}
6363

64+
/** @test */
65+
public function resolveShouldCloseConnectionWhenCancelled()
66+
{
67+
if (!interface_exists('React\Promise\CancellablePromiseInterface')) {
68+
$this->markTestSkipped('Skipped missing CancellablePromiseInterface');
69+
}
70+
71+
$conn = $this->createConnectionMock();
72+
$conn->expects($this->once())->method('close');
73+
74+
$timer = $this->getMock('React\EventLoop\Timer\TimerInterface');
75+
$this->loop
76+
->expects($this->any())
77+
->method('addTimer')
78+
->will($this->returnValue($timer));
79+
80+
$this->executor = $this->createExecutorMock();
81+
$this->executor
82+
->expects($this->once())
83+
->method('createConnection')
84+
->with('8.8.8.8:53', 'udp')
85+
->will($this->returnValue($conn));
86+
87+
$query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
88+
$promise = $this->executor->query('8.8.8.8:53', $query);
89+
90+
$promise->cancel();
91+
92+
$errorback = $this->createCallableMock();
93+
$errorback
94+
->expects($this->once())
95+
->method('__invoke')
96+
->with($this->logicalAnd(
97+
$this->isInstanceOf('React\Dns\Query\CancellationException'),
98+
$this->attribute($this->equalTo('DNS query for igor.io has been cancelled'), 'message')
99+
)
100+
);
101+
102+
$promise->then($this->expectCallableNever(), $errorback);
103+
}
104+
64105
/** @test */
65106
public function resolveShouldRetryWithTcpIfResponseIsTruncated()
66107
{

0 commit comments

Comments
 (0)