1
0
mirror of https://github.com/danog/dns.git synced 2024-11-26 20:14:51 +01:00

Update for Amp v2

This commit is contained in:
Bob Weinand 2016-08-02 23:57:40 +02:00
parent d021dd20f1
commit 46589b730a
5 changed files with 71 additions and 68 deletions

View File

@ -30,12 +30,15 @@
],
"require": {
"php": ">=5.5",
"amphp/amp": "^1",
"amphp/cache": "^0.1",
"amphp/file": "^0.1",
"amphp/amp": "dev-master as 2.0",
"amphp/cache": "dev-amp_v2 as 0.2",
"amphp/file": "dev-amp_v2 as 0.2",
"daverandom/libdns": "^1"
},
"minimum-stability": "dev",
"prefer-stable": true,
"require-dev": {
"amphp/loop": "dev-master",
"phpunit/phpunit": "^4.8",
"fabpot/php-cs-fixer": "^1.9"
},

View File

@ -3,12 +3,14 @@
namespace Amp\Dns;
use Amp\Cache\ArrayCache;
use Amp\CombinatorException;
use Amp\CoroutineResult;
use Amp\MultiReasonException;
use Amp\Coroutine;
use Amp\Deferred;
use Amp\Failure;
use Amp\File\FilesystemException;
use Amp\Success;
use Amp\TimeoutException;
use Interop\Async\Loop;
use LibDNS\Decoder\DecoderFactory;
use LibDNS\Encoder\EncoderFactory;
use LibDNS\Messages\MessageFactory;
@ -42,7 +44,7 @@ class DefaultResolver implements Resolver {
$this->serverUriMap = [];
$this->serverIdTimeoutMap = [];
$this->now = \time();
$this->serverTimeoutWatcher = \Amp\repeat(function ($watcherId) {
$this->serverTimeoutWatcher = \Amp\repeat(1000, function ($watcherId) {
$this->now = $now = \time();
foreach ($this->serverIdTimeoutMap as $id => $expiry) {
if ($now > $expiry) {
@ -52,10 +54,8 @@ class DefaultResolver implements Resolver {
if (empty($this->serverIdMap)) {
\Amp\disable($watcherId);
}
}, 1000, $options = [
"enable" => true,
"keep_alive" => false,
]);
});
\Amp\unreference($this->serverTimeoutWatcher);
}
/**
@ -75,12 +75,12 @@ class DefaultResolver implements Resolver {
}
/**
* {$inheritdoc}
* {@inheritdoc}
*/
public function query($name, $type, array $options = []) {
$handler = [$this, empty($options["recurse"]) ? "doResolve" : "doRecurse"];
$types = (array) $type;
return $this->pipeResult(\Amp\resolve($handler($name, $types, $options)), $types);
return $this->pipeResult(new Coroutine($handler($name, $types, $options)), $types);
}
private function isValidHostName($name) {
@ -92,8 +92,8 @@ REGEX;
}
// flatten $result while preserving order according to $types (append unspecified types for e.g. Record::ALL queries)
private function pipeResult($promise, array $types) {
return \Amp\pipe($promise, function (array $result) use ($types) {
private function pipeResult($awaitable, array $types) {
return \Amp\pipe($awaitable, function (array $result) use ($types) {
$retval = [];
foreach ($types as $type) {
if (isset($result[$type])) {
@ -110,7 +110,7 @@ REGEX;
if (!isset($options["hosts"]) || $options["hosts"]) {
static $hosts = null;
if ($hosts === null || !empty($options["reload_hosts"])) {
return \Amp\pipe(\Amp\resolve($this->loadHostsFile()), function ($value) use (&$hosts, $name, $types, $options) {
return \Amp\pipe(new Coroutine($this->loadHostsFile()), function ($value) use (&$hosts, $name, $types, $options) {
unset($options["reload_hosts"]); // avoid recursion
$hosts = $value;
return $this->recurseWithHosts($name, $types, $options);
@ -128,7 +128,7 @@ REGEX;
}
}
return \Amp\resolve($this->doRecurse($name, $types, $options));
return new Coroutine($this->doRecurse($name, $types, $options));
}
private function doRecurse($name, array $types, $options) {
@ -139,10 +139,10 @@ REGEX;
$types = array_merge($types, [Record::CNAME, Record::DNAME]);
$lookupName = $name;
for ($i = 0; $i < 30; $i++) {
$result = (yield \Amp\resolve($this->doResolve($lookupName, $types, $options)));
$result = (yield new Coroutine($this->doResolve($lookupName, $types, $options)));
if (count($result) > isset($result[Record::CNAME]) + isset($result[Record::DNAME])) {
unset($result[Record::CNAME], $result[Record::DNAME]);
yield new CoroutineResult($result);
yield Coroutine::result($result);
return;
}
// @TODO check for potentially using recursion and iterate over *all* CNAME/DNAME
@ -200,20 +200,20 @@ REGEX;
);
}
$promisor = new Deferred;
$deferred = new Deferred;
$server->pendingRequests[$requestId] = true;
$this->pendingRequests[$requestId] = [$promisor, $name, $type, $uri];
$this->pendingRequests[$requestId] = [$deferred, $name, $type, $uri];
return $promisor->promise();
return $deferred->getAwaitable();
}
private function doResolve($name, array $types, $options) {
if (!$this->config) {
$this->config = (yield \Amp\resolve($this->loadResolvConf()));
$this->config = (yield new Coroutine($this->loadResolvConf()));
}
if (empty($types)) {
yield new CoroutineResult([]);
yield Coroutine::result([]);
return;
}
@ -237,7 +237,7 @@ REGEX;
if (empty(array_filter($result))) {
throw new NoRecordException("No records returned for {$name} (cached result)");
} else {
yield new CoroutineResult($result);
yield Coroutine::result($result);
return;
}
}
@ -256,29 +256,29 @@ REGEX;
}
foreach ($types as $type) {
$promises[] = $this->doRequest($uri, $name, $type);
$awaitables[] = $this->doRequest($uri, $name, $type);
}
try {
list( , $resultArr) = (yield \Amp\timeout(\Amp\some($promises), $timeout));
list( , $resultArr) = (yield \Amp\timeout(\Amp\some($awaitables), $timeout));
foreach ($resultArr as $value) {
$result += $value;
}
} catch (\Amp\TimeoutException $e) {
} catch (TimeoutException $e) {
if (substr($uri, 0, 6) == "tcp://") {
throw new TimeoutException(
"Name resolution timed out for {$name}"
);
} else {
$options["server"] = \preg_replace("#[a-z.]+://#", "tcp://", $uri);
yield new CoroutineResult(\Amp\resolve($this->doResolve($name, $types, $options)));
yield Coroutine::result(\Amp\coroutine($this->doResolve($name, $types, $options)));
return;
}
} catch (ResolutionException $e) {
if (empty($result)) { // if we have no cached results
throw $e;
}
} catch (CombinatorException $e) { // if all promises in Amp\some fail
} catch (MultiReasonException $e) { // if all promises in Amp\some fail
if (empty($result)) { // if we have no cached results
foreach ($e->getExceptions() as $ex) {
if ($ex instanceof NoRecordException) {
@ -289,7 +289,7 @@ REGEX;
}
}
yield new CoroutineResult($result);
yield Coroutine::result($result);
}
/** @link http://man7.org/linux/man-pages/man5/resolv.conf.5.html */
@ -349,7 +349,7 @@ REGEX;
}
}
yield new CoroutineResult($result);
yield Coroutine::result($result);
}
private function loadHostsFile($path = null) {
@ -397,7 +397,7 @@ REGEX;
}
}
yield new CoroutineResult($data);
yield Coroutine::result($data);
}
private function parseCustomServerUri($uri) {
@ -462,27 +462,25 @@ REGEX;
$server->buffer = "";
$server->length = INF;
$server->pendingRequests = [];
$server->watcherId = \Amp\onReadable($socket, $this->makePrivateCallable("onReadable"), [
"enable" => true,
"keep_alive" => true,
]);
$server->watcherId = \Amp\onReadable($socket, $this->makePrivateCallable("onReadable"));
\Amp\unreference($server->watcherId);
$this->serverIdMap[$id] = $server;
$this->serverUriMap[$uri] = $server;
if (substr($uri, 0, 6) == "tcp://") {
$promisor = new Deferred;
$server->connect = $promisor->promise();
$watcher = \Amp\onWritable($server->socket, static function($watcher) use ($server, $promisor, &$timer) {
$deferred = new Deferred;
$server->connect = $deferred->getAwaitable();
$watcher = \Amp\onWritable($server->socket, static function($watcher) use ($server, $deferred, &$timer) {
\Amp\cancel($watcher);
\Amp\cancel($timer);
unset($server->connect);
$promisor->succeed();
$deferred->resolve();
});
$timer = \Amp\once(function() use ($id, $promisor, $watcher, $uri) {
$timer = \Amp\delay(5000, function() use ($id, $deferred, $watcher, $uri) {
\Amp\cancel($watcher);
$this->unloadServer($id);
$promisor->fail(new TimeoutException("Name resolution timed out, could not connect to server at $uri"));
}, 5000);
$deferred->fail(new TimeoutException("Name resolution timed out, could not connect to server at $uri"));
});
}
return $server;
@ -505,8 +503,8 @@ REGEX;
}
if ($error && $server->pendingRequests) {
foreach (array_keys($server->pendingRequests) as $requestId) {
list($promisor) = $this->pendingRequests[$requestId];
$promisor->fail($error);
list($deferred) = $this->pendingRequests[$requestId];
$deferred->fail($error);
}
}
}
@ -568,13 +566,13 @@ REGEX;
}
private function processDecodedResponse($serverId, $requestId, $response) {
list($promisor, $name, $type, $uri) = $this->pendingRequests[$requestId];
list($deferred, $name, $type, $uri) = $this->pendingRequests[$requestId];
// Retry via tcp if message has been truncated
if ($response->isTruncated()) {
if (\substr($uri, 0, 6) != "tcp://") {
$uri = \preg_replace("#[a-z.]+://#", "tcp://", $uri);
$promisor->succeed($this->doRequest($uri, $name, $type));
$deferred->resolve($this->doRequest($uri, $name, $type));
} else {
$this->finalizeResult($serverId, $requestId, new ResolutionException(
"Server returned truncated response"
@ -602,7 +600,7 @@ REGEX;
return;
}
list($promisor, $name) = $this->pendingRequests[$requestId];
list($deferred, $name) = $this->pendingRequests[$requestId];
$server = $this->serverIdMap[$serverId];
unset(
$this->pendingRequests[$requestId],
@ -614,7 +612,7 @@ REGEX;
\Amp\enable($this->serverTimeoutWatcher);
}
if ($error) {
$promisor->fail($error);
$deferred->fail($error);
} else {
foreach ($result as $type => $records) {
$minttl = INF;
@ -625,7 +623,7 @@ REGEX;
}
$this->arrayCache->set("$name#$type", $records, $minttl);
}
$promisor->succeed($result);
$deferred->resolve($result);
}
}

View File

@ -1,22 +1,27 @@
<?php
namespace Amp\Dns;
use Interop\Async\Loop;
const LOOP_STATE_IDENTIFIER = Resolver::class;
/**
* Retrieve the application-wide dns resolver instance
*
* @param \Amp\Dns\Resolver $assign Optionally specify a new default dns resolver instance
* @param \Amp\Dns\Resolver $resolver Optionally specify a new default dns resolver instance
* @return \Amp\Dns\Resolver Returns the application-wide dns resolver instance
*/
function resolver(Resolver $assign = null) {
static $resolver;
if ($assign) {
return ($resolver = $assign);
} elseif ($resolver) {
return $resolver;
} else {
return $resolver = driver();
function resolver(Resolver $resolver = null) {
if ($resolver === null) {
$resolver = Loop::fetchState(LOOP_STATE_IDENTIFIER);
if ($resolver) {
return $resolver;
}
$resolver = driver();
}
Loop::storeState(LOOP_STATE_IDENTIFIER, $resolver);
return $resolver;
}
/**
* Create a new dns resolver best-suited for the current environment
@ -55,7 +60,7 @@ function driver() {
*
* @param string $name The hostname to resolve
* @param array $options
* @return \Amp\Promise
* @return \Interop\Async\Awaitable
* @TODO add boolean "clear_cache" option flag
*/
function resolve($name, array $options = []) {
@ -67,7 +72,7 @@ function resolve($name, array $options = []) {
* @param string $name Unlike resolve(), query() allows for requesting _any_ name (as DNS RFC allows for arbitrary strings)
* @param int|int[] $type Use constants of Amp\Dns\Record
* @param array $options @see resolve documentation
* @return \Amp\Promise
* @return \Interop\Async\Awaitable
*/
function query($name, $type, array $options = []) {
return resolver()->query($name, $type, $options);

View File

@ -3,15 +3,11 @@
namespace Amp\Dns\Test;
class IntegrationTest extends \PHPUnit_Framework_TestCase {
protected function setUp() {
\Amp\reactor(\Amp\driver());
}
/**
* @group internet
*/
public function testResolve() {
\Amp\run(function () {
\Amp\execute(function () {
$names = [
"google.com",
"github.com",

View File

@ -2,6 +2,7 @@
namespace Amp\Dns\Test;
use Amp\Coroutine;
use ReflectionObject;
class ResolvConfTest extends \PHPUnit_Framework_TestCase {
@ -10,7 +11,7 @@ class ResolvConfTest extends \PHPUnit_Framework_TestCase {
$method = $reflector->getMethod("loadResolvConf");
$method->setAccessible(true);
$result = \Amp\wait(\Amp\resolve($method->invoke(\Amp\Dns\resolver(), __DIR__ . "/data/resolv.conf")));
$result = \Amp\wait(new Coroutine($method->invoke(\Amp\Dns\resolver(), __DIR__ . "/data/resolv.conf")));
$this->assertSame([
"nameservers" => [
@ -27,7 +28,7 @@ class ResolvConfTest extends \PHPUnit_Framework_TestCase {
$method = $reflector->getMethod("loadResolvConf");
$method->setAccessible(true);
$result = \Amp\wait(\Amp\resolve($method->invoke(\Amp\Dns\resolver(), __DIR__ . "/data/invalid.conf")));
$result = \Amp\wait(new Coroutine($method->invoke(\Amp\Dns\resolver(), __DIR__ . "/data/invalid.conf")));
$this->assertSame([
"nameservers" => [