Skip to content

Commit

Permalink
dh: Add Delegate after N requests handler
Browse files Browse the repository at this point in the history
  • Loading branch information
rmandvikar committed Feb 26, 2024
1 parent 2d93045 commit b34f2b4
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 0 deletions.
67 changes: 67 additions & 0 deletions src/rm.DelegatingHandlers/DelegateAfterNRequestsHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace rm.DelegatingHandlers;

/// <summary>
/// Runs delegates after N requests.
/// </summary>
public class DelegateAfterNRequestsHandler : DelegatingHandler
{
private readonly IDelegateAfterNRequestsHandlerSettings delegateAfterNRequestsHandlerSettings;

private long n;

/// <inheritdoc cref="DelegateAfterNRequestsHandler" />
public DelegateAfterNRequestsHandler(
IDelegateAfterNRequestsHandlerSettings delegateAfterNRequestsHandlerSettings)
{
this.delegateAfterNRequestsHandlerSettings = delegateAfterNRequestsHandlerSettings
?? throw new ArgumentNullException(nameof(delegateAfterNRequestsHandlerSettings));
}

protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var nValue = Interlocked.Read(ref n);
var thresholdMet = nValue >= delegateAfterNRequestsHandlerSettings.N;
if (thresholdMet)
{
await delegateAfterNRequestsHandlerSettings.PreDelegate(request)
.ConfigureAwait(false);
}

var response = await base.SendAsync(request, cancellationToken)
.ConfigureAwait(false);

if (thresholdMet)
{
await delegateAfterNRequestsHandlerSettings.PostDelegate(request, response)
.ConfigureAwait(false);
}

if (nValue < delegateAfterNRequestsHandlerSettings.N)
{
Interlocked.Increment(ref n);
}

return response;
}
}

public interface IDelegateAfterNRequestsHandlerSettings
{
long N { get; }
Func<HttpRequestMessage, Task> PreDelegate { get; }
Func<HttpRequestMessage, HttpResponseMessage, Task> PostDelegate { get; }
}

public record class DelegateAfterNRequestsHandlerSettings : IDelegateAfterNRequestsHandlerSettings
{
public long N { get; init; }
public Func<HttpRequestMessage, Task> PreDelegate { get; init; }
public Func<HttpRequestMessage, HttpResponseMessage, Task> PostDelegate { get; init; }
}
120 changes: 120 additions & 0 deletions tests/rm.DelegatingHandlersTest/DelegateAfterNRequestsHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
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 postDelegateExecutedCount = 0;
var delegateAfterNRequestsHandler = new DelegateAfterNRequestsHandler(
new DelegateAfterNRequestsHandlerSettings
{
N = n,
PreDelegate = (request) =>
{
preDelegateExecutedCount++;
return Task.CompletedTask;
},
PostDelegate = (request, response) =>
{
postDelegateExecutedCount++;
return Task.CompletedTask;
},
});

using var invoker = HttpMessageInvokerFactory.Create(
fixture.Create<HttpMessageHandler>(), delegateAfterNRequestsHandler);

for (int i = 0; i < n; i++)
{
using var _ = await invoker.SendAsync(fixture.Create<HttpRequestMessage>(), CancellationToken.None);
}
using var __ = await invoker.SendAsync(fixture.Create<HttpRequestMessage>(), CancellationToken.None);

Assert.AreEqual(1, preDelegateExecutedCount);
Assert.AreEqual(1, postDelegateExecutedCount);
}

[Test]
public async Task Runs_PreDelegate_After()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());

var n = 5;
var preDelegateExecutedCount = 0;
var postDelegateExecutedCount = 0;
var delegateAfterNRequestsHandler = new DelegateAfterNRequestsHandler(
new DelegateAfterNRequestsHandlerSettings
{
N = n,
PreDelegate = (request) =>
{
preDelegateExecutedCount++;
return Task.CompletedTask;
},
PostDelegate = (request, response) =>
{
postDelegateExecutedCount++;
return Task.CompletedTask;
},
});

using var invoker = HttpMessageInvokerFactory.Create(
fixture.Create<HttpMessageHandler>(), delegateAfterNRequestsHandler);

for (int i = 0; i < n + 1; i++)
{
using var _ = await invoker.SendAsync(fixture.Create<HttpRequestMessage>(), CancellationToken.None);
}
using var __ = await invoker.SendAsync(fixture.Create<HttpRequestMessage>(), CancellationToken.None);

Assert.AreEqual(2, preDelegateExecutedCount);
Assert.AreEqual(2, postDelegateExecutedCount);
}

[Test]
public async Task Does_Not_Run_PreDelegate()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());

var n = 5;
var preDelegateExecutedCount = 0;
var postDelegateExecutedCount = 0;
var delegateAfterNRequestsHandler = new DelegateAfterNRequestsHandler(
new DelegateAfterNRequestsHandlerSettings
{
N = n,
PreDelegate = (request) =>
{
preDelegateExecutedCount++;
return Task.CompletedTask;
},
PostDelegate = (request, response) =>
{
postDelegateExecutedCount++;
return Task.CompletedTask;
},
});

using var invoker = HttpMessageInvokerFactory.Create(
fixture.Create<HttpMessageHandler>(), delegateAfterNRequestsHandler);

for (int i = 0; i < n; i++)
{
using var _ = await invoker.SendAsync(fixture.Create<HttpRequestMessage>(), CancellationToken.None);
}
Assert.AreEqual(0, preDelegateExecutedCount);
Assert.AreEqual(0, postDelegateExecutedCount);
}
}

0 comments on commit b34f2b4

Please sign in to comment.