Skip to content

Commit 4d1d39a

Browse files
authored
Add async ISessionAffinityPolicy APIs (#1990)
* Add async ISessionAffinityPolicy APIs * Consume CT changes
1 parent 7fcbcf4 commit 4d1d39a

File tree

5 files changed

+64
-10
lines changed

5 files changed

+64
-10
lines changed

src/ReverseProxy/SessionAffinity/AffinitizeTransform.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
using System;
5+
using System.Diagnostics;
56
using System.Threading.Tasks;
67
using Microsoft.AspNetCore.Http;
78
using Yarp.ReverseProxy.Model;
@@ -31,8 +32,15 @@ public override ValueTask ApplyAsync(ResponseTransformContext context)
3132
{
3233
return default;
3334
}
34-
var selectedDestination = proxyFeature.ProxiedDestination!;
35-
_sessionAffinityPolicy.AffinitizeResponse(context.HttpContext, proxyFeature.Route.Cluster!, options!, selectedDestination);
36-
return default;
35+
36+
Debug.Assert(proxyFeature.Route.Cluster is not null);
37+
Debug.Assert(proxyFeature.ProxiedDestination is not null);
38+
39+
return _sessionAffinityPolicy.AffinitizeResponseAsync(
40+
context.HttpContext,
41+
proxyFeature.Route.Cluster,
42+
options,
43+
proxyFeature.ProxiedDestination,
44+
context.CancellationToken);
3745
}
3846
}

src/ReverseProxy/SessionAffinity/ISessionAffinityPolicy.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT License.
33

44
using System.Collections.Generic;
5+
using System.Threading;
6+
using System.Threading.Tasks;
57
using Microsoft.AspNetCore.Http;
68
using Yarp.ReverseProxy.Configuration;
79
using Yarp.ReverseProxy.Model;
@@ -28,6 +30,22 @@ public interface ISessionAffinityPolicy
2830
/// <returns><see cref="AffinityResult"/> carrying the found affinitized destinations if any and the <see cref="AffinityStatus"/>.</returns>
2931
AffinityResult FindAffinitizedDestinations(HttpContext context, ClusterState cluster, SessionAffinityConfig config, IReadOnlyList<DestinationState> destinations);
3032

33+
/// <summary>
34+
/// Finds <see cref="DestinationState"/> to which the current request is affinitized by the affinity key.
35+
/// </summary>
36+
/// <param name="context">Current request's context.</param>
37+
/// <param name="cluster">Current request's cluster.</param>
38+
/// <param name="config">Affinity config.</param>
39+
/// <param name="destinations"><see cref="DestinationState"/>s available for the request.</param>
40+
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
41+
/// <returns><see cref="AffinityResult"/> carrying the found affinitized destinations if any and the <see cref="AffinityStatus"/>.</returns>
42+
ValueTask<AffinityResult> FindAffinitizedDestinationsAsync(HttpContext context, ClusterState cluster, SessionAffinityConfig config, IReadOnlyList<DestinationState> destinations, CancellationToken cancellationToken)
43+
{
44+
cancellationToken.ThrowIfCancellationRequested();
45+
46+
return new ValueTask<AffinityResult>(FindAffinitizedDestinations(context, cluster, config, destinations));
47+
}
48+
3149
/// <summary>
3250
/// Affinitize the current response to the given <see cref="DestinationState"/> by setting the affinity key extracted from <see cref="DestinationState"/>.
3351
/// </summary>
@@ -36,4 +54,20 @@ public interface ISessionAffinityPolicy
3654
/// <param name="config">Affinity config.</param>
3755
/// <param name="destination"><see cref="DestinationState"/> to which request is to be affinitized.</param>
3856
void AffinitizeResponse(HttpContext context, ClusterState cluster, SessionAffinityConfig config, DestinationState destination);
57+
58+
/// <summary>
59+
/// Affinitize the current response to the given <see cref="DestinationState"/> by setting the affinity key extracted from <see cref="DestinationState"/>.
60+
/// </summary>
61+
/// <param name="context">Current request's context.</param>
62+
/// <param name="cluster">Current request's cluster.</param>
63+
/// <param name="config">Affinity config.</param>
64+
/// <param name="destination"><see cref="DestinationState"/> to which request is to be affinitized.</param>
65+
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
66+
ValueTask AffinitizeResponseAsync(HttpContext context, ClusterState cluster, SessionAffinityConfig config, DestinationState destination, CancellationToken cancellationToken)
67+
{
68+
cancellationToken.ThrowIfCancellationRequested();
69+
70+
AffinitizeResponse(context, cluster, config, destination);
71+
return default;
72+
}
3973
}

src/ReverseProxy/SessionAffinity/SessionAffinityMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ private async Task InvokeInternal(HttpContext context, IReverseProxyFeature prox
5454
var cluster = proxyFeature.Route.Cluster!;
5555

5656
var policy = _sessionAffinityPolicies.GetRequiredServiceById(config.Policy, SessionAffinityConstants.Policies.HashCookie);
57-
var affinityResult = policy.FindAffinitizedDestinations(context, cluster, config, destinations);
57+
var affinityResult = await policy.FindAffinitizedDestinationsAsync(context, cluster, config, destinations, context.RequestAborted);
5858

5959
switch (affinityResult.Status)
6060
{

test/ReverseProxy.Tests/SessionAffinity/AffinitizeTransformTests.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Linq;
55
using System.Net.Http;
6+
using System.Threading;
67
using System.Threading.Tasks;
78
using Microsoft.AspNetCore.Http;
89
using Moq;
@@ -22,7 +23,14 @@ public async Task ApplyAsync_InvokeAffinitizeRequest()
2223
var cluster = GetCluster();
2324
var destination = cluster.Destinations.Values.First();
2425
var provider = new Mock<ISessionAffinityPolicy>(MockBehavior.Strict);
25-
provider.Setup(p => p.AffinitizeResponse(It.IsAny<HttpContext>(), It.IsAny<ClusterState>(), It.IsNotNull<SessionAffinityConfig>(), It.IsAny<DestinationState>()));
26+
provider
27+
.Setup(p => p.AffinitizeResponseAsync(
28+
It.IsAny<HttpContext>(),
29+
It.IsAny<ClusterState>(),
30+
It.IsNotNull<SessionAffinityConfig>(),
31+
It.IsAny<DestinationState>(),
32+
It.IsAny<CancellationToken>()))
33+
.Returns(new ValueTask());
2634

2735
var transform = new AffinitizeTransform(provider.Object);
2836

test/ReverseProxy.Tests/SessionAffinity/SessionAffinityMiddlewareTests.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Yarp.ReverseProxy.Configuration;
1414
using Yarp.ReverseProxy.Model;
1515
using Yarp.ReverseProxy.Forwarder;
16+
using System.Threading;
1617

1718
namespace Yarp.ReverseProxy.SessionAffinity.Tests;
1819

@@ -153,21 +154,24 @@ internal IReadOnlyList<Mock<ISessionAffinityPolicy>> RegisterAffinityPolicies(
153154
policy.SetupGet(p => p.Name).Returns(mode);
154155
if (lookupMiddlewareTest)
155156
{
156-
policy.Setup(p => p.FindAffinitizedDestinations(
157+
policy.Setup(p => p.FindAffinitizedDestinationsAsync(
157158
It.IsAny<HttpContext>(),
158159
It.IsAny<ClusterState>(),
159160
ClusterConfig.Config.SessionAffinity,
160-
expectedDestinations))
161-
.Returns(new AffinityResult(destinations, status.Value))
161+
expectedDestinations,
162+
It.IsAny<CancellationToken>()))
163+
.Returns(new ValueTask<AffinityResult>(new AffinityResult(destinations, status.Value)))
162164
.Callback(() => callback(policy.Object));
163165
}
164166
else
165167
{
166-
policy.Setup(p => p.AffinitizeResponse(
168+
policy.Setup(p => p.AffinitizeResponseAsync(
167169
It.IsAny<HttpContext>(),
168170
It.IsAny<ClusterState>(),
169171
ClusterConfig.Config.SessionAffinity,
170-
expectedDestinations[0]))
172+
expectedDestinations[0],
173+
It.IsAny<CancellationToken>()))
174+
.Returns(new ValueTask())
171175
.Callback(() => callback(policy.Object));
172176
}
173177
result.Add(policy);

0 commit comments

Comments
 (0)