DD.WellWorkover.Cloud/AsbCloudWebApi/Middlewares/UserConnectionsLimitMiddlware.cs
2023-05-19 16:51:41 +05:00

74 lines
2.9 KiB
C#

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Middlewares
{
/// <summary>
/// This is not real middleware it`s part of PermissionsMiddlware.
/// DO NOT register it in setup.cs as middleware.
/// </summary>
class UserConnectionsLimitMiddlware
{
private readonly int parallelRequestsToController;
private readonly RequestDelegate next;
private readonly byte[] responseBody;
private readonly ConcurrentDictionary<int, ConcurrentDictionary<string, int>> stat = new ();
private readonly IEnumerable<string>? controllerNames;
public UserConnectionsLimitMiddlware(RequestDelegate next, IConfiguration configuration)
{
const int parallelRequestsToControllerDefault = 8;
this.next = next;
var parallelRequestsToController = configuration.GetSection("userLimits")?.GetValue<int>("parallelRequestsToController") ?? parallelRequestsToControllerDefault;
this.parallelRequestsToController = parallelRequestsToController > 0
? parallelRequestsToController
: parallelRequestsToControllerDefault;
controllerNames = configuration.GetSection("userLimits")?.GetValue<IEnumerable<string>>("controllerNames");
var bodyText = $"<html><head><title>Too Many Requests</title></head><body><h1>Too Many Requests</h1><p>I only allow {this.parallelRequestsToController} parallel requests per user. Try again soon.</p></body></html>";
responseBody = System.Text.Encoding.UTF8.GetBytes(bodyText);
}
public async Task InvokeAsync(HttpContext context, int idUser, string controllerName)
{
if(controllerNames?.Any(n => controllerName.StartsWith(n)) == false)
{
await next(context);
return;
}
var userStat = stat.GetOrAdd(idUser, idUser => new());
var count = userStat.AddOrUpdate(controllerName, 0, (k, v) => v);
if(count + 1 < parallelRequestsToController)
{
try
{
userStat[controllerName]++;
await next(context);
}
finally
{
userStat[controllerName]--;
}
}
else
{
context.Response.Clear();
context.Response.StatusCode = (int)System.Net.HttpStatusCode.TooManyRequests;
context.Response.Headers.RetryAfter = "1000";
context.Response.Headers.ContentType = "text/html";
await context.Response.BodyWriter.WriteAsync(responseBody);
}
}
}
}