mirror of https://github.com/grpc/grpc-java.git
core: lookup TXT records when doing name resolution
This commit is contained in:
parent
e576c4cb4c
commit
b7833dab05
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.grpc.Attributes;
|
||||
|
|
@ -41,15 +43,22 @@ import io.grpc.internal.SharedResourceHolder.Resource;
|
|||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
|
||||
/**
|
||||
* A DNS-based {@link NameResolver}.
|
||||
|
|
@ -57,9 +66,19 @@ import javax.annotation.concurrent.GuardedBy;
|
|||
* <p>Each {@code A} or {@code AAAA} record emits an {@link EquivalentAddressGroup} in the list
|
||||
* passed to {@link NameResolver.Listener#onUpdate}
|
||||
*
|
||||
* @see DnsNameResolverFactory
|
||||
* @see DnsNameResolverProvider
|
||||
*/
|
||||
class DnsNameResolver extends NameResolver {
|
||||
final class DnsNameResolver extends NameResolver {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DnsNameResolver.class.getName());
|
||||
|
||||
private static final boolean isJndiAvailable = jndiAvailable();
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean enableJndi = false;
|
||||
|
||||
private DelegateResolver delegateResolver = pickDelegateResolver();
|
||||
|
||||
private final String authority;
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
|
@ -83,7 +102,6 @@ class DnsNameResolver extends NameResolver {
|
|||
Resource<ExecutorService> executorResource) {
|
||||
// TODO: if a DNS server is provided as nsAuthority, use it.
|
||||
// https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java
|
||||
|
||||
this.timerServiceResource = timerServiceResource;
|
||||
this.executorResource = executorResource;
|
||||
// Must prepend a "//" to the name when constructing a URI, otherwise it will be treated as an
|
||||
|
|
@ -128,7 +146,6 @@ class DnsNameResolver extends NameResolver {
|
|||
private final Runnable resolutionRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InetAddress[] inetAddrs;
|
||||
Listener savedListener;
|
||||
synchronized (DnsNameResolver.this) {
|
||||
// If this task is started by refresh(), there might already be a scheduled task.
|
||||
|
|
@ -149,10 +166,10 @@ class DnsNameResolver extends NameResolver {
|
|||
savedListener.onAddresses(Collections.singletonList(server), Attributes.EMPTY);
|
||||
return;
|
||||
}
|
||||
|
||||
ResolutionResults resolvedInetAddrs;
|
||||
try {
|
||||
inetAddrs = getAllByName(host);
|
||||
} catch (UnknownHostException e) {
|
||||
resolvedInetAddrs = delegateResolver.resolve(host);
|
||||
} catch (Exception e) {
|
||||
synchronized (DnsNameResolver.this) {
|
||||
if (shutdown) {
|
||||
return;
|
||||
|
|
@ -168,8 +185,7 @@ class DnsNameResolver extends NameResolver {
|
|||
}
|
||||
// Each address forms an EAG
|
||||
ArrayList<EquivalentAddressGroup> servers = new ArrayList<EquivalentAddressGroup>();
|
||||
for (int i = 0; i < inetAddrs.length; i++) {
|
||||
InetAddress inetAddr = inetAddrs[i];
|
||||
for (InetAddress inetAddr : resolvedInetAddrs.addresses) {
|
||||
servers.add(new EquivalentAddressGroup(new InetSocketAddress(inetAddr, port)));
|
||||
}
|
||||
savedListener.onAddresses(servers, Attributes.EMPTY);
|
||||
|
|
@ -192,12 +208,6 @@ class DnsNameResolver extends NameResolver {
|
|||
}
|
||||
};
|
||||
|
||||
// To be mocked out in tests
|
||||
@VisibleForTesting
|
||||
InetAddress[] getAllByName(String host) throws UnknownHostException {
|
||||
return InetAddress.getAllByName(host);
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void resolve() {
|
||||
if (resolving || shutdown) {
|
||||
|
|
@ -226,4 +236,150 @@ class DnsNameResolver extends NameResolver {
|
|||
final int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
private DelegateResolver pickDelegateResolver() {
|
||||
JdkResolver jdkResolver = new JdkResolver();
|
||||
if (isJndiAvailable && enableJndi) {
|
||||
return new CompositeResolver(jdkResolver, new JndiResolver());
|
||||
}
|
||||
return jdkResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the resolver. This should only be used by testing code.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setDelegateResolver(DelegateResolver delegateResolver) {
|
||||
this.delegateResolver = delegateResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the JNDI DNS resolver is available. This is accomplished by looking up a
|
||||
* particular class. It is believed to be the default (only?) DNS resolver that will actually be
|
||||
* used. It is provided by the OpenJDK, but unlikely Android. Actual resolution will be done by
|
||||
* using a service provider when a hostname query is present, so the {@code DnsContextFactory}
|
||||
* may not actually be used to perform the query. This is believed to be "okay."
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@SuppressWarnings("LiteralClassName")
|
||||
static boolean jndiAvailable() {
|
||||
try {
|
||||
Class.forName("javax.naming.directory.InitialDirContext");
|
||||
Class.forName("com.sun.jndi.dns.DnsContextFactory");
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.log(Level.FINE, "Unable to find JNDI DNS resolver, skipping", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common interface between the delegate resolvers used by DnsNameResolver.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
abstract static class DelegateResolver {
|
||||
abstract ResolutionResults resolve(String host) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the results from a DNS query.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final class ResolutionResults {
|
||||
final List<InetAddress> addresses;
|
||||
final List<String> txtRecords;
|
||||
|
||||
ResolutionResults(List<InetAddress> addresses, List<String> txtRecords) {
|
||||
this.addresses = Collections.unmodifiableList(checkNotNull(addresses, "addresses"));
|
||||
this.txtRecords = Collections.unmodifiableList(checkNotNull(txtRecords, "txtRecords"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A composite DNS resolver that uses both the JDK and JNDI resolvers as delegate. It is
|
||||
* expected that two DNS queries will be executed, with the second one being from JNDI.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final class CompositeResolver extends DelegateResolver {
|
||||
|
||||
private final DelegateResolver jdkResovler;
|
||||
private final DelegateResolver jndiResovler;
|
||||
|
||||
CompositeResolver(DelegateResolver jdkResovler, DelegateResolver jndiResovler) {
|
||||
this.jdkResovler = jdkResovler;
|
||||
this.jndiResovler = jndiResovler;
|
||||
}
|
||||
|
||||
@Override
|
||||
ResolutionResults resolve(String host) throws Exception {
|
||||
ResolutionResults jdkResults = jdkResovler.resolve(host);
|
||||
List<InetAddress> addresses = jdkResults.addresses;
|
||||
List<String> txtRecords = Collections.emptyList();
|
||||
try {
|
||||
ResolutionResults jdniResults = jndiResovler.resolve(host);
|
||||
txtRecords = jdniResults.txtRecords;
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Failed to resolve TXT results", e);
|
||||
}
|
||||
|
||||
return new ResolutionResults(addresses, txtRecords);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The default name resolver provided with the JDK. This is unable to lookup TXT records, but
|
||||
* provides address ordering sorted according to RFC 3484. This is true on OpenJDK, because it
|
||||
* in turn calls into libc which sorts addresses in order of reachability.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final class JdkResolver extends DelegateResolver {
|
||||
|
||||
@Override
|
||||
ResolutionResults resolve(String host) throws Exception {
|
||||
return new ResolutionResults(
|
||||
Arrays.asList(InetAddress.getAllByName(host)),
|
||||
Collections.<String>emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A resolver that uses JNDI. This class is capable of looking up both addresses
|
||||
* and text records, but does not provide ordering guarantees. It is currently not used for
|
||||
* address resolution.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final class JndiResolver extends DelegateResolver {
|
||||
|
||||
private static final String[] rrTypes = new String[]{"TXT"};
|
||||
|
||||
@Override
|
||||
ResolutionResults resolve(String host) throws NamingException {
|
||||
|
||||
InitialDirContext dirContext = new InitialDirContext();
|
||||
javax.naming.directory.Attributes attrs = dirContext.getAttributes("dns:///" + host, rrTypes);
|
||||
List<InetAddress> addresses = new ArrayList<InetAddress>();
|
||||
List<String> txtRecords = new ArrayList<String>();
|
||||
|
||||
NamingEnumeration<? extends Attribute> rrGroups = attrs.getAll();
|
||||
try {
|
||||
while (rrGroups.hasMore()) {
|
||||
Attribute rrEntry = rrGroups.next();
|
||||
assert Arrays.asList(rrTypes).contains(rrEntry.getID());
|
||||
NamingEnumeration<?> rrValues = rrEntry.getAll();
|
||||
try {
|
||||
while (rrValues.hasMore()) {
|
||||
String rrValue = (String) rrValues.next();
|
||||
txtRecords.add(rrValue);
|
||||
}
|
||||
} finally {
|
||||
rrValues.close();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
rrGroups.close();
|
||||
}
|
||||
|
||||
return new ResolutionResults(addresses, txtRecords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
package io.grpc.internal;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
|
@ -40,22 +41,30 @@ import static org.mockito.Mockito.times;
|
|||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.collect.Iterables;
|
||||
import io.grpc.Attributes;
|
||||
import io.grpc.EquivalentAddressGroup;
|
||||
import io.grpc.NameResolver;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.internal.DnsNameResolver.DelegateResolver;
|
||||
import io.grpc.internal.DnsNameResolver.ResolutionResults;
|
||||
import io.grpc.internal.SharedResourceHolder.Resource;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -75,6 +84,7 @@ public class DnsNameResolverTest {
|
|||
private final DnsNameResolverProvider provider = new DnsNameResolverProvider();
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
private final FakeClock fakeExecutor = new FakeClock();
|
||||
private MockResolver mockResolver = new MockResolver();
|
||||
private final Resource<ScheduledExecutorService> fakeTimerServiceResource =
|
||||
new Resource<ScheduledExecutorService>() {
|
||||
@Override
|
||||
|
|
@ -108,9 +118,21 @@ public class DnsNameResolverTest {
|
|||
@Captor
|
||||
private ArgumentCaptor<Status> statusCaptor;
|
||||
|
||||
private DnsNameResolver newResolver(String name, int port) {
|
||||
DnsNameResolver dnsResolver = new DnsNameResolver(
|
||||
null,
|
||||
name,
|
||||
Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(),
|
||||
fakeTimerServiceResource,
|
||||
fakeExecutorResource);
|
||||
dnsResolver.setDelegateResolver(mockResolver);
|
||||
return dnsResolver;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
DnsNameResolver.enableJndi = true;
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -143,21 +165,23 @@ public class DnsNameResolverTest {
|
|||
|
||||
@Test
|
||||
public void resolve() throws Exception {
|
||||
InetAddress[] answer1 = createAddressList(2);
|
||||
InetAddress[] answer2 = createAddressList(1);
|
||||
List<InetAddress> answer1 = createAddressList(2);
|
||||
List<InetAddress> answer2 = createAddressList(1);
|
||||
String name = "foo.googleapis.com";
|
||||
MockResolver resolver = new MockResolver(name, 81, answer1, answer2);
|
||||
|
||||
DnsNameResolver resolver = newResolver(name, 81);
|
||||
mockResolver.addAnswer(answer1).addAnswer(answer2);
|
||||
resolver.start(mockListener);
|
||||
assertEquals(1, fakeExecutor.runDueTasks());
|
||||
verify(mockListener).onAddresses(resultCaptor.capture(), any(Attributes.class));
|
||||
assertEquals(name, resolver.invocations.poll());
|
||||
assertEquals(name, mockResolver.invocations.poll());
|
||||
assertAnswerMatches(answer1, 81, resultCaptor.getValue());
|
||||
assertEquals(0, fakeClock.numPendingTasks());
|
||||
|
||||
resolver.refresh();
|
||||
assertEquals(1, fakeExecutor.runDueTasks());
|
||||
verify(mockListener, times(2)).onAddresses(resultCaptor.capture(), any(Attributes.class));
|
||||
assertEquals(name, resolver.invocations.poll());
|
||||
assertEquals(name, mockResolver.invocations.poll());
|
||||
assertAnswerMatches(answer2, 81, resultCaptor.getValue());
|
||||
assertEquals(0, fakeClock.numPendingTasks());
|
||||
|
||||
|
|
@ -168,12 +192,13 @@ public class DnsNameResolverTest {
|
|||
public void retry() throws Exception {
|
||||
String name = "foo.googleapis.com";
|
||||
UnknownHostException error = new UnknownHostException(name);
|
||||
InetAddress[] answer = createAddressList(2);
|
||||
MockResolver resolver = new MockResolver(name, 81, error, error, answer);
|
||||
List<InetAddress> answer = createAddressList(2);
|
||||
DnsNameResolver resolver = newResolver(name, 81);
|
||||
mockResolver.addAnswer(error).addAnswer(error).addAnswer(answer);
|
||||
resolver.start(mockListener);
|
||||
assertEquals(1, fakeExecutor.runDueTasks());
|
||||
verify(mockListener).onError(statusCaptor.capture());
|
||||
assertEquals(name, resolver.invocations.poll());
|
||||
assertEquals(name, mockResolver.invocations.poll());
|
||||
Status status = statusCaptor.getValue();
|
||||
assertEquals(Status.Code.UNAVAILABLE, status.getCode());
|
||||
assertSame(error, status.getCause());
|
||||
|
|
@ -187,7 +212,7 @@ public class DnsNameResolverTest {
|
|||
fakeClock.forwardNanos(1);
|
||||
assertEquals(1, fakeExecutor.runDueTasks());
|
||||
verify(mockListener, times(2)).onError(statusCaptor.capture());
|
||||
assertEquals(name, resolver.invocations.poll());
|
||||
assertEquals(name, mockResolver.invocations.poll());
|
||||
status = statusCaptor.getValue();
|
||||
assertEquals(Status.Code.UNAVAILABLE, status.getCode());
|
||||
assertSame(error, status.getCause());
|
||||
|
|
@ -202,7 +227,7 @@ public class DnsNameResolverTest {
|
|||
assertEquals(0, fakeClock.numPendingTasks());
|
||||
assertEquals(1, fakeExecutor.runDueTasks());
|
||||
verify(mockListener).onAddresses(resultCaptor.capture(), any(Attributes.class));
|
||||
assertEquals(name, resolver.invocations.poll());
|
||||
assertEquals(name, mockResolver.invocations.poll());
|
||||
assertAnswerMatches(answer, 81, resultCaptor.getValue());
|
||||
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
|
|
@ -212,12 +237,13 @@ public class DnsNameResolverTest {
|
|||
public void refreshCancelsScheduledRetry() throws Exception {
|
||||
String name = "foo.googleapis.com";
|
||||
UnknownHostException error = new UnknownHostException(name);
|
||||
InetAddress[] answer = createAddressList(2);
|
||||
MockResolver resolver = new MockResolver(name, 81, error, answer);
|
||||
List<InetAddress> answer = createAddressList(2);
|
||||
DnsNameResolver resolver = newResolver(name, 81);
|
||||
mockResolver.addAnswer(error).addAnswer(answer);
|
||||
resolver.start(mockListener);
|
||||
assertEquals(1, fakeExecutor.runDueTasks());
|
||||
verify(mockListener).onError(statusCaptor.capture());
|
||||
assertEquals(name, resolver.invocations.poll());
|
||||
assertEquals(name, mockResolver.invocations.poll());
|
||||
Status status = statusCaptor.getValue();
|
||||
assertEquals(Status.Code.UNAVAILABLE, status.getCode());
|
||||
assertSame(error, status.getCause());
|
||||
|
|
@ -230,7 +256,7 @@ public class DnsNameResolverTest {
|
|||
// Refresh cancelled the retry
|
||||
assertEquals(0, fakeClock.numPendingTasks());
|
||||
verify(mockListener).onAddresses(resultCaptor.capture(), any(Attributes.class));
|
||||
assertEquals(name, resolver.invocations.poll());
|
||||
assertEquals(name, mockResolver.invocations.poll());
|
||||
assertAnswerMatches(answer, 81, resultCaptor.getValue());
|
||||
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
|
|
@ -240,12 +266,13 @@ public class DnsNameResolverTest {
|
|||
public void shutdownCancelsScheduledRetry() throws Exception {
|
||||
String name = "foo.googleapis.com";
|
||||
UnknownHostException error = new UnknownHostException(name);
|
||||
MockResolver resolver = new MockResolver(name, 81, error);
|
||||
DnsNameResolver resolver = newResolver(name, 81);
|
||||
mockResolver.addAnswer(error);
|
||||
resolver.start(mockListener);
|
||||
assertEquals(1, fakeExecutor.runDueTasks());
|
||||
|
||||
verify(mockListener).onError(statusCaptor.capture());
|
||||
assertEquals(name, resolver.invocations.poll());
|
||||
assertEquals(name, mockResolver.invocations.poll());
|
||||
Status status = statusCaptor.getValue();
|
||||
assertEquals(Status.Code.UNAVAILABLE, status.getCode());
|
||||
assertSame(error, status.getCause());
|
||||
|
|
@ -260,6 +287,64 @@ public class DnsNameResolverTest {
|
|||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test(timeout = 10000)
|
||||
public void jdkResolverWorks() throws Exception {
|
||||
DnsNameResolver.DelegateResolver resolver = new DnsNameResolver.JdkResolver();
|
||||
|
||||
ResolutionResults results = resolver.resolve("localhost");
|
||||
// Just check that *something* came back.
|
||||
assertThat(results.addresses).isNotEmpty();
|
||||
assertThat(results.txtRecords).isNotNull();
|
||||
}
|
||||
|
||||
@Test(timeout = 10000)
|
||||
public void jndiResolverWorks() throws Exception {
|
||||
Assume.assumeTrue(DnsNameResolver.jndiAvailable());
|
||||
DnsNameResolver.DelegateResolver resolver = new DnsNameResolver.JndiResolver();
|
||||
ResolutionResults results = null;
|
||||
try {
|
||||
results = resolver.resolve("localhost");
|
||||
} catch (javax.naming.NameNotFoundException e) {
|
||||
Assume.assumeNoException(e);
|
||||
}
|
||||
|
||||
assertThat(results.addresses).isEmpty();
|
||||
assertThat(results.txtRecords).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compositeResolverPrefersJdkAddressJndiTxt() throws Exception {
|
||||
MockResolver jdkDelegate = new MockResolver();
|
||||
MockResolver jndiDelegate = new MockResolver();
|
||||
DelegateResolver resolver = new DnsNameResolver.CompositeResolver(jdkDelegate, jndiDelegate);
|
||||
|
||||
List<InetAddress> jdkAnswer = createAddressList(2);
|
||||
jdkDelegate.addAnswer(jdkAnswer, Arrays.asList("jdktxt"));
|
||||
|
||||
List<InetAddress> jdniAnswer = createAddressList(2);
|
||||
jndiDelegate.addAnswer(jdniAnswer, Arrays.asList("jnditxt"));
|
||||
|
||||
ResolutionResults results = resolver.resolve("abc");
|
||||
|
||||
assertThat(results.addresses).containsExactlyElementsIn(jdkAnswer).inOrder();
|
||||
assertThat(results.txtRecords).containsExactly("jnditxt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compositeResolverSkipsAbsentJndi() throws Exception {
|
||||
MockResolver jdkDelegate = new MockResolver();
|
||||
MockResolver jndiDelegate = null;
|
||||
DelegateResolver resolver = new DnsNameResolver.CompositeResolver(jdkDelegate, jndiDelegate);
|
||||
|
||||
List<InetAddress> jdkAnswer = createAddressList(2);
|
||||
jdkDelegate.addAnswer(jdkAnswer);
|
||||
|
||||
ResolutionResults results = resolver.resolve("abc");
|
||||
|
||||
assertThat(results.addresses).containsExactlyElementsIn(jdkAnswer).inOrder();
|
||||
assertThat(results.txtRecords).isEmpty();
|
||||
}
|
||||
|
||||
private void testInvalidUri(URI uri) {
|
||||
try {
|
||||
provider.newNameResolver(uri, NAME_RESOLVER_PARAMS);
|
||||
|
|
@ -278,47 +363,56 @@ public class DnsNameResolverTest {
|
|||
|
||||
private byte lastByte = 0;
|
||||
|
||||
private InetAddress[] createAddressList(int n) throws UnknownHostException {
|
||||
InetAddress[] list = new InetAddress[n];
|
||||
private List<InetAddress> createAddressList(int n) throws UnknownHostException {
|
||||
List<InetAddress> list = new ArrayList<InetAddress>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
list[i] = InetAddress.getByAddress(new byte[] {127, 0, 0, ++lastByte});
|
||||
list.add(InetAddress.getByAddress(new byte[] {127, 0, 0, ++lastByte}));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void assertAnswerMatches(
|
||||
InetAddress[] addrs, int port, List<EquivalentAddressGroup> results) {
|
||||
assertEquals(addrs.length, results.size());
|
||||
for (int i = 0; i < addrs.length; i++) {
|
||||
List<InetAddress> addrs, int port, List<EquivalentAddressGroup> results) {
|
||||
assertEquals(addrs.size(), results.size());
|
||||
for (int i = 0; i < addrs.size(); i++) {
|
||||
EquivalentAddressGroup addrGroup = results.get(i);
|
||||
InetSocketAddress socketAddr =
|
||||
(InetSocketAddress) Iterables.getOnlyElement(addrGroup.getAddresses());
|
||||
assertEquals("Addr " + i, port, socketAddr.getPort());
|
||||
assertEquals("Addr " + i, addrs[i], socketAddr.getAddress());
|
||||
assertEquals("Addr " + i, addrs.get(i), socketAddr.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
private class MockResolver extends DnsNameResolver {
|
||||
final LinkedList<Object> answers = new LinkedList<Object>();
|
||||
final LinkedList<String> invocations = new LinkedList<String>();
|
||||
private static class MockResolver extends DnsNameResolver.DelegateResolver {
|
||||
private final Queue<Object> answers = new LinkedList<Object>();
|
||||
private final Queue<String> invocations = new LinkedList<String>();
|
||||
|
||||
MockResolver(String name, int defaultPort, Object ... answers) {
|
||||
super(null, name, Attributes.newBuilder().set(
|
||||
NameResolver.Factory.PARAMS_DEFAULT_PORT, defaultPort).build(), fakeTimerServiceResource,
|
||||
fakeExecutorResource);
|
||||
for (Object answer : answers) {
|
||||
this.answers.add(answer);
|
||||
}
|
||||
MockResolver addAnswer(List<InetAddress> addresses) {
|
||||
return addAnswer(addresses, null);
|
||||
}
|
||||
|
||||
MockResolver addAnswer(List<InetAddress> addresses, List<String> txtRecords) {
|
||||
answers.add(
|
||||
new ResolutionResults(
|
||||
addresses,
|
||||
MoreObjects.firstNonNull(txtRecords, Collections.<String>emptyList())));
|
||||
return this;
|
||||
}
|
||||
|
||||
MockResolver addAnswer(UnknownHostException ex) {
|
||||
answers.add(ex);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // explosions acceptable.
|
||||
@Override
|
||||
InetAddress[] getAllByName(String host) throws UnknownHostException {
|
||||
ResolutionResults resolve(String host) throws Exception {
|
||||
invocations.add(host);
|
||||
Object answer = answers.poll();
|
||||
if (answer instanceof UnknownHostException) {
|
||||
throw (UnknownHostException) answer;
|
||||
}
|
||||
return (InetAddress[]) answer;
|
||||
return (ResolutionResults) answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue