diff --git a/src/GroundsForSupport.Server/RateLimiting/Extensions.cs b/src/GroundsForSupport.Server/RateLimiting/Extensions.cs new file mode 100644 index 0000000..7a31aae --- /dev/null +++ b/src/GroundsForSupport.Server/RateLimiting/Extensions.cs @@ -0,0 +1,32 @@ +using System.Globalization; +using System.Threading.RateLimiting; + +namespace GroundsForSupport.Server.RateLimiting; + +internal static class Extensions +{ + public static void AddRateLimingPolicies(this IServiceCollection services) + { + services.AddRateLimiter(static o => + { + o.RejectionStatusCode = StatusCodes.Status429TooManyRequests; + + o.OnRejected = static (context, ct) => + { + var response = context.HttpContext.Response; + + if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter)) + { + response.Headers.RetryAfter = ((int)retryAfter.TotalSeconds).ToString(CultureInfo.InvariantCulture); + var resetTime = DateTimeOffset.UtcNow.Add(retryAfter); + var unixTimeMilliseconds = resetTime.ToUnixTimeMilliseconds(); + response.Headers.Append("X-RateLimit-Reset", unixTimeMilliseconds.ToString(CultureInfo.InvariantCulture)); + } + + return ValueTask.CompletedTask; + }; + + o.AddPolicy(FixedRateLimitPolicy.Name, FixedRateLimitPolicy.Partitioner); + }); + } +} \ No newline at end of file diff --git a/src/GroundsForSupport.Server/RateLimiting/FixedRateLimitPolicy.cs b/src/GroundsForSupport.Server/RateLimiting/FixedRateLimitPolicy.cs new file mode 100644 index 0000000..b0e9004 --- /dev/null +++ b/src/GroundsForSupport.Server/RateLimiting/FixedRateLimitPolicy.cs @@ -0,0 +1,20 @@ +using System.Threading.RateLimiting; + +namespace GroundsForSupport.Server.RateLimiting; + +internal static class FixedRateLimitPolicy +{ + public const string Name = "fixed"; + + public static Func> Partitioner => + static context => RateLimitPartition.GetFixedWindowLimiter( + partitionKey: context.Connection.RemoteIpAddress?.ToString() ?? "unknown", + factory: static partition => new FixedWindowRateLimiterOptions + { + PermitLimit = 10, + Window = TimeSpan.FromHours(1), + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 0 + } + ); +} \ No newline at end of file