Skip to content

Commit d586dc4

Browse files
authored
API that allows to remove all resilience handlers from the HTTP client (#5801)
* Add API to clear the http client resilience handlers * Additional tests * Add experimental attribute to the new API * Fix formatting * Add the check for the handler type, formatting * Formatting * Additional test to ensure that new API removes only resilience handlers * Review fixes * Review fixes
1 parent e1c0f17 commit d586dc4

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

src/Libraries/Microsoft.Extensions.Http.Resilience/Resilience/ResilienceHttpClientBuilderExtensions.Resilience.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Net.Http;
67
using Microsoft.Extensions.DependencyInjection;
78
using Microsoft.Extensions.Diagnostics.ExceptionSummarization;
89
using Microsoft.Extensions.Http.Resilience;
910
using Microsoft.Extensions.Http.Resilience.Internal;
11+
using Microsoft.Shared.DiagnosticIds;
1012
using Microsoft.Shared.Diagnostics;
1113
using Polly;
1214
using Polly.Registry;
@@ -75,6 +77,28 @@ public static IHttpResiliencePipelineBuilder AddResilienceHandler(
7577
return pipelineBuilder;
7678
}
7779

80+
/// <summary>
81+
/// Removes all resilience handlers registered earlier.
82+
/// </summary>
83+
/// <param name="builder">The builder instance.</param>
84+
/// <returns>The value of <paramref name="builder"/>.</returns>
85+
[Experimental(diagnosticId: DiagnosticIds.Experiments.Resilience, UrlFormat = DiagnosticIds.UrlFormat)]
86+
public static IHttpClientBuilder RemoveAllResilienceHandlers(this IHttpClientBuilder builder)
87+
{
88+
_ = Throw.IfNull(builder);
89+
_ = builder.ConfigureAdditionalHttpMessageHandlers(static (handlers, _) =>
90+
{
91+
for (int i = handlers.Count - 1; i >= 0; i--)
92+
{
93+
if (handlers[i] is ResilienceHandler)
94+
{
95+
handlers.RemoveAt(i);
96+
}
97+
}
98+
});
99+
return builder;
100+
}
101+
78102
private static Func<HttpRequestMessage, ResiliencePipeline<HttpResponseMessage>> CreatePipelineSelector(IServiceProvider serviceProvider, string pipelineName)
79103
{
80104
var resilienceProvider = serviceProvider.GetRequiredService<ResiliencePipelineProvider<HttpKey>>();

test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Resilience/HttpClientBuilderExtensionsTests.Resilience.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using System.Net;
78
using System.Net.Http;
89
using System.Threading.Tasks;
@@ -286,6 +287,77 @@ public void AddResilienceHandler_AuthorityByCustomSelector_NotValidated()
286287
Assert.NotNull(factory.CreateClient("my-client"));
287288
}
288289

290+
[Fact]
291+
public void RemoveAllResilienceHandlers_ArgumentValidation()
292+
{
293+
var services = new ServiceCollection();
294+
IHttpClientBuilder? builder = null;
295+
Assert.Throws<ArgumentNullException>(() => builder!.RemoveAllResilienceHandlers());
296+
}
297+
298+
[Fact]
299+
public void RemoveAllResilienceHandlers_EnsureHandlersRemoved()
300+
{
301+
var services = new ServiceCollection();
302+
303+
IHttpClientBuilder? builder = services.AddHttpClient("custom");
304+
305+
builder.AddStandardResilienceHandler();
306+
307+
builder.ConfigureAdditionalHttpMessageHandlers((handlers, _) =>
308+
{
309+
Assert.Single(handlers);
310+
});
311+
312+
builder.RemoveAllResilienceHandlers();
313+
314+
builder.ConfigureAdditionalHttpMessageHandlers((handlers, _) =>
315+
{
316+
Assert.Empty(handlers);
317+
});
318+
319+
using ServiceProvider serviceProvider = services.BuildServiceProvider();
320+
serviceProvider.GetRequiredService<IHttpClientFactory>().CreateClient("custom");
321+
}
322+
323+
[Fact]
324+
public void RemoveAllResilienceHandlers_AddHandlersAfterRemoval()
325+
{
326+
var services = new ServiceCollection();
327+
328+
IHttpClientBuilder? builder = services.AddHttpClient("custom");
329+
builder.RemoveAllResilienceHandlers().AddStandardResilienceHandler();
330+
builder.ConfigureAdditionalHttpMessageHandlers((handlers, _) =>
331+
{
332+
Assert.Single(handlers);
333+
});
334+
335+
using ServiceProvider serviceProvider = services.BuildServiceProvider();
336+
serviceProvider.GetRequiredService<IHttpClientFactory>().CreateClient("custom");
337+
}
338+
339+
[Fact]
340+
public void RemoveAllResilienceHandlers_EnsureOnlyResilienceHandlersRemoved()
341+
{
342+
var services = new ServiceCollection();
343+
344+
IHttpClientBuilder? builder = services.AddHttpClient("custom");
345+
346+
builder.AddHttpMessageHandler(() => new TestHandlerStub(HttpStatusCode.OK));
347+
builder.AddStandardResilienceHandler();
348+
349+
builder.RemoveAllResilienceHandlers();
350+
351+
builder.ConfigureAdditionalHttpMessageHandlers((handlers, _) =>
352+
{
353+
Assert.Single(handlers);
354+
Assert.Equal(typeof(TestHandlerStub), handlers.First().GetType());
355+
});
356+
357+
using ServiceProvider serviceProvider = services.BuildServiceProvider();
358+
serviceProvider.GetRequiredService<IHttpClientFactory>().CreateClient("custom");
359+
}
360+
289361
private void ConfigureBuilder(ResiliencePipelineBuilder<HttpResponseMessage> builder) => builder.AddTimeout(TimeSpan.FromSeconds(1));
290362

291363
private class TestMetricsEnricher : MeteringEnricher

0 commit comments

Comments
 (0)