Skip to content

Commit dc44609

Browse files
author
John Luo
committed
Update middleware implementation
1 parent ae286c9 commit dc44609

12 files changed

+135
-255
lines changed

AspNetCore.sln

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,6 +1648,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Featur
16481648
EndProject
16491649
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Features.Tests", "src\Http\Features\test\Microsoft.Extensions.Features.Tests.csproj", "{09FFBC53-3EFF-45C4-9822-5D66089CD6AD}"
16501650
EndProject
1651+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Threading.ResourceLimits", "src\ResourceLimits\src\System.Threading.ResourceLimits.csproj", "{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}"
1652+
EndProject
1653+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ResourceLimits", "ResourceLimits", "{947A136C-8661-41A8-9900-056F66CDCCB6}"
1654+
EndProject
16511655
Global
16521656
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16531657
Debug|Any CPU = Debug|Any CPU
@@ -7827,6 +7831,18 @@ Global
78277831
{09FFBC53-3EFF-45C4-9822-5D66089CD6AD}.Release|x64.Build.0 = Release|Any CPU
78287832
{09FFBC53-3EFF-45C4-9822-5D66089CD6AD}.Release|x86.ActiveCfg = Release|Any CPU
78297833
{09FFBC53-3EFF-45C4-9822-5D66089CD6AD}.Release|x86.Build.0 = Release|Any CPU
7834+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
7835+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
7836+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|x64.ActiveCfg = Debug|Any CPU
7837+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|x64.Build.0 = Debug|Any CPU
7838+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|x86.ActiveCfg = Debug|Any CPU
7839+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|x86.Build.0 = Debug|Any CPU
7840+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
7841+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|Any CPU.Build.0 = Release|Any CPU
7842+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|x64.ActiveCfg = Release|Any CPU
7843+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|x64.Build.0 = Release|Any CPU
7844+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|x86.ActiveCfg = Release|Any CPU
7845+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|x86.Build.0 = Release|Any CPU
78307846
EndGlobalSection
78317847
GlobalSection(SolutionProperties) = preSolution
78327848
HideSolutionNode = FALSE
@@ -8643,6 +8659,8 @@ Global
86438659
{17F28812-983E-4415-A55D-842DD7EC6887} = {627BE8B3-59E6-4F1D-8C9C-76B804D41724}
86448660
{A07D3B13-388B-444F-9E37-DDC0787C4690} = {17F28812-983E-4415-A55D-842DD7EC6887}
86458661
{09FFBC53-3EFF-45C4-9822-5D66089CD6AD} = {17F28812-983E-4415-A55D-842DD7EC6887}
8662+
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8} = {947A136C-8661-41A8-9900-056F66CDCCB6}
8663+
{947A136C-8661-41A8-9900-056F66CDCCB6} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
86468664
EndGlobalSection
86478665
GlobalSection(ExtensibilityGlobals) = postSolution
86488666
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

src/Middleware/RequestLimiter/sample/Startup.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,22 @@ public void ConfigureServices(IServiceCollection services)
2020
{
2121
services.AddControllersWithViews();
2222
services.AddSingleton(new IPAggregatedRateLimiter(2, 2));
23-
services.AddSingleton(new RateLimiter(2, 2));
23+
services.AddSingleton(new TokenBucketRateLimiter(2, 2));
2424

2525
services.AddRequestLimiter(options =>
2626
{
27-
options.SetDefaultPolicy(new ConcurrencyLimiter(100));
27+
options.SetDefaultPolicy(new ConcurrencyLimiter(new ConcurrencyLimiterOptions { ResourceLimit = 100 }));
2828
// TODO: Consider a policy builder
2929
// TODO: Support combining/composing policies
3030
options.AddPolicy("concurrency", policy =>
3131
{
3232
// Add instance
33-
policy.AddLimiter(new ConcurrencyLimiter(1));
33+
policy.AddLimiter(new ConcurrencyLimiter(new ConcurrencyLimiterOptions { ResourceLimit = 1 }));
3434
});
3535
options.AddPolicy("rate", policy =>
3636
{
3737
// Add from DI
38-
policy.AddLimiter<RateLimiter>();
38+
policy.AddLimiter<TokenBucketRateLimiter>();
3939
});
4040
});
4141
}
@@ -59,7 +59,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<
5959
{
6060
await Task.Delay(5000);
6161
await context.Response.WriteAsync("Hello World!");
62-
}).EnforceLimit(new RateLimiter(2, 2));
62+
}).EnforceLimit(new TokenBucketRateLimiter(2, 2));
6363

6464
endpoints.MapGet("/concurrentPolicy", async context =>
6565
{
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading;
6+
using System.Threading.ResourceLimits;
7+
using System.Threading.Tasks;
8+
using Microsoft.AspNetCore.Http;
9+
10+
namespace Microsoft.AspNetCore.RequestLimiter
11+
{
12+
// TODO: this name is too long
13+
internal class AggregatedResourceLimiterOfHttpContextWrapper : AggregatedResourceLimiter<HttpContext>
14+
{
15+
private readonly ResourceLimiter _limiter;
16+
17+
public AggregatedResourceLimiterOfHttpContextWrapper(ResourceLimiter limiter)
18+
{
19+
_limiter = limiter;
20+
}
21+
22+
public override ResourceLease Acquire(HttpContext resourceID, long requestedCount)
23+
{
24+
return _limiter.Acquire(requestedCount);
25+
}
26+
27+
public override long EstimatedCount(HttpContext resourceID)
28+
{
29+
return _limiter.EstimatedCount;
30+
}
31+
32+
public override ValueTask<ResourceLease> WaitAsync(HttpContext resourceID, long requestedCount, CancellationToken cancellationToken = default)
33+
{
34+
return _limiter.WaitAsync(requestedCount, cancellationToken);
35+
}
36+
}
37+
}

src/Middleware/RequestLimiter/src/IPAggregatedRateLimiter.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Concurrent;
3-
using System.Diagnostics.CodeAnalysis;
43
using System.Net;
54
using System.Threading;
65
using System.Threading.ResourceLimits;
@@ -9,14 +8,15 @@
98

109
namespace Microsoft.AspNetCore.RequestLimiter
1110
{
11+
// TODO: update implementation with WaitAsync and use MemoryCache instead of ConcurrentDictionary
1212
public class IPAggregatedRateLimiter : AggregatedResourceLimiter<HttpContext>
1313
{
1414
private long _resourceCount;
1515
private readonly long _maxResourceCount;
1616
private readonly long _newResourcePerSecond;
1717

1818
private Timer _renewTimer;
19-
// This is racy
19+
// TODO: This is racy
2020
private ConcurrentDictionary<IPAddress, long> _cache = new ConcurrentDictionary<IPAddress, long>();
2121

2222
public IPAggregatedRateLimiter(long resourceCount, long newResourcePerSecond)
@@ -40,16 +40,16 @@ public override long EstimatedCount(HttpContext resourceId)
4040
return _cache.TryGetValue(resourceId.Connection.RemoteIpAddress, out var count) ? count : 0;
4141
}
4242

43-
public override Resource Acquire(HttpContext resourceId, long requestedCount)
43+
public override ResourceLease Acquire(HttpContext resourceId, long requestedCount)
4444
{
4545
if (requestedCount > _maxResourceCount)
4646
{
47-
return Resource.FailNoopResource;
47+
return ResourceLease.FailedAcquisition;
4848
}
4949

5050
if (resourceId.Connection.RemoteIpAddress == null)
5151
{
52-
return Resource.SuccessNoopResource;
52+
return ResourceLease.SuccessfulAcquisition;
5353
}
5454

5555
var key = resourceId.Connection.RemoteIpAddress;
@@ -58,7 +58,7 @@ public override Resource Acquire(HttpContext resourceId, long requestedCount)
5858
{
5959
if (_cache.TryAdd(key, requestedCount))
6060
{
61-
return Resource.SuccessNoopResource;
61+
return ResourceLease.SuccessfulAcquisition;
6262
}
6363
}
6464

@@ -69,22 +69,22 @@ public override Resource Acquire(HttpContext resourceId, long requestedCount)
6969
{
7070
if (newCount > _maxResourceCount)
7171
{
72-
return Resource.FailNoopResource;
72+
return ResourceLease.FailedAcquisition;
7373
}
7474

75-
return Resource.SuccessNoopResource;
75+
return ResourceLease.SuccessfulAcquisition;
7676
}
7777
if (!_cache.TryGetValue(key, out count))
7878
{
7979
if (_cache.TryAdd(key, requestedCount))
8080
{
81-
return Resource.SuccessNoopResource;
81+
return ResourceLease.SuccessfulAcquisition;
8282
}
8383
}
8484
}
8585
}
8686

87-
public override ValueTask<Resource> AcquireAsync(HttpContext resourceId, long requestedCount, CancellationToken cancellationToken = default)
87+
public override ValueTask<ResourceLease> WaitAsync(HttpContext resourceId, long requestedCount, CancellationToken cancellationToken = default)
8888
{
8989
throw new NotImplementedException();
9090
}

src/Middleware/RequestLimiter/src/RateLimiter.cs

Lines changed: 0 additions & 122 deletions
This file was deleted.

src/Middleware/RequestLimiter/src/RequestLimitRegistration.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/Middleware/RequestLimiter/src/RequestLimiterAttribute.cs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77

88
namespace Microsoft.AspNetCore.RequestLimiter
99
{
10-
/// <summary>
11-
/// Specifies that the class or method that this attribute is applied to requires the specified authorization.
12-
/// </summary>
1310
// TODO: Double check ordering
1411
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
1512
public class RequestLimitAttribute : Attribute
@@ -22,27 +19,26 @@ public RequestLimitAttribute(string policy)
2219
}
2320

2421
public RequestLimitAttribute(long requestPerSecond)
25-
: this(new RateLimiter(requestPerSecond, requestPerSecond))
26-
{ }
22+
: this(new TokenBucketRateLimiter(requestPerSecond, requestPerSecond)) { }
2723

2824
public RequestLimitAttribute(ResourceLimiter limiter)
29-
{
30-
LimiterRegistration = new RequestLimitRegistration(limiter);
31-
}
25+
: this(_ => new AggregatedResourceLimiterOfHttpContextWrapper(limiter)) { }
3226

3327
public RequestLimitAttribute(AggregatedResourceLimiter<HttpContext> limiter)
28+
: this(_ => limiter) { }
29+
30+
internal RequestLimitAttribute(Func<IServiceProvider, ResourceLimiter> resolveLimiter)
3431
{
35-
LimiterRegistration = new RequestLimitRegistration(limiter);
32+
ResolveLimiter = services => new AggregatedResourceLimiterOfHttpContextWrapper(resolveLimiter(services));
3633
}
3734

38-
// TODO consider constructors that take in types for DI retrieval
39-
public RequestLimitAttribute(RequestLimitRegistration registration)
35+
internal RequestLimitAttribute(Func<IServiceProvider, AggregatedResourceLimiter<HttpContext>> resolveLimiter)
4036
{
41-
LimiterRegistration = registration;
37+
ResolveLimiter = resolveLimiter;
4238
}
4339

44-
public string? Policy { get; set; }
40+
internal string? Policy { get; set; }
4541

46-
public RequestLimitRegistration? LimiterRegistration { get; set; }
42+
internal Func<IServiceProvider, AggregatedResourceLimiter<HttpContext>>? ResolveLimiter { get; set; }
4743
}
4844
}

0 commit comments

Comments
 (0)