Skip to content

Commit

Permalink
Update middleware implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
John Luo committed May 26, 2021
1 parent ae286c9 commit 191575d
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 286 deletions.
18 changes: 18 additions & 0 deletions AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -1648,6 +1648,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Featur
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Features.Tests", "src\Http\Features\test\Microsoft.Extensions.Features.Tests.csproj", "{09FFBC53-3EFF-45C4-9822-5D66089CD6AD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Threading.ResourceLimits", "src\ResourceLimits\src\System.Threading.ResourceLimits.csproj", "{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ResourceLimits", "ResourceLimits", "{947A136C-8661-41A8-9900-056F66CDCCB6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -7827,6 +7831,18 @@ Global
{09FFBC53-3EFF-45C4-9822-5D66089CD6AD}.Release|x64.Build.0 = Release|Any CPU
{09FFBC53-3EFF-45C4-9822-5D66089CD6AD}.Release|x86.ActiveCfg = Release|Any CPU
{09FFBC53-3EFF-45C4-9822-5D66089CD6AD}.Release|x86.Build.0 = Release|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|x64.ActiveCfg = Debug|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|x64.Build.0 = Debug|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|x86.ActiveCfg = Debug|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Debug|x86.Build.0 = Debug|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|Any CPU.Build.0 = Release|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|x64.ActiveCfg = Release|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|x64.Build.0 = Release|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|x86.ActiveCfg = Release|Any CPU
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -8643,6 +8659,8 @@ Global
{17F28812-983E-4415-A55D-842DD7EC6887} = {627BE8B3-59E6-4F1D-8C9C-76B804D41724}
{A07D3B13-388B-444F-9E37-DDC0787C4690} = {17F28812-983E-4415-A55D-842DD7EC6887}
{09FFBC53-3EFF-45C4-9822-5D66089CD6AD} = {17F28812-983E-4415-A55D-842DD7EC6887}
{EA04C8B5-CE40-4E54-AF50-5D56FD1AFDC8} = {947A136C-8661-41A8-9900-056F66CDCCB6}
{947A136C-8661-41A8-9900-056F66CDCCB6} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
Expand Down
22 changes: 10 additions & 12 deletions src/Middleware/RequestLimiter/sample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,20 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton(new IPAggregatedRateLimiter(2, 2));
services.AddSingleton(new RateLimiter(2, 2));
services.AddSingleton(new TokenBucketRateLimiter(2, 2));

services.AddRequestLimiter(options =>
{
options.SetDefaultPolicy(new ConcurrencyLimiter(100));
// TODO: Consider a policy builder
// TODO: Support combining/composing policies
options.AddPolicy("concurrency", policy =>
options.SetDefaultPolicy(new ConcurrencyLimiter(new ConcurrencyLimiterOptions { ResourceLimit = 100 }));
options.AddPolicy("ipPolicy", policy =>
{
// Add instance
policy.AddLimiter(new ConcurrencyLimiter(1));
policy.AddAggregatedLimiter(new IPAggregatedRateLimiter(2, 2));
});
options.AddPolicy("rate", policy =>
{
// Add from DI
policy.AddLimiter<RateLimiter>();
policy.AddLimiter<TokenBucketRateLimiter>();
});
});
}
Expand All @@ -59,25 +57,25 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<
{
await Task.Delay(5000);
await context.Response.WriteAsync("Hello World!");
}).EnforceLimit(new RateLimiter(2, 2));
}).EnforceLimit(new TokenBucketRateLimiter(2, 2));
endpoints.MapGet("/concurrentPolicy", async context =>
endpoints.MapGet("/concurrent", async context =>
{
await Task.Delay(5000);
await context.Response.WriteAsync("Wrote!");
}).EnforceLimit("concurrency");
}).EnforceConcurrencyLimit(concurrentRequests: 1);
endpoints.MapGet("/adhoc", async context =>
{
await Task.Delay(5000);
await context.Response.WriteAsync("Tested!");
}).EnforceLimit(requestPerSecond: 2);
}).EnforceRateLimit(requestPerSecond: 2);
endpoints.MapGet("/ipFromDI", async context =>
{
await Task.Delay(5000);
await context.Response.WriteAsync("IP limited!");
}).EnforceAggregatedLimit<IPAggregatedRateLimiter>();
}).EnforceLimit("ipPolicy");
endpoints.MapGet("/multiple", async context =>
{
Expand Down
34 changes: 34 additions & 0 deletions src/Middleware/RequestLimiter/src/HttpContextLimiter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Threading;
using System.Threading.ResourceLimits;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.RequestLimiter
{
internal class HttpContextLimiter : AggregatedResourceLimiter<HttpContext>
{
private readonly ResourceLimiter _limiter;

public HttpContextLimiter(ResourceLimiter limiter)
{
_limiter = limiter;
}

public override ResourceLease Acquire(HttpContext resourceID, long requestedCount)
{
return _limiter.Acquire(requestedCount);
}

public override long EstimatedCount(HttpContext resourceID)
{
return _limiter.EstimatedCount;
}

public override ValueTask<ResourceLease> WaitAsync(HttpContext resourceID, long requestedCount, CancellationToken cancellationToken = default)
{
return _limiter.WaitAsync(requestedCount, cancellationToken);
}

public static implicit operator HttpContextLimiter(ResourceLimiter limiter) => new HttpContextLimiter(limiter);
}
}
20 changes: 10 additions & 10 deletions src/Middleware/RequestLimiter/src/IPAggregatedRateLimiter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Threading;
using System.Threading.ResourceLimits;
Expand All @@ -9,14 +8,15 @@

namespace Microsoft.AspNetCore.RequestLimiter
{
// TODO: update implementation with WaitAsync and use MemoryCache instead of ConcurrentDictionary
public class IPAggregatedRateLimiter : AggregatedResourceLimiter<HttpContext>
{
private long _resourceCount;
private readonly long _maxResourceCount;
private readonly long _newResourcePerSecond;

private Timer _renewTimer;
// This is racy
// TODO: This is racy
private ConcurrentDictionary<IPAddress, long> _cache = new ConcurrentDictionary<IPAddress, long>();

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

public override Resource Acquire(HttpContext resourceId, long requestedCount)
public override ResourceLease Acquire(HttpContext resourceId, long requestedCount)
{
if (requestedCount > _maxResourceCount)
{
return Resource.FailNoopResource;
return ResourceLease.FailedAcquisition;
}

if (resourceId.Connection.RemoteIpAddress == null)
{
return Resource.SuccessNoopResource;
return ResourceLease.SuccessfulAcquisition;
}

var key = resourceId.Connection.RemoteIpAddress;
Expand All @@ -58,7 +58,7 @@ public override Resource Acquire(HttpContext resourceId, long requestedCount)
{
if (_cache.TryAdd(key, requestedCount))
{
return Resource.SuccessNoopResource;
return ResourceLease.SuccessfulAcquisition;
}
}

Expand All @@ -69,22 +69,22 @@ public override Resource Acquire(HttpContext resourceId, long requestedCount)
{
if (newCount > _maxResourceCount)
{
return Resource.FailNoopResource;
return ResourceLease.FailedAcquisition;
}

return Resource.SuccessNoopResource;
return ResourceLease.SuccessfulAcquisition;
}
if (!_cache.TryGetValue(key, out count))
{
if (_cache.TryAdd(key, requestedCount))
{
return Resource.SuccessNoopResource;
return ResourceLease.SuccessfulAcquisition;
}
}
}
}

public override ValueTask<Resource> AcquireAsync(HttpContext resourceId, long requestedCount, CancellationToken cancellationToken = default)
public override ValueTask<ResourceLease> WaitAsync(HttpContext resourceId, long requestedCount, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
Expand Down
122 changes: 0 additions & 122 deletions src/Middleware/RequestLimiter/src/RateLimiter.cs

This file was deleted.

32 changes: 0 additions & 32 deletions src/Middleware/RequestLimiter/src/RequestLimitRegistration.cs

This file was deleted.

Loading

0 comments on commit 191575d

Please sign in to comment.