Skip to content

Introduce XdsExtensionRegistry#6723

Open
jrhee17 wants to merge 5 commits intoline:mainfrom
jrhee17:feat/xds-support-2
Open

Introduce XdsExtensionRegistry#6723
jrhee17 wants to merge 5 commits intoline:mainfrom
jrhee17:feat/xds-support-2

Conversation

@jrhee17
Copy link
Copy Markdown
Contributor

@jrhee17 jrhee17 commented Apr 15, 2026

This PR is a subset of #6721

Motivation

The xDS module previously scattered extension handling (HTTP filters, transport sockets, network filters) across multiple static registries and utility classes (HttpFilterFactoryRegistry, XdsValidatorIndexRegistry, XdsConverterUtil), each with inconsistent lookup and validation behavior. Validation was also performed redundantly — both during resource parsing and in individual XdsResource constructors — which could be expensive due to full message tree traversal.

This PR introduces XdsExtensionRegistry as the single, bootstrap-scoped registry that unifies extension factory lookup, proto Any unpacking, and validation.

Modifications

Introduce XdsExtensionRegistry to unify unpack/validation behavior

  • Added XdsExtensionFactory as the base interface for all extension factories, providing name() and typeUrls() for dual-key resolution (type URL primary, extension name fallback).
  • Added XdsExtensionRegistry as the central registry that discovers factories via SPI and built-in registration. Provides query() for factory lookup, unpack() for Any fields, and assertValid() for proto validation.
  • The registry is created once per bootstrap in XdsBootstrapImpl and threaded through the pipeline via SubscriptionContext, rather than being a static singleton. This allows users to inject custom factories via the bootstrap builder in the future.
  • Removed HttpFilterFactoryRegistry and XdsValidatorIndexRegistry which are now subsumed by XdsExtensionRegistry.

Consolidate validation into three well-defined points

  • Added XdsResourceValidator which delegates to the highest-priority XdsValidatorIndex loaded via SPI. Validation now happens at exactly three points:
    1. Bootstrap: The entire Bootstrap proto is validated once at construction time.
    2. Dynamic resources: Each resource from a config source is validated on arrival in ResourceParser.parseResources() / parseDeltaResources().
    3. Any unpacking: Opaque Any fields are validated when unpacked via registry.unpack().
  • Removed per-resource validation calls from ClusterXdsResource, ListenerXdsResource, EndpointXdsResource, RouteXdsResource, SecretXdsResource, and VirtualHostXdsResource constructors. This avoids redundant full-message-tree traversals.
  • Removed XdsConverterUtil whose config source validation logic is subsumed by the centralized validation.

HTTP filters now use the extension registry

  • HttpFilterFactory now extends XdsExtensionFactory; create() receives XdsResourceValidator for config unpacking.
  • RouterFilterFactory implements name() / typeUrls() and now properly unpacks the Router proto. Extracted RouterXdsHttpFilter as a public inner class with a router() getter.
  • FilterUtil lookups changed from HttpFilterFactoryRegistry to XdsExtensionRegistry.query().
  • Misc) Moved downstream HTTP filter chain from ListenerSnapshot (one per listener) to RouteEntry (one per route), so downstream filters receive per-route typed_per_filter_config overrides — matching Envoy's behavior of resolving the route before running the HCM filter chain.

Transport sockets now use the extension registry

  • Added TransportSocketFactory as the extension factory interface for transport sockets.
  • Added UpstreamTlsTransportSocketFactory which encapsulates the TLS logic previously hardcoded in TransportSocketStream.
  • Added RawBufferTransportSocketFactory as a simple pass-through for plaintext transport sockets.
  • Refactored TransportSocketStream from monolithic TLS handling to a strategy pattern — queries the registry for the appropriate TransportSocketFactory and delegates creation.

Listener unpacking centralized via XdsUnpackUtil

  • Added XdsUnpackUtil to centralize unpacking of HttpConnectionManager from listener configs and downstream filter resolution.
  • Added HttpConnectionManagerFactory for parsing HttpConnectionManager network filter configs.
  • ListenerResourceParser / ListenerXdsResource: Downstream filters are resolved at parse time via XdsUnpackUtil and stored as a field, rather than computed on demand.

Result

  • Extension resolution (HTTP filters, transport sockets, network filters) is unified under XdsExtensionRegistry with dual-key lookup (type URL + name).
  • Proto validation is consolidated into three well-defined points, avoiding redundant and expensive full-message-tree traversals.
  • The registry is bootstrap-scoped (instance, not static), paving the way for user-injected custom factories, supported-field validation, and custom config sources.

@jrhee17 jrhee17 added this to the 1.39.0 milestone Apr 15, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Centralizes xDS extension/validation via a new XdsExtensionRegistry and XdsResourceValidator. Parsers, streams, transport-socket handling, and filter factories are refactored to use the registry/validator; built-in and SPI extension factories and transport-socket factories are registered. Several ad-hoc validators/registries were removed.

Changes

Cohort / File(s) Summary
Extension registry & validation core
xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java, xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionFactory.java, xds/src/main/java/com/linecorp/armeria/xds/XdsResourceValidator.java
Added central extension registry and validator; SPI-based factory discovery, type-url/name lookups, Any unpack+validate helpers.
Transport-socket factories & stream
xds/src/main/java/com/linecorp/armeria/xds/TransportSocketFactory.java, xds/src/main/java/com/linecorp/armeria/xds/RawBufferTransportSocketFactory.java, xds/src/main/java/com/linecorp/armeria/xds/UpstreamTlsTransportSocketFactory.java, xds/src/main/java/com/linecorp/armeria/xds/TransportSocketStream.java
Introduced transport-socket factory abstraction and implementations; TransportSocketStream delegates to registry-resolved factories instead of inlined TLS parsing.
HTTP connection manager & filter factories
xds/src/main/java/com/linecorp/armeria/xds/HttpConnectionManagerFactory.java, xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactory.java, xds/src/main/java/com/linecorp/armeria/xds/client/endpoint/RouterFilterFactory.java
Added HttpConnectionManagerFactory; refactored HttpFilterFactory to extend XdsExtensionFactory and accept XdsResourceValidator; RouterFilterFactory now unpacks/validates configs via validator.
Resource parser contract & parsers
xds/src/main/java/com/linecorp/armeria/xds/ResourceParser.java, xds/src/main/java/com/linecorp/armeria/xds/ClusterResourceParser.java, xds/src/main/java/com/linecorp/armeria/xds/ListenerResourceParser.java, xds/src/main/java/com/linecorp/armeria/xds/EndpointResourceParser.java, xds/src/main/java/com/linecorp/armeria/xds/RouteResourceParser.java, xds/src/main/java/com/linecorp/armeria/xds/SecretResourceParser.java
Parser API extended to accept XdsExtensionRegistry; parse flows now call extensionRegistry.assertValid(...) and use registry factories for unpacking.
Resource classes: TLS/validator removals
xds/src/main/java/com/linecorp/armeria/xds/ClusterXdsResource.java, .../ListenerXdsResource.java, .../EndpointXdsResource.java, .../RouteXdsResource.java, .../SecretXdsResource.java, .../VirtualHostXdsResource.java
Removed in-constructor calls to old validator registry and removed TLS extraction/caching from ClusterXdsResource; constructors now take registry-parsed inputs or plain messages without asserting via old registry.
Filter resolution & routing wiring
xds/src/main/java/com/linecorp/armeria/xds/FilterUtil.java, xds/src/main/java/com/linecorp/armeria/xds/RouteEntry.java, xds/src/main/java/com/linecorp/armeria/xds/RouteStream.java, xds/src/main/java/com/linecorp/armeria/xds/ListenerManager.java, xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java
Filter resolution now uses XdsExtensionRegistry and new XdsHttpFilter/XdsHttpFilterFactory patterns; downstream/upstream filter builder signatures changed to accept resolved filter list and registry.
Streams, client, coordinator, and context
xds/src/main/java/com/linecorp/armeria/xds/DeltaActualStream.java, .../SotwActualStream.java, .../StateCoordinator.java, .../ConfigSourceClient.java, .../ControlPlaneClientManager.java, .../SubscriptionContext.java, .../DefaultSubscriptionContext.java
Passed XdsExtensionRegistry through state/stream/client layers; StateCoordinator stores registry; SubscriptionContext exposes it; SOTW NACK backoff adjusted and parsing calls updated to take registry.
Bootstrap & initialization
xds/src/main/java/com/linecorp/armeria/xds/XdsBootstrapImpl.java
Bootstrap now creates XdsResourceValidator and XdsExtensionRegistry, asserts bootstrap via registry, and injects registry into downstream components.
Removed ad-hoc utilities
xds/src/main/java/com/linecorp/armeria/xds/XdsConverterUtil.java, xds/src/main/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistry.java, xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactoryRegistry.java
Deleted previous validator/convert utility classes and the eager HttpFilterFactoryRegistry; functionality consolidated into registry/validator.
Tests
xds/src/test/.../StateCoordinatorTest.java, .../SubscriberStorageTest.java, .../XdsExtensionRegistryTest.java, .../XdsResourceValidatorTest.java, .../XdsValidatorIndexRegistryTest.java
Updated tests to construct/use XdsExtensionRegistry; added tests for registry and validator; removed obsolete validator-index test.
New utilities
xds/src/main/java/com/linecorp/armeria/xds/RawBufferTransportSocketFactory.java, xds/src/main/java/com/linecorp/armeria/xds/UpstreamTlsTransportSocketFactory.java
Added new transport socket factory implementations and registrations.

Sequence Diagram

sequenceDiagram
    participant Bootstrap as XdsBootstrapImpl
    participant Validator as XdsResourceValidator
    participant Registry as XdsExtensionRegistry
    participant Parser as ResourceParser
    participant Factory as XdsExtensionFactory
    participant Stream as TransportSocketStream
    participant Resource as XdsResource

    Bootstrap->>Validator: new XdsResourceValidator()
    Validator->>Validator: load SPI validators
    Bootstrap->>Registry: XdsExtensionRegistry.of(validator)
    Registry->>Registry: load/register SPI factories + built-ins

    Parser->>Registry: parseResources(anys, registry, version)
    Registry->>Validator: assertValid(unpackedMessage)
    Parser->>Factory: query(Any/typeUrl or name)
    Factory->>Validator: validator.unpack(Any, MessageType)
    Factory-->>Parser: create(...) -> XdsResource

    Stream->>Registry: context.extensionRegistry()
    Stream->>Factory: query by typeUrl/name (transport socket)
    Factory-->>Stream: SnapshotStream<TransportSocketSnapshot>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

new feature, breaking change

Suggested reviewers

  • trustin
  • ikhoon
  • minwoox

Poem

🐰 I hopped in code to build a nest,

