Skip to content

Commit 880c73e

Browse files
committed
Merge pull request #12 from clue-labs/cancel
Support promise cancellation (cancellation of underlying DNS lookup)
2 parents f052439 + 444d9c2 commit 880c73e

3 files changed

Lines changed: 44 additions & 2 deletions

File tree

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"evenement/evenement": "~1.0|~2.0",
1919
"react/event-loop": ">=0.2, <0.5",
2020
"react/dns": ">=0.2, <0.5",
21-
"react/promise": "~2.0|~1.1"
21+
"react/promise": "~2.1|~1.2"
2222
},
2323
"require-dev": {
2424
"clue/block-react": "~1.0"

src/Factory.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use React\Promise;
88
use React\Datagram\Socket;
99
use \Exception;
10+
use React\Promise\CancellablePromiseInterface;
1011

1112
class Factory
1213
{
@@ -108,6 +109,23 @@ protected function resolveHost($host)
108109
return Promise\reject(new Exception('No resolver given in order to get IP address for given hostname'));
109110
}
110111

111-
return $this->resolver->resolve($host);
112+
$promise = $this->resolver->resolve($host);
113+
114+
// wrap DNS lookup in order to control cancellation behavior
115+
return new Promise\Promise(
116+
function ($resolve, $reject) use ($promise) {
117+
// forward promise resolution
118+
$promise->then($resolve, $reject);
119+
},
120+
function ($_, $reject) use ($promise) {
121+
// reject with custom message once cancelled
122+
$reject(new \RuntimeException('Cancelled creating socket during DNS lookup'));
123+
124+
// (try to) cancel pending DNS lookup, otherwise ignoring its results
125+
if ($promise instanceof CancellablePromiseInterface) {
126+
$promise->cancel();
127+
}
128+
}
129+
);
112130
}
113131
}

tests/FactoryTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,28 @@ public function testCreateServerWithInvalidHostnameWillReject()
119119
{
120120
Block\await($this->factory->createServer('/////'), $this->loop);
121121
}
122+
123+
public function testCancelCreateClientWithCancellableHostnameResolver()
124+
{
125+
$promise = new Promise\Promise(function () { }, $this->expectCallableOnce());
126+
$this->resolver->expects($this->once())->method('resolve')->with('example.com')->willReturn($promise);
127+
128+
$promise = $this->factory->createClient('example.com:0');
129+
$promise->cancel();
130+
131+
$this->setExpectedException('RuntimeException');
132+
Block\await($promise, $this->loop);
133+
}
134+
135+
public function testCancelCreateClientWithUncancellableHostnameResolver()
136+
{
137+
$promise = $this->getMock('React\Promise\PromiseInterface');
138+
$this->resolver->expects($this->once())->method('resolve')->with('example.com')->willReturn($promise);
139+
140+
$promise = $this->factory->createClient('example.com:0');
141+
$promise->cancel();
142+
143+
$this->setExpectedException('RuntimeException');
144+
Block\await($promise, $this->loop);
145+
}
122146
}

0 commit comments

Comments
 (0)