/* * Copyright (C) 2015 SoftIndex LLC. * * Licensed 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.datakernel.dns; import io.datakernel.annotation.Nullable; import io.datakernel.async.ResultCallback; import io.datakernel.bytebuf.ByteBufPool; import io.datakernel.eventloop.Eventloop; import io.datakernel.eventloop.Eventloop.ConcurrentOperationTracker; import io.datakernel.time.SettableCurrentTimeProvider; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import static io.datakernel.bytebuf.ByteBufPool.getPoolItemsString; import static io.datakernel.eventloop.FatalErrorHandlers.rethrowOnAnyError; import static org.junit.Assert.*; public class DnsResolversTest { private IAsyncDnsClient nativeDnsResolver; private Eventloop eventloop; private static final int DNS_SERVER_PORT = 53; private static final InetSocketAddress GOOGLE_PUBLIC_DNS = new InetSocketAddress("8.8.8.8", DNS_SERVER_PORT); private static final InetSocketAddress UNREACHABLE_DNS = new InetSocketAddress("8.0.8.8", DNS_SERVER_PORT); private static final InetSocketAddress LOCAL_DNS = new InetSocketAddress("192.168.0.1", DNS_SERVER_PORT); private final Logger logger = LoggerFactory.getLogger(DnsResolversTest.class); private class ConcurrentDnsResolveCallback extends ResultCallback<InetAddress[]> { private final DnsResolveCallback callback; private final ConcurrentOperationTracker localEventloopConcurrentOperationTracker; private final ConcurrentOperationsCounter counter; public ConcurrentDnsResolveCallback(DnsResolveCallback callback, ConcurrentOperationTracker localEventloopConcurrentOperationTracker, ConcurrentOperationsCounter counter) { this.callback = callback; this.localEventloopConcurrentOperationTracker = localEventloopConcurrentOperationTracker; this.counter = counter; } @Override protected void onResult(InetAddress[] result) { callback.setResult(result); localEventloopConcurrentOperationTracker.complete(); counter.completeOperation(); } @Override protected void onException(Exception e) { callback.setException(e); localEventloopConcurrentOperationTracker.complete(); counter.completeOperation(); } } private class DnsResolveCallback extends ResultCallback<InetAddress[]> { private InetAddress[] result = null; private Exception exception = null; @Override protected void onResult(@Nullable InetAddress[] ips) { this.result = ips; if (ips == null) { return; } System.out.print("Resolved: "); for (int i = 0; i < ips.length; ++i) { System.out.print(ips[i]); if (i != ips.length - 1) { System.out.print(", "); } } System.out.println("."); } @Override protected void onException(Exception e) { this.exception = e; System.out.println("Resolving IP addresses for host failed. Stack trace: "); e.printStackTrace(); } public InetAddress[] getResult() { return result; } public Exception getException() { return exception; } } private final class ConcurrentOperationsCounter { private final int concurrentOperations; private final ConcurrentOperationTracker concurrentOperationTracker; private AtomicInteger operationsCompleted = new AtomicInteger(0); private ConcurrentOperationsCounter(int concurrentOperations, ConcurrentOperationTracker concurrentOperationTracker) { this.concurrentOperationTracker = concurrentOperationTracker; this.concurrentOperations = concurrentOperations; } public void completeOperation() { if (operationsCompleted.incrementAndGet() == concurrentOperations) { concurrentOperationTracker.complete(); } } } @Before public void setUp() throws Exception { ByteBufPool.clear(); ByteBufPool.setSizes(0, Integer.MAX_VALUE); eventloop = Eventloop.create().withFatalErrorHandler(rethrowOnAnyError()); nativeDnsResolver = AsyncDnsClient.create(eventloop).withTimeout(3_000L).withDnsServerAddress(LOCAL_DNS); } @Ignore @Test public void testResolversWithCorrectDomainNames() throws Exception { DnsResolveCallback nativeResult1 = new DnsResolveCallback(); nativeDnsResolver.resolve4("www.google.com", nativeResult1); DnsResolveCallback nativeResult2 = new DnsResolveCallback(); nativeDnsResolver.resolve4("www.stackoverflow.com", nativeResult2); DnsResolveCallback nativeResult3 = new DnsResolveCallback(); nativeDnsResolver.resolve4("microsoft.com", nativeResult3); eventloop.run(); assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems()); } @SafeVarargs private final <V> Set<V> newHashSet(V... result) { Set<V> set = new HashSet<>(); set.addAll(Arrays.asList(result)); return set; } @Ignore @Test public void testResolve6() throws Exception { DnsResolveCallback nativeDnsResolverCallback = new DnsResolveCallback(); nativeDnsResolver.resolve6("www.google.com", nativeDnsResolverCallback); nativeDnsResolver.resolve6("www.stackoverflow.com", new DnsResolveCallback()); nativeDnsResolver.resolve6("www.oracle.com", new DnsResolveCallback()); eventloop.run(); InetAddress nativeResult[] = nativeDnsResolverCallback.result; assertNotNull(nativeResult); Set<InetAddress> nativeResultList = newHashSet(nativeResult); assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems()); } @Ignore @Test public void testResolversWithIncorrectDomainNames() throws Exception { DnsResolveCallback nativeDnsResolverCallback = new DnsResolveCallback(); nativeDnsResolver.resolve4("fsafa", nativeDnsResolverCallback); eventloop.run(); assertTrue(nativeDnsResolverCallback.exception instanceof DnsException); assertNull(nativeDnsResolverCallback.result); assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems()); } @Ignore @Test public void testConcurrentNativeResolver() throws InterruptedException { Eventloop primaryEventloop = this.eventloop; final AsyncDnsClient asyncDnsClient = (AsyncDnsClient) this.nativeDnsResolver; ConcurrentOperationsCounter counter = new ConcurrentOperationsCounter(10, primaryEventloop.startConcurrentOperation()); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.google.com", 0, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.google.com", 0, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.google.com", 0, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.google.com", 500, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.google.com", 1000, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.google.com", 1000, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.google.com", 1500, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.stackoverflow.com", 1500, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.yahoo.com", 1500, counter); resolveInAnotherThreadWithDelay(asyncDnsClient, "www.google.com", 1500, counter); primaryEventloop.run(); assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems()); } private void resolveInAnotherThreadWithDelay(final AsyncDnsClient asyncDnsClient, final String domainName, final int delayMillis, final ConcurrentOperationsCounter counter) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(delayMillis); } catch (InterruptedException e) { logger.warn("Thread interrupted.", e); } Eventloop callerEventloop = Eventloop.create().withFatalErrorHandler(rethrowOnAnyError()); ConcurrentOperationTracker concurrentOperationTracker = callerEventloop.startConcurrentOperation(); ConcurrentDnsResolveCallback callback = new ConcurrentDnsResolveCallback(new DnsResolveCallback(), concurrentOperationTracker, counter); logger.info("Attempting to resolve host {} from thread {}.", domainName, Thread.currentThread().getName()); IAsyncDnsClient dnsClient = asyncDnsClient.adaptToAnotherEventloop(callerEventloop); dnsClient.resolve4(domainName, callback); callerEventloop.run(); logger.info("Thread {} execution finished.", Thread.currentThread().getName()); } }).start(); } public void testCacheInitialize(AsyncDnsClient asyncDnsClient) throws UnknownHostException { DnsQueryResult testResult = DnsQueryResult.successfulQuery("www.google.com", new InetAddress[]{InetAddress.getByName("173.194.113.210"), InetAddress.getByName("173.194.113.209")}, 10, (short) 1); asyncDnsClient.getCache().add(testResult); } public void testErrorCacheInitialize(AsyncDnsClient asyncDnsClient) { asyncDnsClient.getCache().add(new DnsException("www.google.com", DnsMessage.ResponseErrorCode.SERVER_FAILURE)); } @Test public void testNativeDnsResolverCache() throws Exception { DnsResolveCallback callback = new DnsResolveCallback(); testCacheInitialize((AsyncDnsClient) nativeDnsResolver); nativeDnsResolver.resolve4("www.google.com", callback); eventloop.run(); assertNotNull(callback.result); assertNull(callback.exception); assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems()); } @Test public void testNativeDnsResolverErrorCache() throws Exception { DnsResolveCallback callback = new DnsResolveCallback(); testErrorCacheInitialize((AsyncDnsClient) nativeDnsResolver); nativeDnsResolver.resolve4("www.google.com", callback); eventloop.run(); assertTrue(callback.exception instanceof DnsException); assertNull(callback.result); assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems()); } @Test public void testNativeDnsResolverCache2() throws Exception { String domainName = "www.google.com"; DnsQueryResult testResult = DnsQueryResult.successfulQuery(domainName, new InetAddress[]{InetAddress.getByName("173.194.113.210"), InetAddress.getByName("173.194.113.209")}, 3, (short) 1); SettableCurrentTimeProvider timeProvider = SettableCurrentTimeProvider.create().withTime(0); Eventloop eventloop = Eventloop.create().withFatalErrorHandler(rethrowOnAnyError()).withCurrentTimeProvider(timeProvider); AsyncDnsClient nativeResolver = AsyncDnsClient.create(eventloop).withTimeout(3_000L).withDnsServerAddress(GOOGLE_PUBLIC_DNS); DnsCache cache = nativeResolver.getCache(); timeProvider.setTime(0); eventloop.refreshTimestampAndGet(); cache.add(testResult); timeProvider.setTime(1500); eventloop.refreshTimestampAndGet(); nativeResolver.resolve4(domainName, new DnsResolveCallback()); timeProvider.setTime(3500); eventloop.refreshTimestampAndGet(); DnsResolveCallback callback = new DnsResolveCallback(); nativeResolver.resolve4(domainName, callback); eventloop.run(); cache.add(testResult); timeProvider.setTime(70000); eventloop.refreshTimestampAndGet(); nativeResolver.resolve4(domainName, new DnsResolveCallback()); eventloop.run(); assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems()); } @Test public void testTimeout() throws Exception { String domainName = "www.google.com"; AsyncDnsClient asyncDnsClient = AsyncDnsClient.create(eventloop).withTimeout(3_000L).withDnsServerAddress(UNREACHABLE_DNS); asyncDnsClient.resolve4(domainName, new DnsResolveCallback()); eventloop.run(); assertEquals(getPoolItemsString(), ByteBufPool.getCreatedItems(), ByteBufPool.getPoolItems()); } }