xds, rbac: build per route serverInterceptor for httpConfig (#8524)

diff --git a/xds/src/main/java/io/grpc/xds/FilterChainMatchingProtocolNegotiators.java b/xds/src/main/java/io/grpc/xds/FilterChainMatchingProtocolNegotiators.java
index 24cd4e9..b828b86 100644
--- a/xds/src/main/java/io/grpc/xds/FilterChainMatchingProtocolNegotiators.java
+++ b/xds/src/main/java/io/grpc/xds/FilterChainMatchingProtocolNegotiators.java
@@ -59,6 +59,7 @@
 import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.annotation.Nullable;
@@ -135,28 +136,29 @@
 
     static final class FilterChainSelector {
       public static final FilterChainSelector NO_FILTER_CHAIN = new FilterChainSelector(
-              Collections.<FilterChain, ServerRoutingConfig>emptyMap(), null, null);
-      private final Map<FilterChain, ServerRoutingConfig> routingConfigs;
+              Collections.<FilterChain, AtomicReference<ServerRoutingConfig>>emptyMap(),
+          null, new AtomicReference<ServerRoutingConfig>());
+      private final Map<FilterChain, AtomicReference<ServerRoutingConfig>> routingConfigs;
       @Nullable
       private final SslContextProviderSupplier defaultSslContextProviderSupplier;
       @Nullable
-      private final ServerRoutingConfig defaultRoutingConfig;
+      private final AtomicReference<ServerRoutingConfig> defaultRoutingConfig;
 
-      FilterChainSelector(Map<FilterChain, ServerRoutingConfig> routingConfigs,
+      FilterChainSelector(Map<FilterChain, AtomicReference<ServerRoutingConfig>> routingConfigs,
                           @Nullable SslContextProviderSupplier defaultSslContextProviderSupplier,
-                          @Nullable ServerRoutingConfig defaultRoutingConfig) {
+                          @Nullable AtomicReference<ServerRoutingConfig> defaultRoutingConfig) {
         this.routingConfigs = checkNotNull(routingConfigs, "routingConfigs");
         this.defaultSslContextProviderSupplier = defaultSslContextProviderSupplier;
-        this.defaultRoutingConfig = defaultRoutingConfig;
+        this.defaultRoutingConfig = checkNotNull(defaultRoutingConfig, "defaultRoutingConfig");
       }
 
       @VisibleForTesting
-      Map<FilterChain, ServerRoutingConfig> getRoutingConfigs() {
+      Map<FilterChain, AtomicReference<ServerRoutingConfig>> getRoutingConfigs() {
         return routingConfigs;
       }
 
       @VisibleForTesting
-      ServerRoutingConfig getDefaultRoutingConfig() {
+      AtomicReference<ServerRoutingConfig> getDefaultRoutingConfig() {
         return defaultRoutingConfig;
       }
 
@@ -189,7 +191,7 @@
           return new SelectedConfig(
                   routingConfigs.get(selected), selected.getSslContextProviderSupplier());
         }
-        if (defaultRoutingConfig != null) {
+        if (defaultRoutingConfig.get() != null) {
           return new SelectedConfig(defaultRoutingConfig, defaultSslContextProviderSupplier);
         }
         return null;
@@ -393,11 +395,11 @@
    * The FilterChain level configuration.
    */
   private static final class SelectedConfig {
-    private final ServerRoutingConfig routingConfig;
+    private final AtomicReference<ServerRoutingConfig> routingConfig;
     @Nullable
     private final SslContextProviderSupplier sslContextProviderSupplier;
 
-    private SelectedConfig(ServerRoutingConfig routingConfig,
+    private SelectedConfig(AtomicReference<ServerRoutingConfig> routingConfig,
                            @Nullable SslContextProviderSupplier sslContextProviderSupplier) {
       this.routingConfig = checkNotNull(routingConfig, "routingConfig");
       this.sslContextProviderSupplier = sslContextProviderSupplier;
diff --git a/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java b/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java
index 29821f2..e730150 100644
--- a/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java
+++ b/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java
@@ -22,6 +22,7 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.SettableFuture;
 import io.grpc.Attributes;
 import io.grpc.InternalServerInterceptors;
@@ -87,8 +88,9 @@
         }
       });
 
-  public static final Attributes.Key<ServerRoutingConfig> ATTR_SERVER_ROUTING_CONFIG =
-          Attributes.Key.create("io.grpc.xds.ServerWrapper.serverRoutingConfig");
+  public static final Attributes.Key<AtomicReference<ServerRoutingConfig>>
+      ATTR_SERVER_ROUTING_CONFIG =
+      Attributes.Key.create("io.grpc.xds.ServerWrapper.serverRoutingConfig");
 
   @VisibleForTesting
   static final long RETRY_DELAY_NANOS = TimeUnit.MINUTES.toNanos(1);
@@ -346,6 +348,15 @@
     @Nullable
     private FilterChain defaultFilterChain;
     private boolean stopped;
+    private final Map<FilterChain, AtomicReference<ServerRoutingConfig>> savedRdsRoutingConfigRef 
+        = new HashMap<>();
+    private final ServerInterceptor noopInterceptor = new ServerInterceptor() {
+      @Override
+      public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
+          Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+        return next.startCall(call, headers);
+      }
+    };
 
     private DiscoveryState(String resourceName) {
       this.resourceName = checkNotNull(resourceName, "resourceName");
@@ -452,14 +463,16 @@
     }
 
     private void updateSelector() {
-      Map<FilterChain, ServerRoutingConfig> filterChainRouting = new HashMap<>();
+      Map<FilterChain, AtomicReference<ServerRoutingConfig>> filterChainRouting = new HashMap<>();
+      savedRdsRoutingConfigRef.clear();
       for (FilterChain filterChain: filterChains) {
         filterChainRouting.put(filterChain, generateRoutingConfig(filterChain));
       }
       FilterChainSelector selector = new FilterChainSelector(
           Collections.unmodifiableMap(filterChainRouting),
           defaultFilterChain == null ? null : defaultFilterChain.getSslContextProviderSupplier(),
-          defaultFilterChain == null ? null : generateRoutingConfig(defaultFilterChain));
+          defaultFilterChain == null ? new AtomicReference<ServerRoutingConfig>() :
+              generateRoutingConfig(defaultFilterChain));
       List<SslContextProviderSupplier> toRelease = getSuppliersInUse();
       filterChainSelectorManager.updateSelector(selector);
       for (SslContextProviderSupplier e: toRelease) {
@@ -468,18 +481,84 @@
       startDelegateServer();
     }
 
-    private ServerRoutingConfig generateRoutingConfig(FilterChain filterChain) {
+    private AtomicReference<ServerRoutingConfig> generateRoutingConfig(FilterChain filterChain) {
       HttpConnectionManager hcm = filterChain.getHttpConnectionManager();
       if (hcm.virtualHosts() != null) {
-        return ServerRoutingConfig.create(hcm.httpFilterConfigs(),
-            new AtomicReference<>(hcm.virtualHosts()));
+        ImmutableMap<Route, ServerInterceptor> interceptors = generatePerRouteInterceptors(
+                hcm.httpFilterConfigs(), hcm.virtualHosts());
+        return new AtomicReference<>(ServerRoutingConfig.create(hcm.virtualHosts(),interceptors));
       } else {
         RouteDiscoveryState rds = routeDiscoveryStates.get(hcm.rdsName());
         checkNotNull(rds, "rds");
-        return ServerRoutingConfig.create(hcm.httpFilterConfigs(), rds.savedVirtualHosts);
+        AtomicReference<ServerRoutingConfig> serverRoutingConfigRef = new AtomicReference<>();
+        if (rds.savedVirtualHosts != null) {
+          ImmutableMap<Route, ServerInterceptor> interceptors = generatePerRouteInterceptors(
+              hcm.httpFilterConfigs(), rds.savedVirtualHosts);
+          ServerRoutingConfig serverRoutingConfig =
+              ServerRoutingConfig.create(rds.savedVirtualHosts, interceptors);
+          serverRoutingConfigRef.set(serverRoutingConfig);
+        } else {
+          serverRoutingConfigRef.set(ServerRoutingConfig.FAILING_ROUTING_CONFIG);
+        }
+        savedRdsRoutingConfigRef.put(filterChain, serverRoutingConfigRef);
+        return serverRoutingConfigRef;
       }
     }
 
+    private ImmutableMap<Route, ServerInterceptor> generatePerRouteInterceptors(
+        List<NamedFilterConfig> namedFilterConfigs, List<VirtualHost> virtualHosts) {
+      ImmutableMap.Builder<Route, ServerInterceptor> perRouteInterceptors =
+          new ImmutableMap.Builder<>();
+      for (VirtualHost virtualHost : virtualHosts) {
+        for (Route route : virtualHost.routes()) {
+          List<ServerInterceptor> filterInterceptors = new ArrayList<>();
+          Map<String, FilterConfig> selectedOverrideConfigs =
+              new HashMap<>(virtualHost.filterConfigOverrides());
+          selectedOverrideConfigs.putAll(route.filterConfigOverrides());
+          for (NamedFilterConfig namedFilterConfig : namedFilterConfigs) {
+            FilterConfig filterConfig = namedFilterConfig.filterConfig;
+            Filter filter = filterRegistry.get(filterConfig.typeUrl());
+            if (filter instanceof ServerInterceptorBuilder) {
+              ServerInterceptor interceptor =
+                  ((ServerInterceptorBuilder) filter).buildServerInterceptor(
+                      filterConfig, selectedOverrideConfigs.get(namedFilterConfig.name));
+              if (interceptor != null) {
+                filterInterceptors.add(interceptor);
+              }
+            } else {
+              logger.log(Level.WARNING, "HttpFilterConfig(type URL: "
+                  + filterConfig.typeUrl() + ") is not supported on server-side. "
+                  + "Probably a bug at ClientXdsClient verification.");
+            }
+          }
+          ServerInterceptor interceptor = combineInterceptors(filterInterceptors);
+          perRouteInterceptors.put(route, interceptor);
+        }
+      }
+      return perRouteInterceptors.build();
+    }
+
+    private ServerInterceptor combineInterceptors(final List<ServerInterceptor> interceptors) {
+      if (interceptors.isEmpty()) {
+        return noopInterceptor;
+      }
+      if (interceptors.size() == 1) {
+        return interceptors.get(0);
+      }
+      return new ServerInterceptor() {
+        @Override
+        public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
+            Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+          // intercept forward
+          for (int i = interceptors.size() - 1; i >= 0; i--) {
+            next = InternalServerInterceptors.interceptCallHandlerCreate(
+                interceptors.get(i), next);
+          }
+          return next.startCall(call, headers);
+        }
+      };
+    }
+
     private void handleConfigNotFound(StatusException exception) {
       cleanUpRouteDiscoveryStates();
       List<SslContextProviderSupplier> toRelease = getSuppliersInUse();
@@ -508,6 +587,7 @@
         xdsClient.cancelRdsResourceWatch(rdsName, rdsState);
       }
       routeDiscoveryStates.clear();
+      savedRdsRoutingConfigRef.clear();
     }
 
     private List<SslContextProviderSupplier> getSuppliersInUse() {
@@ -544,8 +624,7 @@
 
     private final class RouteDiscoveryState implements RdsResourceWatcher {
       private final String resourceName;
-      private AtomicReference<ImmutableList<VirtualHost>> savedVirtualHosts =
-          new AtomicReference<>();
+      private ImmutableList<VirtualHost> savedVirtualHosts;
       private boolean isPending = true;
 
       private RouteDiscoveryState(String resourceName) {
@@ -560,7 +639,8 @@
             if (!routeDiscoveryStates.containsKey(resourceName)) {
               return;
             }
-            savedVirtualHosts.set(ImmutableList.copyOf(update.virtualHosts));
+            savedVirtualHosts = ImmutableList.copyOf(update.virtualHosts);
+            updateRdsRoutingConfig();
             maybeUpdateSelector();
           }
         });
@@ -575,7 +655,8 @@
               return;
             }
             logger.log(Level.WARNING, "Rds {0} unavailable", resourceName);
-            savedVirtualHosts.set(null);
+            savedVirtualHosts = null;
+            updateRdsRoutingConfig();
             maybeUpdateSelector();
           }
         });
