diff --git a/src/IceRpc.Locator/LocatorInterceptor.cs b/src/IceRpc.Locator/LocatorInterceptor.cs index ff8336d97a..b230647998 100644 --- a/src/IceRpc.Locator/LocatorInterceptor.cs +++ b/src/IceRpc.Locator/LocatorInterceptor.cs @@ -108,13 +108,17 @@ public async Task InvokeAsync(OutgoingRequest request, Cancell ServiceAddress serviceAddress, IEnumerable excludedAddresses) { + // Use the ServerAddressComparer.OptionalTransport comparer so the filter matches the connection layer's + // equality. (ServerAddress? ServerAddress, ImmutableList AltServerAddresses) result = (serviceAddress.ServerAddress, serviceAddress.AltServerAddresses); - if (result.ServerAddress is ServerAddress serverAddress && excludedAddresses.Contains(serverAddress)) + if (result.ServerAddress is ServerAddress serverAddress && + excludedAddresses.Contains(serverAddress, ServerAddressComparer.OptionalTransport)) { result.ServerAddress = null; } - result.AltServerAddresses = result.AltServerAddresses.RemoveAll(e => excludedAddresses.Contains(e)); + result.AltServerAddresses = result.AltServerAddresses.RemoveAll( + e => excludedAddresses.Contains(e, ServerAddressComparer.OptionalTransport)); if (result.ServerAddress is null && result.AltServerAddresses.Count > 0) { diff --git a/tests/IceRpc.Locator.Tests/LocatorInterceptorTests.cs b/tests/IceRpc.Locator.Tests/LocatorInterceptorTests.cs index 49060c3704..70ac31231b 100644 --- a/tests/IceRpc.Locator.Tests/LocatorInterceptorTests.cs +++ b/tests/IceRpc.Locator.Tests/LocatorInterceptorTests.cs @@ -92,6 +92,35 @@ public async Task Resolve_refresh_cache_on_the_second_lookup() Assert.That(locationResolver.RefreshCache, Is.True); } + /// Verifies that an excluded server address that differs only in transport from a newly-resolved one + /// is filtered out: the connection layer treats them as the same physical endpoint, so the locator must use the + /// same OptionalTransport equality when applying RemovedServerAddresses. + [Test] + public async Task Removed_address_with_explicit_transport_filters_resolved_address_without_transport() + { + // Arrange + var invoker = new InlineInvoker((request, cancellationToken) => + Task.FromResult(new IncomingResponse(request, FakeConnectionContext.Instance))); + var resolved = new ServiceAddress(new Uri("ice://localhost:10000/foo")); + var locationResolver = new MockLocationResolver(resolved, adapterId: false); + var sut = new LocatorInterceptor(invoker, locationResolver); + var serviceAddress = new ServiceAddress(Protocol.Ice) { Path = "/foo" }; + using var request = new OutgoingRequest(serviceAddress); + var serverAddressFeature = new ServerAddressFeature(serviceAddress) + { + RemovedServerAddresses = ImmutableList.Create( + new ServerAddress(new Uri("ice://localhost:10000?transport=tcp"))) + }; + request.Features = request.Features.With(serverAddressFeature); + + // Act + await sut.InvokeAsync(request, default); + + // Assert + Assert.That(serverAddressFeature.ServerAddress, Is.Null); + Assert.That(serverAddressFeature.AltServerAddresses, Is.Empty); + } + /// Verifies that the locator interceptor does not set the refresh cache parameter on the second attempt /// to resolve a location if the first attempt returned a non cached result. [Test]