diff --git a/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java b/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java index 266bf5360c..f05e6dd6c9 100644 --- a/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java +++ b/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java @@ -313,7 +313,7 @@ public final class EnvoyServerProtoData { /** * Corresponds to Envoy proto message {@link io.envoyproxy.envoy.api.v2.listener.FilterChain}. */ - static final class FilterChain { + public static final class FilterChain { // Unique name for the FilterChain. private final String name; // TODO(sanjaypujare): flatten structure by moving FilterChainMatch class members here. @@ -391,7 +391,7 @@ public final class EnvoyServerProtoData { * Corresponds to Envoy proto message {@link io.envoyproxy.envoy.api.v2.Listener} & related * classes. */ - static final class Listener { + public static final class Listener { private final String name; @Nullable private final String address; @@ -399,7 +399,8 @@ public final class EnvoyServerProtoData { @Nullable private final FilterChain defaultFilterChain; - Listener(String name, @Nullable String address, + /** Construct a Listener. */ + public Listener(String name, @Nullable String address, List filterChains, @Nullable FilterChain defaultFilterChain) { this.name = checkNotNull(name, "name"); this.address = address; diff --git a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java index 819459d5c3..c02e47db02 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java +++ b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java @@ -238,6 +238,11 @@ public final class XdsClientWrapperForServerSds { } else if (filterChains.size() == 1) { return filterChains.get(0).getSslContextProviderSupplier(); } + if (listener.getDefaultFilterChain() == null) { + // close the connection + throw new RuntimeException( + "no matching filter chain. local: " + localInetAddr + " remote: " + remoteInetAddr); + } return listener.getDefaultFilterChain().getSslContextProviderSupplier(); } @@ -428,7 +433,7 @@ public final class XdsClientWrapperForServerSds { } @VisibleForTesting - XdsClient.LdsResourceWatcher getListenerWatcher() { + public XdsClient.LdsResourceWatcher getListenerWatcher() { return listenerWatcher; } diff --git a/xds/src/test/java/io/grpc/xds/XdsServerTestHelper.java b/xds/src/test/java/io/grpc/xds/XdsServerTestHelper.java index faa3ccac7f..071fbd8a10 100644 --- a/xds/src/test/java/io/grpc/xds/XdsServerTestHelper.java +++ b/xds/src/test/java/io/grpc/xds/XdsServerTestHelper.java @@ -37,7 +37,7 @@ import org.mockito.ArgumentCaptor; /** * Helper methods related to {@link XdsServerBuilder} and related classes. */ -class XdsServerTestHelper { +public class XdsServerTestHelper { private static final String SERVER_URI = "trafficdirector.googleapis.com"; private static final String NODE_ID = @@ -88,7 +88,8 @@ class XdsServerTestHelper { } } - static XdsClientWrapperForServerSds createXdsClientWrapperForServerSds(int port, + /** Create an XdsClientWrapperForServerSds with a mock XdsClient. */ + public static XdsClientWrapperForServerSds createXdsClientWrapperForServerSds(int port, TlsContextManager tlsContextManager) { FakeXdsClientPoolFactory fakeXdsClientPoolFactory = new FakeXdsClientPoolFactory( buildMockXdsClient(tlsContextManager)); @@ -139,6 +140,11 @@ class XdsServerTestHelper { registeredWatcher.onChanged(listenerUpdate); } + public static void generateListenerUpdate( + XdsClient.LdsResourceWatcher registeredWatcher, EnvoyServerProtoData.Listener listener) { + registeredWatcher.onChanged(LdsUpdate.forTcpListener(listener)); + } + static int findFreePort() throws IOException { try (ServerSocket socket = new ServerSocket(0)) { socket.setReuseAddress(true); diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java index 2a6a3c8cc3..a8a7a9c9e3 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java @@ -44,12 +44,14 @@ import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator; import io.grpc.netty.InternalProtocolNegotiators; import io.grpc.xds.Bootstrapper; import io.grpc.xds.CommonBootstrapperTestUtils; +import io.grpc.xds.EnvoyServerProtoData; import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; import io.grpc.xds.InternalXdsAttributes; import io.grpc.xds.TlsContextManager; import io.grpc.xds.XdsClientWrapperForServerSds; import io.grpc.xds.XdsClientWrapperForServerSdsTestMisc; +import io.grpc.xds.XdsServerTestHelper; import io.grpc.xds.internal.sds.SdsProtocolNegotiators.ClientSdsHandler; import io.grpc.xds.internal.sds.SdsProtocolNegotiators.ClientSdsProtocolNegotiator; import io.netty.channel.ChannelHandler; @@ -72,6 +74,7 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.security.cert.CertStoreException; +import java.util.Arrays; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -348,6 +351,54 @@ public class SdsProtocolNegotiatorsTest { } } + @Test + public void noMatchingFilterChain_expectException() { + // we need InetSocketAddress instead of EmbeddedSocketAddress as localAddress for this test + channel = + new EmbeddedChannel() { + @Override + public SocketAddress localAddress() { + return new InetSocketAddress("172.168.1.1", 80); + } + + @Override + public SocketAddress remoteAddress() { + return new InetSocketAddress("172.168.2.2", 90); + } + }; + pipeline = channel.pipeline(); + Bootstrapper.BootstrapInfo bootstrapInfoForServer = CommonBootstrapperTestUtils + .buildBootstrapInfo("google_cloud_private_spiffe-server", SERVER_1_KEY_FILE, + SERVER_1_PEM_FILE, CA_PEM_FILE, null, null, null, null); + + TlsContextManagerImpl tlsContextManager = new TlsContextManagerImpl(bootstrapInfoForServer); + XdsClientWrapperForServerSds xdsClientWrapperForServerSds = + XdsServerTestHelper.createXdsClientWrapperForServerSds(80, tlsContextManager); + xdsClientWrapperForServerSds.start(); + EnvoyServerProtoData.Listener listener = new EnvoyServerProtoData.Listener( + "listener1", "0.0.0.0", Arrays.asList(), null); + XdsServerTestHelper.generateListenerUpdate( + xdsClientWrapperForServerSds.getListenerWatcher(), listener); + + SdsProtocolNegotiators.HandlerPickerHandler handlerPickerHandler = + new SdsProtocolNegotiators.HandlerPickerHandler(grpcHandler, xdsClientWrapperForServerSds, + InternalProtocolNegotiators.serverPlaintext()); + pipeline.addLast(handlerPickerHandler); + channelHandlerCtx = pipeline.context(handlerPickerHandler); + assertThat(channelHandlerCtx).isNotNull(); // should find HandlerPickerHandler + + // kick off protocol negotiation + pipeline.fireUserEventTriggered(InternalProtocolNegotiationEvent.getDefault()); + channelHandlerCtx = pipeline.context(handlerPickerHandler); + assertThat(channelHandlerCtx).isNotNull(); // HandlerPickerHandler still there + try { + channel.checkException(); + fail("exception expected!"); + } catch (Exception e) { + assertThat(e).hasMessageThat().contains("no matching filter chain"); + } + } + @Test public void clientSdsProtocolNegotiatorNewHandler_fireProtocolNegotiationEvent() throws InterruptedException, TimeoutException, ExecutionException {