Revert "Allow to detect failed query caused by an Timeout / IO error and also not cache these."

This reverts commit 12a413bf02 as it needs some more changes due some changes that were merged into 4.1 before.
This commit is contained in:
Norman Maurer 2017-11-22 22:05:29 +01:00
parent 12a413bf02
commit 433dbeb149
7 changed files with 33 additions and 148 deletions

View File

@ -65,8 +65,6 @@ public interface DnsCache {
/** /**
* Cache the resolution failure for a given hostname. * Cache the resolution failure for a given hostname.
* Be aware this <strong>won't</strong> be called with timeout / cancel / transport exceptions.
*
* @param hostname the hostname * @param hostname the hostname
* @param additionals the additional records * @param additionals the additional records
* @param cause the resolution failure * @param cause the resolution failure

View File

@ -891,24 +891,6 @@ public class DnsNameResolver extends InetNameResolver {
return query0(nameServerAddr, question, toArray(additionals, false), promise); return query0(nameServerAddr, question, toArray(additionals, false), promise);
} }
/**
* Returns {@code true} if the {@link Throwable} was caused by an timeout or transport error.
* These methods can be used on the {@link Future#cause()} that is returned by the various methods exposed by this
* {@link DnsNameResolver}.
*/
public static boolean isTransportOrTimeoutError(Throwable cause) {
return cause != null && cause.getCause() instanceof DnsNameResolverException;
}
/**
* Returns {@code true} if the {@link Throwable} was caused by an timeout.
* These methods can be used on the {@link Future#cause()} that is returned by the various methods exposed by this
* {@link DnsNameResolver}.
*/
public static boolean isTimeoutError(Throwable cause) {
return cause != null && cause.getCause() instanceof DnsNameResolverTimeoutException;
}
final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query0( final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query0(
InetSocketAddress nameServerAddr, DnsQuestion question, InetSocketAddress nameServerAddr, DnsQuestion question,
DnsRecord[] additionals, DnsRecord[] additionals,

View File

@ -189,11 +189,11 @@ abstract class DnsNameResolverContext<T> {
assert recordTypes.length > 0; assert recordTypes.length > 0;
final int end = recordTypes.length - 1; final int end = recordTypes.length - 1;
for (int i = 0; i < end; ++i) { for (int i = 0; i < end; ++i) {
if (!query(hostname, recordTypes[i], nameServerAddressStream.duplicate(), promise, null)) { if (!query(hostname, recordTypes[i], nameServerAddressStream.duplicate(), promise)) {
return; return;
} }
} }
query(hostname, recordTypes[end], nameServerAddressStream, promise, null); query(hostname, recordTypes[end], nameServerAddressStream, promise);
} }
/** /**
@ -283,20 +283,19 @@ abstract class DnsNameResolverContext<T> {
private void query(final DnsServerAddressStream nameServerAddrStream, final int nameServerAddrStreamIndex, private void query(final DnsServerAddressStream nameServerAddrStream, final int nameServerAddrStreamIndex,
final DnsQuestion question, final DnsQuestion question,
final Promise<T> promise, Throwable cause) { final Promise<T> promise) {
query(nameServerAddrStream, nameServerAddrStreamIndex, question, query(nameServerAddrStream, nameServerAddrStreamIndex, question,
parent.dnsQueryLifecycleObserverFactory().newDnsQueryLifecycleObserver(question), promise, cause); parent.dnsQueryLifecycleObserverFactory().newDnsQueryLifecycleObserver(question), promise);
} }
private void query(final DnsServerAddressStream nameServerAddrStream, private void query(final DnsServerAddressStream nameServerAddrStream,
final int nameServerAddrStreamIndex, final int nameServerAddrStreamIndex,
final DnsQuestion question, final DnsQuestion question,
final DnsQueryLifecycleObserver queryLifecycleObserver, final DnsQueryLifecycleObserver queryLifecycleObserver,
final Promise<T> promise, final Promise<T> promise) {
final Throwable cause) {
if (nameServerAddrStreamIndex >= nameServerAddrStream.size() || allowedQueries == 0 || promise.isCancelled()) { if (nameServerAddrStreamIndex >= nameServerAddrStream.size() || allowedQueries == 0 || promise.isCancelled()) {
tryToFinishResolve(nameServerAddrStream, nameServerAddrStreamIndex, question, queryLifecycleObserver, tryToFinishResolve(nameServerAddrStream, nameServerAddrStreamIndex, question, queryLifecycleObserver,
promise, cause); promise);
return; return;
} }
@ -320,22 +319,21 @@ abstract class DnsNameResolverContext<T> {
return; return;
} }
final Throwable queryCause = future.cause();
try { try {
if (queryCause == null) { if (future.isSuccess()) {
onResponse(nameServerAddrStream, nameServerAddrStreamIndex, question, future.getNow(), onResponse(nameServerAddrStream, nameServerAddrStreamIndex, question, future.getNow(),
queryLifecycleObserver, promise); queryLifecycleObserver, promise);
} else { } else {
// Server did not respond or I/O error occurred; try again. // Server did not respond or I/O error occurred; try again.
queryLifecycleObserver.queryFailed(queryCause); queryLifecycleObserver.queryFailed(future.cause());
query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, promise, queryCause); query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, promise);
} }
} finally { } finally {
tryToFinishResolve(nameServerAddrStream, nameServerAddrStreamIndex, question, tryToFinishResolve(nameServerAddrStream, nameServerAddrStreamIndex, question,
// queryLifecycleObserver has already been terminated at this point so we must // queryLifecycleObserver has already been terminated at this point so we must
// not allow it to be terminated again by tryToFinishResolve. // not allow it to be terminated again by tryToFinishResolve.
NoopDnsQueryLifecycleObserver.INSTANCE, NoopDnsQueryLifecycleObserver.INSTANCE,
promise, queryCause); promise);
} }
} }
}); });
@ -368,7 +366,7 @@ abstract class DnsNameResolverContext<T> {
// Retry with the next server if the server did not tell us that the domain does not exist. // Retry with the next server if the server did not tell us that the domain does not exist.
if (code != DnsResponseCode.NXDOMAIN) { if (code != DnsResponseCode.NXDOMAIN) {
query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question,
queryLifecycleObserver.queryNoAnswer(code), promise, null); queryLifecycleObserver.queryNoAnswer(code), promise);
} else { } else {
queryLifecycleObserver.queryFailed(NXDOMAIN_QUERY_FAILED_EXCEPTION); queryLifecycleObserver.queryFailed(NXDOMAIN_QUERY_FAILED_EXCEPTION);
} }
@ -422,7 +420,7 @@ abstract class DnsNameResolverContext<T> {
if (!nameServers.isEmpty()) { if (!nameServers.isEmpty()) {
query(parent.uncachedRedirectDnsServerStream(nameServers), 0, question, query(parent.uncachedRedirectDnsServerStream(nameServers), 0, question,
queryLifecycleObserver.queryRedirected(unmodifiableList(nameServers)), promise, null); queryLifecycleObserver.queryRedirected(unmodifiableList(nameServers)), promise);
return true; return true;
} }
} }
@ -603,8 +601,7 @@ abstract class DnsNameResolverContext<T> {
final int nameServerAddrStreamIndex, final int nameServerAddrStreamIndex,
final DnsQuestion question, final DnsQuestion question,
final DnsQueryLifecycleObserver queryLifecycleObserver, final DnsQueryLifecycleObserver queryLifecycleObserver,
final Promise<T> promise, final Promise<T> promise) {
final Throwable cause) {
// There are no queries left to try. // There are no queries left to try.
if (!queriesInProgress.isEmpty()) { if (!queriesInProgress.isEmpty()) {
queryLifecycleObserver.queryCancelled(allowedQueries); queryLifecycleObserver.queryCancelled(allowedQueries);
@ -612,7 +609,7 @@ abstract class DnsNameResolverContext<T> {
// There are still some queries we did not receive responses for. // There are still some queries we did not receive responses for.
if (gotPreferredAddress()) { if (gotPreferredAddress()) {
// But it's OK to finish the resolution process if we got a resolved address of the preferred type. // But it's OK to finish the resolution process if we got a resolved address of the preferred type.
finishResolve(promise, cause); finishResolve(promise);
} }
// We did not get any resolved address of the preferred type, so we can't finish the resolution process. // We did not get any resolved address of the preferred type, so we can't finish the resolution process.
@ -625,10 +622,10 @@ abstract class DnsNameResolverContext<T> {
if (queryLifecycleObserver == NoopDnsQueryLifecycleObserver.INSTANCE) { if (queryLifecycleObserver == NoopDnsQueryLifecycleObserver.INSTANCE) {
// If the queryLifecycleObserver has already been terminated we should create a new one for this // If the queryLifecycleObserver has already been terminated we should create a new one for this
// fresh query. // fresh query.
query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, promise, cause); query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, promise);
} else { } else {
query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, queryLifecycleObserver, query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, queryLifecycleObserver,
promise, cause); promise);
} }
return; return;
} }
@ -636,15 +633,11 @@ abstract class DnsNameResolverContext<T> {
queryLifecycleObserver.queryFailed(NAME_SERVERS_EXHAUSTED_EXCEPTION); queryLifecycleObserver.queryFailed(NAME_SERVERS_EXHAUSTED_EXCEPTION);
// .. and we could not find any A/AAAA records. // .. and we could not find any A/AAAA records.
if (!triedCNAME) {
// If cause != null we know this was caused by a timeout / cancel / transport exception. In this case we
// won't try to resolve the CNAME as we only should do this if we could not get the A/AAAA records because
// these not exists and the DNS server did probably signal it.
if (cause == null && !triedCNAME) {
// As the last resort, try to query CNAME, just in case the name server has it. // As the last resort, try to query CNAME, just in case the name server has it.
triedCNAME = true; triedCNAME = true;
query(hostname, DnsRecordType.CNAME, getNameServers(hostname), promise, null); query(hostname, DnsRecordType.CNAME, getNameServers(hostname), promise);
return; return;
} }
} else { } else {
@ -652,7 +645,7 @@ abstract class DnsNameResolverContext<T> {
} }
// We have at least one resolved address or tried CNAME as the last resort.. // We have at least one resolved address or tried CNAME as the last resort..
finishResolve(promise, cause); finishResolve(promise);
} }
private boolean gotPreferredAddress() { private boolean gotPreferredAddress() {
@ -671,7 +664,7 @@ abstract class DnsNameResolverContext<T> {
return false; return false;
} }
private void finishResolve(Promise<T> promise, Throwable cause) { private void finishResolve(Promise<T> promise) {
if (!queriesInProgress.isEmpty()) { if (!queriesInProgress.isEmpty()) {
// If there are queries in progress, we should cancel it because we already finished the resolution. // If there are queries in progress, we should cancel it because we already finished the resolution.
for (Iterator<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> i = queriesInProgress.iterator(); for (Iterator<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> i = queriesInProgress.iterator();
@ -710,15 +703,10 @@ abstract class DnsNameResolverContext<T> {
.append(' '); .append(' ');
} }
} }
final UnknownHostException unknownHostException = new UnknownHostException(buf.toString()); final UnknownHostException cause = new UnknownHostException(buf.toString());
if (cause == null) {
// Only cache if the failure was not because of an IO error / timeout that was caused by the query resolveCache.cache(hostname, additionals, cause, parent.ch.eventLoop());
// itself. promise.tryFailure(cause);
resolveCache.cache(hostname, additionals, unknownHostException, parent.ch.eventLoop());
} else {
unknownHostException.initCause(cause);
}
promise.tryFailure(unknownHostException);
} }
abstract boolean finishResolve(Class<? extends InetAddress> addressType, List<DnsCacheEntry> resolvedEntries, abstract boolean finishResolve(Class<? extends InetAddress> addressType, List<DnsCacheEntry> resolvedEntries,
@ -759,7 +747,7 @@ abstract class DnsNameResolverContext<T> {
queryLifecycleObserver.queryFailed(cause); queryLifecycleObserver.queryFailed(cause);
PlatformDependent.throwException(cause); PlatformDependent.throwException(cause);
} }
query(stream, 0, cnameQuestion, queryLifecycleObserver.queryCNAMEd(cnameQuestion), promise, null); query(stream, 0, cnameQuestion, queryLifecycleObserver.queryCNAMEd(cnameQuestion), promise);
} }
if (parent.supportsAAAARecords()) { if (parent.supportsAAAARecords()) {
try { try {
@ -770,17 +758,17 @@ abstract class DnsNameResolverContext<T> {
queryLifecycleObserver.queryFailed(cause); queryLifecycleObserver.queryFailed(cause);
PlatformDependent.throwException(cause); PlatformDependent.throwException(cause);
} }
query(stream, 0, cnameQuestion, queryLifecycleObserver.queryCNAMEd(cnameQuestion), promise, null); query(stream, 0, cnameQuestion, queryLifecycleObserver.queryCNAMEd(cnameQuestion), promise);
} }
} }
private boolean query(String hostname, DnsRecordType type, DnsServerAddressStream dnsServerAddressStream, private boolean query(String hostname, DnsRecordType type, DnsServerAddressStream dnsServerAddressStream,
Promise<T> promise, Throwable cause) { Promise<T> promise) {
final DnsQuestion question = newQuestion(hostname, type); final DnsQuestion question = newQuestion(hostname, type);
if (question == null) { if (question == null) {
return false; return false;
} }
query(dnsServerAddressStream, 0, question, promise, cause); query(dnsServerAddressStream, 0, question, promise);
return true; return true;
} }

View File

@ -26,7 +26,7 @@ import java.net.InetSocketAddress;
* A {@link RuntimeException} raised when {@link DnsNameResolver} failed to perform a successful query. * A {@link RuntimeException} raised when {@link DnsNameResolver} failed to perform a successful query.
*/ */
@UnstableApi @UnstableApi
public class DnsNameResolverException extends RuntimeException { public final class DnsNameResolverException extends RuntimeException {
private static final long serialVersionUID = -8826717909627131850L; private static final long serialVersionUID = -8826717909627131850L;

View File

@ -1,35 +0,0 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.resolver.dns;
import io.netty.handler.codec.dns.DnsQuestion;
import io.netty.util.internal.UnstableApi;
import java.net.InetSocketAddress;
/**
* A {@link DnsNameResolverException} raised when {@link DnsNameResolver} failed to perform a successful query because
* of an timeout. In this case you may want to retry the operation.
*/
@UnstableApi
public final class DnsNameResolverTimeoutException extends DnsNameResolverException {
private static final long serialVersionUID = -8826717969627131854L;
public DnsNameResolverTimeoutException(
InetSocketAddress remoteAddress, DnsQuestion question, String message) {
super(remoteAddress, question, message);
}
}

View File

@ -213,13 +213,12 @@ final class DnsQueryContext {
.append(" (no stack trace available)"); .append(" (no stack trace available)");
final DnsNameResolverException e; final DnsNameResolverException e;
if (cause == null) { if (cause != null) {
// This was caused by an timeout so use DnsNameResolverTimeoutException to allow the user to
// handle it special (like retry the query).
e = new DnsNameResolverTimeoutException(nameServerAddr, question(), buf.toString());
} else {
e = new DnsNameResolverException(nameServerAddr, question(), buf.toString(), cause); e = new DnsNameResolverException(nameServerAddr, question(), buf.toString(), cause);
} else {
e = new DnsNameResolverException(nameServerAddr, question(), buf.toString());
} }
promise.tryFailure(e); promise.tryFailure(e);
} }
} }

View File

@ -1322,51 +1322,4 @@ public class DnsNameResolverTest {
return rm.getEntry(); return rm.getEntry();
} }
} }
@Test(timeout = 3000)
public void testTimeoutNotCached() {
DnsCache cache = new DnsCache() {
@Override
public void clear() {
// NOOP
}
@Override
public boolean clear(String hostname) {
return false;
}
@Override
public List<? extends DnsCacheEntry> get(String hostname, DnsRecord[] additionals) {
return Collections.emptyList();
}
@Override
public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, InetAddress address,
long originalTtl, EventLoop loop) {
fail("Should not be cached");
return null;
}
@Override
public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable cause, EventLoop loop) {
fail("Should not be cached");
return null;
}
};
DnsNameResolverBuilder builder = newResolver();
builder.queryTimeoutMillis(100)
.authoritativeDnsServerCache(cache)
.resolveCache(cache)
.nameServerProvider(new SingletonDnsServerAddressStreamProvider(
new InetSocketAddress(NetUtil.LOCALHOST, 12345)));
DnsNameResolver resolver = builder.build();
Future<InetAddress> result = resolver.resolve("doesnotexist.netty.io").awaitUninterruptibly();
Throwable cause = result.cause();
assertTrue(cause instanceof UnknownHostException);
assertTrue(cause.getCause() instanceof DnsNameResolverTimeoutException);
assertTrue(DnsNameResolver.isTimeoutError(cause));
assertTrue(DnsNameResolver.isTransportOrTimeoutError(cause));
resolver.close();
}
} }