@@ -596,6 +677,25 @@
         });
       }
 
+      private void updateRdsRoutingConfig() {
+        for (FilterChain filterChain : savedRdsRoutingConfigRef.keySet()) {
+          if (resourceName.equals(filterChain.getHttpConnectionManager().rdsName())) {
+            ServerRoutingConfig updatedRoutingConfig;
+            if (savedVirtualHosts == null) {
+              updatedRoutingConfig = ServerRoutingConfig.FAILING_ROUTING_CONFIG;
+            } else {
+              ImmutableMap<Route, ServerInterceptor> updatedInterceptors =
+                  generatePerRouteInterceptors(
+                      filterChain.getHttpConnectionManager().httpFilterConfigs(),
+                      savedVirtualHosts);
+              updatedRoutingConfig = ServerRoutingConfig.create(savedVirtualHosts,
+                  updatedInterceptors);
+            }
+            savedRdsRoutingConfigRef.get(filterChain).set(updatedRoutingConfig);
+          }
+        }
+      }
+
       // Update the selector to use the most recently updated configs only after all rds have been
       // discovered for the first time. Later changes on rds will be applied through virtual host
       // list atomic ref.
@@ -632,18 +732,16 @@
     @Override
     public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
         Metadata headers, ServerCallHandler<ReqT, RespT> next) {
-      ServerRoutingConfig routingConfig = call.getAttributes().get(ATTR_SERVER_ROUTING_CONFIG);
-      if (routingConfig == null) {
-        String errorMsg = "Missing xDS routing config.";
+      AtomicReference<ServerRoutingConfig> routingConfigRef =
+          call.getAttributes().get(ATTR_SERVER_ROUTING_CONFIG);
+      ServerRoutingConfig routingConfig = routingConfigRef == null ? null :
+          routingConfigRef.get();
+      if (routingConfig == null || routingConfig == ServerRoutingConfig.FAILING_ROUTING_CONFIG) {
+        String errorMsg = "Missing or broken xDS routing config: RDS config unavailable.";
         call.close(Status.UNAVAILABLE.withDescription(errorMsg), new Metadata());
         return new Listener<ReqT>() {};
       }
-      List<VirtualHost> virtualHosts = routingConfig.virtualHosts().get();
-      if (virtualHosts == null) {
-        String errorMsg = "Missing xDS routing config VirtualHosts due to RDS config unavailable.";
-        call.close(Status.UNAVAILABLE.withDescription(errorMsg), new Metadata());
-        return new Listener<ReqT>() {};
-      }
+      List<VirtualHost> virtualHosts = routingConfig.virtualHosts();
       VirtualHost virtualHost = RoutingUtils.findVirtualHostForHostName(
           virtualHosts, call.getAuthority());
       if (virtualHost == null) {
@@ -653,14 +751,11 @@
         return new Listener<ReqT>() {};
       }
       Route selectedRoute = null;
-      Map<String, FilterConfig> selectedOverrideConfigs =
-              new HashMap<>(virtualHost.filterConfigOverrides());
       MethodDescriptor<ReqT, RespT> method = call.getMethodDescriptor();
       for (Route route : virtualHost.routes()) {
         if (RoutingUtils.matchRoute(
             route.routeMatch(), "/" + method.getFullMethodName(), headers, random)) {
           selectedRoute = route;
-          selectedOverrideConfigs.putAll(route.filterConfigOverrides());
           break;
         }
       }
@@ -670,48 +765,12 @@
             new Metadata());
         return new ServerCall.Listener<ReqT>() {};
       }
