From 698fb33651298ba4e486f9a098d605ff694fd23e Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Thu, 10 Nov 2022 13:59:48 +0500 Subject: [PATCH] fix #7582867 --- .../UserConnectionsLimitMiddlwareTest.cs | 114 +++++++++++------- .../UserConnectionsLimitMiddlware.cs | 22 ++-- 2 files changed, 84 insertions(+), 52 deletions(-) diff --git a/AsbCloudWebApi.Tests/Middlware/UserConnectionsLimitMiddlwareTest.cs b/AsbCloudWebApi.Tests/Middlware/UserConnectionsLimitMiddlwareTest.cs index b3ea33f8..9b2c99a7 100644 --- a/AsbCloudWebApi.Tests/Middlware/UserConnectionsLimitMiddlwareTest.cs +++ b/AsbCloudWebApi.Tests/Middlware/UserConnectionsLimitMiddlwareTest.cs @@ -9,7 +9,42 @@ namespace AsbCloudWebApi.Tests.Middlware { public class UserConnectionsLimitMiddlwareTest { - private readonly HttpClient httpClient; + const int iterations2Block = 8; + + //Данные в тестовой БД + //select + // tw.id, + // t_stat.minDate, + // t_stat.maxDate + //from( + // select + + // id_telemetry, + // count(1) as count, + // min("date") as minDate, + // max("date") as maxDate + + // from t_telemetry_data_saub + + // group by id_telemetry + //) as t_stat + //join t_well tw on tw.id_telemetry = t_stat.id_telemetry + //where tw is not null + //order by t_stat.count + //limit 10; + private readonly (int, DateTime, DateTime)[] wells = new[] + { + (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)), + (199, new DateTime(2022, 09, 15, 11, 27, 18, DateTimeKind.Utc), new DateTime(2022, 09, 20, 14, 00, 23, DateTimeKind.Utc)), + (6 , new DateTime(2021, 09, 20, 00, 35, 03, DateTimeKind.Utc), new DateTime(2021, 09, 25, 06, 46, 17, DateTimeKind.Utc)), + (41 , new DateTime(2021, 12, 10, 00, 59, 52, DateTimeKind.Utc), new DateTime(2022, 10, 31, 15, 29, 24, DateTimeKind.Utc)), + (100, new DateTime(2022, 04, 24, 03, 04, 05, DateTimeKind.Utc), new DateTime(2022, 04, 29, 11, 38, 36, DateTimeKind.Utc)), + (154, new DateTime(2022, 03, 28, 10, 09, 14, DateTimeKind.Utc), new DateTime(2022, 06, 14, 15, 01, 12, DateTimeKind.Utc)), + (5 , new DateTime(2021, 09, 25, 08, 09, 37, DateTimeKind.Utc), new DateTime(2021, 10, 01, 14, 39, 51, DateTimeKind.Utc)), + (1 , new DateTime(2021, 09, 10, 01, 32, 42, DateTimeKind.Utc), new DateTime(2021, 09, 18, 00, 35, 22, 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() { @@ -20,66 +55,53 @@ namespace AsbCloudWebApi.Tests.Middlware }) .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 - // tw.id, - // t_stat.minDate, - // t_stat.maxDate - //from( - // select - - // id_telemetry, - // count(1) as count, - // min("date") as minDate, - // max("date") as maxDate - - // from t_telemetry_data_saub - - // group by id_telemetry - //) as t_stat - //join t_well tw on tw.id_telemetry = t_stat.id_telemetry - //where tw is not null - //order by t_stat.count - //limit 10; - - var wells = new [] - { - (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)), - (199, new DateTime(2022, 09, 15, 11, 27, 18, DateTimeKind.Utc), new DateTime(2022, 09, 20, 14, 00, 23, DateTimeKind.Utc)), - (6 , new DateTime(2021, 09, 20, 00, 35, 03, DateTimeKind.Utc), new DateTime(2021, 09, 25, 06, 46, 17, DateTimeKind.Utc)), - (41 , new DateTime(2021, 12, 10, 00, 59, 52, DateTimeKind.Utc), new DateTime(2022, 10, 31, 15, 29, 24, DateTimeKind.Utc)), - (100, new DateTime(2022, 04, 24, 03, 04, 05, DateTimeKind.Utc), new DateTime(2022, 04, 29, 11, 38, 36, DateTimeKind.Utc)), - (154, new DateTime(2022, 03, 28, 10, 09, 14, DateTimeKind.Utc), new DateTime(2022, 06, 14, 15, 01, 12, DateTimeKind.Utc)), - (5 , new DateTime(2021, 09, 25, 08, 09, 37, DateTimeKind.Utc), new DateTime(2021, 10, 01, 14, 39, 51, DateTimeKind.Utc)), - (1 , new DateTime(2021, 09, 10, 01, 32, 42, DateTimeKind.Utc), new DateTime(2021, 09, 18, 00, 35, 22, DateTimeKind.Utc)), - (112, new DateTime(2022, 04, 20, 16, 47, 51, DateTimeKind.Utc), new DateTime(2022, 04, 28, 15, 04, 33, DateTimeKind.Utc)), - }; - var i = 0; - for (; i < 5; i++) + for (; i < iterations2Block; i++) _ = Task.Run(async () => { var well = wells[i]; 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 Task.Delay(1000); }); var well = wells[i]; 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); } + + [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) { 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}"; return url; } + + private static HttpClient MakeHttpClient() + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Authorization = new("Bearer", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGV2IiwiaWRDb21wYW55IjoiMSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJuYmYiOjE2NjY1ODY2MjAsImV4cCI6MTY5ODE0NDIyMCwiaXNzIjoiYSIsImF1ZCI6ImEifQ.zqBdR4nYB87-Xyzv025waasN47i43c9FJ23RfzIvUsM"); + return httpClient; + } } + } diff --git a/AsbCloudWebApi/Middlewares/UserConnectionsLimitMiddlware.cs b/AsbCloudWebApi/Middlewares/UserConnectionsLimitMiddlware.cs index 96b036e2..c1b108f8 100644 --- a/AsbCloudWebApi/Middlewares/UserConnectionsLimitMiddlware.cs +++ b/AsbCloudWebApi/Middlewares/UserConnectionsLimitMiddlware.cs @@ -14,25 +14,26 @@ namespace AsbCloudWebApi.Middlewares /// class UserConnectionsLimitMiddlware { - private readonly RequestDelegate next; private readonly int parallelRequestsToController; - private readonly byte[] body; + private readonly RequestDelegate next; + private readonly byte[] responseBody; private readonly ConcurrentDictionary> stat = new (); private readonly IEnumerable? controllerNames; public UserConnectionsLimitMiddlware(RequestDelegate next, IConfiguration configuration) { + const int parallelRequestsToControllerDefault = 8; this.next = next; - var parallelRequestsToController = configuration.GetSection("userLimits")?.GetValue("parallelRequestsToController") ?? 5; + var parallelRequestsToController = configuration.GetSection("userLimits")?.GetValue("parallelRequestsToController") ?? parallelRequestsToControllerDefault; this.parallelRequestsToController = parallelRequestsToController > 0 ? parallelRequestsToController - : 5; + : parallelRequestsToControllerDefault; controllerNames = configuration.GetSection("userLimits")?.GetValue>("controllerNames"); var bodyText = $"Too Many Requests

Too Many Requests

I only allow {parallelRequestsToController} parallel requests per user. Try again soon.

"; - body = System.Text.Encoding.UTF8.GetBytes(bodyText); + responseBody = System.Text.Encoding.UTF8.GetBytes(bodyText); } public async Task InvokeAsync(HttpContext context, int idUser, string controllerName) @@ -41,14 +42,16 @@ namespace AsbCloudWebApi.Middlewares { await next(context); return; - } + } var userStat = stat.GetOrAdd(idUser, idUser => new()); - var count = userStat.AddOrUpdate(controllerName, 1, (key, value) => value + 1); - if(count < parallelRequestsToController) + var count = userStat.AddOrUpdate(controllerName, 0, (k, v) => v); + + if(count + 1 < parallelRequestsToController) { try { + userStat[controllerName]++; await next(context); } finally @@ -60,10 +63,9 @@ namespace AsbCloudWebApi.Middlewares { 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(body); + await context.Response.BodyWriter.WriteAsync(responseBody); } } }