diff --git a/src/rm.DelegatingHandlers/DelegateAfterNRequestsHandler.cs b/src/rm.DelegatingHandlers/DelegateAfterNRequestsHandler.cs new file mode 100644 index 0000000..4ce608b --- /dev/null +++ b/src/rm.DelegatingHandlers/DelegateAfterNRequestsHandler.cs @@ -0,0 +1,57 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace rm.DelegatingHandlers; + +/// +/// Runs delegates after N requests. +/// +public class DelegateAfterNRequestsHandler : DelegatingHandler +{ + private readonly IDelegateAfterNRequestsHandlerSettings delegateAfterNRequestsHandlerSettings; + + private long n; + + /// + public DelegateAfterNRequestsHandler( + IDelegateAfterNRequestsHandlerSettings delegateAfterNRequestsHandlerSettings) + { + this.delegateAfterNRequestsHandlerSettings = delegateAfterNRequestsHandlerSettings + ?? throw new ArgumentNullException(nameof(delegateAfterNRequestsHandlerSettings)); + } + + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) + { + if (Interlocked.Read(ref n) >= delegateAfterNRequestsHandlerSettings.N) + { + await delegateAfterNRequestsHandlerSettings.PreDelegate(request) + .ConfigureAwait(false); + } + + var response = await base.SendAsync(request, cancellationToken) + .ConfigureAwait(false); + + if (Interlocked.Read(ref n) < delegateAfterNRequestsHandlerSettings.N) + { + Interlocked.Increment(ref n); + } + + return response; + } +} + +public interface IDelegateAfterNRequestsHandlerSettings +{ + long N { get; } + Func PreDelegate { get; } +} + +public record class DelegateAfterNRequestsHandlerSettings : IDelegateAfterNRequestsHandlerSettings +{ + public long N { get; init; } + public Func PreDelegate { get; init; } +} diff --git a/tests/rm.DelegatingHandlersTest/DelegateAfterNRequestsHandlerTests.cs b/tests/rm.DelegatingHandlersTest/DelegateAfterNRequestsHandlerTests.cs new file mode 100644 index 0000000..b6322e6 --- /dev/null +++ b/tests/rm.DelegatingHandlersTest/DelegateAfterNRequestsHandlerTests.cs @@ -0,0 +1,99 @@ +using System.Net.Http; +using AutoFixture; +using AutoFixture.AutoMoq; +using NUnit.Framework; +using rm.DelegatingHandlers; + +namespace rm.DelegatingHandlersTest; + +[TestFixture] +public class DelegateAfterNRequestsHandlerTests +{ + [Test] + public async Task Runs_PreDelegate() + { + var fixture = new Fixture().Customize(new AutoMoqCustomization()); + + var n = 5; + var preDelegateExecutedCount = 0; + var delegateAfterNRequestsHandler = new DelegateAfterNRequestsHandler( + new DelegateAfterNRequestsHandlerSettings + { + N = n, + PreDelegate = (request) => + { + preDelegateExecutedCount++; + return Task.CompletedTask; + } + }); + + using var invoker = HttpMessageInvokerFactory.Create( + fixture.Create(), delegateAfterNRequestsHandler); + + for (int i = 0; i < n; i++) + { + using var _ = await invoker.SendAsync(fixture.Create(), CancellationToken.None); + } + using var __ = await invoker.SendAsync(fixture.Create(), CancellationToken.None); + + Assert.AreEqual(1, preDelegateExecutedCount); + } + + [Test] + public async Task Runs_PreDelegate_After() + { + var fixture = new Fixture().Customize(new AutoMoqCustomization()); + + var n = 5; + var preDelegateExecutedCount = 0; + var delegateAfterNRequestsHandler = new DelegateAfterNRequestsHandler( + new DelegateAfterNRequestsHandlerSettings + { + N = n, + PreDelegate = (request) => + { + preDelegateExecutedCount++; + return Task.CompletedTask; + } + }); + + using var invoker = HttpMessageInvokerFactory.Create( + fixture.Create(), delegateAfterNRequestsHandler); + + for (int i = 0; i < n + 1; i++) + { + using var _ = await invoker.SendAsync(fixture.Create(), CancellationToken.None); + } + using var __ = await invoker.SendAsync(fixture.Create(), CancellationToken.None); + + Assert.AreEqual(2, preDelegateExecutedCount); + } + + [Test] + public async Task Does_Not_Run_PreDelegate() + { + var fixture = new Fixture().Customize(new AutoMoqCustomization()); + + var n = 5; + var preDelegateExecutedCount = 0; + var delegateAfterNRequestsHandler = new DelegateAfterNRequestsHandler( + new DelegateAfterNRequestsHandlerSettings + { + N = n, + PreDelegate = (request) => + { + preDelegateExecutedCount++; + return Task.CompletedTask; + } + }); + + using var invoker = HttpMessageInvokerFactory.Create( + fixture.Create(), delegateAfterNRequestsHandler); + + for (int i = 0; i < n; i++) + { + using var _ = await invoker.SendAsync(fixture.Create(), CancellationToken.None); + } + Assert.AreEqual(0, preDelegateExecutedCount); + } +}