-      List<ServerInterceptor> filterInterceptors = new ArrayList<>();
-      for (NamedFilterConfig namedFilterConfig : routingConfig.httpFilterConfigs()) {
-        FilterConfig filterConfig = namedFilterConfig.filterConfig;
-        Filter filter = filterRegistry.get(filterConfig.typeUrl());
-        if (filter instanceof ServerInterceptorBuilder) {
-          ServerInterceptor interceptor =
-              ((ServerInterceptorBuilder) filter).buildServerInterceptor(
-                  filterConfig, selectedOverrideConfigs.get(namedFilterConfig.name));
-          if (interceptor != null) {
-            filterInterceptors.add(interceptor);
-          }
-        } else {
-          call.close(
-              Status.UNAVAILABLE.withDescription("HttpFilterConfig(type URL: "
-                      + filterConfig.typeUrl() + ") is not supported on server-side."),
-              new Metadata());
-          return new Listener<ReqT>() {};
-        }
+      ServerInterceptor routeInterceptor = noopInterceptor;
+      Map<Route, ServerInterceptor> perRouteInterceptors = routingConfig.interceptors();
+      if (perRouteInterceptors != null && perRouteInterceptors.get(selectedRoute) != null) {
+        routeInterceptor = perRouteInterceptors.get(selectedRoute);
       }
-      ServerInterceptor interceptor = combineInterceptors(filterInterceptors);
-      return interceptor.interceptCall(call, headers, next);
-    }
-
-    private ServerInterceptor combineInterceptors(final List<ServerInterceptor> interceptors) {
-      if (interceptors.isEmpty()) {
-        return noopInterceptor;
-      }
-      if (interceptors.size() == 1) {
-        return interceptors.get(0);
-      }
-      return new ServerInterceptor() {
-        @Override
-        public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
-            Metadata headers, ServerCallHandler<ReqT, RespT> next) {
-          // intercept forward
-          for (int i = interceptors.size() - 1; i >= 0; i--) {
-            next = InternalServerInterceptors.interceptCallHandlerCreate(
-                interceptors.get(i), next);
-          }
-          return next.startCall(call, headers);
-        }
-      };
+      return routeInterceptor.interceptCall(call, headers, next);
     }
   }
 
@@ -720,20 +779,24 @@
    */
   @AutoValue
   abstract static class ServerRoutingConfig {
-    // Top level http filter configs.
-    abstract ImmutableList<NamedFilterConfig> httpFilterConfigs();
+    @VisibleForTesting
+    static final ServerRoutingConfig FAILING_ROUTING_CONFIG = ServerRoutingConfig.create(
+        ImmutableList.<VirtualHost>of(), ImmutableMap.<Route, ServerInterceptor>of());
 
-    abstract AtomicReference<ImmutableList<VirtualHost>> virtualHosts();
+    abstract ImmutableList<VirtualHost> virtualHosts();
+
+    // Prebuilt per route server interceptors from http filter configs.
+    abstract ImmutableMap<Route, ServerInterceptor> interceptors();
 
     /**
      * Server routing configuration.
      * */
-    public static ServerRoutingConfig create(List<NamedFilterConfig> httpFilterConfigs,
-        AtomicReference<ImmutableList<VirtualHost>> virtualHosts) {
-      checkNotNull(httpFilterConfigs, "httpFilterConfigs");
+    public static ServerRoutingConfig create(
+        ImmutableList<VirtualHost> virtualHosts,
+        ImmutableMap<Route, ServerInterceptor> interceptors) {
       checkNotNull(virtualHosts, "virtualHosts");
-      return new AutoValue_XdsServerWrapper_ServerRoutingConfig(
-              ImmutableList.copyOf(httpFilterConfigs), virtualHosts);
+      checkNotNull(interceptors, "interceptors");
+      return new AutoValue_XdsServerWrapper_ServerRoutingConfig(virtualHosts, interceptors);
     }
   }
 }
diff --git a/xds/src/test/java/io/grpc/xds/FilterChainMatchingProtocolNegotiatorsTest.java b/xds/src/test/java/io/grpc/xds/FilterChainMatchingProtocolNegotiatorsTest.java
index 5b7b1bf..32a7bc1 100644
--- a/xds/src/test/java/io/grpc/xds/FilterChainMatchingProtocolNegotiatorsTest.java
+++ b/xds/src/test/java/io/grpc/xds/FilterChainMatchingProtocolNegotiatorsTest.java
@@ -26,6 +26,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.SettableFuture;
+import io.grpc.ServerInterceptor;
 import io.grpc.internal.TestUtils.NoopChannelLogger;
 import io.grpc.netty.GrpcHttp2ConnectionHandler;
 import io.grpc.netty.InternalProtocolNegotiationEvent;
@@ -93,8 +94,12 @@
   private static final String LOCAL_IP = "10.1.2.3";  // dest
   private static final String REMOTE_IP = "10.4.2.3"; // source
   private static final int PORT = 7000;
