diff --git a/core/src/main/java/io/grpc/EquivalentAddressGroup.java b/core/src/main/java/io/grpc/EquivalentAddressGroup.java index ef64a90be1..49efbbf6a0 100644 --- a/core/src/main/java/io/grpc/EquivalentAddressGroup.java +++ b/core/src/main/java/io/grpc/EquivalentAddressGroup.java @@ -31,6 +31,8 @@ package io.grpc; +import com.google.common.base.Preconditions; + import java.net.SocketAddress; import java.util.ArrayList; import java.util.Collections; @@ -49,6 +51,7 @@ public final class EquivalentAddressGroup { private final List addrs; public EquivalentAddressGroup(List addrs) { + Preconditions.checkArgument(!addrs.isEmpty(), "addrs is empty"); this.addrs = Collections.unmodifiableList(new ArrayList(addrs)); } diff --git a/core/src/main/java/io/grpc/LoadBalancer.java b/core/src/main/java/io/grpc/LoadBalancer.java index fbc2f79915..fec3ae222b 100644 --- a/core/src/main/java/io/grpc/LoadBalancer.java +++ b/core/src/main/java/io/grpc/LoadBalancer.java @@ -68,6 +68,9 @@ public abstract class LoadBalancer { * Handles newly resolved addresses and service config from name resolution system. * *

Implementations should not modify the given {@code servers}. + * + * @param servers the resolved server addresses. Never empty. + * @param config extra configuration data from naming system. */ public void handleResolvedAddresses(List servers, Attributes config) { } diff --git a/core/src/main/java/io/grpc/NameResolver.java b/core/src/main/java/io/grpc/NameResolver.java index b6c8e4d129..3e52107436 100644 --- a/core/src/main/java/io/grpc/NameResolver.java +++ b/core/src/main/java/io/grpc/NameResolver.java @@ -119,6 +119,9 @@ public abstract class NameResolver { * Handles updates on resolved addresses and config. * *

Implementations will not modify the given {@code servers}. + * + * @param servers the resolved server addresses. An empty list will trigger {@link #onError} + * @param config extra configuration data from naming system */ void onUpdate(List servers, Attributes config); diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java index 82f903a854..284cba4b17 100644 --- a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java +++ b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java @@ -168,7 +168,11 @@ public final class ManagedChannelImpl extends ManagedChannel implements WithLogI this.nameResolver.start(new NameResolver.Listener() { @Override public void onUpdate(List servers, Attributes config) { - loadBalancer.handleResolvedAddresses(servers, config); + if (servers.isEmpty()) { + onError(Status.UNAVAILABLE.withDescription("NameResolver returned an empty list")); + } else { + loadBalancer.handleResolvedAddresses(servers, config); + } } @Override diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java index ed49239d7a..ad75482e1b 100644 --- a/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java +++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java @@ -362,6 +362,27 @@ public class ManagedChannelImplTest { Status status = statusCaptor.getValue(); assertSame(error.getCode(), status.getCode()); assertSame(error.getCause(), status.getCause()); + assertEquals(1, loadBalancerFactory.balancers.size()); + verify(loadBalancerFactory.balancers.get(0)).handleNameResolutionError(same(error)); + } + + @Test + public void nameResolverReturnsEmptyList() { + String errorDescription = "NameResolver returned an empty list"; + ManagedChannel channel = createChannel( + new FakeNameResolverFactory(new ArrayList()), NO_INTERCEPTOR); + ClientCall call = channel.newCall(method, CallOptions.DEFAULT); + call.start(mockCallListener, new Metadata()); + ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Status.class); + verify(mockCallListener, timeout(1000)).onClose(statusCaptor.capture(), any(Metadata.class)); + Status status = statusCaptor.getValue(); + assertSame(Status.Code.UNAVAILABLE, status.getCode()); + assertTrue(status.getDescription(), status.getDescription().contains(errorDescription)); + assertEquals(1, loadBalancerFactory.balancers.size()); + verify(loadBalancerFactory.balancers.get(0)).handleNameResolutionError(statusCaptor.capture()); + status = statusCaptor.getValue(); + assertSame(Status.Code.UNAVAILABLE, status.getCode()); + assertEquals(errorDescription, status.getDescription()); } @Test