-
-
Notifications
You must be signed in to change notification settings - Fork 62
Expand file tree
/
Copy pathFactory.php
More file actions
227 lines (206 loc) · 8.68 KB
/
Factory.php
File metadata and controls
227 lines (206 loc) · 8.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<?php
namespace React\Dns\Resolver;
use React\Cache\ArrayCache;
use React\Cache\CacheInterface;
use React\Dns\Config\Config;
use React\Dns\Config\HostsFile;
use React\Dns\Config\Options;
use React\Dns\Query\CachingExecutor;
use React\Dns\Query\CoopExecutor;
use React\Dns\Query\ExecutorInterface;
use React\Dns\Query\FallbackExecutor;
use React\Dns\Query\HostsFileExecutor;
use React\Dns\Query\RetryExecutor;
use React\Dns\Query\SearchingExecutor;
use React\Dns\Query\SelectiveTransportExecutor;
use React\Dns\Query\TcpTransportExecutor;
use React\Dns\Query\TimeoutExecutor;
use React\Dns\Query\UdpTransportExecutor;
use React\EventLoop\Loop;
use React\EventLoop\LoopInterface;
final class Factory
{
/**
* Creates a DNS resolver instance for the given DNS config
*
* As of v1.7.0 it's recommended to pass a `Config` object instead of a
* single nameserver address. If the given config contains more than one DNS
* nameserver, all DNS nameservers will be used in order. The primary DNS
* server will always be used first before falling back to the secondary or
* tertiary DNS server.
*
* @param Config|string $config DNS Config object (recommended) or single nameserver address
* @param ?LoopInterface $loop
* @return \React\Dns\Resolver\ResolverInterface
* @throws \InvalidArgumentException for invalid DNS server address
* @throws \UnderflowException when given DNS Config object has an empty list of nameservers
*/
public function create($config, ?LoopInterface $loop = null)
{
$executor = $this->decorateHostsFileExecutor($this->createExecutor($config, $loop ?: Loop::get()));
return new Resolver($executor);
}
/**
* Creates a cached DNS resolver instance for the given DNS config and cache
*
* As of v1.7.0 it's recommended to pass a `Config` object instead of a
* single nameserver address. If the given config contains more than one DNS
* nameserver, all DNS nameservers will be used in order. The primary DNS
* server will always be used first before falling back to the secondary or
* tertiary DNS server.
*
* @param Config|string $config DNS Config object (recommended) or single nameserver address
* @param ?LoopInterface $loop
* @param ?CacheInterface $cache
* @return \React\Dns\Resolver\ResolverInterface
* @throws \InvalidArgumentException for invalid DNS server address
* @throws \UnderflowException when given DNS Config object has an empty list of nameservers
*/
public function createCached($config, ?LoopInterface $loop = null, ?CacheInterface $cache = null)
{
// default to keeping maximum of 256 responses in cache unless explicitly given
if (!($cache instanceof CacheInterface)) {
$cache = new ArrayCache(256);
}
$executor = $this->createExecutor($config, $loop ?: Loop::get());
$executor = new CachingExecutor($executor, $cache);
$executor = $this->decorateHostsFileExecutor($executor);
return new Resolver($executor);
}
/**
* Tries to load the hosts file and decorates the given executor on success
*
* @param ExecutorInterface $executor
* @return ExecutorInterface
* @codeCoverageIgnore
*/
private function decorateHostsFileExecutor(ExecutorInterface $executor)
{
try {
$executor = new HostsFileExecutor(
HostsFile::loadFromPathBlocking(),
$executor
);
} catch (\RuntimeException $e) {
// ignore this file if it can not be loaded
}
// Windows does not store localhost in hosts file by default but handles this internally
// To compensate for this, we explicitly use hard-coded defaults for localhost
if (DIRECTORY_SEPARATOR === '\\') {
$executor = new HostsFileExecutor(
new HostsFile("127.0.0.1 localhost\n::1 localhost"),
$executor
);
}
return $executor;
}
/**
* @param Config|string $nameserver
* @param LoopInterface $loop
* @return CoopExecutor
* @throws \InvalidArgumentException for invalid DNS server address
* @throws \UnderflowException when given DNS Config object has an empty list of nameservers
*/
private function createExecutor($nameserver, LoopInterface $loop)
{
if ($nameserver instanceof Config) {
if (!$nameserver->nameservers) {
throw new \UnderflowException('Empty config with no DNS servers');
}
// Hard-coded to check up to 3 DNS servers to match default limits in place in most systems (see MAXNS config).
// Note to future self: Recursion isn't too hard, but how deep do we really want to go?
$primary = reset($nameserver->nameservers);
$secondary = next($nameserver->nameservers);
$tertiary = next($nameserver->nameservers);
if ($tertiary !== false) {
// 3 DNS servers given => nest first with fallback for second and third
return $this->createTopLevelDecoratingExectors(
new FallbackExecutor(
$this->createSingleExecutor($primary, $nameserver->options, $loop),
new FallbackExecutor(
$this->createSingleExecutor($secondary, $nameserver->options, $loop),
$this->createSingleExecutor($tertiary, $nameserver->options, $loop)
)
),
$nameserver
);
} elseif ($secondary !== false) {
// 2 DNS servers given => fallback from first to second
return $this->createTopLevelDecoratingExectors(
new FallbackExecutor(
$this->createSingleExecutor($primary, $nameserver->options, $loop),
$this->createSingleExecutor($secondary, $nameserver->options, $loop)
),
$nameserver
);
} else {
// 1 DNS server given => use single executor
$nameserver = $primary;
}
}
return $this->createTopLevelDecoratingExectors($this->createSingleExecutor($nameserver, new Options(), $loop), $nameserver);
}
private function createTopLevelDecoratingExectors(ExecutorInterface $executor, $nameserver)
{
$executor = new RetryExecutor($executor, (is_string($nameserver) ? new Options() : $nameserver->options)->attempts);
if ($nameserver instanceof Config && count($nameserver->search) > 0) {
$executor = new SearchingExecutor($executor, $nameserver->options->ndots, ...$nameserver->search);
}
return new CoopExecutor($executor);
}
/**
* @param string $nameserver
* @param Options $options
* @param LoopInterface $loop
* @return ExecutorInterface
* @throws \InvalidArgumentException for invalid DNS server address
*/
private function createSingleExecutor($nameserver, Options $options, LoopInterface $loop)
{
$parts = \parse_url($nameserver);
if (isset($parts['scheme']) && $parts['scheme'] === 'tcp') {
$executor = $this->createTcpExecutor($nameserver, $options->timeout, $loop);
} elseif (isset($parts['scheme']) && $parts['scheme'] === 'udp') {
$executor = $this->createUdpExecutor($nameserver, $options->timeout, $loop);
} else {
$executor = new SelectiveTransportExecutor(
$this->createUdpExecutor($nameserver, $options->timeout, $loop),
$this->createTcpExecutor($nameserver, $options->timeout, $loop)
);
}
return $executor;
}
/**
* @param string $nameserver
* @param int $timeout
* @param LoopInterface $loop
* @return TimeoutExecutor
* @throws \InvalidArgumentException for invalid DNS server address
*/
private function createTcpExecutor($nameserver, int $timeout, LoopInterface $loop)
{
return new TimeoutExecutor(
new TcpTransportExecutor($nameserver, $loop),
$timeout,
$loop
);
}
/**
* @param string $nameserver
* @param int $timeout
* @param LoopInterface $loop
* @return TimeoutExecutor
* @throws \InvalidArgumentException for invalid DNS server address
*/
private function createUdpExecutor($nameserver, int $timeout, LoopInterface $loop)
{
return new TimeoutExecutor(
new UdpTransportExecutor(
$nameserver,
$loop
),
$timeout,
$loop
);
}
}