-  private final ServerRoutingConfig noopConfig = ServerRoutingConfig.create(
-          new ArrayList<NamedFilterConfig>(), new AtomicReference<ImmutableList<VirtualHost>>());
+  private final AtomicReference<ServerRoutingConfig> noopConfig = new AtomicReference<>(
+      ServerRoutingConfig.create(ImmutableList.<VirtualHost>of(),
+          ImmutableMap.<Route, ServerInterceptor>of()));
+  final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
+  final SettableFuture<AtomicReference<ServerRoutingConfig>> routingSettable =
+      SettableFuture.create();
 
   @After
   @SuppressWarnings("FutureReturnValueIgnored")
@@ -108,15 +113,14 @@
 
   @Test
   public void nofilterChainMatch_defaultSslContext() throws Exception {
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
 
     SslContextProviderSupplier defaultSsl = new SslContextProviderSupplier(createTls(),
             tlsContextManager);
     selectorManager.updateSelector(new FilterChainSelector(
-            new HashMap<FilterChain, ServerRoutingConfig>(), defaultSsl, noopConfig));
+            new HashMap<FilterChain, AtomicReference<ServerRoutingConfig>>(),
+        defaultSsl, noopConfig));
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
     setupChannel("172.168.1.1", "172.168.1.2", 80, filterChainMatchingHandler);
@@ -138,7 +142,8 @@
   @Test
   public void noFilterChainMatch_noDefaultSslContext() {
     selectorManager.updateSelector(new FilterChainSelector(
-            new HashMap<FilterChain, ServerRoutingConfig>(), null, null));
+            new HashMap<FilterChain, AtomicReference<ServerRoutingConfig>>(),
+        null, new AtomicReference<ServerRoutingConfig>()));
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
     setupChannel("172.168.1.1", "172.168.2.2", 90, filterChainMatchingHandler);
@@ -156,7 +161,7 @@
     ChannelHandler next = new ChannelInboundHandlerAdapter();
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     selectorManager.updateSelector(new FilterChainSelector(
-            new HashMap<FilterChain, ServerRoutingConfig>(), null, noopConfig));
+            new HashMap<FilterChain, AtomicReference<ServerRoutingConfig>>(), null, noopConfig));
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
     setupChannel("172.168.1.1", "172.168.2.2", 90, filterChainMatchingHandler);
@@ -175,7 +180,7 @@
     assertThat(msg).isNull();
 
     selectorManager.updateSelector(new FilterChainSelector(
-            new HashMap<FilterChain, ServerRoutingConfig>(), null, noopConfig));
+            new HashMap<FilterChain, AtomicReference<ServerRoutingConfig>>(), null, noopConfig));
     assertThat(channel.readOutbound().getClass().getName())
         .isEqualTo("io.grpc.netty.GracefulServerCloseCommand");
   }
@@ -199,11 +204,9 @@
             tlsContextManager);
 
     selectorManager.updateSelector(new FilterChainSelector(ImmutableMap.of(filterChain, noopConfig),
-            null, null));
+            null, new AtomicReference<ServerRoutingConfig>()));
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -243,8 +246,6 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -281,17 +282,16 @@
                     tlsContextForDefaultFilterChain, tlsContextManager);
 
     ServerRoutingConfig routingConfig = ServerRoutingConfig.create(
-            new ArrayList<NamedFilterConfig>(), new AtomicReference<>(
-                ImmutableList.of(createVirtualHost("virtual"))));
+                ImmutableList.of(createVirtualHost("virtual")),
+        ImmutableMap.<Route, ServerInterceptor>of());
     selectorManager.updateSelector(new FilterChainSelector(
-            ImmutableMap.of(filterChainWithDestPort, routingConfig),
+            ImmutableMap.of(filterChainWithDestPort,
+                new AtomicReference<ServerRoutingConfig>(routingConfig)),
             defaultFilterChain.getSslContextProviderSupplier(), noopConfig));
 
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -333,8 +333,6 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -377,8 +375,6 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -417,12 +413,11 @@
 
     selectorManager.updateSelector(new FilterChainSelector(
             ImmutableMap.of(filterChain0Length, noopConfig),
-            defaultFilterChain.getSslContextProviderSupplier(), null));
+            defaultFilterChain.getSslContextProviderSupplier(),
+        new AtomicReference<ServerRoutingConfig>()));
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -480,8 +475,6 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -538,8 +531,6 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -596,8 +587,6 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
 
@@ -658,8 +647,6 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -700,8 +687,7 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
+
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -714,8 +700,6 @@
 
   @Test
   public void sourceTypeLocal() throws Exception {
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     EnvoyServerProtoData.DownstreamTlsContext tlsContextMatch =
@@ -755,8 +739,6 @@
   @Test
   public void sourcePrefixRange_moreSpecificWith2Wins()
           throws Exception {
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
 
@@ -817,7 +799,6 @@
   @Test
   public void sourcePrefixRange_2Matchers_expectException()
           throws UnknownHostException {
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
     ChannelHandler next = new ChannelInboundHandlerAdapter() {
       @Override
       public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
@@ -932,8 +913,6 @@
 
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -1074,7 +1053,7 @@
     EnvoyServerProtoData.FilterChain defaultFilterChain = new EnvoyServerProtoData.FilterChain(
             "filter-chain-7", null, HTTP_CONNECTION_MANAGER, null, tlsContextManager);
 
-    Map<FilterChain, ServerRoutingConfig> map = new HashMap<>();
+    Map<FilterChain, AtomicReference<ServerRoutingConfig>> map = new HashMap<>();
     map.put(filterChain1, randomConfig("1"));
     map.put(filterChain2, randomConfig("2"));
     map.put(filterChain3, randomConfig("3"));
@@ -1087,8 +1066,6 @@
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
 
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -1161,8 +1138,6 @@
 
     FilterChainMatchingHandler filterChainMatchingHandler =
             new FilterChainMatchingHandler(grpcHandler, selectorManager, mockDelegate);
-    final SettableFuture<SslContextProviderSupplier> sslSet = SettableFuture.create();
-    final SettableFuture<ServerRoutingConfig> routingSettable = SettableFuture.create();
     ChannelHandler next = captureAttrHandler(sslSet, routingSettable);
     when(mockDelegate.newHandler(grpcHandler)).thenReturn(next);
     setupChannel(LOCAL_IP, REMOTE_IP, 15000, filterChainMatchingHandler);
@@ -1186,10 +1161,11 @@
             ImmutableMap.<String, FilterConfig>of());
   }
 
-  private static ServerRoutingConfig randomConfig(String domain) {
-    return ServerRoutingConfig.create(
-        new ArrayList<NamedFilterConfig>(), new AtomicReference<>(
-            ImmutableList.of(createVirtualHost(domain))));
+  private static AtomicReference<ServerRoutingConfig> randomConfig(String domain) {
+    return new AtomicReference<>(
+        ServerRoutingConfig.create(ImmutableList.of(createVirtualHost(domain)),
+        ImmutableMap.<Route, ServerInterceptor>of())
+    );
   }
 
   private EnvoyServerProtoData.DownstreamTlsContext createTls() {
@@ -1218,7 +1194,7 @@
 
   private static ChannelHandler captureAttrHandler(
           final SettableFuture<SslContextProviderSupplier> sslSet,
-          final SettableFuture<ServerRoutingConfig> routingSettable) {
+          final SettableFuture<AtomicReference<ServerRoutingConfig>> routingSettable) {
     return new ChannelInboundHandlerAdapter() {
       @Override
       public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
diff --git a/xds/src/test/java/io/grpc/xds/FilterChainSelectorManagerTest.java b/xds/src/test/java/io/grpc/xds/FilterChainSelectorManagerTest.java
index d7b883f..a3a2218 100644
--- a/xds/src/test/java/io/grpc/xds/FilterChainSelectorManagerTest.java
+++ b/xds/src/test/java/io/grpc/xds/FilterChainSelectorManagerTest.java
@@ -19,8 +19,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import io.grpc.ServerInterceptor;
 import io.grpc.xds.EnvoyServerProtoData.FilterChain;
-import io.grpc.xds.Filter.NamedFilterConfig;
 import io.grpc.xds.FilterChainMatchingProtocolNegotiators.FilterChainMatchingHandler.FilterChainSelector;
 import io.grpc.xds.FilterChainSelectorManager.Closer;
 import io.grpc.xds.XdsServerWrapper.ServerRoutingConfig;
@@ -33,13 +34,15 @@
 @RunWith(JUnit4.class)
 public final class FilterChainSelectorManagerTest {
   private FilterChainSelectorManager manager = new FilterChainSelectorManager();
-  private ServerRoutingConfig noopConfig = ServerRoutingConfig.create(
-          Collections.<NamedFilterConfig>emptyList(),
-          new AtomicReference<ImmutableList<VirtualHost>>());
+  private AtomicReference<ServerRoutingConfig> noopConfig = new AtomicReference<>(
+      ServerRoutingConfig.create(ImmutableList.<VirtualHost>of(),
+      ImmutableMap.<VirtualHost.Route, ServerInterceptor>of()));
   private FilterChainSelector selector1 = new FilterChainSelector(
-            Collections.<FilterChain,ServerRoutingConfig>emptyMap(), null, null);
+            Collections.<FilterChain,AtomicReference<ServerRoutingConfig>>emptyMap(),
+      null, new AtomicReference<ServerRoutingConfig>());
   private FilterChainSelector selector2 = new FilterChainSelector(
-            Collections.<FilterChain,ServerRoutingConfig>emptyMap(), null, noopConfig);
+            Collections.<FilterChain,AtomicReference<ServerRoutingConfig>>emptyMap(),
+      null, noopConfig);
   private CounterRunnable runnable1 = new CounterRunnable();
   private CounterRunnable runnable2 = new CounterRunnable();
 
diff --git a/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java b/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java
index d442136..c109bb4 100644
--- a/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java
+++ b/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java
@@ -104,6 +104,8 @@
   private FakeXdsClient xdsClient = new FakeXdsClient();
   private FilterRegistry filterRegistry = FilterRegistry.getDefaultRegistry();
   private XdsServerWrapper xdsServerWrapper;
+  private ServerRoutingConfig noopConfig = ServerRoutingConfig.create(
+      ImmutableList.<VirtualHost>of(), ImmutableMap.<Route, ServerInterceptor>of());
 
   @Before
   public void setup() {
@@ -380,9 +382,9 @@
     assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
         .isEqualTo(1);
     ServerRoutingConfig realConfig =
-        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(filterChain);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(httpConnectionManager.virtualHosts());
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(httpConnectionManager.httpFilterConfigs());
+        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(filterChain).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(httpConnectionManager.virtualHosts());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
     verify(listener).onServing();
     verify(mockServer).start();
   }
@@ -429,26 +431,21 @@
     start.get(5000, TimeUnit.MILLISECONDS);
     verify(mockServer).start();
     ServerRoutingConfig realConfig =
-        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f0);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f0).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-0")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f0.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
     assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
         .isEqualTo(2);
-    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f2);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f2).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-1")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f2.getHttpConnectionManager().httpFilterConfigs());
-    realConfig = selectorManager.getSelectorToUpdateSelector().getDefaultRoutingConfig();
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
+    realConfig = selectorManager.getSelectorToUpdateSelector().getDefaultRoutingConfig().get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-2")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f3.getHttpConnectionManager().httpFilterConfigs());
     assertThat(selectorManager.getSelectorToUpdateSelector().getDefaultSslContextProviderSupplier())
-        .isEqualTo(
-            f3.getSslContextProviderSupplier());
+        .isEqualTo(f3.getSslContextProviderSupplier());
   }
 
   @Test
@@ -481,22 +478,20 @@
     start.get(5000, TimeUnit.MILLISECONDS);
     verify(mockServer, times(1)).start();
     ServerRoutingConfig realConfig =
-        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f0);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f0).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-0")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f0.getHttpConnectionManager().httpFilterConfigs());
-    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
-        Collections.singletonList(createVirtualHost("virtual-host-0")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f1.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
 
-    realConfig = selectorManager.getSelectorToUpdateSelector().getDefaultRoutingConfig();
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-0")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f2.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
+
+    realConfig = selectorManager.getSelectorToUpdateSelector().getDefaultRoutingConfig().get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
+        Collections.singletonList(createVirtualHost("virtual-host-0")));
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
     assertThat(selectorManager.getSelectorToUpdateSelector().getDefaultSslContextProviderSupplier())
         .isSameInstanceAs(f2.getSslContextProviderSupplier());
 
@@ -513,25 +508,22 @@
 
     assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
         .isEqualTo(2);
-    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f5);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f5).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-1")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f5.getHttpConnectionManager().httpFilterConfigs());
-    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f3);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
+    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f3).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-0")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f3.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
 
-    realConfig = selectorManager.getSelectorToUpdateSelector().getDefaultRoutingConfig();
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+    realConfig = selectorManager.getSelectorToUpdateSelector().getDefaultRoutingConfig().get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-1")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f4.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
+
     assertThat(selectorManager.getSelectorToUpdateSelector().getDefaultSslContextProviderSupplier())
-        .isSameInstanceAs(
-            f4.getSslContextProviderSupplier());
+        .isSameInstanceAs(f4.getSslContextProviderSupplier());
     verify(mockServer, times(1)).start();
     xdsServerWrapper.shutdown();
     verify(mockServer, times(1)).shutdown();
@@ -567,35 +559,31 @@
     assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
         .isEqualTo(2);
     ServerRoutingConfig realConfig =
-        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1);
-    assertThat(realConfig.virtualHosts().get()).isNull();
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f1.getHttpConnectionManager().httpFilterConfigs());
-    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f0);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(hcmVirtual.virtualHosts());
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f0.getHttpConnectionManager().httpFilterConfigs());
+        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1).get();
+    assertThat(realConfig.virtualHosts()).isEmpty();
+    assertThat(realConfig.interceptors()).isEmpty();
+
+    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f0).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(hcmVirtual.virtualHosts());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
 
     xdsClient.deliverRdsUpdate("r0",
             Collections.singletonList(createVirtualHost("virtual-host-1")));