Extensions found and neatly dressed,
Validators tidy, factories play,
Parsers hand the right config away,
XDS hops onward—springtime in the test!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Introduce XdsExtensionRegistry' clearly summarizes the main change, accurately reflecting the introduction of a new unified registry as the primary focus of this PR.
Description check ✅ Passed The PR description comprehensively explains the motivation, modifications, and results. It clearly describes the introduction of XdsExtensionRegistry, consolidation of validation logic, and refactoring of HTTP filters and transport sockets.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
xds/src/main/java/com/linecorp/armeria/xds/XdsStreamSubscriber.java (1)

56-65: ⚠️ Potential issue | 🟡 Minor

restartTimer() can accumulate multiple pending absent timers.

If this method is called more than once before timeout, prior scheduled tasks are not canceled, which can trigger duplicate onAbsent() notifications.

Suggested fix
 void restartTimer() {
     if (!enableAbsentOnTimeout) {
         return;
     }
+    maybeCancelAbsentTimer();
 
     initialAbsentFuture = eventLoop.schedule(() -> {
         initialAbsentFuture = null;
         onAbsent();
     }, timeoutMillis, TimeUnit.MILLISECONDS);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsStreamSubscriber.java` around
lines 56 - 65, restartTimer() can schedule multiple pending timers because it
doesn't cancel any previously scheduled initialAbsentFuture; modify
restartTimer() to check initialAbsentFuture and cancel it (e.g.,
initialAbsentFuture.cancel(false)) if it's non-null and not done before
assigning a new scheduled future on eventLoop, preserving existing checks for
enableAbsentOnTimeout and retaining the same behavior that clears
initialAbsentFuture and calls onAbsent() when the timeout fires.
🧹 Nitpick comments (6)
xds/src/main/java/com/linecorp/armeria/xds/CompositeXdsStream.java (1)

28-38: Document stream supplier contract or add deduplication.

The API accepts a supplier with no explicit guarantee that distinct stream instances are created per XdsType. All current call sites correctly create distinct instances, but the contract should be clarified. Either document that suppliers must return distinct instances, or add identity deduplication in close() to guard against accidental reuse.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xds/src/main/java/com/linecorp/armeria/xds/CompositeXdsStream.java` around
lines 28 - 38, The constructor of CompositeXdsStream accepts a streamSupplier
without guaranteeing distinct instances per XdsType; to fix, update the contract
and add identity-based deduplication in close(): in CompositeXdsStream.close()
use an identity-based Set (e.g., Collections.newSetFromMap(new
IdentityHashMap<>())) to track seen XdsStream instances and call close() only
once per unique instance; mention in the constructor/Javadoc that suppliers
should preferably return distinct instances but the close() protects against
accidental reuse of the same instance across types.
xds/src/main/java/com/linecorp/armeria/xds/SecretXdsResource.java (1)

41-43: Add a defensive null-check for secret in constructor.

This keeps failure mode explicit at construction time rather than surfacing as later NPEs.

Suggested fix
 private SecretXdsResource(Secret secret, String version, long revision) {
     super(version, revision);
-    this.secret = secret;
+    this.secret = java.util.Objects.requireNonNull(secret, "secret");
 }
As per coding guidelines, follow LY OSS style for null checks using `Objects.requireNonNull`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xds/src/main/java/com/linecorp/armeria/xds/SecretXdsResource.java` around
lines 41 - 43, Add a defensive null-check in the SecretXdsResource constructor:
use Objects.requireNonNull on the secret parameter (in SecretXdsResource(Secret
secret, String version, long revision)) and provide a clear message (e.g.,
"secret") so construction fails fast; also ensure java.util.Objects is imported
if not already and then assign the checked value to the instance field 'secret'.
xds/src/main/java/com/linecorp/armeria/xds/XdsUnpackUtil.java (1)

33-45: Consider replacing assert with explicit null check.

The assert factory != null on line 41 will be a no-op when assertions are disabled (which is typical in production). If reaching this point without a factory is a programming error that should never occur, consider using Objects.requireNonNull or Preconditions.checkNotNull to ensure consistent behavior regardless of assertion settings.

♻️ Suggested fix
     `@Nullable`
     static HttpConnectionManager unpackConnectionManager(Listener listener,
                                                          XdsExtensionRegistry registry) {
         if (listener.getApiListener().hasApiListener()) {
             final Any apiListener = listener.getApiListener().getApiListener();
             final HttpConnectionManagerFactory factory =
                     registry.queryByTypeUrl(apiListener.getTypeUrl(),
                                             HttpConnectionManagerFactory.class);
-            assert factory != null;
+            if (factory == null) {
+                throw new IllegalStateException(
+                        "No HttpConnectionManagerFactory registered for type URL: " +
+                        apiListener.getTypeUrl());
+            }
             return factory.create(apiListener, registry.validator());
         }
         return null;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsUnpackUtil.java` around lines
33 - 45, In unpackConnectionManager(Listener listener, XdsExtensionRegistry
registry) replace the reliance on a Java assert for the
HttpConnectionManagerFactory lookup with an explicit null-check and fail-fast
behavior: after calling registry.queryByTypeUrl(...,
HttpConnectionManagerFactory.class) verify the returned factory is non-null
(e.g. Objects.requireNonNull or throw new IllegalStateException with a clear
message referencing the typeUrl and listener) before invoking
factory.create(apiListener, registry.validator()); this ensures consistent
runtime behavior when HttpConnectionManagerFactory is missing.
it/xds-client/src/test/java/com/linecorp/armeria/xds/it/XdsControlPlaneErrorHandlingTest.java (1)

90-92: Static shared state across tests - verify isolation.

The version, cache, and nackTracker are static and shared across all parameterized test executions. While setUp resets nackTracker, the cache retains snapshots from prior tests and version keeps incrementing. This should be fine since each test sets its own snapshot before assertions, but consider if test ordering or parallel execution could cause flakiness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/XdsControlPlaneErrorHandlingTest.java`
around lines 90 - 92, The static shared state (version, cache, nackTracker) can
leak between parameterized test runs; fix by ensuring isolation: either make
version and cache instance fields instead of static, or reinitialize them in the
test setUp method (e.g., version = new AtomicLong(); cache = new
SimpleCache<>(node -> GROUP); and call nackTracker.reset() as already done) so
each test gets fresh instances; update references to version/cache accordingly
and keep nackTracker.reset() in setUp to guarantee a clean state before each
test.
it/xds-client/src/test/java/com/linecorp/armeria/xds/it/DeltaXdsPreprocessorTest.java (1)

93-95: Minor: Add final modifier for consistency.

Other @RegisterExtension fields in this class are declared static final. Consider adding final to eventLoop for consistency.

Suggested fix
     `@RegisterExtension`
     `@Order`(3)
-    static EventLoopExtension eventLoop = new EventLoopExtension();
+    static final EventLoopExtension eventLoop = new EventLoopExtension();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/DeltaXdsPreprocessorTest.java`
around lines 93 - 95, Make the field declaration for the EventLoopExtension
consistent by adding the final modifier: change the declaration of the static
field eventLoop (type EventLoopExtension) in DeltaXdsPreprocessorTest to be
static final so it matches the other `@RegisterExtension` fields in the class.
xds/src/main/java/com/linecorp/armeria/xds/ListenerXdsResource.java (1)

116-121: Consider adding Javadoc for future public exposure.

The downstreamFilters() accessor is currently package-private, which is fine. If this method is intended to become public in the future, consider adding Javadoc now.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xds/src/main/java/com/linecorp/armeria/xds/ListenerXdsResource.java` around
lines 116 - 121, Add Javadoc to the downstreamFilters() accessor in
ListenerXdsResource describing what it returns and any expectations for callers:
state that it returns the pre-resolved downstream XdsHttpFilter instances,
whether the returned List is mutable/immutable and thread-safety assumptions,
include an `@return` tag, and mention nullability (never null) or alternative
behavior; keep the method package-private but document that it may be made
public in the future so callers understand its contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@xds/src/main/java/com/linecorp/armeria/xds/DeltaDiscoveryStub.java`:
- Around line 63-64: The switch default in DeltaDiscoveryStub.basic() (and the
analogous default in SotwDiscoveryStub) throws java.lang.Error for unsupported
XdsType values; change these to throw new IllegalArgumentException("Unexpected
value: " + type) (or similar IllegalArgumentException) so the code uses a proper
checked runtime exception for bad input/enum values; update both
DeltaDiscoveryStub.basic() and the matching method in SotwDiscoveryStub to
replace throw new Error(...) with throw new IllegalArgumentException(...) and
keep the original message text for clarity.

In `@xds/src/main/java/com/linecorp/armeria/xds/TransportSocketFactory.java`:
- Line 2: Revert the copyright header year change in TransportSocketFactory.java
back to the original year; locate the file/class TransportSocketFactory and
restore the previous copyright line (do not update to 2026) so the file keeps
its original header year.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsBootstrapImpl.java`:
- Around line 51-53: The bootstrap validation is performed after the
DirectoryWatchService is already created, which can leak resources if validation
throws; before creating DirectoryWatchService, run validation by instantiating
XdsResourceValidator and XdsExtensionRegistry and calling
extensionRegistry.assertValid(bootstrap), or if you must keep
DirectoryWatchService creation earlier, ensure any partially initialized state
is closed on validation failure by calling the
DirectoryWatchService.close()/shutdown method in the catch/finally surrounding
extensionRegistry.assertValid(bootstrap); update the code paths that reference
XdsResourceValidator, XdsExtensionRegistry,
extensionRegistry.assertValid(bootstrap), and DirectoryWatchService accordingly.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java`:
- Around line 73-79: The register method currently overwrites existing entries
in byName and byTypeUrl which hides duplicate XdsExtensionFactory registrations;
update register(XdsExtensionFactory factory, Map<String,XdsExtensionFactory>
byName, Map<String,XdsExtensionFactory> byTypeUrl) to detect collisions before
inserting: if byName already contains factory.name() or byTypeUrl already
contains any typeUrl from factory.typeUrls(), throw an IllegalStateException (or
a clear RuntimeException) that includes the conflicting name/typeUrl and the
existing and new factory implementations to fail fast rather than silently
overwriting.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsResourceValidator.java`:
- Around line 76-82: In XdsResourceValidator.unpack(Any message, Class<T> clazz)
add explicit null checks using Objects.requireNonNull for both parameters
(message and clazz) at the top of the method, and when throwing the
IllegalArgumentException on InvalidProtocolBufferException include richer
context (e.g., clazz.getName() and message.getTypeUrl() or other identifying
info) in the exception message so the error clearly identifies what failed to
unpack; update the message passed to Objects.requireNonNull to be a meaningful
description like "message must not be null" / "clazz must not be null".

In `@xds/src/test/java/com/linecorp/armeria/xds/SubscriberStorageTest.java`:
- Around line 68-84: The CapturingWatcher class has plain fields missingType and
missingName that are written in onResourceDoesNotExist (event-loop thread) and
read by Awaitility/test thread, which is racy; make these cross-thread safe by
changing the fields in CapturingWatcher to have proper concurrency semantics
(e.g., declare missingType as volatile XdsType missingType and missingName as
volatile String missingName, or use
AtomicReference<XdsType>/AtomicReference<String>) so that writes in
onResourceDoesNotExist establish a happens-before edge and the test reads see
the updated values.

In `@xds/src/test/java/com/linecorp/armeria/xds/XdsClientIntegrationTest.java`:
- Around line 94-100: The test currently calls
watcher.pollChanged(ClusterSnapshot.class) once, which only consumes a single
queued event and can leave older intermediate ClusterSnapshot events in
watcher.events(), causing flakiness; replace the single poll with a loop that
repeatedly polls/consumes ClusterSnapshot events from the watcher until none
remain, keeping the last non-null ClusterSnapshot as the snapshot to assert
against (still using ClusterSnapshot and watcher.pollChanged/ watcher.events()
APIs), and then assert that the last snapshot.xdsResource().resource() equals
the expected cluster; apply the same draining change to the other similar blocks
that use watcher.pollChanged(ClusterSnapshot.class) (the blocks corresponding to
the other occurrences mentioned).

---

Outside diff comments:
In `@xds/src/main/java/com/linecorp/armeria/xds/XdsStreamSubscriber.java`:
- Around line 56-65: restartTimer() can schedule multiple pending timers because
it doesn't cancel any previously scheduled initialAbsentFuture; modify
restartTimer() to check initialAbsentFuture and cancel it (e.g.,
initialAbsentFuture.cancel(false)) if it's non-null and not done before
assigning a new scheduled future on eventLoop, preserving existing checks for
enableAbsentOnTimeout and retaining the same behavior that clears
initialAbsentFuture and calls onAbsent() when the timeout fires.

---

Nitpick comments:
In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/DeltaXdsPreprocessorTest.java`:
- Around line 93-95: Make the field declaration for the EventLoopExtension
consistent by adding the final modifier: change the declaration of the static
field eventLoop (type EventLoopExtension) in DeltaXdsPreprocessorTest to be
static final so it matches the other `@RegisterExtension` fields in the class.

In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/XdsControlPlaneErrorHandlingTest.java`:
- Around line 90-92: The static shared state (version, cache, nackTracker) can
leak between parameterized test runs; fix by ensuring isolation: either make
version and cache instance fields instead of static, or reinitialize them in the
test setUp method (e.g., version = new AtomicLong(); cache = new
SimpleCache<>(node -> GROUP); and call nackTracker.reset() as already done) so
each test gets fresh instances; update references to version/cache accordingly
and keep nackTracker.reset() in setUp to guarantee a clean state before each
test.

In `@xds/src/main/java/com/linecorp/armeria/xds/CompositeXdsStream.java`:
- Around line 28-38: The constructor of CompositeXdsStream accepts a
streamSupplier without guaranteeing distinct instances per XdsType; to fix,
update the contract and add identity-based deduplication in close(): in
CompositeXdsStream.close() use an identity-based Set (e.g.,
Collections.newSetFromMap(new IdentityHashMap<>())) to track seen XdsStream
instances and call close() only once per unique instance; mention in the
constructor/Javadoc that suppliers should preferably return distinct instances
but the close() protects against accidental reuse of the same instance across
types.

In `@xds/src/main/java/com/linecorp/armeria/xds/ListenerXdsResource.java`:
- Around line 116-121: Add Javadoc to the downstreamFilters() accessor in
ListenerXdsResource describing what it returns and any expectations for callers:
state that it returns the pre-resolved downstream XdsHttpFilter instances,
whether the returned List is mutable/immutable and thread-safety assumptions,
include an `@return` tag, and mention nullability (never null) or alternative
behavior; keep the method package-private but document that it may be made
public in the future so callers understand its contract.

In `@xds/src/main/java/com/linecorp/armeria/xds/SecretXdsResource.java`:
- Around line 41-43: Add a defensive null-check in the SecretXdsResource
constructor: use Objects.requireNonNull on the secret parameter (in
SecretXdsResource(Secret secret, String version, long revision)) and provide a
clear message (e.g., "secret") so construction fails fast; also ensure
java.util.Objects is imported if not already and then assign the checked value
to the instance field 'secret'.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsUnpackUtil.java`:
- Around line 33-45: In unpackConnectionManager(Listener listener,
XdsExtensionRegistry registry) replace the reliance on a Java assert for the
HttpConnectionManagerFactory lookup with an explicit null-check and fail-fast
behavior: after calling registry.queryByTypeUrl(...,
HttpConnectionManagerFactory.class) verify the returned factory is non-null
(e.g. Objects.requireNonNull or throw new IllegalStateException with a clear
message referencing the typeUrl and listener) before invoking
factory.create(apiListener, registry.validator()); this ensures consistent
runtime behavior when HttpConnectionManagerFactory is missing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 72f95b20-1467-4d98-b7f9-7b3a38c1b8bf

📥 Commits

Reviewing files that changed from the base of the PR and between aabdc8a and ecf13d3.

📒 Files selected for processing (67)
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/ConfigSourceLifecycleObserverTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/DeltaXdsPreprocessorTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/DeltaXdsResourceWatcherTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/XdsControlPlaneErrorHandlingTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/XdsControlPlaneMatrixTest.java
  • testing-internal/src/main/java/com/linecorp/armeria/internal/testing/InternalTestingBlockHoundIntegration.java
  • xds/src/main/java/com/linecorp/armeria/xds/AbstractXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/AdsXdsStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/ClusterResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/ClusterXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/CompositeXdsStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/ConfigSourceClient.java
  • xds/src/main/java/com/linecorp/armeria/xds/ConfigSourceLifecycleObserver.java
  • xds/src/main/java/com/linecorp/armeria/xds/ControlPlaneClientManager.java
  • xds/src/main/java/com/linecorp/armeria/xds/DefaultConfigSourceLifecycleObserver.java
  • xds/src/main/java/com/linecorp/armeria/xds/DefaultResponseHandler.java
  • xds/src/main/java/com/linecorp/armeria/xds/DefaultSubscriptionContext.java
  • xds/src/main/java/com/linecorp/armeria/xds/DeltaActualStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/DeltaDiscoveryStub.java
  • xds/src/main/java/com/linecorp/armeria/xds/EndpointResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/EndpointXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/FilterUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/HttpConnectionManagerFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerManager.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/RawBufferTransportSocketFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/ResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/ResourceStateStore.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteEntry.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/SecretResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/SecretXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/SotwActualStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/SotwXdsStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/StateCoordinator.java
  • xds/src/main/java/com/linecorp/armeria/xds/SubscriberStorage.java
  • xds/src/main/java/com/linecorp/armeria/xds/SubscriptionContext.java
  • xds/src/main/java/com/linecorp/armeria/xds/TransportSocketFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/TransportSocketStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/UpstreamTlsTransportSocketFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/VirtualHostXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsBootstrapImpl.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsConverterUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsResourceException.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsResourceValidator.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsResponseHandler.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsStreamSubscriber.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsUnpackUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/client/endpoint/RouterFilterFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactoryRegistry.java
  • xds/src/test/java/com/linecorp/armeria/xds/SotwXdsStreamTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/StateCoordinatorTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/SubscriberStorageTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/TestResourceWatcher.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsClientIntegrationTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsExtensionRegistryTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsResourceValidatorTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistryTest.java
💤 Files with no reviewable changes (8)
  • xds/src/test/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistryTest.java
  • xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactoryRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/DefaultResponseHandler.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsConverterUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsResponseHandler.java
  • xds/src/main/java/com/linecorp/armeria/xds/SotwXdsStream.java
  • xds/src/test/java/com/linecorp/armeria/xds/SotwXdsStreamTest.java

Comment thread xds/src/main/java/com/linecorp/armeria/xds/DeltaDiscoveryStub.java
Comment thread xds/src/main/java/com/linecorp/armeria/xds/XdsBootstrapImpl.java
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java (1)

51-52: Remove misleading @VisibleForTesting annotation from of(...) method.

Line 52: This method is called from XdsBootstrapImpl (production code) during normal bootstrap initialization, not just from tests. The @VisibleForTesting annotation contradicts this usage and should be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java` around
lines 51 - 52, The `@VisibleForTesting` annotation on the static method
XdsExtensionRegistry.of(XdsResourceValidator) is misleading because the method
is used by production code (XdsBootstrapImpl); remove the `@VisibleForTesting`
annotation from the of(...) method in class XdsExtensionRegistry so its
visibility correctly reflects production usage and no test-only marker remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java`:
- Around line 51-52: The `@VisibleForTesting` annotation on the static method
XdsExtensionRegistry.of(XdsResourceValidator) is misleading because the method
is used by production code (XdsBootstrapImpl); remove the `@VisibleForTesting`
annotation from the of(...) method in class XdsExtensionRegistry so its
visibility correctly reflects production usage and no test-only marker remains.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 326d66f6-6d7d-42a2-a753-99c8ebafeae3

📥 Commits

Reviewing files that changed from the base of the PR and between ecf13d3 and d74d615.

📒 Files selected for processing (1)
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java

@jrhee17 jrhee17 force-pushed the feat/xds-support-2 branch from d74d615 to 6204fae Compare April 20, 2026 05:56
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
xds/src/main/java/com/linecorp/armeria/xds/XdsResourceValidator.java (1)

34-50: Javadoc says "exactly two levels" but describes three.

The class doc states validation occurs at "exactly two levels" (static, dynamic), then adds unpack(Any, Class) as an "In addition" point — which is itself a third validation site per the PR summary ("Validation occurs at three points: bootstrap construction, dynamic resource parsing, and Any unpacking"). Consider rewording for consistency so readers don't miss the Any-unpacking validation entry point.

♻️ Suggested wording
- * <p>Validation is performed at exactly two levels:
+ * <p>Validation is performed at the following three points:
  * <ul>
- *   <li><b>Static resources</b> — {`@link` XdsBootstrapImpl} calls {`@link` `#assertValid`(Object)} once
+ *   <li><b>Static resources</b> — {`@link` XdsBootstrapImpl} calls {`@link` `#assertValid`(Object)} once
@@
- *       resources (NACK'd back to the control plane).</li>
+ *       resources (NACK'd back to the control plane).</li>
+ *   <li><b>{`@code` Any}-typed fields</b> — {`@link` `#unpack`(Any, Class)} validates each unpacked
+ *       message because protobuf treats {`@code` Any} as an opaque blob that parent-level validation
+ *       cannot recurse into.</li>
  * </ul>
- *
- * <p>In addition, {`@link` `#unpack`(Any, Class)} is used for {`@code` google.protobuf.Any}-typed
- * fields that cannot be validated by parent recursion (since {`@code` Any} is opaque to protobuf
- * field traversal).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsResourceValidator.java` around
lines 34 - 50, The doc comment claims validation happens "exactly two levels"
but then adds a third point for Any unpacking; update the class Javadoc for
XdsResourceValidator to consistently describe three validation sites: bootstrap
construction (XdsBootstrapImpl calling assertValid(Object)), dynamic resource
parsing (assertValid(Object) on DiscoveryResponse unpacked resources), and
Any-type field handling (unpack(Any, Class) for google.protobuf.Any fields).
Mention all three explicitly and remove the "exactly two levels" phrase so
readers see the Any-unpacking step as a first-class validation site.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@xds/src/main/java/com/linecorp/armeria/xds/UpstreamTlsTransportSocketFactory.java`:
- Around line 94-109: The code currently picks only the first entry via get(0)
in the branches that build tlsCertStream and silently drops additional entries;
update the branches in UpstreamTlsTransportSocketFactory that reference
commonTlsContext.getTlsCertificatesList() and
commonTlsContext.getTlsCertificateSdsSecretConfigsList() to detect when size() >
1 and surface that fact (either by logging a warning with the list size and
which entry is being used, e.g., via the existing logger, or by failing fast
with an exception), and keep the existing behavior of using the first entry
(TlsCertificateStream/SecretStream + map(Optional::of)) unless you explicitly
choose to iterate/merge—ensure both branches that use get(0) are changed
consistently to emit the warning or reject on extra entries.

In `@xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java`:
- Around line 105-107: The `@VisibleForTesting` annotation on queryByTypeUrl is
misleading because it's called from production code (e.g.,
XdsUnpackUtil.unpackConnectionManager uses registry.queryByTypeUrl(...)); remove
the `@VisibleForTesting` annotation from the method declaration in
XdsExtensionRegistry and, if needed for visibility, ensure the method's access
modifier (queryByTypeUrl) remains appropriate for its callers (adjust to
package-public or public to match usage) and update any method javadoc to
reflect that it is used by production code.
- Around line 51-69: Remove the `@VisibleForTesting` annotation from the static
factory method XdsExtensionRegistry.of(XdsResourceValidator) because it is used
as the primary production construction entry point (see XdsBootstrapImpl usage);
edit the XdsExtensionRegistry class to delete the `@VisibleForTesting` import and
the annotation placed on the of(...) method so the method is no longer marked
test-only.

---

Nitpick comments:
In `@xds/src/main/java/com/linecorp/armeria/xds/XdsResourceValidator.java`:
- Around line 34-50: The doc comment claims validation happens "exactly two
levels" but then adds a third point for Any unpacking; update the class Javadoc
for XdsResourceValidator to consistently describe three validation sites:
bootstrap construction (XdsBootstrapImpl calling assertValid(Object)), dynamic
resource parsing (assertValid(Object) on DiscoveryResponse unpacked resources),
and Any-type field handling (unpack(Any, Class) for google.protobuf.Any fields).
Mention all three explicitly and remove the "exactly two levels" phrase so
readers see the Any-unpacking step as a first-class validation site.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 04ec1849-c8e8-4346-bb16-9a0748e330e1

📥 Commits

Reviewing files that changed from the base of the PR and between d74d615 and e7e4967.

📒 Files selected for processing (45)
  • xds/src/main/java/com/linecorp/armeria/xds/ClusterResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/ClusterXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/ConfigSourceClient.java
  • xds/src/main/java/com/linecorp/armeria/xds/ControlPlaneClientManager.java
  • xds/src/main/java/com/linecorp/armeria/xds/DefaultSubscriptionContext.java
  • xds/src/main/java/com/linecorp/armeria/xds/DeltaActualStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/EndpointResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/EndpointXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/FilterUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/HttpConnectionManagerFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerManager.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/RawBufferTransportSocketFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/ResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteEntry.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/SecretResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/SecretXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/SotwActualStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/StateCoordinator.java
  • xds/src/main/java/com/linecorp/armeria/xds/SubscriptionContext.java
  • xds/src/main/java/com/linecorp/armeria/xds/TransportSocketFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/TransportSocketStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/UpstreamTlsTransportSocketFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/VirtualHostXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsBootstrapImpl.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsConverterUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsResourceValidator.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsStreamSubscriber.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsUnpackUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/client/endpoint/RouterFilterFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactoryRegistry.java
  • xds/src/test/java/com/linecorp/armeria/xds/StateCoordinatorTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/SubscriberStorageTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsExtensionRegistryTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsResourceValidatorTest.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistryTest.java
💤 Files with no reviewable changes (9)
  • xds/src/main/java/com/linecorp/armeria/xds/RouteXdsResource.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistryTest.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsConverterUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/SecretXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsValidatorIndexRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactoryRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/EndpointXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/ClusterXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/VirtualHostXdsResource.java
✅ Files skipped from review due to trivial changes (2)
  • xds/src/main/java/com/linecorp/armeria/xds/XdsStreamSubscriber.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionFactory.java
🚧 Files skipped from review as they are similar to previous changes (18)
  • xds/src/main/java/com/linecorp/armeria/xds/SubscriptionContext.java
  • xds/src/main/java/com/linecorp/armeria/xds/ClusterResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/ConfigSourceClient.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java
  • xds/src/main/java/com/linecorp/armeria/xds/EndpointResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/ControlPlaneClientManager.java
  • xds/src/main/java/com/linecorp/armeria/xds/RawBufferTransportSocketFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/DefaultSubscriptionContext.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteResourceParser.java
  • xds/src/test/java/com/linecorp/armeria/xds/StateCoordinatorTest.java
  • xds/src/main/java/com/linecorp/armeria/xds/filter/HttpFilterFactory.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsExtensionRegistryTest.java
  • xds/src/main/java/com/linecorp/armeria/xds/ResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsBootstrapImpl.java
  • xds/src/test/java/com/linecorp/armeria/xds/XdsResourceValidatorTest.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsUnpackUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/TransportSocketStream.java

Comment thread xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java Outdated
@jrhee17 jrhee17 force-pushed the feat/xds-support-2 branch from e7e4967 to 923062d Compare April 20, 2026 06:21
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
xds/src/main/java/com/linecorp/armeria/xds/FilterUtil.java (1)

85-107: Consider moving the ConfigTypeCase check before the registry lookup.

Two minor consequences of performing the checkArgument after resolveInstance has looked up and (potentially) returned null for an optional filter:

  1. For an optional filter that uses the deprecated config variant (neither TYPED_CONFIG nor CONFIGTYPE_NOT_SET), the invalid config type is silently ignored rather than rejected.
  2. For a non-optional filter whose factory is unknown and whose ConfigTypeCase is invalid, the user sees "Unknown HTTP filter..." instead of the more precise "Only 'typed_config' is supported" message.

Neither is a correctness bug, but fail-fast validation of the config type case tends to produce clearer error messages. Not a blocker.

♻️ Proposed reordering
     static XdsHttpFilter resolveInstance(
             XdsExtensionRegistry extensionRegistry,
             HttpFilter httpFilter, `@Nullable` Any perRouteConfig) {
+        checkArgument(httpFilter.getConfigTypeCase() == ConfigTypeCase.TYPED_CONFIG ||
+                      httpFilter.getConfigTypeCase() == ConfigTypeCase.CONFIGTYPE_NOT_SET,
+                      "Only 'typed_config' is supported, but '%s' was supplied",
+                      httpFilter.getConfigTypeCase());
         final Any defaultConfig = httpFilter.getTypedConfig();
         final Any filterConfig = perRouteConfig != null ? perRouteConfig : defaultConfig;
         final HttpFilterFactory factory = extensionRegistry.query(
                 filterConfig, httpFilter.getName(), HttpFilterFactory.class);
         if (factory == null) {
             if (!httpFilter.getIsOptional()) {
                 throw new IllegalArgumentException(
                         "Unknown HTTP filter '" + httpFilter.getName() +
                         "': no HttpFilterFactory registered. Register an " +
                         "HttpFilterFactory implementation to handle this filter.");
             }
             return null;
         }
-        checkArgument(httpFilter.getConfigTypeCase() == ConfigTypeCase.TYPED_CONFIG ||
-                      httpFilter.getConfigTypeCase() == ConfigTypeCase.CONFIGTYPE_NOT_SET,
-                      "Only 'typed_config' is supported, but '%s' was supplied",
-                      httpFilter.getConfigTypeCase());
         return factory.create(httpFilter, filterConfig, extensionRegistry.validator());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@xds/src/main/java/com/linecorp/armeria/xds/FilterUtil.java` around lines 85 -
107, Move the ConfigTypeCase validation to the top of resolveInstance so the
filter's config type is validated before querying the registry: call
checkArgument(httpFilter.getConfigTypeCase() == ConfigTypeCase.TYPED_CONFIG ||
httpFilter.getConfigTypeCase() == ConfigTypeCase.CONFIGTYPE_NOT_SET, ...)
immediately inside resolveInstance, then perform the
extensionRegistry.query(filterConfig, httpFilter.getName(),
HttpFilterFactory.class) lookup and the existing optional/unknown-factory
handling, leaving factory.create(...) unchanged; this ensures invalid config
types are rejected before extensionRegistry.query() and preserves behavior for
optional filters and non-optional unknown factories.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@xds/src/main/java/com/linecorp/armeria/xds/FilterUtil.java`:
- Around line 85-107: Move the ConfigTypeCase validation to the top of
resolveInstance so the filter's config type is validated before querying the
registry: call checkArgument(httpFilter.getConfigTypeCase() ==
ConfigTypeCase.TYPED_CONFIG || httpFilter.getConfigTypeCase() ==
ConfigTypeCase.CONFIGTYPE_NOT_SET, ...) immediately inside resolveInstance, then
perform the extensionRegistry.query(filterConfig, httpFilter.getName(),
HttpFilterFactory.class) lookup and the existing optional/unknown-factory
handling, leaving factory.create(...) unchanged; this ensures invalid config
types are rejected before extensionRegistry.query() and preserves behavior for
optional filters and non-optional unknown factories.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a9db1a73-0e88-479f-bc66-2220d274da2c

📥 Commits

Reviewing files that changed from the base of the PR and between e7e4967 and 923062d.

📒 Files selected for processing (12)
  • xds/src/main/java/com/linecorp/armeria/xds/FilterUtil.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerManager.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerXdsResource.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/SotwActualStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsBootstrapImpl.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionFactory.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsResourceValidator.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsStreamSubscriber.java
  • xds/src/test/java/com/linecorp/armeria/xds/SubscriberStorageTest.java
✅ Files skipped from review due to trivial changes (1)
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionFactory.java
🚧 Files skipped from review as they are similar to previous changes (5)
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerManager.java
  • xds/src/main/java/com/linecorp/armeria/xds/RouteStream.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsExtensionRegistry.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerResourceParser.java
  • xds/src/main/java/com/linecorp/armeria/xds/XdsBootstrapImpl.java

@jrhee17 jrhee17 marked this pull request as ready for review April 21, 2026 00:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant