GitHub PullRequestReviewCommentEvent at 2014-10-14 09:28:17
Repository: netty
Actor: normanmaurer
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/netty/netty/pulls/comments/18817164","id":18817164,"diff_hunk":"@@ -0,0 +1,782 @@\n+/*\n+ * Copyright 2014 The Netty Project\n+ *\n+ * The Netty Project licenses this file to you under the Apache License,\n+ * version 2.0 (the \"License\"); you may not use this file except in compliance\n+ * with the License. You may obtain a copy of the License at:\n+ *\n+ *   http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n+ * License for the specific language governing permissions and limitations\n+ * under the License.\n+ */\n+package io.netty.resolver.dns;\n+\n+import io.netty.bootstrap.Bootstrap;\n+import io.netty.channel.ChannelFactory;\n+import io.netty.channel.ChannelFuture;\n+import io.netty.channel.ChannelFutureListener;\n+import io.netty.channel.ChannelHandlerContext;\n+import io.netty.channel.ChannelInboundHandlerAdapter;\n+import io.netty.channel.ChannelInitializer;\n+import io.netty.channel.EventLoop;\n+import io.netty.channel.FixedRecvByteBufAllocator;\n+import io.netty.channel.ReflectiveChannelFactory;\n+import io.netty.channel.socket.DatagramChannel;\n+import io.netty.channel.socket.InternetProtocolFamily;\n+import io.netty.handler.codec.dns.DnsClass;\n+import io.netty.handler.codec.dns.DnsQueryEncoder;\n+import io.netty.handler.codec.dns.DnsQuestion;\n+import io.netty.handler.codec.dns.DnsResource;\n+import io.netty.handler.codec.dns.DnsResponse;\n+import io.netty.handler.codec.dns.DnsResponseCode;\n+import io.netty.handler.codec.dns.DnsResponseDecoder;\n+import io.netty.resolver.NameResolver;\n+import io.netty.resolver.SimpleNameResolver;\n+import io.netty.util.ReferenceCountUtil;\n+import io.netty.util.collection.IntObjectHashMap;\n+import io.netty.util.concurrent.Future;\n+import io.netty.util.concurrent.Promise;\n+import io.netty.util.concurrent.ScheduledFuture;\n+import io.netty.util.internal.OneTimeTask;\n+import io.netty.util.internal.PlatformDependent;\n+import io.netty.util.internal.SystemPropertyUtil;\n+import io.netty.util.internal.logging.InternalLogger;\n+import io.netty.util.internal.logging.InternalLoggerFactory;\n+\n+import java.net.IDN;\n+import java.net.InetSocketAddress;\n+import java.util.Iterator;\n+import java.util.List;\n+import java.util.Map.Entry;\n+import java.util.concurrent.ConcurrentMap;\n+import java.util.concurrent.TimeUnit;\n+import java.util.concurrent.atomic.AtomicReferenceArray;\n+\n+/**\n+ * A DNS-based {@link NameResolver}.\n+ */\n+public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {\n+\n+    private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolver.class);\n+\n+    static final InetSocketAddress ANY_LOCAL_ADDR = new InetSocketAddress(0);\n+\n+    private static final InternetProtocolFamily PREFERRED_PROTOCOL_FAMILY;\n+\n+    static {\n+        // Note that we did not use SystemPropertyUtil.getBoolean() here to emulate the behavior of JDK.\n+        if (\"true\".equalsIgnoreCase(SystemPropertyUtil.get(\"java.net.preferIPv6Addresses\"))) {\n+            PREFERRED_PROTOCOL_FAMILY = InternetProtocolFamily.IPv6;\n+            logger.debug(\"-Djava.net.preferIPv6Addresses: true\");\n+        } else {\n+            PREFERRED_PROTOCOL_FAMILY = InternetProtocolFamily.IPv4;\n+            logger.debug(\"-Djava.net.preferIPv6Addresses: false\");\n+        }\n+    }\n+\n+    private static final DnsResponseDecoder DECODER = new DnsResponseDecoder();\n+    private static final DnsQueryEncoder ENCODER = new DnsQueryEncoder();\n+\n+    final Iterable<InetSocketAddress> nameServerAddresses;\n+    final DatagramChannel ch;\n+\n+    /**\n+     * An array whose index is the ID of a DNS query and whose value is the promise of the corresponsing response. We\n+     * don't use {@link IntObjectHashMap} or map-like data structure here because 64k elements are fairly small, which\n+     * is only about 512KB.\n+     */\n+    final AtomicReferenceArray<DnsQueryContext> promises = new AtomicReferenceArray<DnsQueryContext>(65536);\n+\n+    /**\n+     * The cache for {@link #query(DnsQuestion)}\n+     */\n+    final ConcurrentMap<DnsQuestion, DnsCacheEntry> queryCache = PlatformDependent.newConcurrentHashMap();\n+\n+    private final DnsResponseHandler responseHandler = new DnsResponseHandler();\n+\n+    private volatile long queryTimeoutMillis = 5000;\n+\n+    // The default TTL values here respect the TTL returned by the DNS server and do not cache the negative response.\n+    private volatile int minTtl;\n+    private volatile int maxTtl = Integer.MAX_VALUE;\n+    private volatile int negativeTtl;\n+    private volatile int maxTriesPerQuery = 2;\n+\n+    private volatile InternetProtocolFamily preferredAddressType = PREFERRED_PROTOCOL_FAMILY;\n+    private volatile boolean recursionDesired = true;\n+    private volatile int maxQueriesPerResolve = 8;\n+\n+    private volatile int maxPayloadSize;\n+    private volatile DnsClass maxPayloadSizeClass; // EDNS uses the CLASS field as the payload size field.\n+\n+    /**\n+     * Creates a new DNS-based name resolver that communicates with a single DNS server.\n+     *\n+     * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers\n+     * @param channelType the type of the {@link DatagramChannel} to create\n+     * @param nameServerAddress the address of the DNS server\n+     */\n+    public DnsNameResolver(\n+            EventLoop eventLoop, Class<? extends DatagramChannel> channelType,\n+            InetSocketAddress nameServerAddress) {\n+        this(eventLoop, channelType, ANY_LOCAL_ADDR, nameServerAddress);\n+    }\n+\n+    /**\n+     * Creates a new DNS-based name resolver that communicates with a single DNS server.\n+     *\n+     * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers\n+     * @param channelType the type of the {@link DatagramChannel} to create\n+     * @param localAddress the local address of the {@link DatagramChannel}\n+     * @param nameServerAddress the address of the DNS server\n+     */\n+    public DnsNameResolver(\n+            EventLoop eventLoop, Class<? extends DatagramChannel> channelType,\n+            InetSocketAddress localAddress, InetSocketAddress nameServerAddress) {\n+        this(eventLoop, new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddress);\n+    }\n+\n+    /**\n+     * Creates a new DNS-based name resolver that communicates with a single DNS server.\n+     *\n+     * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers\n+     * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}\n+     * @param nameServerAddress the address of the DNS server\n+     */\n+    public DnsNameResolver(\n+            EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,\n+            InetSocketAddress nameServerAddress) {\n+        this(eventLoop, channelFactory, ANY_LOCAL_ADDR, nameServerAddress);\n+    }\n+\n+    /**\n+     * Creates a new DNS-based name resolver that communicates with a single DNS server.\n+     *\n+     * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers\n+     * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}\n+     * @param localAddress the local address of the {@link DatagramChannel}\n+     * @param nameServerAddress the address of the DNS server\n+     */\n+    public DnsNameResolver(\n+            EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,\n+            InetSocketAddress localAddress, InetSocketAddress nameServerAddress) {\n+        this(eventLoop, channelFactory, localAddress, DnsServerAddresses.singleton(nameServerAddress));\n+    }\n+\n+    /**\n+     * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.\n+     *\n+     * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers\n+     * @param channelType the type of the {@link DatagramChannel} to create\n+     * @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new {@link Iterator} is\n+     *                            created from this {@link Iterable} to determine which DNS server should be contacted\n+     *                            for the next retry in case of failure.\n+     */\n+    public DnsNameResolver(\n+            EventLoop eventLoop, Class<? extends DatagramChannel> channelType,\n+            Iterable<InetSocketAddress> nameServerAddresses) {\n+        this(eventLoop, channelType, ANY_LOCAL_ADDR, nameServerAddresses);\n+    }\n+\n+    /**\n+     * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.\n+     *\n+     * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers\n+     * @param channelType the type of the {@link DatagramChannel} to create\n+     * @param localAddress the local address of the {@link DatagramChannel}\n+     * @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new {@link Iterator} is\n+     *                            created from this {@link Iterable} to determine which DNS server should be contacted\n+     *                            for the next retry in case of failure.\n+     */\n+    public DnsNameResolver(\n+            EventLoop eventLoop, Class<? extends DatagramChannel> channelType,\n+            InetSocketAddress localAddress, Iterable<InetSocketAddress> nameServerAddresses) {\n+        this(eventLoop, new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddresses);\n+    }\n+\n+    /**\n+     * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.\n+     *\n+     * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers\n+     * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}\n+     * @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new {@link Iterator} is\n+     *                            created from this {@link Iterable} to determine which DNS server should be contacted\n+     *                            for the next retry in case of failure.\n+     */\n+    public DnsNameResolver(\n+            EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,\n+            Iterable<InetSocketAddress> nameServerAddresses) {\n+        this(eventLoop, channelFactory, ANY_LOCAL_ADDR, nameServerAddresses);\n+    }\n+\n+    /**\n+     * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.\n+     *\n+     * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers\n+     * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}\n+     * @param localAddress the local address of the {@link DatagramChannel}\n+     * @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new {@link Iterator} is\n+     *                            created from this {@link Iterable} to determine which DNS server should be contacted\n+     *                            for the next retry in case of failure.\n+     */\n+    public DnsNameResolver(\n+            EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,\n+            InetSocketAddress localAddress, Iterable<InetSocketAddress> nameServerAddresses) {\n+\n+        super(eventLoop);\n+\n+        if (channelFactory == null) {\n+            throw new NullPointerException(\"channelFactory\");\n+        }\n+        if (nameServerAddresses == null) {\n+            throw new NullPointerException(\"nameServerAddresses\");\n+        }\n+        if (!nameServerAddresses.iterator().hasNext()) {\n+            throw new NullPointerException(\"nameServerAddresses is empty\");\n+        }\n+        if (localAddress == null) {\n+            throw new NullPointerException(\"localAddress\");\n+        }\n+\n+        this.nameServerAddresses = nameServerAddresses;\n+        ch = newChannel(channelFactory, localAddress);\n+\n+        setMaxPayloadSize(4096);\n+    }\n+\n+    private DatagramChannel newChannel(\n+            ChannelFactory<? extends DatagramChannel> channelFactory, InetSocketAddress localAddress) {\n+\n+        Bootstrap b = new Bootstrap();\n+        b.group(executor());\n+        b.channelFactory(channelFactory);\n+        b.handler(new ChannelInitializer<DatagramChannel>() {\n+            @Override\n+            protected void initChannel(DatagramChannel ch) throws Exception {\n+                ch.pipeline().addLast(DECODER, ENCODER, responseHandler);\n+            }\n+        });\n+\n+        DatagramChannel ch = (DatagramChannel) b.bind(localAddress).channel();\n+        ch.closeFuture().addListener(new ChannelFutureListener() {\n+            @Override\n+            public void operationComplete(ChannelFuture future) throws Exception {\n+                clearCache();\n+            }\n+        });\n+\n+        return ch;\n+    }\n+\n+    /**\n+     * Returns the minimum TTL of the cached DNS resource records (in seconds).\n+     *\n+     * @see #maxTtl()\n+     * @see #setTtl(int, int)\n+     */\n+    public int minTtl() {\n+        return minTtl;\n+    }\n+\n+    /**\n+     * Returns the maximum TTL of the cached DNS resource records (in seconds).\n+     *\n+     * @see #minTtl()\n+     * @see #setTtl(int, int)\n+     */\n+    public int maxTtl() {\n+        return maxTtl;\n+    }\n+\n+    /**\n+     * Sets the minimum and maximum TTL of the cached DNS resource records (in seconds). If the TTL of the DNS resource\n+     * record returned by the DNS server is less than the minimum TTL or greater than the maximum TTL, this resolver\n+     * will ignore the TTL from the DNS server and use the minimum TTL or the maximum TTL instead respectively.\n+     * The default value is {@code 0} and {@link Integer#MAX_VALUE}, which practically tells this resolver to respect\n+     * the TTL from the DNS server.\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #minTtl()\n+     * @see #maxTtl()\n+     */\n+    public DnsNameResolver setTtl(int minTtl, int maxTtl) {\n+        if (minTtl < 0) {\n+            throw new IllegalArgumentException(\"minTtl: \" + minTtl + \" (expected: >= 0)\");\n+        }\n+        if (maxTtl < 0) {\n+            throw new IllegalArgumentException(\"maxTtl: \" + maxTtl + \" (expected: >= 0)\");\n+        }\n+        if (minTtl > maxTtl) {\n+            throw new IllegalArgumentException(\n+                    \"minTtl: \" + minTtl + \", maxTtl: \" + maxTtl + \" (expected: 0 <= minTtl <= maxTtl)\");\n+        }\n+\n+        this.maxTtl = maxTtl;\n+        this.minTtl = minTtl;\n+\n+        return this;\n+    }\n+\n+    /**\n+     * Returns the TTL of the cache for the failed DNS queries (in seconds).  The default value is {@code 0}, which\n+     * disables the cache for negative results.\n+     *\n+     * @see #setNegativeTtl(int)\n+     */\n+    public int negativeTtl() {\n+        return negativeTtl;\n+    }\n+\n+    /**\n+     * Sets the TTL of the cache for the failed DNS queries (in seconds).\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #negativeTtl()\n+     */\n+    public DnsNameResolver setNegativeTtl(int negativeTtl) {\n+        if (negativeTtl < 0) {\n+            throw new IllegalArgumentException(\"negativeTtl: \" + negativeTtl + \" (expected: >= 0)\");\n+        }\n+\n+        this.negativeTtl = negativeTtl;\n+\n+        return this;\n+    }\n+\n+    /**\n+     * Returns the timeout of each DNS query performed by this resolver (in milliseconds).\n+     * The default value is 5 seconds.\n+     *\n+     * @see #setQueryTimeoutMillis(long)\n+     */\n+    public long queryTimeoutMillis() {\n+        return queryTimeoutMillis;\n+    }\n+\n+    /**\n+     * Sets the timeout of each DNS query performed by this resolver (in milliseconds).\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #queryTimeoutMillis()\n+     */\n+    public DnsNameResolver setQueryTimeoutMillis(long queryTimeoutMillis) {\n+        if (queryTimeoutMillis < 0) {\n+            throw new IllegalArgumentException(\"queryTimeoutMillis: \" + queryTimeoutMillis + \" (expected: >= 0)\");\n+        }\n+\n+        this.queryTimeoutMillis = queryTimeoutMillis;\n+\n+        return this;\n+    }\n+\n+    /**\n+     * Returns the maximum number of tries for each query. The default value is 2 times.\n+     *\n+     * @see #setMaxTriesPerQuery(int)\n+     */\n+    public int maxTriesPerQuery() {\n+        return maxTriesPerQuery;\n+    }\n+\n+    /**\n+     * Sets the maximum number of tries for each query.\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #maxTriesPerQuery()\n+     */\n+    public DnsNameResolver setMaxTriesPerQuery(int maxTriesPerQuery) {\n+        if (maxTriesPerQuery < 1) {\n+            throw new IllegalArgumentException(\"maxTries: \" + maxTriesPerQuery + \" (expected: > 0)\");\n+        }\n+\n+        this.maxTriesPerQuery = maxTriesPerQuery;\n+\n+        return this;\n+    }\n+\n+    /**\n+     * Returns the preferred protocol family of the resolved address.\n+     * The default value depends on the value of the system property {@code \"java.net.preferIPv6Addresses\"}.\n+     *\n+     * @see #setPreferredAddressType(InternetProtocolFamily)\n+     */\n+    public InternetProtocolFamily preferredAddressType() {\n+        return preferredAddressType;\n+    }\n+\n+    /**\n+     * Sets the preferred protocol family of the resolved address. If a host name has both IPv4 and IPv6 addresses,\n+     * this resolver will choose the one of the preferred protocol family.\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #preferredAddressType()\n+     */\n+    public DnsNameResolver setPreferredAddressType(InternetProtocolFamily preferredAddressType) {\n+        if (preferredAddressType == null) {\n+            throw new NullPointerException(\"preferredAddressType\");\n+        }\n+\n+        this.preferredAddressType = preferredAddressType;\n+\n+        return this;\n+    }\n+\n+    /**\n+     * Returns {@code true} if and only if this resolver sends a DNS query with the RD (recursion desired) flag set.\n+     * The default value is {@code true}.\n+     *\n+     * @see #setRecursionDesired(boolean)\n+     */\n+    public boolean isRecursionDesired() {\n+        return recursionDesired;\n+    }\n+\n+    /**\n+     * Sets if this resolver has to send a DNS query with the RD (recursion desired) flag set.\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #isRecursionDesired()\n+     */\n+    public DnsNameResolver setRecursionDesired(boolean recursionDesired) {\n+        this.recursionDesired = recursionDesired;\n+        return this;\n+    }\n+\n+    /**\n+     * Returns the maximum allowed number of DNS queries to send when resolving a host name.\n+     * The default value is {@code 8}.\n+     *\n+     * @see #setMaxQueriesPerResolve(int)\n+     */\n+    public int maxQueriesPerResolve() {\n+        return maxQueriesPerResolve;\n+    }\n+\n+    /**\n+     * Sets the maximum allowed number of DNS queries to send when resolving a host name.\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #maxQueriesPerResolve()\n+     */\n+    public DnsNameResolver setMaxQueriesPerResolve(int maxQueriesPerResolve) {\n+        if (maxQueriesPerResolve <= 0) {\n+            throw new IllegalArgumentException(\"maxQueriesPerResolve: \" + maxQueriesPerResolve + \" (expected: > 0)\");\n+        }\n+\n+        this.maxQueriesPerResolve = maxQueriesPerResolve;\n+\n+        return this;\n+    }\n+\n+    /**\n+     * Returns the capacity of the datagram packet buffer (in bytes).  The default value is {@code 4096} bytes.\n+     *\n+     * @see #setMaxPayloadSize(int)\n+     */\n+    public int maxPayloadSize() {\n+        return maxPayloadSize;\n+    }\n+\n+    /**\n+     * Sets the capacity of the datagram packet buffer (in bytes).  The default value is {@code 4096} bytes.\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #maxPayloadSize()\n+     */\n+    public DnsNameResolver setMaxPayloadSize(int maxPayloadSize) {\n+        if (maxPayloadSize <= 0) {\n+            throw new IllegalArgumentException(\"maxPayloadSize: \" + maxPayloadSize + \" (expected: > 0)\");\n+        }\n+\n+        this.maxPayloadSize = maxPayloadSize;\n+        maxPayloadSizeClass = DnsClass.valueOf(maxPayloadSize);\n+        ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));\n+\n+        return this;\n+    }\n+\n+    DnsClass maxPayloadSizeClass() {\n+        return maxPayloadSizeClass;\n+    }\n+\n+    /**\n+     * Clears all the DNS resource records cached by this resolver.\n+     *\n+     * @return {@code this}\n+     *\n+     * @see #clearCache(DnsQuestion)\n+     */\n+    public DnsNameResolver clearCache() {\n+        for (Iterator<Entry<DnsQuestion, DnsCacheEntry>> i = queryCache.entrySet().iterator(); i.hasNext();) {\n+            Entry<DnsQuestion, DnsCacheEntry> e = i.next();\n+            i.remove();\n+            e.getValue().release();\n+        }\n+\n+        return this;\n+    }\n+\n+    /**\n+     * Clears the DNS resource record of the specified DNS question from the cache of this resolver.\n+     */\n+    public boolean clearCache(DnsQuestion question) {\n+        DnsCacheEntry e = queryCache.remove(question);\n+        if (e != null) {\n+            e.release();\n+            return true;\n+        } else {\n+            return false;\n+        }\n+    }\n+\n+    /**\n+     * Closes the internal datagram channel used for sending and receiving DNS messages, and clears all DNS resource\n+     * records from the cache. Attempting to send a DNS query or to resolve a domain name will fail once this method\n+     * has been called.\n+     */\n+    @Override\n+    public void close() {\n+        ch.close();\n+    }\n+\n+    @Override\n+    protected EventLoop executor() {\n+        return (EventLoop) super.executor();\n+    }\n+\n+    @Override\n+    protected boolean doIsResolved(InetSocketAddress address) {\n+        return !address.isUnresolved();\n+    }\n+\n+    @Override\n+    protected void doResolve(InetSocketAddress unresolvedAddress, Promise<InetSocketAddress> promise) throws Exception {\n+        final String hostname = IDN.toASCII(unresolvedAddress.getHostString());\n+        final int port = unresolvedAddress.getPort();\n+\n+        final DnsNameResolverContext ctx = new DnsNameResolverContext(this, hostname, port, promise);\n+\n+        ctx.resolve();\n+    }\n+\n+    /**\n+     * Sends a DNS query with the specified question.\n+     */\n+    public Future<DnsResponse> query(DnsQuestion question) {\n+        return query(nameServerAddresses, question);\n+    }\n+\n+    /**\n+     * Sends a DNS query with the specified question.\n+     */\n+    public Future<DnsResponse> query(DnsQuestion question, Promise<DnsResponse> promise) {\n+        return query(nameServerAddresses, question, promise);\n+    }\n+\n+    /**\n+     * Sends a DNS query with the specified question using the specified name server list.\n+     */\n+    public Future<DnsResponse> query(Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question) {\n+        if (nameServerAddresses == null) {\n+            throw new NullPointerException(\"nameServerAddresses\");\n+        }\n+        if (question == null) {\n+            throw new NullPointerException(\"question\");\n+        }\n+\n+        final EventLoop eventLoop = ch.eventLoop();\n+        final DnsCacheEntry cachedResult = queryCache.get(question);\n+        if (cachedResult != null) {\n+            if (cachedResult.response != null) {\n+                return eventLoop.newSucceededFuture(cachedResult.response.retain());\n+            } else {\n+                return eventLoop.newFailedFuture(cachedResult.cause);\n+            }\n+        } else {\n+            return query0(nameServerAddresses, question, eventLoop.<DnsResponse>newPromise());\n+        }\n+    }\n+\n+    /**\n+     * Sends a DNS query with the specified question using the specified name server list.\n+     */\n+    public Future<DnsResponse> query(\n+            Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question, Promise<DnsResponse> promise) {\n+\n+        if (nameServerAddresses == null) {\n+            throw new NullPointerException(\"nameServerAddresses\");\n+        }\n+        if (question == null) {\n+            throw new NullPointerException(\"question\");\n+        }\n+        if (promise == null) {\n+            throw new NullPointerException(\"promise\");\n+        }\n+\n+        final DnsCacheEntry cachedResult = queryCache.get(question);\n+        if (cachedResult != null) {\n+            if (cachedResult.response != null) {\n+                return promise.setSuccess(cachedResult.response.retain());\n+            } else {\n+                return promise.setFailure(cachedResult.cause);\n+            }\n+        } else {\n+            return query0(nameServerAddresses, question, promise);\n+        }","path":"resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java","position":642,"original_position":637,"commit_id":"763b38409325168144db081c5cacc5a6cec59bc5","original_commit_id":"736bfd8e49970b6cc77398aa6798bd5ad3de66ae","user":{"login":"normanmaurer","id":439362,"avatar_url":"https://avatars.githubusercontent.com/u/439362?v=2","gravatar_id":"","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","site_admin":false},"body":"you are right... I need more coffee :/ Ignore me.","created_at":"2014-10-14T09:28:17Z","updated_at":"2014-10-14T09:28:17Z","html_url":"https://github.com/netty/netty/pull/2917#discussion_r18817164","pull_request_url":"https://api.github.com/repos/netty/netty/pulls/2917","_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/comments/18817164"},"html":{"href":"https://github.com/netty/netty/pull/2917#discussion_r18817164"},"pull_request":{"href":"https://api.github.com/repos/netty/netty/pulls/2917"}}},"pull_request":{"url":"https://api.github.com/repos/netty/netty/pulls/2917","id":21499968,"html_url":"https://github.com/netty/netty/pull/2917","diff_url":"https://github.com/netty/netty/pull/2917.diff","patch_url":"https://github.com/netty/netty/pull/2917.patch","issue_url":"https://api.github.com/repos/netty/netty/issues/2917","number":2917,"state":"open","locked":false,"title":"Name resolver API + implementations","user":{"login":"trustin","id":173918,"avatar_url":"https://avatars.githubusercontent.com/u/173918?v=2","gravatar_id":"","url":"https://api.github.com/users/trustin","html_url":"https://github.com/trustin","followers_url":"https://api.github.com/users/trustin/followers","following_url":"https://api.github.com/users/trustin/following{/other_user}","gists_url":"https://api.github.com/users/trustin/gists{/gist_id}","starred_url":"https://api.github.com/users/trustin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trustin/subscriptions","organizations_url":"https://api.github.com/users/trustin/orgs","repos_url":"https://api.github.com/users/trustin/repos","events_url":"https://api.github.com/users/trustin/events{/privacy}","received_events_url":"https://api.github.com/users/trustin/received_events","type":"User","site_admin":false},"body":"Motivation:\r\n\r\nSo far, we relied on the domain name resolution mechanism provided by\r\nJDK.  It served its purpose very well, but had the following\r\nshortcomings:\r\n\r\n- Domain name resolution is performed in a blocking manner.\r\n  This becomes a problem when a user has to connect to thousands of\r\n  different hosts. e.g. web crawlers\r\n- It is impossible to employ an alternative cache/retry policy.\r\n  e.g. lower/upper bound in TTL, round-robin\r\n- It is impossible to employ an alternative name resolution mechanism.\r\n  e.g. Zookeeper-based name resolver\r\n\r\nModification:\r\n\r\n- Add the resolver API in the new module: netty-resolver\r\n- Implement the DNS-based resolver: netty-resolver-dns\r\n  .. which uses netty-codec-dns\r\n- Make ChannelFactory reusable because it's now used by\r\n  io.netty.bootstrap, io.netty.resolver.dns, and potentially by other\r\n  modules in the future\r\n  - Move ChannelFactory from io.netty.bootstrap to io.netty.channel\r\n  - Deprecate the old ChannelFactory\r\n  - Add ReflectiveChannelFactory\r\n\r\nResult:\r\n\r\nIt is trivial to resolve a large number of domain names asynchronously.\r\n\r\nExample:\r\n\r\n```java\r\nNameResolverGroup<InetSocketAddress> resolvers = new DnsNameResolver(...);\r\nBootstrap b = new Bootstrap();\r\n...\r\nb.resolver(resolvers);\r\n...\r\nb.connect();\r\n\r\nresolvers.close();\r\n```","created_at":"2014-09-19T14:15:12Z","updated_at":"2014-10-14T09:28:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f5efc5fcc96235c261fd98b54cedc0c11326f61c","assignee":{"login":"trustin","id":173918,"avatar_url":"https://avatars.githubusercontent.com/u/173918?v=2","gravatar_id":"","url":"https://api.github.com/users/trustin","html_url":"https://github.com/trustin","followers_url":"https://api.github.com/users/trustin/followers","following_url":"https://api.github.com/users/trustin/following{/other_user}","gists_url":"https://api.github.com/users/trustin/gists{/gist_id}","starred_url":"https://api.github.com/users/trustin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trustin/subscriptions","organizations_url":"https://api.github.com/users/trustin/orgs","repos_url":"https://api.github.com/users/trustin/repos","events_url":"https://api.github.com/users/trustin/events{/privacy}","received_events_url":"https://api.github.com/users/trustin/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/netty/netty/milestones/84","labels_url":"https://api.github.com/repos/netty/netty/milestones/84/labels","id":561880,"number":84,"title":"4.1.0.Final","description":null,"creator":{"login":"trustin","id":173918,"avatar_url":"https://avatars.githubusercontent.com/u/173918?v=2","gravatar_id":"","url":"https://api.github.com/users/trustin","html_url":"https://github.com/trustin","followers_url":"https://api.github.com/users/trustin/followers","following_url":"https://api.github.com/users/trustin/following{/other_user}","gists_url":"https://api.github.com/users/trustin/gists{/gist_id}","starred_url":"https://api.github.com/users/trustin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trustin/subscriptions","organizations_url":"https://api.github.com/users/trustin/orgs","repos_url":"https://api.github.com/users/trustin/repos","events_url":"https://api.github.com/users/trustin/events{/privacy}","received_events_url":"https://api.github.com/users/trustin/received_events","type":"User","site_admin":false},"open_issues":20,"closed_issues":7,"state":"open","created_at":"2014-02-07T08:42:42Z","updated_at":"2014-10-14T05:49:11Z","due_on":null},"commits_url":"https://api.github.com/repos/netty/netty/pulls/2917/commits","review_comments_url":"https://api.github.com/repos/netty/netty/pulls/2917/comments","review_comment_url":"https://api.github.com/repos/netty/netty/pulls/comments/{number}","comments_url":"https://api.github.com/repos/netty/netty/issues/2917/comments","statuses_url":"https://api.github.com/repos/netty/netty/statuses/763b38409325168144db081c5cacc5a6cec59bc5","head":{"label":"netty:name_resolver","ref":"name_resolver","sha":"763b38409325168144db081c5cacc5a6cec59bc5","user":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=2","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"repo":{"id":1064563,"name":"netty","full_name":"netty/netty","owner":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=2","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/netty/netty","description":"Netty project - an event-driven asynchronous network application framework","fork":false,"url":"https://api.github.com/repos/netty/netty","forks_url":"https://api.github.com/repos/netty/netty/forks","keys_url":"https://api.github.com/repos/netty/netty/keys{/key_id}","collaborators_url":"https://api.github.com/repos/netty/netty/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/netty/netty/teams","hooks_url":"https://api.github.com/repos/netty/netty/hooks","issue_events_url":"https://api.github.com/repos/netty/netty/issues/events{/number}","events_url":"https://api.github.com/repos/netty/netty/events","assignees_url":"https://api.github.com/repos/netty/netty/assignees{/user}","branches_url":"https://api.github.com/repos/netty/netty/branches{/branch}","tags_url":"https://api.github.com/repos/netty/netty/tags","blobs_url":"https://api.github.com/repos/netty/netty/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/netty/netty/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/netty/netty/git/refs{/sha}","trees_url":"https://api.github.com/repos/netty/netty/git/trees{/sha}","statuses_url":"https://api.github.com/repos/netty/netty/statuses/{sha}","languages_url":"https://api.github.com/repos/netty/netty/languages","stargazers_url":"https://api.github.com/repos/netty/netty/stargazers","contributors_url":"https://api.github.com/repos/netty/netty/contributors","subscribers_url":"https://api.github.com/repos/netty/netty/subscribers","subscription_url":"https://api.github.com/repos/netty/netty/subscription","commits_url":"https://api.github.com/repos/netty/netty/commits{/sha}","git_commits_url":"https://api.github.com/repos/netty/netty/git/commits{/sha}","comments_url":"https://api.github.com/repos/netty/netty/comments{/number}","issue_comment_url":"https://api.github.com/repos/netty/netty/issues/comments/{number}","contents_url":"https://api.github.com/repos/netty/netty/contents/{+path}","compare_url":"https://api.github.com/repos/netty/netty/compare/{base}...{head}","merges_url":"https://api.github.com/repos/netty/netty/merges","archive_url":"https://api.github.com/repos/netty/netty/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/netty/netty/downloads","issues_url":"https://api.github.com/repos/netty/netty/issues{/number}","pulls_url":"https://api.github.com/repos/netty/netty/pulls{/number}","milestones_url":"https://api.github.com/repos/netty/netty/milestones{/number}","notifications_url":"https://api.github.com/repos/netty/netty/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/netty/netty/labels{/name}","releases_url":"https://api.github.com/repos/netty/netty/releases{/id}","created_at":"2010-11-09T09:22:21Z","updated_at":"2014-10-14T06:42:48Z","pushed_at":"2014-10-14T09:24:14Z","git_url":"git://github.com/netty/netty.git","ssh_url":"git@github.com:netty/netty.git","clone_url":"https://github.com/netty/netty.git","svn_url":"https://github.com/netty/netty","homepage":"http://netty.io/","size":118031,"stargazers_count":3609,"watchers_count":3609,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1710,"mirror_url":null,"open_issues_count":185,"forks":1710,"open_issues":185,"watchers":3609,"default_branch":"master"}},"base":{"label":"netty:4.1","ref":"4.1","sha":"2309a75d15ffbd063b9a710df4fa1ec3c63702f9","user":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=2","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"repo":{"id":1064563,"name":"netty","full_name":"netty/netty","owner":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=2","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/netty/netty","description":"Netty project - an event-driven asynchronous network application framework","fork":false,"url":"https://api.github.com/repos/netty/netty","forks_url":"https://api.github.com/repos/netty/netty/forks","keys_url":"https://api.github.com/repos/netty/netty/keys{/key_id}","collaborators_url":"https://api.github.com/repos/netty/netty/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/netty/netty/teams","hooks_url":"https://api.github.com/repos/netty/netty/hooks","issue_events_url":"https://api.github.com/repos/netty/netty/issues/events{/number}","events_url":"https://api.github.com/repos/netty/netty/events","assignees_url":"https://api.github.com/repos/netty/netty/assignees{/user}","branches_url":"https://api.github.com/repos/netty/netty/branches{/branch}","tags_url":"https://api.github.com/repos/netty/netty/tags","blobs_url":"https://api.github.com/repos/netty/netty/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/netty/netty/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/netty/netty/git/refs{/sha}","trees_url":"https://api.github.com/repos/netty/netty/git/trees{/sha}","statuses_url":"https://api.github.com/repos/netty/netty/statuses/{sha}","languages_url":"https://api.github.com/repos/netty/netty/languages","stargazers_url":"https://api.github.com/repos/netty/netty/stargazers","contributors_url":"https://api.github.com/repos/netty/netty/contributors","subscribers_url":"https://api.github.com/repos/netty/netty/subscribers","subscription_url":"https://api.github.com/repos/netty/netty/subscription","commits_url":"https://api.github.com/repos/netty/netty/commits{/sha}","git_commits_url":"https://api.github.com/repos/netty/netty/git/commits{/sha}","comments_url":"https://api.github.com/repos/netty/netty/comments{/number}","issue_comment_url":"https://api.github.com/repos/netty/netty/issues/comments/{number}","contents_url":"https://api.github.com/repos/netty/netty/contents/{+path}","compare_url":"https://api.github.com/repos/netty/netty/compare/{base}...{head}","merges_url":"https://api.github.com/repos/netty/netty/merges","archive_url":"https://api.github.com/repos/netty/netty/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/netty/netty/downloads","issues_url":"https://api.github.com/repos/netty/netty/issues{/number}","pulls_url":"https://api.github.com/repos/netty/netty/pulls{/number}","milestones_url":"https://api.github.com/repos/netty/netty/milestones{/number}","notifications_url":"https://api.github.com/repos/netty/netty/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/netty/netty/labels{/name}","releases_url":"https://api.github.com/repos/netty/netty/releases{/id}","created_at":"2010-11-09T09:22:21Z","updated_at":"2014-10-14T06:42:48Z","pushed_at":"2014-10-14T09:24:14Z","git_url":"git://github.com/netty/netty.git","ssh_url":"git@github.com:netty/netty.git","clone_url":"https://github.com/netty/netty.git","svn_url":"https://github.com/netty/netty","homepage":"http://netty.io/","size":118031,"stargazers_count":3609,"watchers_count":3609,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1710,"mirror_url":null,"open_issues_count":185,"forks":1710,"open_issues":185,"watchers":3609,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/2917"},"html":{"href":"https://github.com/netty/netty/pull/2917"},"issue":{"href":"https://api.github.com/repos/netty/netty/issues/2917"},"comments":{"href":"https://api.github.com/repos/netty/netty/issues/2917/comments"},"review_comments":{"href":"https://api.github.com/repos/netty/netty/pulls/2917/comments"},"review_comment":{"href":"https://api.github.com/repos/netty/netty/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/netty/netty/pulls/2917/commits"},"statuses":{"href":"https://api.github.com/repos/netty/netty/statuses/763b38409325168144db081c5cacc5a6cec59bc5"}}}}