-    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-1")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f1.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
 
     xdsClient.rdsWatchers.get("r0").onError(Status.CANCELLED);
-    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-1")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f1.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
 
     xdsClient.rdsWatchers.get("r0").onResourceDoesNotExist("r0");
-    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1);
-    assertThat(realConfig.virtualHosts().get()).isNull();
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        f1.getHttpConnectionManager().httpFilterConfigs());
+    realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(f1).get();
+    assertThat(realConfig.virtualHosts()).isEmpty();
+    assertThat(realConfig.interceptors()).isEmpty();
   }
 
   @Test
@@ -648,11 +636,11 @@
     assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
         .isEqualTo(1);
     ServerRoutingConfig realConfig =
-        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(filterChain1);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+        selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().get(filterChain1).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-1")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        filterChain1.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
+
     // xds update after start
     xdsClient.deliverRdsUpdate("rds",
             Collections.singletonList(createVirtualHost("virtual-host-2")));
@@ -664,11 +652,11 @@
     assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
         .isEqualTo(1);
     realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs()
-        .get(filterChain1);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+        .get(filterChain1).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-2")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        filterChain1.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
+
     assertThat(sslSupplier1.isShutdown()).isFalse();
 
     // not serving after serving
@@ -705,11 +693,11 @@
     assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
         .isEqualTo(1);
     realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs()
-        .get(filterChain2);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+        .get(filterChain2).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-1")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        filterChain2.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
+
     assertThat(executor.numPendingTasks()).isEqualTo(1);
     xdsClient.ldsWatcher.onResourceDoesNotExist(ldsResource);
     verify(mockServer, times(4)).shutdown();
@@ -733,11 +721,11 @@
     assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
         .isEqualTo(1);
     realConfig = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs()
-        .get(filterChain3);
-    assertThat(realConfig.virtualHosts().get()).isEqualTo(
+        .get(filterChain3).get();
+    assertThat(realConfig.virtualHosts()).isEqualTo(
         Collections.singletonList(createVirtualHost("virtual-host-1")));
-    assertThat(realConfig.httpFilterConfigs()).isEqualTo(
-        filterChain3.getHttpConnectionManager().httpFilterConfigs());
+    assertThat(realConfig.interceptors()).isEqualTo(ImmutableMap.of());
+
     xdsServerWrapper.shutdown();
     verify(mockServer, times(5)).shutdown();
     assertThat(sslSupplier3.isShutdown()).isTrue();
@@ -747,9 +735,9 @@
 
   @Test
   @SuppressWarnings("unchecked")
-  public void interceptor_notServerInterceptor() throws Exception {
+  public void interceptor_success() throws Exception {
     ArgumentCaptor<ConfigApplyingInterceptor> interceptorCaptor =
-            ArgumentCaptor.forClass(ConfigApplyingInterceptor.class);
+        ArgumentCaptor.forClass(ConfigApplyingInterceptor.class);
     final SettableFuture<Server> start = SettableFuture.create();
     Executors.newSingleThreadExecutor().execute(new Runnable() {
       @Override
@@ -764,26 +752,36 @@
     xdsClient.ldsResource.get(5, TimeUnit.SECONDS);
     verify(mockBuilder).intercept(interceptorCaptor.capture());
     ConfigApplyingInterceptor interceptor = interceptorCaptor.getValue();
-    ServerRoutingConfig routingConfig = createRoutingConfig("/FooService/barMethod",
-            "foo.google.com", "filter-type-url");
+    RouteMatch routeMatch =
+        RouteMatch.create(
+            PathMatcher.fromPath("/FooService/barMethod", true),
+            Collections.<HeaderMatcher>emptyList(), null);
+    Route route = Route.forAction(routeMatch, null,
+        ImmutableMap.<String, FilterConfig>of());
+    VirtualHost virtualHost  = VirtualHost.create(
+        "v1", Collections.singletonList("foo.google.com"), Arrays.asList(route),
+        ImmutableMap.<String, FilterConfig>of());
+    final List<Integer> interceptorTrace = new ArrayList<>();
+    ServerInterceptor interceptor0 = new ServerInterceptor() {
+      @Override
+      public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
+          Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+        interceptorTrace.add(0);
+        return next.startCall(call, headers);
+      }
+    };
+    ServerRoutingConfig realConfig = ServerRoutingConfig.create(
+        ImmutableList.of(virtualHost), ImmutableMap.of(route, interceptor0));
     ServerCall<Void, Void> serverCall = mock(ServerCall.class);
-    when(serverCall.getAttributes()).thenReturn(
-            Attributes.newBuilder().set(ATTR_SERVER_ROUTING_CONFIG, routingConfig).build());
     when(serverCall.getMethodDescriptor()).thenReturn(createMethod("FooService/barMethod"));
+    when(serverCall.getAttributes()).thenReturn(
+        Attributes.newBuilder().set(ATTR_SERVER_ROUTING_CONFIG,
+            new AtomicReference<>(realConfig)).build());
     when(serverCall.getAuthority()).thenReturn("foo.google.com");
-
-    Filter filter = mock(Filter.class);
-    when(filter.typeUrls()).thenReturn(new String[]{"filter-type-url"});
-    filterRegistry.register(filter);
     ServerCallHandler<Void, Void> next = mock(ServerCallHandler.class);
     interceptor.interceptCall(serverCall, new Metadata(), next);
-    verify(next, never()).startCall(any(ServerCall.class), any(Metadata.class));
-    ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class);
-    verify(serverCall).close(statusCaptor.capture(), any(Metadata.class));
-    Status status = statusCaptor.getValue();
-    assertThat(status.getCode()).isEqualTo(Status.UNAVAILABLE.getCode());
-    assertThat(status.getDescription()).isEqualTo(
-            "HttpFilterConfig(type URL: filter-type-url) is not supported on server-side.");
+    verify(next).startCall(eq(serverCall), any(Metadata.class));
+    assertThat(interceptorTrace).isEqualTo(Arrays.asList(0));
   }
 
   @Test
@@ -809,7 +807,8 @@
             "foo.google.com", "filter-type-url");
     ServerCall<Void, Void> serverCall = mock(ServerCall.class);
     when(serverCall.getAttributes()).thenReturn(
-            Attributes.newBuilder().set(ATTR_SERVER_ROUTING_CONFIG, routingConfig).build());
+        Attributes.newBuilder().set(ATTR_SERVER_ROUTING_CONFIG,
+            new AtomicReference<>(routingConfig)).build());
     when(serverCall.getAuthority()).thenReturn("not-match.google.com");
 
     Filter filter = mock(Filter.class);
