99use React \Dns \Query \CachingExecutor ;
1010use React \Dns \Query \CoopExecutor ;
1111use React \Dns \Query \ExecutorInterface ;
12+ use React \Dns \Query \FallbackExecutor ;
1213use React \Dns \Query \HostsFileExecutor ;
1314use React \Dns \Query \RetryExecutor ;
1415use React \Dns \Query \SelectiveTransportExecutor ;
@@ -24,8 +25,9 @@ final class Factory
2425 *
2526 * As of v1.7.0 it's recommended to pass a `Config` object instead of a
2627 * single nameserver address. If the given config contains more than one DNS
27- * nameserver, only the primary will be used at the moment. A future version
28- * may take advantage of fallback DNS servers.
28+ * nameserver, all DNS nameservers will be used in order. The primary DNS
29+ * server will always be used first before falling back to the secondary DNS
30+ * server.
2931 *
3032 * @param Config|string $config DNS Config object (recommended) or single nameserver address
3133 * @param LoopInterface $loop
@@ -45,8 +47,9 @@ public function create($config, LoopInterface $loop)
4547 *
4648 * As of v1.7.0 it's recommended to pass a `Config` object instead of a
4749 * single nameserver address. If the given config contains more than one DNS
48- * nameserver, only the primary will be used at the moment. A future version
49- * may take advantage of fallback DNS servers.
50+ * nameserver, all DNS nameservers will be used in order. The primary DNS
51+ * server will always be used first before falling back to the secondary DNS
52+ * server.
5053 *
5154 * @param Config|string $config DNS Config object (recommended) or single nameserver address
5255 * @param LoopInterface $loop
@@ -109,12 +112,38 @@ private function decorateHostsFileExecutor(ExecutorInterface $executor)
109112 private function createExecutor ($ nameserver , LoopInterface $ loop )
110113 {
111114 if ($ nameserver instanceof Config) {
112- $ nameserver = \reset ($ nameserver ->nameservers );
113- if ($ nameserver === false ) {
115+ if (!$ nameserver ->nameservers ) {
114116 throw new \UnderflowException ('Empty config with no DNS servers ' );
115117 }
118+
119+ $ primary = reset ($ nameserver ->nameservers );
120+ $ secondary = next ($ nameserver ->nameservers );
121+
122+ if ($ secondary !== false ) {
123+ return new CoopExecutor (
124+ new RetryExecutor (
125+ new FallbackExecutor (
126+ $ this ->createSingleExecutor ($ primary , $ loop ),
127+ $ this ->createSingleExecutor ($ secondary , $ loop )
128+ )
129+ )
130+ );
131+ } else {
132+ $ nameserver = $ primary ;
133+ }
116134 }
117135
136+ return new CoopExecutor (new RetryExecutor ($ this ->createSingleExecutor ($ nameserver , $ loop )));
137+ }
138+
139+ /**
140+ * @param string $nameserver
141+ * @param LoopInterface $loop
142+ * @return ExecutorInterface
143+ * @throws \InvalidArgumentException for invalid DNS server address
144+ */
145+ private function createSingleExecutor ($ nameserver , LoopInterface $ loop )
146+ {
118147 $ parts = \parse_url ($ nameserver );
119148
120149 if (isset ($ parts ['scheme ' ]) && $ parts ['scheme ' ] === 'tcp ' ) {
@@ -128,9 +157,15 @@ private function createExecutor($nameserver, LoopInterface $loop)
128157 );
129158 }
130159
131- return new CoopExecutor ( new RetryExecutor ( $ executor)) ;
160+ return $ executor ;
132161 }
133162
163+ /**
164+ * @param string $nameserver
165+ * @param LoopInterface $loop
166+ * @return TimeoutExecutor
167+ * @throws \InvalidArgumentException for invalid DNS server address
168+ */
134169 private function createTcpExecutor ($ nameserver , LoopInterface $ loop )
135170 {
136171 return new TimeoutExecutor (
@@ -140,6 +175,12 @@ private function createTcpExecutor($nameserver, LoopInterface $loop)
140175 );
141176 }
142177
178+ /**
179+ * @param string $nameserver
180+ * @param LoopInterface $loop
181+ * @return TimeoutExecutor
182+ * @throws \InvalidArgumentException for invalid DNS server address
183+ */
143184 private function createUdpExecutor ($ nameserver , LoopInterface $ loop )
144185 {
145186 return new TimeoutExecutor (
0 commit comments