Skip to content

Commit 21c7238

Browse files
committed
Load hosts file by default
1 parent 1bc2f40 commit 21c7238

4 files changed

Lines changed: 62 additions & 8 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ $loop->run();
4040

4141
See also the [first example](examples).
4242

43+
> Note that the factory loads the hosts file from the filesystem once when
44+
creating the resolver instance.
45+
Ideally, this method should thus be executed only once before the loop starts
46+
and not repeatedly while it is running.
47+
4348
Pending DNS queries can be cancelled by cancelling its pending promise like so:
4449

4550
```php

src/Resolver/Factory.php

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,24 @@
44

55
use React\Cache\ArrayCache;
66
use React\Cache\CacheInterface;
7-
use React\Dns\Query\Executor;
8-
use React\Dns\Query\CachedExecutor;
9-
use React\Dns\Query\RecordCache;
7+
use React\Dns\Config\HostsFile;
108
use React\Dns\Protocol\Parser;
119
use React\Dns\Protocol\BinaryDumper;
12-
use React\EventLoop\LoopInterface;
10+
use React\Dns\Query\CachedExecutor;
11+
use React\Dns\Query\Executor;
12+
use React\Dns\Query\ExecutorInterface;
13+
use React\Dns\Query\HostsFileExecutor;
14+
use React\Dns\Query\RecordCache;
1315
use React\Dns\Query\RetryExecutor;
1416
use React\Dns\Query\TimeoutExecutor;
17+
use React\EventLoop\LoopInterface;
1518

1619
class Factory
1720
{
1821
public function create($nameserver, LoopInterface $loop)
1922
{
2023
$nameserver = $this->addPortToServerIfMissing($nameserver);
21-
$executor = $this->createRetryExecutor($loop);
24+
$executor = $this->decorateHostsFileExecutor($this->createRetryExecutor($loop));
2225

2326
return new Resolver($nameserver, $executor);
2427
}
@@ -30,11 +33,30 @@ public function createCached($nameserver, LoopInterface $loop, CacheInterface $c
3033
}
3134

3235
$nameserver = $this->addPortToServerIfMissing($nameserver);
33-
$executor = $this->createCachedExecutor($loop, $cache);
36+
$executor = $this->decorateHostsFileExecutor($this->createCachedExecutor($loop, $cache));
3437

3538
return new Resolver($nameserver, $executor);
3639
}
3740

41+
/**
42+
* Tries to load the hosts file and decorates the given executor on success
43+
*
44+
* @param ExecutorInterface $executor
45+
* @return ExecutorInterface
46+
* @codeCoverageIgnore
47+
*/
48+
private function decorateHostsFileExecutor(ExecutorInterface $executor)
49+
{
50+
try {
51+
$hosts = HostsFile::loadFromPathBlocking();
52+
} catch (\RuntimeException $e) {
53+
// ignore this file if it can not be loaded
54+
return $executor;
55+
}
56+
57+
return new HostsFileExecutor($hosts, $executor);
58+
}
59+
3860
protected function createExecutor(LoopInterface $loop)
3961
{
4062
return new TimeoutExecutor(

tests/FunctionalResolverTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ public function setUp()
1717
$this->resolver = $factory->create('8.8.8.8', $this->loop);
1818
}
1919

20+
public function testResolveLocalhostResolves()
21+
{
22+
if (DIRECTORY_SEPARATOR === '\\') {
23+
$this->markTestSkipped('Not supported on Windows');
24+
}
25+
26+
$promise = $this->resolver->resolve('localhost');
27+
$promise->then($this->expectCallableOnce(), $this->expectCallableNever());
28+
29+
$this->loop->run();
30+
}
31+
2032
public function testResolveGoogleResolves()
2133
{
2234
$promise = $this->resolver->resolve('google.com');

tests/Resolver/FactoryTest.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use React\Dns\Resolver\Factory;
66
use React\Tests\Dns\TestCase;
7+
use React\Dns\Query\HostsFileExecutor;
78

89
class FactoryTest extends TestCase
910
{
@@ -39,7 +40,7 @@ public function createCachedShouldCreateResolverWithCachedExecutor()
3940
$resolver = $factory->createCached('8.8.8.8:53', $loop);
4041

4142
$this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver);
42-
$executor = $this->getResolverPrivateMemberValue($resolver, 'executor');
43+
$executor = $this->getResolverPrivateExecutor($resolver);
4344
$this->assertInstanceOf('React\Dns\Query\CachedExecutor', $executor);
4445
$recordCache = $this->getCachedExecutorPrivateMemberValue($executor, 'cache');
4546
$recordCacheCache = $this->getRecordCachePrivateMemberValue($recordCache, 'cache');
@@ -57,7 +58,7 @@ public function createCachedShouldCreateResolverWithCachedExecutorWithCustomCach
5758
$resolver = $factory->createCached('8.8.8.8:53', $loop, $cache);
5859

5960
$this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver);
60-
$executor = $this->getResolverPrivateMemberValue($resolver, 'executor');
61+
$executor = $this->getResolverPrivateExecutor($resolver);
6162
$this->assertInstanceOf('React\Dns\Query\CachedExecutor', $executor);
6263
$recordCache = $this->getCachedExecutorPrivateMemberValue($executor, 'cache');
6364
$recordCacheCache = $this->getRecordCachePrivateMemberValue($recordCache, 'cache');
@@ -92,6 +93,20 @@ public static function factoryShouldAddDefaultPortProvider()
9293
);
9394
}
9495

96+
private function getResolverPrivateExecutor($resolver)
97+
{
98+
$executor = $this->getResolverPrivateMemberValue($resolver, 'executor');
99+
100+
if ($executor instanceof HostsFileExecutor) {
101+
$reflector = new \ReflectionProperty('React\Dns\Query\HostsFileExecutor', 'fallback');
102+
$reflector->setAccessible(true);
103+
104+
$executor = $reflector->getValue($executor);
105+
}
106+
107+
return $executor;
108+
}
109+
95110
private function getResolverPrivateMemberValue($resolver, $field)
96111
{
97112
$reflector = new \ReflectionProperty('React\Dns\Resolver\Resolver', $field);

0 commit comments

Comments
 (0)