@@ -848,7 +847,8 @@
             "foo.google.com", "filter-type-url");
     ServerCall<Void, Void> serverCall = mock(ServerCall.class);
     when(serverCall.getAttributes()).thenReturn(
-            Attributes.newBuilder().set(ATTR_SERVER_ROUTING_CONFIG, routingConfig).build());
+            Attributes.newBuilder()
+                .set(ATTR_SERVER_ROUTING_CONFIG, new AtomicReference<>(routingConfig)).build());
     when(serverCall.getMethodDescriptor()).thenReturn(createMethod("NotMatchMethod"));
     when(serverCall.getAuthority()).thenReturn("foo.google.com");
 
@@ -884,12 +884,11 @@
     xdsClient.ldsResource.get(5, TimeUnit.SECONDS);
     verify(mockBuilder).intercept(interceptorCaptor.capture());
     ConfigApplyingInterceptor interceptor = interceptorCaptor.getValue();
-    ServerRoutingConfig failingConfig = ServerRoutingConfig.create(
-            ImmutableList.<NamedFilterConfig>of(), new AtomicReference<ImmutableList<VirtualHost>>()
-    );
     ServerCall<Void, Void> serverCall = mock(ServerCall.class);
+
     when(serverCall.getAttributes()).thenReturn(
-            Attributes.newBuilder().set(ATTR_SERVER_ROUTING_CONFIG, failingConfig).build());
+        Attributes.newBuilder().set(ATTR_SERVER_ROUTING_CONFIG,
+            new AtomicReference<>(ServerRoutingConfig.FAILING_ROUTING_CONFIG)).build());
 
     ServerCallHandler<Void, Void> next = mock(ServerCallHandler.class);
     interceptor.interceptCall(serverCall, new Metadata(), next);
@@ -899,14 +898,12 @@
     Status status = statusCaptor.getValue();
     assertThat(status.getCode()).isEqualTo(Status.UNAVAILABLE.getCode());
     assertThat(status.getDescription()).isEqualTo(
-        "Missing xDS routing config VirtualHosts due to RDS config unavailable.");
+        "Missing or broken xDS routing config: RDS config unavailable.");
   }
 
   @Test
   @SuppressWarnings("unchecked")
-  public void interceptors() throws Exception {
-    ArgumentCaptor<ConfigApplyingInterceptor> interceptorCaptor =
-            ArgumentCaptor.forClass(ConfigApplyingInterceptor.class);
+  public void buildInterceptor_inline() throws Exception {
     final SettableFuture<Server> start = SettableFuture.create();
     Executors.newSingleThreadExecutor().execute(new Runnable() {
       @Override
@@ -919,14 +916,12 @@
       }
     });
     xdsClient.ldsResource.get(5, TimeUnit.SECONDS);
-    verify(mockBuilder).intercept(interceptorCaptor.capture());
-    final ConfigApplyingInterceptor interceptor = interceptorCaptor.getValue();
     RouteMatch routeMatch =
-            RouteMatch.create(
-                    PathMatcher.fromPath("/FooService/barMethod", true),
-                    Collections.<HeaderMatcher>emptyList(), null);
+        RouteMatch.create(
+            PathMatcher.fromPath("/FooService/barMethod", true),
+            Collections.<HeaderMatcher>emptyList(), null);
     Filter filter = mock(Filter.class, withSettings()
-            .extraInterfaces(ServerInterceptorBuilder.class));
+        .extraInterfaces(ServerInterceptorBuilder.class));
     when(filter.typeUrls()).thenReturn(new String[]{"filter-type-url"});
     filterRegistry.register(filter);
     FilterConfig f0 = mock(FilterConfig.class);
@@ -936,7 +931,7 @@
     ServerInterceptor interceptor0 = new ServerInterceptor() {
       @Override
       public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
-            Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+          Metadata headers, ServerCallHandler<ReqT, RespT> next) {
         interceptorTrace.add(0);
         return next.startCall(call, headers);
       }
@@ -949,55 +944,130 @@
         return next.startCall(call, headers);
       }
     };
-
+    when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, null))
+        .thenReturn(interceptor0);
+    when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, f0Override))
+        .thenReturn(interceptor1);
+    Route route = Route.forAction(routeMatch, null,
+        ImmutableMap.<String, FilterConfig>of());
     VirtualHost virtualHost  = VirtualHost.create(
-            "v1", Collections.singletonList("foo.google.com"),
-            Arrays.asList(Route.forAction(routeMatch, null,
-                    ImmutableMap.<String, FilterConfig>of())),
-            ImmutableMap.of("filter-config-name-0", f0Override));
-    ServerRoutingConfig routingConfig = ServerRoutingConfig.create(
-            Arrays.asList(new NamedFilterConfig("filter-config-name-0", f0),
-                    new NamedFilterConfig("filter-config-name-1", f0)),
-        new AtomicReference<>(ImmutableList.<VirtualHost>of(virtualHost))
-    );
+        "v1", Collections.singletonList("foo.google.com"), Arrays.asList(route),
+        ImmutableMap.of("filter-config-name-0", f0Override));
+    HttpConnectionManager hcmVirtual = HttpConnectionManager.forVirtualHosts(
+        0L, Collections.singletonList(virtualHost),
+        Arrays.asList(new NamedFilterConfig("filter-config-name-0", f0),
+            new NamedFilterConfig("filter-config-name-1", f0)));
+    EnvoyServerProtoData.FilterChain filterChain = createFilterChain("filter-chain-0", hcmVirtual);
+    xdsClient.deliverLdsUpdate(Collections.singletonList(filterChain), null);
+    start.get(5000, TimeUnit.MILLISECONDS);
+    verify(mockServer).start();
+    assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
+        .isEqualTo(1);
+    ServerInterceptor realInterceptor = selectorManager.getSelectorToUpdateSelector()
+        .getRoutingConfigs().get(filterChain).get().interceptors().get(route);
+    assertThat(realInterceptor).isNotNull();
+
     ServerCall<Void, Void> serverCall = mock(ServerCall.class);
     ServerCallHandler<Void, Void> mockNext = mock(ServerCallHandler.class);
     final ServerCall.Listener<Void> listener = new ServerCall.Listener<Void>() {};
     when(mockNext.startCall(any(ServerCall.class), any(Metadata.class))).thenReturn(listener);
-    when(serverCall.getAttributes()).thenReturn(
-            Attributes.newBuilder().set(ATTR_SERVER_ROUTING_CONFIG, routingConfig).build());
-    when(serverCall.getMethodDescriptor()).thenReturn(createMethod("FooService/barMethod"));
-    when(serverCall.getAuthority()).thenReturn("foo.google.com");
-
-    when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, f0Override))
-            .thenReturn(null);
-    when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, null))
-            .thenReturn(null);
-    ServerCall.Listener<Void> configApplyingInterceptorListener =
-            interceptor.interceptCall(serverCall, new Metadata(), mockNext);
-    assertThat(configApplyingInterceptorListener).isSameInstanceAs(listener);
+    realInterceptor.interceptCall(serverCall, new Metadata(), mockNext);
+    assertThat(interceptorTrace).isEqualTo(Arrays.asList(1, 0));
     verify(mockNext).startCall(eq(serverCall), any(Metadata.class));
-    assertThat(interceptorTrace).isEqualTo(Arrays.asList());
+  }
 
