This commit is contained in:
ngfrolov 2022-11-10 13:59:48 +05:00
parent 64641c5bc3
commit 698fb33651
2 changed files with 84 additions and 52 deletions

View File

@ -9,24 +9,8 @@ namespace AsbCloudWebApi.Tests.Middlware
{ {
public class UserConnectionsLimitMiddlwareTest public class UserConnectionsLimitMiddlwareTest
{ {
private readonly HttpClient httpClient; const int iterations2Block = 8;
public UserConnectionsLimitMiddlwareTest()
{
var host = Host.CreateDefaultBuilder(Array.Empty<string>())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.Build();
host.Start();
httpClient = new ();
httpClient.DefaultRequestHeaders.Authorization = new("Bearer", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGV2IiwiaWRDb21wYW55IjoiMSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJuYmYiOjE2NjY1ODY2MjAsImV4cCI6MTY5ODE0NDIyMCwiaXNzIjoiYSIsImF1ZCI6ImEifQ.zqBdR4nYB87-Xyzv025waasN47i43c9FJ23RfzIvUsM");
}
[Fact]
public async Task Send_n_requests_and_get_blocked()
{
//Данные в тестовой БД //Данные в тестовой БД
//select //select
// tw.id, // tw.id,
@ -48,8 +32,7 @@ namespace AsbCloudWebApi.Tests.Middlware
//where tw is not null //where tw is not null
//order by t_stat.count //order by t_stat.count
//limit 10; //limit 10;
private readonly (int, DateTime, DateTime)[] wells = new[]
var wells = new []
{ {
(191, new DateTime(2022, 09, 01, 21, 43, 00, DateTimeKind.Utc), new DateTime(2022, 09, 04, 07, 37, 31, DateTimeKind.Utc)), (191, new DateTime(2022, 09, 01, 21, 43, 00, DateTimeKind.Utc), new DateTime(2022, 09, 04, 07, 37, 31, DateTimeKind.Utc)),
(3 , new DateTime(2021, 09, 16, 06, 13, 33, DateTimeKind.Utc), new DateTime(2021, 09, 20, 00, 29, 28, DateTimeKind.Utc)), (3 , new DateTime(2021, 09, 16, 06, 13, 33, DateTimeKind.Utc), new DateTime(2021, 09, 20, 00, 29, 28, DateTimeKind.Utc)),
@ -63,23 +46,62 @@ namespace AsbCloudWebApi.Tests.Middlware
(112, new DateTime(2022, 04, 20, 16, 47, 51, DateTimeKind.Utc), new DateTime(2022, 04, 28, 15, 04, 33, DateTimeKind.Utc)), (112, new DateTime(2022, 04, 20, 16, 47, 51, DateTimeKind.Utc), new DateTime(2022, 04, 28, 15, 04, 33, DateTimeKind.Utc)),
}; };
public UserConnectionsLimitMiddlwareTest()
{
var host = Host.CreateDefaultBuilder(Array.Empty<string>())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.Build();
host.Start();
}
[Fact]
public async Task Send_n_requests_and_get_blocked()
{
var i = 0; var i = 0;
for (; i < 5; i++) for (; i < iterations2Block; i++)
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
var well = wells[i]; var well = wells[i];
var url = MakeUrl(well.Item1, well.Item2, well.Item3); var url = MakeUrl(well.Item1, well.Item2, well.Item3);
var response = await httpClient.GetAsync(url); var response = await MakeHttpClient().GetAsync(url);
//await response.Content.ReadAsStringAsync(); //await response.Content.ReadAsStringAsync();
await Task.Delay(1000); await Task.Delay(1000);
}); });
var well = wells[i]; var well = wells[i];
var url = MakeUrl(well.Item1, well.Item2, well.Item3); var url = MakeUrl(well.Item1, well.Item2, well.Item3);
var response = await httpClient.GetAsync(url); var response = await MakeHttpClient().GetAsync(url);
Assert.Equal(System.Net.HttpStatusCode.TooManyRequests, response.StatusCode); Assert.Equal(System.Net.HttpStatusCode.TooManyRequests, response.StatusCode);
} }
[Fact]
public async Task Send_n_requests_and_get_blocked_then_restored()
{
var i = 0;
var tasks = new Task[iterations2Block];
for (; i < iterations2Block; i++)
tasks[i] = Task.Run(async () =>
{
var well = wells[i];
var url = MakeUrl(well.Item1, well.Item2, well.Item3);
var response = await MakeHttpClient().GetAsync(url);
await Task.Delay(1000);
});
var well = wells[i];
var url = MakeUrl(well.Item1, well.Item2, well.Item3);
var response = await MakeHttpClient().GetAsync(url);
Assert.Equal(System.Net.HttpStatusCode.TooManyRequests, response.StatusCode);
Task.WaitAll(tasks);
response = await MakeHttpClient().GetAsync(url);
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
}
private static string MakeUrl(int idWell, DateTime dateBegin, DateTime dateEnd) private static string MakeUrl(int idWell, DateTime dateBegin, DateTime dateEnd)
{ {
var interval = (dateEnd - dateBegin).TotalSeconds; var interval = (dateEnd - dateBegin).TotalSeconds;
@ -87,5 +109,13 @@ namespace AsbCloudWebApi.Tests.Middlware
var url = $"http://127.0.0.1:5000/api/TelemetryDataSaub/{idWell}?begin={dateBeginString}&intervalSec={interval}&approxPointsCount={interval}"; var url = $"http://127.0.0.1:5000/api/TelemetryDataSaub/{idWell}?begin={dateBeginString}&intervalSec={interval}&approxPointsCount={interval}";
return url; return url;
} }
private static HttpClient MakeHttpClient()
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new("Bearer", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGV2IiwiaWRDb21wYW55IjoiMSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJuYmYiOjE2NjY1ODY2MjAsImV4cCI6MTY5ODE0NDIyMCwiaXNzIjoiYSIsImF1ZCI6ImEifQ.zqBdR4nYB87-Xyzv025waasN47i43c9FJ23RfzIvUsM");
return httpClient;
} }
}
} }

