Skip to content

Commit 68f4fae

Browse files
authored
Exclude Aspire service discovery resolver from internal HttpClient used by Eureka (#1598)
1 parent 6f7dfa2 commit 68f4fae

File tree

3 files changed

+60
-4
lines changed

3 files changed

+60
-4
lines changed

src/Discovery/src/Eureka/EurekaServiceCollectionExtensions.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace Steeltoe.Discovery.Eureka;
2020
public static class EurekaServiceCollectionExtensions
2121
{
2222
private const string SpringDiscoveryEnabled = "spring:cloud:discovery:enabled";
23+
private const string ResolvingHttpDelegatingHandlerName = "Microsoft.Extensions.ServiceDiscovery.Http.ResolvingHttpDelegatingHandler";
2324

2425
/// <summary>
2526
/// Configures to use <see cref="EurekaDiscoveryClient" /> for service discovery.
@@ -108,7 +109,7 @@ private static void AddEurekaClient(IServiceCollection services)
108109
services.ConfigureCertificateOptions("Eureka");
109110

110111
IHttpClientBuilder eurekaHttpClientBuilder = services.AddHttpClient("Eureka");
111-
eurekaHttpClientBuilder.ConfigureAdditionalHttpMessageHandlers((defaultHandlers, _) => RemoveDiscoveryHttpDelegatingHandler(defaultHandlers));
112+
eurekaHttpClientBuilder.ConfigureAdditionalHttpMessageHandlers((defaultHandlers, _) => RemoveDiscoveryHttpHandlers(defaultHandlers));
112113

113114
eurekaHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(serviceProvider =>
114115
{
@@ -122,7 +123,7 @@ private static void AddEurekaClient(IServiceCollection services)
122123
});
123124

124125
IHttpClientBuilder eurekaTokenHttpClientBuilder = services.AddHttpClient("AccessTokenForEureka");
125-
eurekaTokenHttpClientBuilder.ConfigureAdditionalHttpMessageHandlers((defaultHandlers, _) => RemoveDiscoveryHttpDelegatingHandler(defaultHandlers));
126+
eurekaTokenHttpClientBuilder.ConfigureAdditionalHttpMessageHandlers((defaultHandlers, _) => RemoveDiscoveryHttpHandlers(defaultHandlers));
126127

127128
eurekaTokenHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(serviceProvider =>
128129
{
@@ -139,12 +140,19 @@ private static void AddEurekaClient(IServiceCollection services)
139140
services.AddSingleton<EurekaClient>();
140141
}
141142

142-
private static void RemoveDiscoveryHttpDelegatingHandler(ICollection<DelegatingHandler> defaultHandlers)
143+
private static void RemoveDiscoveryHttpHandlers(ICollection<DelegatingHandler> defaultHandlers)
143144
{
145+
// Prevent infinite recursion: The inner HttClient used by EurekaDiscoveryClient must not use service discovery.
146+
144147
DelegatingHandler[] discoveryHandlers = defaultHandlers.Where(handler =>
145148
{
146149
Type handlerType = handler.GetType();
147150

151+
if (handlerType.FullName == ResolvingHttpDelegatingHandlerName)
152+
{
153+
return true;
154+
}
155+
148156
if (handlerType.IsConstructedGenericType)
149157
{
150158
Type handlerOpenType = handlerType.GetGenericTypeDefinition();
@@ -160,7 +168,6 @@ private static void RemoveDiscoveryHttpDelegatingHandler(ICollection<DelegatingH
160168

161169
foreach (DelegatingHandler discoveryHandler in discoveryHandlers)
162170
{
163-
// Prevent infinite recursion: DiscoveryHttpDelegatingHandler depends on EurekaDiscoveryClient.
164171
defaultHandlers.Remove(discoveryHandler);
165172
}
166173
}

src/Discovery/test/HttpClients.Test/DiscoveryWebApplicationBuilderExtensionsTest.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
using Microsoft.Extensions.Configuration;
88
using Microsoft.Extensions.DependencyInjection;
99
using Microsoft.Extensions.Hosting;
10+
using Microsoft.Extensions.ServiceDiscovery.Http;
11+
using RichardSzalay.MockHttp;
1012
using Steeltoe.Common.Discovery;
13+
using Steeltoe.Common.Http.HttpClientPooling;
1114
using Steeltoe.Common.TestResources;
1215
using Steeltoe.Discovery.Consul;
1316
using Steeltoe.Discovery.Eureka;
@@ -77,4 +80,34 @@ public async Task AddEurekaDiscoveryClient_WorksWithGlobalServiceDiscovery()
7780

7881
await action.Should().NotThrowAsync();
7982
}
83+
84+
[Fact]
85+
public async Task AddEurekaDiscoveryClient_WorksWithAspireGlobalServiceDiscovery()
86+
{
87+
WebApplicationBuilder builder = TestWebApplicationBuilderFactory.Create();
88+
89+
builder.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
90+
{
91+
["Eureka:Client:ShouldFetchRegistry"] = "false",
92+
["Eureka:Client:ShouldRegisterWithEureka"] = "false"
93+
});
94+
95+
builder.Services.AddEurekaDiscoveryClient();
96+
builder.Services.AddTransient<ResolvingHttpDelegatingHandler>();
97+
builder.Services.ConfigureHttpClientDefaults(action => action.AddHttpMessageHandler<ResolvingHttpDelegatingHandler>());
98+
99+
var handler = new DelegateToMockHttpClientHandler();
100+
handler.Mock.Expect(HttpMethod.Get, "http://localhost:8761/eureka/apps").Respond("application/json", "{}");
101+
102+
await using WebApplication host = builder.Build();
103+
104+
host.Services.GetRequiredService<HttpClientHandlerFactory>().Using(handler);
105+
106+
var discoveryClient = host.Services.GetRequiredService<EurekaDiscoveryClient>();
107+
Func<Task> action = async () => await discoveryClient.FetchRegistryAsync(true, TestContext.Current.CancellationToken);
108+
109+
await action.Should().NotThrowAsync();
110+
111+
handler.Mock.VerifyNoOutstandingExpectation();
112+
}
80113
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#pragma warning disable IDE0130 // Namespace does not match folder structure
6+
// ReSharper disable once CheckNamespace
7+
namespace Microsoft.Extensions.ServiceDiscovery.Http;
8+
#pragma warning restore IDE0130 // Namespace does not match folder structure
9+
10+
internal sealed class ResolvingHttpDelegatingHandler : DelegatingHandler
11+
{
12+
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
13+
{
14+
throw new InvalidOperationException("This handler should have been removed from the HttpClient pipeline.");
15+
}
16+
}

0 commit comments

Comments
 (0)