-    when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, f0Override))
-            .thenReturn(null);
+  @Test
+  @SuppressWarnings("unchecked")
+  public void buildInterceptor_rds() throws Exception {
+    final SettableFuture<Server> start = SettableFuture.create();
+    Executors.newSingleThreadExecutor().execute(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          start.set(xdsServerWrapper.start());
+        } catch (Exception ex) {
+          start.setException(ex);
+        }
+      }
+    });
+    xdsClient.ldsResource.get(5, TimeUnit.SECONDS);
+
+    Filter filter = mock(Filter.class, withSettings()
+        .extraInterfaces(ServerInterceptorBuilder.class));
+    when(filter.typeUrls()).thenReturn(new String[]{"filter-type-url"});
+    filterRegistry.register(filter);
+    FilterConfig f0 = mock(FilterConfig.class);
+    FilterConfig f0Override = mock(FilterConfig.class);
+    when(f0.typeUrl()).thenReturn("filter-type-url");
+    final List<Integer> interceptorTrace = new ArrayList<>();
+    ServerInterceptor interceptor0 = new ServerInterceptor() {
+      @Override
+      public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
+          Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+        interceptorTrace.add(0);
+        return next.startCall(call, headers);
+      }
+    };
+    ServerInterceptor interceptor1 = new ServerInterceptor() {
+      @Override
+      public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
+          Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+        interceptorTrace.add(1);
+        return next.startCall(call, headers);
+      }
+    };
     when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, null))
-            .thenReturn(interceptor0);
-    configApplyingInterceptorListener = interceptor.interceptCall(
-            serverCall, new Metadata(), mockNext);
-    assertThat(configApplyingInterceptorListener).isSameInstanceAs(listener);
+        .thenReturn(interceptor0);
+    when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, f0Override))
+        .thenReturn(interceptor1);
+    RouteMatch routeMatch =
+        RouteMatch.create(
+            PathMatcher.fromPath("/FooService/barMethod", true),
+            Collections.<HeaderMatcher>emptyList(), null);
+
+    HttpConnectionManager rdsHcm = HttpConnectionManager.forRdsName(0L, "r0",
+        Arrays.asList(new NamedFilterConfig("filter-config-name-0", f0),
+            new NamedFilterConfig("filter-config-name-1", f0)));
+    EnvoyServerProtoData.FilterChain filterChain = createFilterChain("filter-chain-0", rdsHcm);
+    xdsClient.deliverLdsUpdate(Collections.singletonList(filterChain), null);
+    Route route = Route.forAction(routeMatch, null,
+        ImmutableMap.<String, FilterConfig>of());
+    VirtualHost virtualHost  = VirtualHost.create(
+        "v1", Collections.singletonList("foo.google.com"), Arrays.asList(route),
+        ImmutableMap.of("filter-config-name-0", f0Override));
+    xdsClient.rdsCount.await(5, TimeUnit.SECONDS);
+    xdsClient.deliverRdsUpdate("r0", Collections.singletonList(virtualHost));
+    start.get(5000, TimeUnit.MILLISECONDS);
+    verify(mockServer).start();
+    assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs().size())
+        .isEqualTo(1);
+    ServerInterceptor realInterceptor = selectorManager.getSelectorToUpdateSelector()
+        .getRoutingConfigs().get(filterChain).get().interceptors().get(route);
+    assertThat(realInterceptor).isNotNull();
+
+    ServerCall<Void, Void> serverCall = mock(ServerCall.class);
+    ServerCallHandler<Void, Void> mockNext = mock(ServerCallHandler.class);
+    final ServerCall.Listener<Void> listener = new ServerCall.Listener<Void>() {};
+    when(mockNext.startCall(any(ServerCall.class), any(Metadata.class))).thenReturn(listener);
+    realInterceptor.interceptCall(serverCall, new Metadata(), mockNext);
+    assertThat(interceptorTrace).isEqualTo(Arrays.asList(1, 0));
+    verify(mockNext).startCall(eq(serverCall), any(Metadata.class));
+
+    virtualHost  = VirtualHost.create(
+        "v1", Collections.singletonList("foo.google.com"), Arrays.asList(route),
+         ImmutableMap.<String, FilterConfig>of());
+    xdsClient.deliverRdsUpdate("r0", Collections.singletonList(virtualHost));
+    realInterceptor = selectorManager.getSelectorToUpdateSelector().getRoutingConfigs()
+        .get(filterChain).get().interceptors().get(route);
+    assertThat(realInterceptor).isNotNull();
+    interceptorTrace.clear();
+    realInterceptor.interceptCall(serverCall, new Metadata(), mockNext);
+    assertThat(interceptorTrace).isEqualTo(Arrays.asList(0, 0));
     verify(mockNext, times(2)).startCall(eq(serverCall), any(Metadata.class));
-    assertThat(interceptorTrace).isEqualTo(Arrays.asList(0));
 
-    when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, f0Override))
-            .thenReturn(interceptor0);
-    when(((ServerInterceptorBuilder)filter).buildServerInterceptor(f0, null))
-            .thenReturn(interceptor1);
-    configApplyingInterceptorListener = interceptor.interceptCall(
-            serverCall, new Metadata(), mockNext);
-    assertThat(configApplyingInterceptorListener).isSameInstanceAs(listener);
-    verify(mockNext, times(3)).startCall(eq(serverCall), any(Metadata.class));
-    assertThat(interceptorTrace).isEqualTo(Arrays.asList(0, 0, 1));
+    xdsClient.rdsWatchers.get("r0").onResourceDoesNotExist("r0");
+    assertThat(selectorManager.getSelectorToUpdateSelector().getRoutingConfigs()
+        .get(filterChain).get()).isEqualTo(noopConfig);
   }
 
   private static FilterChain createFilterChain(String name, HttpConnectionManager hcm) {
@@ -1012,8 +1082,12 @@
   }
 
   private static HttpConnectionManager createRds(String name) {
+    return createRds(name, null);
+  }
+
+  private static HttpConnectionManager createRds(String name, FilterConfig filterConfig) {
     return HttpConnectionManager.forRdsName(0L, name,
-            Arrays.asList(new NamedFilterConfig("named-config-" + name, null)));
+        Arrays.asList(new NamedFilterConfig("named-config-" + name, filterConfig)));
   }
 
   private static EnvoyServerProtoData.FilterChainMatch createMatch() {
@@ -1041,9 +1115,8 @@
             Collections.<String, FilterConfig>emptyMap());
     FilterConfig f0 = mock(FilterConfig.class);
     when(f0.typeUrl()).thenReturn(filterType);
-    return ServerRoutingConfig.create(
-        Arrays.asList(new NamedFilterConfig("filter-config-name-0", f0)),
-        new AtomicReference<>(ImmutableList.<VirtualHost>of(virtualHost))
+    return ServerRoutingConfig.create(ImmutableList.<VirtualHost>of(virtualHost),
+        ImmutableMap.<Route, ServerInterceptor>of()
     );
   }