View File

@ -14,25 +14,26 @@ namespace AsbCloudWebApi.Middlewares
/// </summary> /// </summary>
class UserConnectionsLimitMiddlware class UserConnectionsLimitMiddlware
{ {
private readonly RequestDelegate next;
private readonly int parallelRequestsToController; private readonly int parallelRequestsToController;
private readonly byte[] body; private readonly RequestDelegate next;
private readonly byte[] responseBody;
private readonly ConcurrentDictionary<int, ConcurrentDictionary<string, int>> stat = new (); private readonly ConcurrentDictionary<int, ConcurrentDictionary<string, int>> stat = new ();
private readonly IEnumerable<string>? controllerNames; private readonly IEnumerable<string>? controllerNames;
public UserConnectionsLimitMiddlware(RequestDelegate next, IConfiguration configuration) public UserConnectionsLimitMiddlware(RequestDelegate next, IConfiguration configuration)
{ {
const int parallelRequestsToControllerDefault = 8;
this.next = next; this.next = next;
var parallelRequestsToController = configuration.GetSection("userLimits")?.GetValue<int>("parallelRequestsToController") ?? 5; var parallelRequestsToController = configuration.GetSection("userLimits")?.GetValue<int>("parallelRequestsToController") ?? parallelRequestsToControllerDefault;
this.parallelRequestsToController = parallelRequestsToController > 0 this.parallelRequestsToController = parallelRequestsToController > 0
? parallelRequestsToController ? parallelRequestsToController
: 5; : parallelRequestsToControllerDefault;
controllerNames = configuration.GetSection("userLimits")?.GetValue<IEnumerable<string>>("controllerNames"); 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 {parallelRequestsToController} parallel requests per user. Try again soon.</p></body></html>"; var bodyText = $"<html><head><title>Too Many Requests</title></head><body><h1>Too Many Requests</h1><p>I only allow {parallelRequestsToController} parallel requests per user. Try again soon.</p></body></html>";
body = System.Text.Encoding.UTF8.GetBytes(bodyText); responseBody = System.Text.Encoding.UTF8.GetBytes(bodyText);
} }
public async Task InvokeAsync(HttpContext context, int idUser, string controllerName) public async Task InvokeAsync(HttpContext context, int idUser, string controllerName)
@ -44,11 +45,13 @@ namespace AsbCloudWebApi.Middlewares
} }
var userStat = stat.GetOrAdd(idUser, idUser => new()); var userStat = stat.GetOrAdd(idUser, idUser => new());
var count = userStat.AddOrUpdate(controllerName, 1, (key, value) => value + 1); var count = userStat.AddOrUpdate(controllerName, 0, (k, v) => v);
if(count < parallelRequestsToController)
if(count + 1 < parallelRequestsToController)
{ {
try try
{ {
userStat[controllerName]++;
await next(context); await next(context);
} }
finally finally
@ -60,10 +63,9 @@ namespace AsbCloudWebApi.Middlewares
{ {
context.Response.Clear(); context.Response.Clear();
context.Response.StatusCode = (int)System.Net.HttpStatusCode.TooManyRequests; context.Response.StatusCode = (int)System.Net.HttpStatusCode.TooManyRequests;
context.Response.Headers.RetryAfter = "1000"; context.Response.Headers.RetryAfter = "1000";
context.Response.Headers.ContentType = "text/html"; context.Response.Headers.ContentType = "text/html";
await context.Response.BodyWriter.WriteAsync(body); await context.Response.BodyWriter.WriteAsync(responseBody);
} }
} }
} }