#define FIRST // FIRST ADMIN FIXED SLIDING CONCUR TOKEN FIXED2 JWT #if NEVER #elif FIXED // using Microsoft.AspNetCore.RateLimiting; using System.Threading.RateLimiting; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRateLimiter(_ => _ .AddFixedWindowLimiter(policyName: "fixed", options => { options.PermitLimit = 4; options.Window = TimeSpan.FromSeconds(12); options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; options.QueueLimit = 2; })); var app = builder.Build(); app.UseRateLimiter(); static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); app.MapGet("/", () => Results.Ok($"Hello {GetTicks()}")) .RequireRateLimiting("fixed"); app.Run(); // #elif FIXED2 // using System.Threading.RateLimiting; using Microsoft.AspNetCore.RateLimiting; using WebRateLimitAuth.Models; var builder = WebApplication.CreateBuilder(args); builder.Services.Configure( builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit)); var myOptions = new MyRateLimitOptions(); builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); var fixedPolicy = "fixed"; builder.Services.AddRateLimiter(_ => _ .AddFixedWindowLimiter(policyName: fixedPolicy, options => { options.PermitLimit = myOptions.PermitLimit; options.Window = TimeSpan.FromSeconds(myOptions.Window); options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; options.QueueLimit = myOptions.QueueLimit; })); var app = builder.Build(); app.UseRateLimiter(); static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); app.MapGet("/", () => Results.Ok($"Fixed Window Limiter {GetTicks()}")) .RequireRateLimiting(fixedPolicy); app.Run(); // #elif SLIDING // using Microsoft.AspNetCore.RateLimiting; using System.Threading.RateLimiting; using WebRateLimitAuth.Models; var builder = WebApplication.CreateBuilder(args); var myOptions = new MyRateLimitOptions(); builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); var slidingPolicy = "sliding"; builder.Services.AddRateLimiter(_ => _ .AddSlidingWindowLimiter(policyName: slidingPolicy, options => { options.PermitLimit = myOptions.PermitLimit; options.Window = TimeSpan.FromSeconds(myOptions.Window); options.SegmentsPerWindow = myOptions.SegmentsPerWindow; options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; options.QueueLimit = myOptions.QueueLimit; })); var app = builder.Build(); app.UseRateLimiter(); static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); app.MapGet("/", () => Results.Ok($"Sliding Window Limiter {GetTicks()}")) .RequireRateLimiting(slidingPolicy); app.Run(); // #elif CONCUR // Quick test 10 users, 9 seconds -> 982 requests, 900 errors // using Microsoft.AspNetCore.RateLimiting; using System.Threading.RateLimiting; using WebRateLimitAuth.Models; var builder = WebApplication.CreateBuilder(args); var concurrencyPolicy = "Concurrency"; var myOptions = new MyRateLimitOptions(); builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); builder.Services.AddRateLimiter(_ => _ .AddConcurrencyLimiter(policyName: concurrencyPolicy, options => { options.PermitLimit = myOptions.PermitLimit; options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; options.QueueLimit = myOptions.QueueLimit; })); var app = builder.Build(); app.UseRateLimiter(); static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); app.MapGet("/", async () => { await Task.Delay(500); return Results.Ok($"Concurrency Limiter {GetTicks()}"); }).RequireRateLimiting(concurrencyPolicy); app.Run(); // #elif TOKEN // Quick test 20 users, 20 seconds -> 8965 requests 2,250 errors // using Microsoft.AspNetCore.RateLimiting; using System.Threading.RateLimiting; using WebRateLimitAuth.Models; var builder = WebApplication.CreateBuilder(args); var tokenPolicy = "token"; var myOptions = new MyRateLimitOptions(); builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); builder.Services.AddRateLimiter(_ => _ .AddTokenBucketLimiter(policyName: tokenPolicy, options => { options.TokenLimit = myOptions.TokenLimit; options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; options.QueueLimit = myOptions.QueueLimit; options.ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod); options.TokensPerPeriod = myOptions.TokensPerPeriod; options.AutoReplenishment = myOptions.AutoReplenishment; })); var app = builder.Build(); app.UseRateLimiter(); static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); app.MapGet("/", () => Results.Ok($"Token Limiter {GetTicks()}")) .RequireRateLimiting(tokenPolicy); app.Run(); // #elif FIRST // // // Preceding code removed for brevity. using System.Globalization; using System.Net; using System.Threading.RateLimiting; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using WebRateLimitAuth; using WebRateLimitAuth.Data; using WebRateLimitAuth.Models; var builder = WebApplication.CreateBuilder(args); var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores(); builder.Services.Configure( builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit)); builder.Services.AddRazorPages(); builder.Services.AddControllersWithViews(); var userPolicyName = "user"; var helloPolicy = "hello"; var myOptions = new MyRateLimitOptions(); builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); builder.Services.AddRateLimiter(limiterOptions => { limiterOptions.OnRejected = (context, cancellationToken) => { if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter)) { context.HttpContext.Response.Headers.RetryAfter = ((int) retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo); } context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; context.HttpContext.RequestServices.GetService()? .CreateLogger("Microsoft.AspNetCore.RateLimitingMiddleware") .LogWarning("OnRejected: {GetUserEndPoint}", GetUserEndPoint(context.HttpContext)); return new ValueTask(); }; limiterOptions.AddPolicy(helloPolicy); limiterOptions.AddPolicy(userPolicyName, context => { var username = "anonymous user"; if (context.User.Identity?.IsAuthenticated is true) { username = context.User.ToString()!; } return RateLimitPartition.GetSlidingWindowLimiter(username, _ => new SlidingWindowRateLimiterOptions { PermitLimit = myOptions.PermitLimit, QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = myOptions.QueueLimit, Window = TimeSpan.FromSeconds(myOptions.Window), SegmentsPerWindow = myOptions.SegmentsPerWindow }); }); limiterOptions.GlobalLimiter = PartitionedRateLimiter.Create(context => { IPAddress? remoteIpAddress = context.Connection.RemoteIpAddress; if (!IPAddress.IsLoopback(remoteIpAddress!)) { return RateLimitPartition.GetTokenBucketLimiter (remoteIpAddress!, _ => new TokenBucketRateLimiterOptions { TokenLimit = myOptions.TokenLimit2, QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = myOptions.QueueLimit, ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod), TokensPerPeriod = myOptions.TokensPerPeriod, AutoReplenishment = myOptions.AutoReplenishment }); } return RateLimitPartition.GetNoLimiter(IPAddress.Loopback); }); }); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseRateLimiter(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages().RequireRateLimiting(userPolicyName); app.MapDefaultControllerRoute(); static string GetUserEndPoint(HttpContext context) => $"User {context.User.Identity?.Name ?? "Anonymous"} endpoint:{context.Request.Path}" + $" {context.Connection.RemoteIpAddress}"; static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); app.MapGet("/a", (HttpContext context) => $"{GetUserEndPoint(context)} {GetTicks()}") .RequireRateLimiting(userPolicyName); app.MapGet("/b", (HttpContext context) => $"{GetUserEndPoint(context)} {GetTicks()}") .RequireRateLimiting(helloPolicy); app.MapGet("/c", (HttpContext context) => $"{GetUserEndPoint(context)} {GetTicks()}"); app.Run(); // // #elif ADMIN // using System.Threading.RateLimiting; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.RateLimiting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Primitives; using WebRateLimitAuth.Data; using WebRateLimitAuth.Models; var builder = WebApplication.CreateBuilder(args); var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores(); builder.Services.AddRazorPages(); builder.Services.AddControllersWithViews(); // var getPolicyName = "get"; var postPolicyName = "post"; var myOptions = new MyRateLimitOptions(); builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); builder.Services.AddRateLimiter(_ => _ .AddConcurrencyLimiter(policyName: getPolicyName, options => { options.PermitLimit = myOptions.PermitLimit; options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; options.QueueLimit = myOptions.QueueLimit; }) .AddPolicy(policyName: postPolicyName, partitioner: httpContext => { string userName = httpContext.User.Identity?.Name ?? string.Empty; if (!StringValues.IsNullOrEmpty(userName)) { return RateLimitPartition.GetTokenBucketLimiter(userName, _ => new TokenBucketRateLimiterOptions { TokenLimit = myOptions.TokenLimit2, QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = myOptions.QueueLimit, ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod), TokensPerPeriod = myOptions.TokensPerPeriod, AutoReplenishment = myOptions.AutoReplenishment }); } return RateLimitPartition.GetTokenBucketLimiter("Anon", _ => new TokenBucketRateLimiterOptions { TokenLimit = myOptions.TokenLimit, QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = myOptions.QueueLimit, ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod), TokensPerPeriod = myOptions.TokensPerPeriod, AutoReplenishment = true }); })); // var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseRateLimiter(); app.UseAuthentication(); app.UseAuthorization(); static string GetUserEndPointMethod(HttpContext context) => $"Hello {context.User.Identity?.Name ?? "Anonymous"} " + $"Endpoint:{context.Request.Path} Method: {context.Request.Method}"; app.MapGet("/test", (HttpContext context) => $"{GetUserEndPointMethod(context)}") .RequireRateLimiting(getPolicyName); app.MapRazorPages().RequireRateLimiting(getPolicyName) .RequireRateLimiting(postPolicyName); app.MapDefaultControllerRoute(); app.Run(); // #elif JWT // using System.Threading.RateLimiting; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Primitives; using WebRateLimitAuth.Models; var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthorization(); builder.Services.AddAuthentication("Bearer").AddJwtBearer(); var myOptions = new MyRateLimitOptions(); builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); var jwtPolicyName = "jwt"; builder.Services.AddRateLimiter(limiterOptions => { limiterOptions.RejectionStatusCode = StatusCodes.Status429TooManyRequests; limiterOptions.AddPolicy(policyName: jwtPolicyName, partitioner: httpContext => { var accessToken = httpContext.Features.Get()? .AuthenticateResult?.Properties?.GetTokenValue("access_token")?.ToString() ?? string.Empty; if (!StringValues.IsNullOrEmpty(accessToken)) { return RateLimitPartition.GetTokenBucketLimiter(accessToken, _ => new TokenBucketRateLimiterOptions { TokenLimit = myOptions.TokenLimit2, QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = myOptions.QueueLimit, ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod), TokensPerPeriod = myOptions.TokensPerPeriod, AutoReplenishment = myOptions.AutoReplenishment }); } return RateLimitPartition.GetTokenBucketLimiter("Anon", _ => new TokenBucketRateLimiterOptions { TokenLimit = myOptions.TokenLimit, QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = myOptions.QueueLimit, ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod), TokensPerPeriod = myOptions.TokensPerPeriod, AutoReplenishment = true }); }); }); var app = builder.Build(); app.UseAuthorization(); app.UseRateLimiter(); app.MapGet("/", () => "Hello, World!"); app.MapGet("/jwt", (HttpContext context) => $"Hello {GetUserEndPointMethod(context)}") .RequireRateLimiting(jwtPolicyName) .RequireAuthorization(); app.MapPost("/post", (HttpContext context) => $"Hello {GetUserEndPointMethod(context)}") .RequireRateLimiting(jwtPolicyName) .RequireAuthorization(); app.Run(); static string GetUserEndPointMethod(HttpContext context) => $"Hello {context.User.Identity?.Name ?? "Anonymous"} " + $"Endpoint:{context.Request.Path} Method: {context.Request.Method}"; // #endif