From d2040a9c94e42c9bca88c658136c63113f700b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 25 Jun 2021 12:20:36 +0200 Subject: [PATCH] Internal improvement to avoid unhandled rejection for future Promise API --- src/HappyEyeBallsConnectionBuilder.php | 12 ++++++----- tests/HappyEyeBallsConnectionBuilderTest.php | 21 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/HappyEyeBallsConnectionBuilder.php b/src/HappyEyeBallsConnectionBuilder.php index 3c7d5c8d..4ec671a3 100644 --- a/src/HappyEyeBallsConnectionBuilder.php +++ b/src/HappyEyeBallsConnectionBuilder.php @@ -84,8 +84,8 @@ public function connect() $that->resolverPromises[Message::TYPE_AAAA] = $that->resolve(Message::TYPE_AAAA, $reject)->then($lookupResolve(Message::TYPE_AAAA)); $that->resolverPromises[Message::TYPE_A] = $that->resolve(Message::TYPE_A, $reject)->then(function (array $ips) use ($that, &$timer) { - // happy path: IPv6 has resolved already, continue with IPv4 addresses - if ($that->resolved[Message::TYPE_AAAA] === true) { + // happy path: IPv6 has resolved already (or could not resolve), continue with IPv4 addresses + if ($that->resolved[Message::TYPE_AAAA] === true || !$ips) { return $ips; } @@ -117,8 +117,9 @@ public function connect() * @internal * @param int $type DNS query type * @param callable $reject - * @return \React\Promise\PromiseInterface Returns a promise - * that resolves list of IP addresses on success or rejects with an \Exception on error. + * @return \React\Promise\PromiseInterface Returns a promise that + * always resolves with a list of IP addresses on success or an empty + * list on error. */ public function resolve($type, $reject) { @@ -145,7 +146,8 @@ public function resolve($type, $reject) $reject(new \RuntimeException($that->error())); } - throw $e; + // Exception already handled above, so don't throw an unhandled rejection here + return array(); }); } diff --git a/tests/HappyEyeBallsConnectionBuilderTest.php b/tests/HappyEyeBallsConnectionBuilderTest.php index 80c118ba..d948b735 100644 --- a/tests/HappyEyeBallsConnectionBuilderTest.php +++ b/tests/HappyEyeBallsConnectionBuilderTest.php @@ -677,6 +677,27 @@ public function testCancelConnectWillRejectPromiseAndCancelPendingIpv6Connection $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled', $exception->getMessage()); } + public function testResolveWillReturnResolvedPromiseWithEmptyListWhenDnsResolverFails() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock(); + + $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock(); + $resolver->expects($this->once())->method('resolveAll')->with('reactphp.org', Message::TYPE_A)->willReturn(\React\Promise\reject(new \RuntimeException())); + + $uri = 'tcp://reactphp.org:80'; + $host = 'reactphp.org'; + $parts = parse_url($uri); + + $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts); + + $promise = $builder->resolve(Message::TYPE_A, $this->expectCallableNever()); + + $this->assertInstanceof('React\Promise\PromiseInterface', $promise); + $promise->then($this->expectCallableOnceWith(array()), $this->expectCallableNever()); + } + public function testAttemptConnectionWillConnectViaConnectorToGivenIpWithPortAndHostnameFromUriParts() { $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();