22
33package org .xbill .DNS ;
44
5+ import java .io .IOException ;
56import java .net .InetSocketAddress ;
67import java .net .UnknownHostException ;
78import java .time .Duration ;
@@ -34,9 +35,11 @@ private static class Resolution {
3435 private final int retriesPerResolver ;
3536 private List <ResolverEntry > resolvers ;
3637 private int currentResolver ;
38+ private long endTime ;
3739
3840 Resolution (ExtendedResolver eres , Message query ) {
3941 resolvers = new ArrayList <>(eres .resolvers );
42+ endTime = System .nanoTime () + eres .timeout .toNanos ();
4043 if (eres .loadBalance ) {
4144 int start = eres .lbStart .updateAndGet (i -> i ++ % resolvers .size ());
4245 if (start > 0 ) {
@@ -99,14 +102,26 @@ private Void handle(Message result, Throwable ex, CompletableFuture<Message> f)
99102 ex .getMessage ());
100103
101104 failureCounter .incrementAndGet ();
102- // go to next resolver, until retries on all resolvers are exhausted
103- currentResolver = (currentResolver + 1 ) % resolvers .size ();
104- if (attempts [currentResolver ] < retriesPerResolver ) {
105- send ().handleAsync ((r , t ) -> handle (r , t , f ));
106- return null ;
107- }
108105
109- f .completeExceptionally (ex );
106+ if (endTime - System .nanoTime () < 0 ) {
107+ f .completeExceptionally (
108+ new IOException (
109+ "Timed out while trying to resolve "
110+ + query .getQuestion ().getName ()
111+ + "/"
112+ + Type .string (query .getQuestion ().type )
113+ + ", id="
114+ + query .getHeader ().getID ()));
115+ } else {
116+ // go to next resolver, until retries on all resolvers are exhausted
117+ currentResolver = (currentResolver + 1 ) % resolvers .size ();
118+ if (attempts [currentResolver ] < retriesPerResolver ) {
119+ send ().handleAsync ((r , t ) -> handle (r , t , f ));
120+ return null ;
121+ }
122+
123+ f .completeExceptionally (ex );
124+ }
110125 } else {
111126 failureCounter .updateAndGet (i -> i > 0 ? (int ) Math .log (i ) : 0 );
112127 f .complete (result );
@@ -136,16 +151,25 @@ public String toString() {
136151 *
137152 * @since 3.2
138153 */
139- public static final Duration DEFAULT_TIMEOUT = Duration .ofSeconds (5 );
154+ public static final Duration DEFAULT_TIMEOUT = Duration .ofSeconds (10 );
155+
156+ /**
157+ * Default timeout until resolving with one of the used resolvers fails.
158+ *
159+ * @since 3.2
160+ */
161+ public static final Duration DEFAULT_RESOLVER_TIMEOUT = Duration .ofSeconds (5 );
140162
141163 private final List <ResolverEntry > resolvers = new CopyOnWriteArrayList <>();
142- private boolean loadBalance ;
143164 private final AtomicInteger lbStart = new AtomicInteger ();
165+ private boolean loadBalance ;
144166 private int retries = 3 ;
167+ private Duration timeout = DEFAULT_TIMEOUT ;
145168
146169 /**
147170 * Creates a new Extended Resolver. The default {@link ResolverConfig} is used to determine the
148- * servers for which {@link SimpleResolver}s are initialized.
171+ * servers for which {@link SimpleResolver}s are initialized. The timeout for each server is
172+ * initialized with {@link #DEFAULT_RESOLVER_TIMEOUT}.
149173 */
150174 public ExtendedResolver () {
151175 List <InetSocketAddress > servers = ResolverConfig .getCurrentConfig ().servers ();
@@ -154,14 +178,15 @@ public ExtendedResolver() {
154178 .map (
155179 server -> {
156180 Resolver r = new SimpleResolver (server );
157- r .setTimeout (DEFAULT_TIMEOUT );
181+ r .setTimeout (DEFAULT_RESOLVER_TIMEOUT );
158182 return new ResolverEntry (r );
159183 })
160184 .collect (Collectors .toSet ()));
161185 }
162186
163187 /**
164- * Creates a new Extended Resolver
188+ * Creates a new instance with {@link SimpleResolver}s. The timeout for each server is initialized
189+ * with {@link #DEFAULT_RESOLVER_TIMEOUT}.
165190 *
166191 * @param servers An array of server names or IP addresses for which {@link SimpleResolver}s are
167192 * initialized.
@@ -175,7 +200,7 @@ public ExtendedResolver(String[] servers) throws UnknownHostException {
175200 server -> {
176201 try {
177202 Resolver r = new SimpleResolver (server );
178- r .setTimeout (DEFAULT_TIMEOUT );
203+ r .setTimeout (DEFAULT_RESOLVER_TIMEOUT );
179204 return new ResolverEntry (r );
180205 } catch (UnknownHostException e ) {
181206 throw new RuntimeException (e );
@@ -200,18 +225,16 @@ public ExtendedResolver(Resolver[] resolvers) {
200225 }
201226
202227 /**
203- * Creates a new Extended Resolver
228+ * Creates a new {@link ExtendedResolver}. No timeout value is applied to the individual
229+ * resolvers, make sure their timeout is smaller than the timeout of this {@link
230+ * ExtendedResolver}.
204231 *
205232 * @param resolvers An iterable of pre-initialized {@link Resolver}s.
206233 */
207234 public ExtendedResolver (Iterable <Resolver > resolvers ) {
208235 this .resolvers .addAll (
209236 StreamSupport .stream (resolvers .spliterator (), false )
210- .map (
211- resolver -> {
212- resolver .setTimeout (DEFAULT_TIMEOUT );
213- return new ResolverEntry (resolver );
214- })
237+ .map (ResolverEntry ::new )
215238 .collect (Collectors .toSet ()));
216239 }
217240
@@ -250,11 +273,14 @@ public void setTSIGKey(TSIG key) {
250273 }
251274 }
252275
276+ @ Override
277+ public Duration getTimeout () {
278+ return timeout ;
279+ }
280+
253281 @ Override
254282 public void setTimeout (Duration timeout ) {
255- for (ResolverEntry re : resolvers ) {
256- re .resolver .setTimeout (timeout );
257- }
283+ this .timeout = timeout ;
258284 }
259285
260286 /**
0 commit comments