• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20378

29 Jul 2026 05:42PM UTC coverage: 89.156% (-0.02%) from 89.175%
#20378

push

github

web-flow
xds: Supports injecting custom ldsResourceNameResolvers (#12925)

Also add support for creating XdsServerBuilder with SocketAddresses.

38296 of 42954 relevant lines covered (89.16%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.4
/../xds/src/main/java/io/grpc/xds/XdsServerWrapper.java
1
/*
2
 * Copyright 2021 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.xds;
18

19
import static com.google.common.base.Preconditions.checkNotNull;
20
import static com.google.common.base.Preconditions.checkState;
21
import static io.grpc.xds.client.Bootstrapper.XDSTP_SCHEME;
22

23
import com.google.auto.value.AutoValue;
24
import com.google.common.annotations.VisibleForTesting;
25
import com.google.common.collect.ImmutableList;
26
import com.google.common.collect.ImmutableMap;
27
import com.google.common.net.HostAndPort;
28
import com.google.common.net.InetAddresses;
29
import com.google.common.util.concurrent.SettableFuture;
30
import io.envoyproxy.envoy.config.core.v3.SocketAddress.Protocol;
31
import io.grpc.Attributes;
32
import io.grpc.ChannelConfigurator;
33
import io.grpc.InternalServerInterceptors;
34
import io.grpc.Metadata;
35
import io.grpc.MethodDescriptor;
36
import io.grpc.MetricRecorder;
37
import io.grpc.Server;
38
import io.grpc.ServerBuilder;
39
import io.grpc.ServerCall;
40
import io.grpc.ServerCall.Listener;
41
import io.grpc.ServerCallHandler;
42
import io.grpc.ServerInterceptor;
43
import io.grpc.ServerServiceDefinition;
44
import io.grpc.Status;
45
import io.grpc.StatusException;
46
import io.grpc.StatusOr;
47
import io.grpc.SynchronizationContext;
48
import io.grpc.SynchronizationContext.ScheduledHandle;
49
import io.grpc.internal.GrpcUtil;
50
import io.grpc.internal.ObjectPool;
51
import io.grpc.internal.SharedResourceHolder;
52
import io.grpc.xds.EnvoyServerProtoData.FilterChain;
53
import io.grpc.xds.Filter.FilterConfig;
54
import io.grpc.xds.Filter.FilterContext;
55
import io.grpc.xds.Filter.NamedFilterConfig;
56
import io.grpc.xds.FilterChainMatchingProtocolNegotiators.FilterChainMatchingHandler.FilterChainSelector;
57
import io.grpc.xds.ThreadSafeRandom.ThreadSafeRandomImpl;
58
import io.grpc.xds.VirtualHost.Route;
59
import io.grpc.xds.XdsListenerResource.LdsUpdate;
60
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
61
import io.grpc.xds.XdsServerBuilder.XdsServingStatusListener;
62
import io.grpc.xds.client.Bootstrapper.BootstrapInfo;
63
import io.grpc.xds.client.XdsClient;
64
import io.grpc.xds.client.XdsClient.ResourceWatcher;
65
import io.grpc.xds.internal.security.SslContextProviderSupplier;
66
import java.io.IOException;
67
import java.net.InetAddress;
68
import java.net.SocketAddress;
69
import java.util.ArrayList;
70
import java.util.HashMap;
71
import java.util.HashSet;
72
import java.util.List;
73
import java.util.Map;
74
import java.util.Set;
75
import java.util.concurrent.CountDownLatch;
76
import java.util.concurrent.ExecutionException;
77
import java.util.concurrent.ScheduledExecutorService;
78
import java.util.concurrent.TimeUnit;
79
import java.util.concurrent.atomic.AtomicBoolean;
80
import java.util.concurrent.atomic.AtomicReference;
81
import java.util.function.Function;
82
import java.util.logging.Level;
83
import java.util.logging.Logger;
84
import javax.annotation.Nullable;
85

86
final class XdsServerWrapper extends Server {
87
  private static final Logger logger = Logger.getLogger(XdsServerWrapper.class.getName());
1✔
88

89
  private final SynchronizationContext syncContext = new SynchronizationContext(
1✔
90
      new Thread.UncaughtExceptionHandler() {
1✔
91
        @Override
92
        public void uncaughtException(Thread t, Throwable e) {
93
          logger.log(Level.SEVERE, "Exception!" + e);
×
94
          // TODO(chengyuanzhang): implement cleanup.
95
        }
×
96
      });
97

98
  public static final Attributes.Key<AtomicReference<ServerRoutingConfig>>
99
      ATTR_SERVER_ROUTING_CONFIG =
1✔
100
      Attributes.Key.create("io.grpc.xds.ServerWrapper.serverRoutingConfig");
1✔
101

102
  @VisibleForTesting
103
  static final long RETRY_DELAY_NANOS = TimeUnit.MINUTES.toNanos(1);
1✔
104
  private final String listenerAddress;
105
  private final ServerBuilder<?> delegateBuilder;
106
  private boolean sharedTimeService;
107
  private final ScheduledExecutorService timeService;
108
  private final FilterRegistry filterRegistry;
109
  private final ThreadSafeRandom random = ThreadSafeRandomImpl.instance;
1✔
110
  private final XdsClientPoolFactory xdsClientPoolFactory;
111
  private final @Nullable Map<String, ?> bootstrapOverride;
112
  private final @Nullable Function<String, String> ldsResourceNameResolver;
113
  private final XdsServingStatusListener listener;
114
  private final FilterChainSelectorManager filterChainSelectorManager;
115
  private final AtomicBoolean started = new AtomicBoolean(false);
1✔
116
  private final AtomicBoolean shutdown = new AtomicBoolean(false);
1✔
117
  private boolean isServing;
118
  private final CountDownLatch internalTerminationLatch = new CountDownLatch(1);
1✔
119
  private final SettableFuture<Exception> initialStartFuture = SettableFuture.create();
1✔
120
  private boolean initialStarted;
121
  private ScheduledHandle restartTimer;
122
  private ObjectPool<XdsClient> xdsClientPool;
123
  private XdsClient xdsClient;
124
  private DiscoveryState discoveryState;
125
  private volatile Server delegate;
126

127
  // Must be accessed in syncContext.
128
  // Filter instances are unique per Server, per FilterChain, and per filter's name+typeUrl.
129
  // FilterChain.name -> <NamedFilterConfig.filterStateKey -> filter_instance>.
130
  private final HashMap<String, HashMap<String, Filter>> activeFilters = new HashMap<>();
1✔
131
  // Default filter chain Filter instances are unique per Server, and per filter's name+typeUrl.
132
  // NamedFilterConfig.filterStateKey -> filter_instance.
133
  private final HashMap<String, Filter> activeFiltersDefaultChain = new HashMap<>();
1✔
134

135
  private final ChannelConfigurator channelConfigurator;
136

137
  XdsServerWrapper(
138
      String listenerAddress,
139
      ServerBuilder<?> delegateBuilder,
140
      XdsServingStatusListener listener,
141
      FilterChainSelectorManager filterChainSelectorManager,
142
      XdsClientPoolFactory xdsClientPoolFactory,
143
      @Nullable Map<String, ?> bootstrapOverride,
144
      @Nullable Function<String, String> ldsResourceNameResolver,
145
      FilterRegistry filterRegistry,
146
      ChannelConfigurator channelConfigurator) {
147
    this(
1✔
148
        listenerAddress,
149
        delegateBuilder,
150
        listener,
151
        filterChainSelectorManager,
152
        xdsClientPoolFactory,
153
        bootstrapOverride,
154
        ldsResourceNameResolver,
155
        filterRegistry,
156
        SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE),
1✔
157
        channelConfigurator);
158
    sharedTimeService = true;
1✔
159
  }
1✔
160

161
  XdsServerWrapper(
162
      String listenerAddress,
163
      ServerBuilder<?> delegateBuilder,
164
      XdsServingStatusListener listener,
165
      FilterChainSelectorManager filterChainSelectorManager,
166
      XdsClientPoolFactory xdsClientPoolFactory,
167
      @Nullable Map<String, ?> bootstrapOverride,
168
      FilterRegistry filterRegistry,
169
      ChannelConfigurator channelConfigurator) {
170
    this(
×
171
        listenerAddress,
172
        delegateBuilder,
173
        listener,
174
        filterChainSelectorManager,
175
        xdsClientPoolFactory,
176
        bootstrapOverride,
177
        null,
178
        filterRegistry,
179
        SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE),
×
180
        channelConfigurator);
181
    sharedTimeService = true;
×
182
  }
×
183

184
  XdsServerWrapper(
185
      String listenerAddress,
186
      ServerBuilder<?> delegateBuilder,
187
      XdsServingStatusListener listener,
188
      FilterChainSelectorManager filterChainSelectorManager,
189
      XdsClientPoolFactory xdsClientPoolFactory,
190
      @Nullable Map<String, ?> bootstrapOverride,
191
      FilterRegistry filterRegistry) {
192
    this(
1✔
193
        listenerAddress,
194
        delegateBuilder,
195
        listener,
196
        filterChainSelectorManager,
197
        xdsClientPoolFactory,
198
        bootstrapOverride,
199
        null,
200
        filterRegistry,
201
        SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE),
1✔
202
        builder -> { });
×
203
    sharedTimeService = true;
1✔
204
  }
1✔
205

206
  XdsServerWrapper(
207
      String listenerAddress,
208
      ServerBuilder<?> delegateBuilder,
209
      XdsServingStatusListener listener,
210
      FilterChainSelectorManager filterChainSelectorManager,
211
      XdsClientPoolFactory xdsClientPoolFactory,
212
      @Nullable Map<String, ?> bootstrapOverride,
213
      @Nullable Function<String, String> ldsResourceNameResolver,
214
      FilterRegistry filterRegistry) {
215
    this(
1✔
216
        listenerAddress,
217
        delegateBuilder,
218
        listener,
219
        filterChainSelectorManager,
220
        xdsClientPoolFactory,
221
        bootstrapOverride,
222
        ldsResourceNameResolver,
223
        filterRegistry,
224
        SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE),
1✔
225
        builder -> { });
×
226
    sharedTimeService = true;
1✔
227
  }
1✔
228

229
  @VisibleForTesting
230
  XdsServerWrapper(
231
          String listenerAddress,
232
          ServerBuilder<?> delegateBuilder,
233
          XdsServingStatusListener listener,
234
          FilterChainSelectorManager filterChainSelectorManager,
235
          XdsClientPoolFactory xdsClientPoolFactory,
236
          @Nullable Map<String, ?> bootstrapOverride,
237
          FilterRegistry filterRegistry,
238
          ScheduledExecutorService timeService) {
239
    this(
1✔
240
        listenerAddress,
241
        delegateBuilder,
242
        listener,
243
        filterChainSelectorManager,
244
        xdsClientPoolFactory,
245
        bootstrapOverride,
246
        null,
247
        filterRegistry,
248
        timeService,
249
        builder -> { });
×
250
  }
1✔
251

252
  @VisibleForTesting
253
  XdsServerWrapper(
254
      String listenerAddress,
255
      ServerBuilder<?> delegateBuilder,
256
      XdsServingStatusListener listener,
257
      FilterChainSelectorManager filterChainSelectorManager,
258
      XdsClientPoolFactory xdsClientPoolFactory,
259
      @Nullable Map<String, ?> bootstrapOverride,
260
      @Nullable Function<String, String> ldsResourceNameResolver,
261
      FilterRegistry filterRegistry,
262
      ScheduledExecutorService timeService) {
263
    this(
×
264
        listenerAddress,
265
        delegateBuilder,
266
        listener,
267
        filterChainSelectorManager,
268
        xdsClientPoolFactory,
269
        bootstrapOverride,
270
        ldsResourceNameResolver,
271
        filterRegistry,
272
        timeService,
273
        builder -> { });
×
274
  }
×
275

276
  @VisibleForTesting
277
  XdsServerWrapper(
278
          String listenerAddress,
279
          ServerBuilder<?> delegateBuilder,
280
          XdsServingStatusListener listener,
281
          FilterChainSelectorManager filterChainSelectorManager,
282
          XdsClientPoolFactory xdsClientPoolFactory,
283
          @Nullable Map<String, ?> bootstrapOverride,
284
          FilterRegistry filterRegistry,
285
          ScheduledExecutorService timeService,
286
          ChannelConfigurator channelConfigurator) {
287
    this(
1✔
288
        listenerAddress,
289
        delegateBuilder,
290
        listener,
291
        filterChainSelectorManager,
292
        xdsClientPoolFactory,
293
        bootstrapOverride,
294
        null,
295
        filterRegistry,
296
        timeService,
297
        channelConfigurator);
298
  }
1✔
299

300
  @VisibleForTesting
301
  XdsServerWrapper(
302
      String listenerAddress,
303
      ServerBuilder<?> delegateBuilder,
304
      XdsServingStatusListener listener,
305
      FilterChainSelectorManager filterChainSelectorManager,
306
      XdsClientPoolFactory xdsClientPoolFactory,
307
      @Nullable Map<String, ?> bootstrapOverride,
308
      @Nullable Function<String, String> ldsResourceNameResolver,
309
      FilterRegistry filterRegistry,
310
      ScheduledExecutorService timeService,
311
      ChannelConfigurator channelConfigurator) {
1✔
312
    this.listenerAddress = checkNotNull(listenerAddress, "listenerAddress");
1✔
313
    this.delegateBuilder = checkNotNull(delegateBuilder, "delegateBuilder");
1✔
314
    this.delegateBuilder.intercept(new ConfigApplyingInterceptor());
1✔
315
    this.listener = checkNotNull(listener, "listener");
1✔
316
    this.filterChainSelectorManager
1✔
317
        = checkNotNull(filterChainSelectorManager, "filterChainSelectorManager");
1✔
318
    this.xdsClientPoolFactory = checkNotNull(xdsClientPoolFactory, "xdsClientPoolFactory");
1✔
319
    this.bootstrapOverride = bootstrapOverride;
1✔
320
    this.ldsResourceNameResolver = ldsResourceNameResolver;
1✔
321
    this.timeService = checkNotNull(timeService, "timeService");
1✔
322
    this.filterRegistry = checkNotNull(filterRegistry,"filterRegistry");
1✔
323
    this.delegate = delegateBuilder.build();
1✔
324
    this.channelConfigurator = checkNotNull(channelConfigurator, "channelConfigurator");
1✔
325
  }
1✔
326

327
  @Override
328
  public Server start() throws IOException {
329
    checkState(started.compareAndSet(false, true), "Already started");
1✔
330
    syncContext.execute(new Runnable() {
1✔
331
      @Override
332
      public void run() {
333
        internalStart();
1✔
334
      }
1✔
335
    });
336
    Exception exception;
337
    try {
338
      exception = initialStartFuture.get();
1✔
339
    } catch (InterruptedException | ExecutionException e) {
×
340
      throw new RuntimeException(e);
×
341
    }
1✔
342
    if (exception != null) {
1✔
343
      throw (exception instanceof IOException) ? (IOException) exception :
1✔
344
              new IOException(exception);
1✔
345
    }
346
    return this;
1✔
347
  }
348

349
  private void internalStart() {
350
    try {
351
      BootstrapInfo bootstrapInfo;
352
      if (bootstrapOverride == null) {
1✔
353
        bootstrapInfo = GrpcBootstrapperImpl.defaultBootstrap();
×
354
      } else {
355
        bootstrapInfo = new GrpcBootstrapperImpl().bootstrap(bootstrapOverride);
1✔
356
      }
357
      xdsClientPool = xdsClientPoolFactory.getOrCreate(
1✔
358
          "#server", bootstrapInfo, new MetricRecorder() {},
1✔
359
          channelConfigurator);
360
    } catch (Exception e) {
×
361
      StatusException statusException = Status.UNAVAILABLE.withDescription(
×
362
              "Failed to initialize xDS").withCause(e).asException();
×
363
      listener.onNotServing(statusException);
×
364
      initialStartFuture.set(statusException);
×
365
      return;
×
366
    }
1✔
367
    xdsClient = xdsClientPool.getObject();
1✔
368
    String resourceName;
369
    if (ldsResourceNameResolver != null) {
1✔
370
      resourceName = ldsResourceNameResolver.apply(listenerAddress);
1✔
371
    } else {
372
      String listenerTemplate = xdsClient.getBootstrapInfo().serverListenerResourceNameTemplate();
1✔
373
      if (listenerTemplate == null) {
1✔
374
        StatusException statusException =
1✔
375
            Status.UNAVAILABLE
376
                .withDescription("Can only support xDS v3 with listener resource name template")
1✔
377
                .asException();
1✔
378
        listener.onNotServing(statusException);
1✔
379
        initialStartFuture.set(statusException);
1✔
380
        xdsClient = xdsClientPool.returnObject(xdsClient);
1✔
381
        return;
1✔
382
      }
383
      String replacement = listenerAddress;
1✔
384
      if (listenerTemplate.startsWith(XDSTP_SCHEME)) {
1✔
385
        replacement = XdsClient.percentEncodePath(replacement);
1✔
386
      }
387
      resourceName = listenerTemplate.replaceAll("%s", replacement);
1✔
388
    }
389
    discoveryState = new DiscoveryState(resourceName);
1✔
390
  }
1✔
391

392
  @Override
393
  public Server shutdown() {
394
    if (!shutdown.compareAndSet(false, true)) {
1✔
395
      return this;
1✔
396
    }
397
    syncContext.execute(new Runnable() {
1✔
398
      @Override
399
      public void run() {
400
        if (!delegate.isShutdown()) {
1✔
401
          delegate.shutdown();
1✔
402
        }
403
        internalShutdown();
1✔
404
      }
1✔
405
    });
406
    return this;
1✔
407
  }
408

409
  @Override
410
  public Server shutdownNow() {
411
    if (!shutdown.compareAndSet(false, true)) {
1✔
412
      return this;
1✔
413
    }
414
    syncContext.execute(new Runnable() {
1✔
415
      @Override
416
      public void run() {
417
        if (!delegate.isShutdown()) {
1✔
418
          delegate.shutdownNow();
1✔
419
        }
420
        internalShutdown();
1✔
421
        initialStartFuture.set(new IOException("server is forcefully shut down"));
1✔
422
      }
1✔
423
    });
424
    return this;
1✔
425
  }
426

427
  // Must run in SynchronizationContext
428
  private void internalShutdown() {
429
    logger.log(Level.FINER, "Shutting down XdsServerWrapper");
1✔
430
    if (discoveryState != null) {
1✔
431
      discoveryState.shutdown();
1✔
432
    }
433
    if (xdsClient != null) {
1✔
434
      xdsClient = xdsClientPool.returnObject(xdsClient);
1✔
435
    }
436
    if (restartTimer != null) {
1✔
437
      restartTimer.cancel();
1✔
438
    }
439
    if (sharedTimeService) {
1✔
440
      SharedResourceHolder.release(GrpcUtil.TIMER_SERVICE, timeService);
1✔
441
    }
442
    isServing = false;
1✔
443
    internalTerminationLatch.countDown();
1✔
444
  }
1✔
445

446
  @Override
447
  public boolean isShutdown() {
448
    return shutdown.get();
1✔
449
  }
450

451
  @Override
452
  public boolean isTerminated() {
453
    return internalTerminationLatch.getCount() == 0 && delegate.isTerminated();
1✔
454
  }
455

456
  @Override
457
  public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
458
    long startTime = System.nanoTime();
1✔
459
    if (!internalTerminationLatch.await(timeout, unit)) {
1✔
460
      return false;
×
461
    }
462
    long remainingTime = unit.toNanos(timeout) - (System.nanoTime() - startTime);
1✔
463
    return delegate.awaitTermination(remainingTime, TimeUnit.NANOSECONDS);
1✔
464
  }
465

466
  @Override
467
  public void awaitTermination() throws InterruptedException {
468
    internalTerminationLatch.await();
1✔
469
    delegate.awaitTermination();
1✔
470
  }
1✔
471

472
  @Override
473
  public int getPort() {
474
    return delegate.getPort();
1✔
475
  }
476

477
  @Override
478
  public List<? extends SocketAddress> getListenSockets() {
479
    return delegate.getListenSockets();
1✔
480
  }
481

482
  @Override
483
  public List<ServerServiceDefinition> getServices() {
484
    return delegate.getServices();
×
485
  }
486

487
  @Override
488
  public List<ServerServiceDefinition> getImmutableServices() {
489
    return delegate.getImmutableServices();
×
490
  }
491

492
  @Override
493
  public List<ServerServiceDefinition> getMutableServices() {
494
    return delegate.getMutableServices();
×
495
  }
496

497
  // Must run in SynchronizationContext
498
  private void startDelegateServer() {
499
    if (restartTimer != null && restartTimer.isPending()) {
1✔
500
      return;
×
501
    }
502
    if (isServing) {
1✔
503
      return;
1✔
504
    }
505
    if (delegate.isShutdown()) {
1✔
506
      delegate = delegateBuilder.build();
1✔
507
    }
508
    try {
509
      delegate.start();
1✔
510
      listener.onServing();
1✔
511
      isServing = true;
1✔
512
      if (!initialStarted) {
1✔
513
        initialStarted = true;
1✔
514
        initialStartFuture.set(null);
1✔
515
      }
516
      logger.log(Level.FINER, "Delegate server started.");
1✔
517
    } catch (IOException e) {
1✔
518
      logger.log(Level.FINE, "Fail to start delegate server: {0}", e);
1✔
519
      if (!initialStarted) {
1✔
520
        initialStarted = true;
1✔
521
        initialStartFuture.set(e);
1✔
522
      } else {
523
        listener.onNotServing(e);
1✔
524
      }
525
      restartTimer = syncContext.schedule(
1✔
526
        new RestartTask(), RETRY_DELAY_NANOS, TimeUnit.NANOSECONDS, timeService);
527
    }
1✔
528
  }
1✔
529

530
  private final class RestartTask implements Runnable {
1✔
531
    @Override
532
    public void run() {
533
      startDelegateServer();
1✔
534
    }
1✔
535
  }
536

537
  private final class DiscoveryState implements ResourceWatcher<LdsUpdate> {
538
    private final String resourceName;
539
    // RDS resource name is the key.
540
    private final Map<String, RouteDiscoveryState> routeDiscoveryStates = new HashMap<>();
1✔
541
    // Track pending RDS resources using rds name.
542
    private final Set<String> pendingRds = new HashSet<>();
1✔
543
    // Most recently discovered filter chains.
544
    private List<FilterChain> filterChains = new ArrayList<>();
1✔
545
    // Most recently discovered default filter chain.
546
    @Nullable
547
    private FilterChain defaultFilterChain;
548
    private boolean stopped;
549
    private final Map<FilterChain, AtomicReference<ServerRoutingConfig>> savedRdsRoutingConfigRef 
1✔
550
        = new HashMap<>();
551
    private final ServerInterceptor noopInterceptor = new ServerInterceptor() {
1✔
552
      @Override
553
      public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
554
          Metadata headers, ServerCallHandler<ReqT, RespT> next) {
555
        return next.startCall(call, headers);
1✔
556
      }
557
    };
558

559
    private DiscoveryState(String resourceName) {
1✔
560
      this.resourceName = checkNotNull(resourceName, "resourceName");
1✔
561
      xdsClient.watchXdsResource(
1✔
562
          XdsListenerResource.getInstance(), resourceName, this, syncContext);
1✔
563
    }
1✔
564

565
    @Override
566
    public void onResourceChanged(final StatusOr<LdsUpdate> update) {
567
      if (stopped) {
1✔
568
        return;
×
569
      }
570

571
      if (!update.hasValue()) {
1✔
572
        Status status = update.getStatus();
1✔
573
        StatusException statusException = Status.UNAVAILABLE.withDescription(
1✔
574
                String.format("Listener %s unavailable: %s", resourceName, status.getDescription()))
1✔
575
            .withCause(status.asException())
1✔
576
            .asException();
1✔
577
        handleConfigNotFoundOrMismatch(statusException);
1✔
578
        return;
1✔
579
      }
580

581
      final LdsUpdate ldsUpdate = update.getValue();
1✔
582
      logger.log(Level.FINEST, "Received Lds update {0}", ldsUpdate);
1✔
583
      if (ldsUpdate.listener() == null) {
1✔
584
        handleConfigNotFoundOrMismatch(
1✔
585
            Status.NOT_FOUND.withDescription("Listener is null in LdsUpdate").asException());
1✔
586
        return;
1✔
587
      }
588
      String ldsAddress = ldsUpdate.listener().address();
1✔
589
      if (ldsAddress == null || ldsUpdate.listener().protocol() != Protocol.TCP
1✔
590
          || !ipAddressesMatch(ldsAddress)) {
1✔
591
        handleConfigNotFoundOrMismatch(
1✔
592
            Status.UNKNOWN.withDescription(
1✔
593
                String.format(
1✔
594
                    "Listener address mismatch: expected %s, but got %s.",
595
                    listenerAddress, ldsAddress)).asException());
1✔
596
        return;
1✔
597
      }
598

599
      if (!pendingRds.isEmpty()) {
1✔
600
        // filter chain state has not yet been applied to filterChainSelectorManager and there
601
        releaseSuppliersInFlight();
1✔
602
        pendingRds.clear();
1✔
603
      }
604

605
      filterChains = ldsUpdate.listener().filterChains();
1✔
606
      defaultFilterChain = ldsUpdate.listener().defaultFilterChain();
1✔
607
      updateActiveFilters();
1✔
608

609
      List<FilterChain> allFilterChains = filterChains;
1✔
610
      if (defaultFilterChain != null) {
1✔
611
        allFilterChains = new ArrayList<>(filterChains);
1✔
612
        allFilterChains.add(defaultFilterChain);
1✔
613
      }
614

615
      Set<String> allRds = new HashSet<>();
1✔
616
      for (FilterChain filterChain : allFilterChains) {
1✔
617
        HttpConnectionManager hcm = filterChain.httpConnectionManager();
1✔
618
        if (hcm.virtualHosts() == null) {
1✔
619
          RouteDiscoveryState rdsState = routeDiscoveryStates.get(hcm.rdsName());
1✔
620
          if (rdsState == null) {
1✔
621
            rdsState = new RouteDiscoveryState(hcm.rdsName());
1✔
622
            routeDiscoveryStates.put(hcm.rdsName(), rdsState);
1✔
623
            xdsClient.watchXdsResource(XdsRouteConfigureResource.getInstance(),
1✔
624
                hcm.rdsName(), rdsState, syncContext);
1✔
625
          }
626
          if (rdsState.isPending) {
1✔
627
            pendingRds.add(hcm.rdsName());
1✔
628
          }
629
          allRds.add(hcm.rdsName());
1✔
630
        }
631
      }
1✔
632

633
      for (Map.Entry<String, RouteDiscoveryState> entry: routeDiscoveryStates.entrySet()) {
1✔
634
        if (!allRds.contains(entry.getKey())) {
1✔
635
          xdsClient.cancelXdsResourceWatch(XdsRouteConfigureResource.getInstance(),
1✔
636
              entry.getKey(), entry.getValue());
1✔
637
        }
638
      }
1✔
639
      routeDiscoveryStates.keySet().retainAll(allRds);
1✔
640
      if (pendingRds.isEmpty()) {
1✔
641
        updateSelector();
1✔
642
      }
643
    }
1✔
644

645
    @Override
646
    public void onAmbientError(final Status error) {
647
      if (stopped) {
1✔
648
        return;
×
649
      }
650
      String description = error.getDescription() == null ? "" : error.getDescription() + " ";
1✔
651
      Status errorWithNodeId = error.withDescription(
1✔
652
          description + "xDS node ID: " + xdsClient.getBootstrapInfo().node().getId());
1✔
653
      logger.log(Level.FINE, "Error from XdsClient", errorWithNodeId);
1✔
654

655
      if (!isServing) {
1✔
656
        listener.onNotServing(errorWithNodeId.asException());
×
657
      }
658
    }
1✔
659

660
    private boolean ipAddressesMatch(String ldsAddress) {
661
      HostAndPort ldsAddressHnP = HostAndPort.fromString(ldsAddress);
1✔
662
      HostAndPort listenerAddressHnP = HostAndPort.fromString(listenerAddress);
1✔
663
      if (!ldsAddressHnP.hasPort() || !listenerAddressHnP.hasPort()
1✔
664
          || ldsAddressHnP.getPort() != listenerAddressHnP.getPort()) {
1✔
665
        return false;
1✔
666
      }
667
      InetAddress listenerIp = InetAddresses.forString(listenerAddressHnP.getHost());
1✔
668
      InetAddress ldsIp = InetAddresses.forString(ldsAddressHnP.getHost());
1✔
669
      return listenerIp.equals(ldsIp);
1✔
670
    }
671

672
    private void shutdown() {
673
      stopped = true;
1✔
674
      cleanUpRouteDiscoveryStates();
1✔
675
      logger.log(Level.FINE, "Stop watching LDS resource {0}", resourceName);
1✔
676
      xdsClient.cancelXdsResourceWatch(XdsListenerResource.getInstance(), resourceName, this);
1✔
677
      shutdownActiveFilters();
1✔
678
      List<SslContextProviderSupplier> toRelease = getSuppliersInUse();
1✔
679
      filterChainSelectorManager.updateSelector(FilterChainSelector.NO_FILTER_CHAIN);
1✔
680
      for (SslContextProviderSupplier s: toRelease) {
1✔
681
        s.close();
1✔
682
      }
1✔
683
      releaseSuppliersInFlight();
1✔
684
    }
1✔
685

686
    private void updateSelector() {
687
      // This is regenerated in generateRoutingConfig() calls below.
688
      savedRdsRoutingConfigRef.clear();
1✔
689

690
      // Prepare server routing config map.
691
      ImmutableMap.Builder<FilterChain, AtomicReference<ServerRoutingConfig>> routingConfigs =
692
          ImmutableMap.builder();
1✔
693
      for (FilterChain filterChain: filterChains) {
1✔
694
        HashMap<String, Filter> chainFilters = activeFilters.get(filterChain.name());
1✔
695
        routingConfigs.put(filterChain, generateRoutingConfig(filterChain, chainFilters));
1✔
696
      }
1✔
697

698
      // Prepare the new selector.
699
      FilterChainSelector selector;
700
      if (defaultFilterChain != null) {
1✔
701
        selector = new FilterChainSelector(
1✔
702
            routingConfigs.build(),
1✔
703
            defaultFilterChain.sslContextProviderSupplier(),
1✔
704
            generateRoutingConfig(defaultFilterChain, activeFiltersDefaultChain));
1✔
705
      } else {
706
        selector = new FilterChainSelector(routingConfigs.build());
1✔
707
      }
708

709
      // Prepare the list of current selector's resources to close later.
710
      List<SslContextProviderSupplier> oldSslSuppliers = getSuppliersInUse();
1✔
711

712
      // Swap the selectors, initiate a graceful shutdown of the old one.
713
      logger.log(Level.FINEST, "Updating selector {0}", selector);
1✔
714
      filterChainSelectorManager.updateSelector(selector);
1✔
715

716
      // Release old resources.
717
      for (SslContextProviderSupplier supplier: oldSslSuppliers) {
1✔
718
        supplier.close();
1✔
719
      }
1✔
720

721
      // Now that we have valid Transport Socket config, we can start/restart listening on a port.
722
      startDelegateServer();
1✔
723
    }
1✔
724

725
    // called in syncContext
726
    private void updateActiveFilters() {
727
      Set<String> removedChains = new HashSet<>(activeFilters.keySet());
1✔
728
      for (FilterChain filterChain: filterChains) {
1✔
729
        removedChains.remove(filterChain.name());
1✔
730
        updateActiveFiltersForChain(
1✔
731
            activeFilters.computeIfAbsent(filterChain.name(), k -> new HashMap<>()),
1✔
732
            filterChain.httpConnectionManager().httpFilterConfigs());
1✔
733
      }
1✔
734

735
      // Shutdown all filters of chains missing from the LDS.
736
      for (String chainToShutdown : removedChains) {
1✔
737
        HashMap<String, Filter> filtersToShutdown = activeFilters.get(chainToShutdown);
1✔
738
        checkNotNull(filtersToShutdown, "filtersToShutdown of chain %s", chainToShutdown);
1✔
739
        updateActiveFiltersForChain(filtersToShutdown, null);
1✔
740
        activeFilters.remove(chainToShutdown);
1✔
741
      }
1✔
742

743
      // Default chain.
744
      ImmutableList<NamedFilterConfig> defaultChainConfigs = null;
1✔
745
      if (defaultFilterChain != null) {
1✔
746
        defaultChainConfigs = defaultFilterChain.httpConnectionManager().httpFilterConfigs();
1✔
747
      }
748
      updateActiveFiltersForChain(activeFiltersDefaultChain, defaultChainConfigs);
1✔
749
    }
1✔
750

751
    // called in syncContext
752
    private void shutdownActiveFilters() {
753
      for (HashMap<String, Filter> chainFilters : activeFilters.values()) {
1✔
754
        checkNotNull(chainFilters, "chainFilters");
1✔
755
        updateActiveFiltersForChain(chainFilters, null);
1✔
756
      }
1✔
757
      activeFilters.clear();
1✔
758
      updateActiveFiltersForChain(activeFiltersDefaultChain, null);
1✔
759
    }
1✔
760

761
    // called in syncContext
762
    private void updateActiveFiltersForChain(
763
        Map<String, Filter> chainFilters, @Nullable List<NamedFilterConfig> filterConfigs) {
764
      if (filterConfigs == null) {
1✔
765
        filterConfigs = ImmutableList.of();
1✔
766
      }
767

768
      Set<String> filtersToShutdown = new HashSet<>(chainFilters.keySet());
1✔
769
      for (NamedFilterConfig namedFilter : filterConfigs) {
1✔
770
        String typeUrl = namedFilter.filterConfig.typeUrl();
1✔
771
        String filterKey = namedFilter.filterStateKey();
1✔
772

773
        Filter.Provider provider = filterRegistry.get(typeUrl);
1✔
774
        checkNotNull(provider, "provider %s", typeUrl);
1✔
775
        Filter filter = chainFilters.computeIfAbsent(
1✔
776
            filterKey, k -> provider.newInstance(
1✔
777
                FilterContext.create(namedFilter.name, new MetricRecorder() {})));
1✔
778
        checkNotNull(filter, "filter %s", filterKey);
1✔
779
        filtersToShutdown.remove(filterKey);
1✔
780
      }
1✔
781

782
      // Shutdown filters not present in current HCM.
783
      for (String filterKey : filtersToShutdown) {
1✔
784
        Filter filterToShutdown = chainFilters.remove(filterKey);
1✔
785
        checkNotNull(filterToShutdown, "filterToShutdown %s", filterKey);
1✔
786
        filterToShutdown.close();
1✔
787
      }
1✔
788
    }
1✔
789

790
    private AtomicReference<ServerRoutingConfig> generateRoutingConfig(
791
        FilterChain filterChain, Map<String, Filter> chainFilters) {
792
      HttpConnectionManager hcm = filterChain.httpConnectionManager();
1✔
793
      ServerRoutingConfig routingConfig;
794

795
      // Inlined routes.
796
      ImmutableList<VirtualHost> vhosts = hcm.virtualHosts();
1✔
797
      if (vhosts != null) {
1✔
798
        routingConfig = ServerRoutingConfig.create(vhosts,
1✔
799
            generatePerRouteInterceptors(hcm.httpFilterConfigs(), vhosts, chainFilters));
1✔
800
        return new AtomicReference<>(routingConfig);
1✔
801
      }
802

803
      // Routes from RDS.
804
      RouteDiscoveryState rds = routeDiscoveryStates.get(hcm.rdsName());
1✔
805
      checkNotNull(rds, "rds");
1✔
806

807
      ImmutableList<VirtualHost> savedVhosts = rds.savedVirtualHosts;
1✔
808
      if (savedVhosts != null) {
1✔
809
        routingConfig = ServerRoutingConfig.create(savedVhosts,
1✔
810
            generatePerRouteInterceptors(hcm.httpFilterConfigs(), savedVhosts, chainFilters));
1✔
811
      } else {
812
        routingConfig = ServerRoutingConfig.FAILING_ROUTING_CONFIG;
1✔
813
      }
814
      AtomicReference<ServerRoutingConfig> routingConfigRef = new AtomicReference<>(routingConfig);
1✔
815
      savedRdsRoutingConfigRef.put(filterChain, routingConfigRef);
1✔
816
      return routingConfigRef;
1✔
817
    }
818

819
    private ImmutableMap<Route, ServerInterceptor> generatePerRouteInterceptors(
820
        @Nullable List<NamedFilterConfig> filterConfigs,
821
        List<VirtualHost> virtualHosts,
822
        Map<String, Filter> chainFilters) {
823
      syncContext.throwIfNotInThisSynchronizationContext();
1✔
824

825
      checkNotNull(chainFilters, "chainFilters");
1✔
826
      ImmutableMap.Builder<Route, ServerInterceptor> perRouteInterceptors =
1✔
827
          new ImmutableMap.Builder<>();
828

829
      for (VirtualHost virtualHost : virtualHosts) {
1✔
830
        for (Route route : virtualHost.routes()) {
1✔
831
          // Short circuit.
832
          if (filterConfigs == null) {
1✔
833
            perRouteInterceptors.put(route, noopInterceptor);
×
834
            continue;
×
835
          }
836

837
          // Override vhost filter configs with more specific per-route configs.
838
          Map<String, FilterConfig> perRouteOverrides = ImmutableMap.<String, FilterConfig>builder()
1✔
839
              .putAll(virtualHost.filterConfigOverrides())
1✔
840
              .putAll(route.filterConfigOverrides())
1✔
841
              .buildKeepingLast();
1✔
842

843
          // Interceptors for this vhost/route combo.
844
          List<ServerInterceptor> interceptors = new ArrayList<>(filterConfigs.size());
1✔
845
          for (NamedFilterConfig namedFilter : filterConfigs) {
1✔
846
            String name = namedFilter.name;
1✔
847
            FilterConfig config = namedFilter.filterConfig;
1✔
848
            FilterConfig overrideConfig = perRouteOverrides.get(name);
1✔
849
            String filterKey = namedFilter.filterStateKey();
1✔
850

851
            Filter filter = chainFilters.get(filterKey);
1✔
852
            checkNotNull(filter, "chainFilters.get(%s)", filterKey);
1✔
853
            ServerInterceptor interceptor = filter.buildServerInterceptor(config, overrideConfig);
1✔
854

855
            if (interceptor != null) {
1✔
856
              interceptors.add(interceptor);
1✔
857
            }
858
          }
1✔
859

860
          // Combine interceptors produced by different filters into a single one that executes
861
          // them sequentially. The order is preserved.
862
          perRouteInterceptors.put(route, combineInterceptors(interceptors));
1✔
863
        }
1✔
864
      }
1✔
865

866
      return perRouteInterceptors.buildOrThrow();
1✔
867
    }
868

869
    private ServerInterceptor combineInterceptors(final List<ServerInterceptor> interceptors) {
870
      if (interceptors.isEmpty()) {
1✔
871
        return noopInterceptor;
1✔
872
      }
873
      if (interceptors.size() == 1) {
1✔
874
        return interceptors.get(0);
×
875
      }
876
      return new ServerInterceptor() {
1✔
877
        @Override
878
        public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
879
            Metadata headers, ServerCallHandler<ReqT, RespT> next) {
880
          // intercept forward
881
          for (int i = interceptors.size() - 1; i >= 0; i--) {
1✔
882
            next = InternalServerInterceptors.interceptCallHandlerCreate(
1✔
883
                interceptors.get(i), next);
1✔
884
          }
885
          return next.startCall(call, headers);
1✔
886
        }
887
      };
888
    }
889

890
    private void handleConfigNotFoundOrMismatch(StatusException exception) {
891
      cleanUpRouteDiscoveryStates();
1✔
892
      shutdownActiveFilters();
1✔
893
      List<SslContextProviderSupplier> toRelease = getSuppliersInUse();
1✔
894
      filterChainSelectorManager.updateSelector(FilterChainSelector.NO_FILTER_CHAIN);
1✔
895
      for (SslContextProviderSupplier s: toRelease) {
1✔
896
        s.close();
1✔
897
      }
1✔
898
      if (restartTimer != null) {
1✔
899
        restartTimer.cancel();
1✔
900
      }
901
      if (!delegate.isShutdown()) {
1✔
902
        delegate.shutdown();  // let in-progress calls finish
1✔
903
      }
904
      isServing = false;
1✔
905
      listener.onNotServing(exception);
1✔
906
    }
1✔
907

908
    private void cleanUpRouteDiscoveryStates() {
909
      for (RouteDiscoveryState rdsState : routeDiscoveryStates.values()) {
1✔
910
        String rdsName = rdsState.resourceName;
1✔
911
        logger.log(Level.FINE, "Stop watching RDS resource {0}", rdsName);
1✔
912
        xdsClient.cancelXdsResourceWatch(XdsRouteConfigureResource.getInstance(), rdsName,
1✔
913
            rdsState);
914
      }
1✔
915
      routeDiscoveryStates.clear();
1✔
916
      savedRdsRoutingConfigRef.clear();
1✔
917
    }
1✔
918

919
    private List<SslContextProviderSupplier> getSuppliersInUse() {
920
      List<SslContextProviderSupplier> toRelease = new ArrayList<>();
1✔
921
      FilterChainSelector selector = filterChainSelectorManager.getSelectorToUpdateSelector();
1✔
922
      if (selector != null) {
1✔
923
        for (FilterChain f: selector.getRoutingConfigs().keySet()) {
1✔
924
          if (f.sslContextProviderSupplier() != null) {
1✔
925
            toRelease.add(f.sslContextProviderSupplier());
1✔
926
          }
927
        }
1✔
928
        SslContextProviderSupplier defaultSupplier =
1✔
929
                selector.getDefaultSslContextProviderSupplier();
1✔
930
        if (defaultSupplier != null) {
1✔
931
          toRelease.add(defaultSupplier);
1✔
932
        }
933
      }
934
      return toRelease;
1✔
935
    }
936

937
    private void releaseSuppliersInFlight() {
938
      SslContextProviderSupplier supplier;
939
      for (FilterChain filterChain : filterChains) {
1✔
940
        supplier = filterChain.sslContextProviderSupplier();
1✔
941
        if (supplier != null) {
1✔
942
          supplier.close();
1✔
943
        }
944
      }
1✔
945
      if (defaultFilterChain != null
1✔
946
              && (supplier = defaultFilterChain.sslContextProviderSupplier()) != null) {
1✔
947
        supplier.close();
1✔
948
      }
949
    }
1✔
950

951
    private final class RouteDiscoveryState implements ResourceWatcher<RdsUpdate> {
952
      private final String resourceName;
953
      private ImmutableList<VirtualHost> savedVirtualHosts;
954
      private boolean isPending = true;
1✔
955

956
      private RouteDiscoveryState(String resourceName) {
1✔
957
        this.resourceName = checkNotNull(resourceName, "resourceName");
1✔
958
      }
1✔
959

960
      @Override
961
      public void onResourceChanged(final StatusOr<RdsUpdate> update) {
962
        syncContext.execute(() -> {
1✔
963
          if (!routeDiscoveryStates.containsKey(resourceName)) {
1✔
964
            return; // Watcher has been cancelled.
1✔
965
          }
966

967
          if (update.hasValue()) {
1✔
968
            if (savedVirtualHosts == null && !isPending) {
1✔
969
              logger.log(Level.WARNING, "Received valid Rds {0} configuration.", resourceName);
1✔
970
            }
971
            savedVirtualHosts = ImmutableList.copyOf(update.getValue().virtualHosts);
1✔
972
          } else {
973
            logger.log(Level.WARNING, "Rds {0} unavailable: {1}",
1✔
974
                new Object[]{resourceName, update.getStatus()});
1✔
975
            savedVirtualHosts = null;
1✔
976
          }
977
          // In both cases, a change has occurred that requires a config update.
978
          updateRdsRoutingConfig();
1✔
979
          maybeUpdateSelector();
1✔
980
        });
1✔
981
      }
1✔
982

983
      @Override
984
      public void onAmbientError(final Status error) {
985
        syncContext.execute(() -> {
1✔
986
          if (!routeDiscoveryStates.containsKey(resourceName)) {
1✔
987
            return; // Watcher has been cancelled.
×
988
          }
989
          String description = error.getDescription() == null ? "" : error.getDescription() + " ";
1✔
990
          Status errorWithNodeId = error.withDescription(
1✔
991
              description + "xDS node ID: " + xdsClient.getBootstrapInfo().node().getId());
1✔
992
          logger.log(Level.WARNING, "Error loading RDS resource {0} from XdsClient: {1}.",
1✔
993
              new Object[]{resourceName, errorWithNodeId});
994

995
          // Per gRFC A88, ambient errors should not trigger a configuration change.
996
          // Therefore, we do NOT call maybeUpdateSelector() here.
997
        });
1✔
998
      }
1✔
999

1000
      private void updateRdsRoutingConfig() {
1001
        for (FilterChain filterChain : savedRdsRoutingConfigRef.keySet()) {
1✔
1002
          HttpConnectionManager hcm = filterChain.httpConnectionManager();
1✔
1003
          if (!resourceName.equals(hcm.rdsName())) {
1✔
1004
            continue;
1✔
1005
          }
1006

1007
          ServerRoutingConfig updatedRoutingConfig;
1008
          if (savedVirtualHosts == null) {
1✔
1009
            updatedRoutingConfig = ServerRoutingConfig.FAILING_ROUTING_CONFIG;
1✔
1010
          } else {
1011
            HashMap<String, Filter> chainFilters = activeFilters.get(filterChain.name());
1✔
1012
            ImmutableMap<Route, ServerInterceptor> interceptors = generatePerRouteInterceptors(
1✔
1013
                hcm.httpFilterConfigs(), savedVirtualHosts, chainFilters);
1✔
1014
            updatedRoutingConfig = ServerRoutingConfig.create(savedVirtualHosts, interceptors);
1✔
1015
          }
1016

1017
          logger.log(Level.FINEST, "Updating filter chain {0} rds routing config: {1}",
1✔
1018
              new Object[]{filterChain.name(), updatedRoutingConfig});
1✔
1019
          savedRdsRoutingConfigRef.get(filterChain).set(updatedRoutingConfig);
1✔
1020
        }
1✔
1021
      }
1✔
1022

1023
      // Update the selector to use the most recently updated configs only after all rds have been
1024
      // discovered for the first time. Later changes on rds will be applied through virtual host
1025
      // list atomic ref.
1026
      private void maybeUpdateSelector() {
1027
        isPending = false;
1✔
1028
        boolean isLastPending = pendingRds.remove(resourceName) && pendingRds.isEmpty();
1✔
1029
        if (isLastPending) {
1✔
1030
          updateSelector();
1✔
1031
        }
1032
      }
1✔
1033
    }
1034
  }
1035

1036
  @VisibleForTesting
1037
  final class ConfigApplyingInterceptor implements ServerInterceptor {
1✔
1038
    private final ServerInterceptor noopInterceptor = new ServerInterceptor() {
1✔
1039
      @Override
1040
      public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
1041
          Metadata headers, ServerCallHandler<ReqT, RespT> next) {
1042
        return next.startCall(call, headers);
×
1043
      }
1044
    };
1045

1046
    @Override
1047
    public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
1048
        Metadata headers, ServerCallHandler<ReqT, RespT> next) {
1049
      AtomicReference<ServerRoutingConfig> routingConfigRef =
1✔
1050
          call.getAttributes().get(ATTR_SERVER_ROUTING_CONFIG);
1✔
1051
      ServerRoutingConfig routingConfig = routingConfigRef == null ? null :
1✔
1052
          routingConfigRef.get();
1✔
1053
      if (routingConfig == null || routingConfig == ServerRoutingConfig.FAILING_ROUTING_CONFIG) {
1✔
1054
        String errorMsg = "Missing or broken xDS routing config: RDS config unavailable.";
1✔
1055
        call.close(Status.UNAVAILABLE.withDescription(errorMsg), new Metadata());
1✔
1056
        return new Listener<ReqT>() {};
1✔
1057
      }
1058
      List<VirtualHost> virtualHosts = routingConfig.virtualHosts();
1✔
1059
      VirtualHost virtualHost = RoutingUtils.findVirtualHostForHostName(
1✔
1060
          virtualHosts, call.getAuthority());
1✔
1061
      if (virtualHost == null) {
1✔
1062
        call.close(
1✔
1063
            Status.UNAVAILABLE.withDescription("Could not find xDS virtual host matching RPC"),
1✔
1064
            new Metadata());
1065
        return new Listener<ReqT>() {};
1✔
1066
      }
1067
      Route selectedRoute = null;
1✔
1068
      MethodDescriptor<ReqT, RespT> method = call.getMethodDescriptor();
1✔
1069
      for (Route route : virtualHost.routes()) {
1✔
1070
        if (RoutingUtils.matchRoute(
1✔
1071
            route.routeMatch(), "/" + method.getFullMethodName(), headers, random)) {
1✔
1072
          selectedRoute = route;
1✔
1073
          break;
1✔
1074
        }
1075
      }
1✔
1076
      if (selectedRoute == null) {
1✔
1077
        call.close(Status.UNAVAILABLE.withDescription("Could not find xDS route matching RPC"),
1✔
1078
            new Metadata());
1079
        return new ServerCall.Listener<ReqT>() {};
1✔
1080
      }
1081
      if (selectedRoute.routeAction() != null) {
1✔
1082
        call.close(Status.UNAVAILABLE.withDescription("Invalid xDS route action for matching "
1✔
1083
            + "route: only Route.non_forwarding_action should be allowed."), new Metadata());
1084
        return new ServerCall.Listener<ReqT>() {};
1✔
1085
      }
1086
      ServerInterceptor routeInterceptor = noopInterceptor;
1✔
1087
      Map<Route, ServerInterceptor> perRouteInterceptors = routingConfig.interceptors();
1✔
1088
      if (perRouteInterceptors != null && perRouteInterceptors.get(selectedRoute) != null) {
1✔
1089
        routeInterceptor = perRouteInterceptors.get(selectedRoute);
1✔
1090
      }
1091
      return routeInterceptor.interceptCall(call, headers, next);
1✔
1092
    }
1093
  }
1094

1095
  /**
1096
   * The HttpConnectionManager level configuration.
1097
   */
1098
  @AutoValue
1099
  abstract static class ServerRoutingConfig {
1✔
1100
    @VisibleForTesting
1101
    static final ServerRoutingConfig FAILING_ROUTING_CONFIG = ServerRoutingConfig.create(
1✔
1102
        ImmutableList.<VirtualHost>of(), ImmutableMap.<Route, ServerInterceptor>of());
1✔
1103

1104
    abstract ImmutableList<VirtualHost> virtualHosts();
1105

1106
    // Prebuilt per route server interceptors from http filter configs.
1107
    abstract ImmutableMap<Route, ServerInterceptor> interceptors();
1108

1109
    /**
1110
     * Server routing configuration.
1111
     * */
1112
    public static ServerRoutingConfig create(
1113
        ImmutableList<VirtualHost> virtualHosts,
1114
        ImmutableMap<Route, ServerInterceptor> interceptors) {
1115
      checkNotNull(virtualHosts, "virtualHosts");
1✔
1116
      checkNotNull(interceptors, "interceptors");
1✔
1117
      return new AutoValue_XdsServerWrapper_ServerRoutingConfig(virtualHosts, interceptors);
1✔
1118
    }
1119
  }
1120
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc