Skip to content

Commit 3686e51

Browse files
committed
Improve error reporting when TCP/IP connection fails (happy eyeballs)
1 parent ddd91a2 commit 3686e51

File tree

3 files changed

+42
-4
lines changed

3 files changed

+42
-4
lines changed

src/HappyEyeBallsConnectionBuilder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public function check($resolve, $reject)
152152
$that->cleanUp();
153153

154154
$resolve($connection);
155-
}, function () use ($that, $ip, $reject) {
155+
}, function (\Exception $e) use ($that, $ip, $reject) {
156156
unset($that->connectionPromises[$ip]);
157157

158158
$that->failureCount++;
@@ -164,7 +164,7 @@ public function check($resolve, $reject)
164164
if ($that->ipsCount === $that->failureCount) {
165165
$that->cleanUp();
166166

167-
$reject(new \RuntimeException('All attempts to connect to "' . $that->host . '" have failed'));
167+
$reject(new \RuntimeException('Connection to ' . $that->uri . ' failed: ' . $e->getMessage()));
168168
}
169169
});
170170

tests/HappyEyeBallsConnectionBuilderTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,44 @@ public function testConnectWillStartAndCancelResolutionTimerAndStartAttemptTimer
293293
$deferred->resolve(array('::1'));
294294
}
295295

296+
public function testConnectWillRejectWhenOnlyTcpConnectionRejectsAndCancelNextAttemptTimerImmediately()
297+
{
298+
$timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
299+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
300+
$loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
301+
$loop->expects($this->once())->method('cancelTimer')->with($timer);
302+
303+
$deferred = new Deferred();
304+
$connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
305+
$connector->expects($this->once())->method('connect')->with('tcp://[::1]:80?hostname=reactphp.org')->willReturn($deferred->promise());
306+
307+
$resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
308+
$resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
309+
array('reactphp.org', Message::TYPE_AAAA),
310+
array('reactphp.org', Message::TYPE_A)
311+
)->willReturnOnConsecutiveCalls(
312+
\React\Promise\resolve(array('::1')),
313+
\React\Promise\reject(new \RuntimeException('ignored'))
314+
);
315+
316+
$uri = 'tcp://reactphp.org:80';
317+
$host = 'reactphp.org';
318+
$parts = parse_url($uri);
319+
320+
$builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
321+
322+
$promise = $builder->connect();
323+
$deferred->reject(new \RuntimeException('Connection refused'));
324+
325+
$exception = null;
326+
$promise->then(null, function ($e) use (&$exception) {
327+
$exception = $e;
328+
});
329+
330+
$this->assertInstanceOf('RuntimeException', $exception);
331+
$this->assertEquals('Connection to tcp://reactphp.org:80 failed: Connection refused', $exception->getMessage());
332+
}
333+
296334
public function testCancelConnectWillRejectPromiseAndCancelBothDnsLookups()
297335
{
298336
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();

tests/HappyEyeBallsConnectorTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,13 +338,13 @@ public function testRejectsWithTcpConnectorRejectionIfGivenIp()
338338

339339
/**
340340
* @expectedException RuntimeException
341-
* @expectedExceptionMessage All attempts to connect to "example.com" have failed
341+
* @expectedExceptionMessage Connection to example.com:80 failed: Connection refused
342342
* @dataProvider provideIpvAddresses
343343
*/
344344
public function testRejectsWithTcpConnectorRejectionAfterDnsIsResolved(array $ipv6, array $ipv4)
345345
{
346346
$that = $this;
347-
$promise = Promise\reject(new \RuntimeException('Connection failed'));
347+
$promise = Promise\reject(new \RuntimeException('Connection refused'));
348348
$this->resolver->expects($this->at(0))->method('resolveAll')->with($this->equalTo('example.com'), $this->anything())->willReturn(Promise\resolve($ipv6));
349349
$this->resolver->expects($this->at(1))->method('resolveAll')->with($this->equalTo('example.com'), $this->anything())->willReturn(Promise\resolve($ipv4));
350350
$this->tcp->expects($this->any())->method('connect')->with($this->stringContains(':80?hostname=example.com'))->willReturn($promise);

0 commit comments

Comments
 (0)