From c371218672c006489ea629be68cf608ebb0a4a41 Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Wed, 2 Feb 2022 23:25:03 -0500 Subject: [PATCH 1/2] prevent saturation of the user retrieval API --- .../Statistics/TwitterStatisticsHandler.cs | 9 ++++++++- src/BirdsiteLive.Twitter/TwitterUserService.cs | 9 ++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/BirdsiteLive.Twitter/Statistics/TwitterStatisticsHandler.cs b/src/BirdsiteLive.Twitter/Statistics/TwitterStatisticsHandler.cs index 668be76..8063985 100644 --- a/src/BirdsiteLive.Twitter/Statistics/TwitterStatisticsHandler.cs +++ b/src/BirdsiteLive.Twitter/Statistics/TwitterStatisticsHandler.cs @@ -13,6 +13,8 @@ namespace BirdsiteLive.Statistics.Domain void CalledTweetApi(); void CalledTimelineApi(); ApiStatistics GetStatistics(); + + int GetCurrentUserCalls(); } //Rate limits: https://developer.twitter.com/en/docs/twitter-api/v1/rate-limits @@ -60,7 +62,12 @@ namespace BirdsiteLive.Statistics.Domain foreach (var old in oldSnapshots) _snapshots.TryRemove(old, out var data); } - public void CalledUserApi() //GET users/show - 900/15mins + public int GetCurrentUserCalls() + { + return _userCalls; + } + + public void CalledUserApi() //GET users/show - 300/15mins { Interlocked.Increment(ref _userCalls); } diff --git a/src/BirdsiteLive.Twitter/TwitterUserService.cs b/src/BirdsiteLive.Twitter/TwitterUserService.cs index 6a27dc1..df6e5ad 100644 --- a/src/BirdsiteLive.Twitter/TwitterUserService.cs +++ b/src/BirdsiteLive.Twitter/TwitterUserService.cs @@ -32,6 +32,12 @@ namespace BirdsiteLive.Twitter public TwitterUser GetUser(string username) { + //Check if API is saturated + var currentCalls = _statisticsHandler.GetCurrentUserCalls(); + var maxCalls = _statisticsHandler.GetStatistics().UserCallsMax; + if (currentCalls > maxCalls) return null; + + //Proceed to account retrieval _twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized(); ExceptionHandler.SwallowWebExceptions = false; @@ -49,9 +55,6 @@ namespace BirdsiteLive.Twitter catch (Exception e) { _logger.LogError(e, "Error retrieving user {Username}", username); - - // TODO keep track of error, see where to remove user if too much errors - return null; } From bf7baba789f592770532ca2dbd3b58877ffb1cb3 Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Thu, 3 Feb 2022 01:06:32 -0500 Subject: [PATCH 2/2] added proper return on TooManyRequest case --- src/BirdsiteLive/Controllers/UsersController.cs | 15 +++++++++++---- src/BirdsiteLive/Views/Users/ApiSaturated.cshtml | 13 +++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 src/BirdsiteLive/Views/Users/ApiSaturated.cshtml diff --git a/src/BirdsiteLive/Controllers/UsersController.cs b/src/BirdsiteLive/Controllers/UsersController.cs index 73be8b0..aa4f272 100644 --- a/src/BirdsiteLive/Controllers/UsersController.cs +++ b/src/BirdsiteLive/Controllers/UsersController.cs @@ -13,6 +13,7 @@ using BirdsiteLive.Common.Regexes; using BirdsiteLive.Common.Settings; using BirdsiteLive.Domain; using BirdsiteLive.Models; +using BirdsiteLive.Statistics.Domain; using BirdsiteLive.Tools; using BirdsiteLive.Twitter; using BirdsiteLive.Twitter.Models; @@ -32,9 +33,10 @@ namespace BirdsiteLive.Controllers private readonly IStatusService _statusService; private readonly InstanceSettings _instanceSettings; private readonly ILogger _logger; + private readonly ITwitterStatisticsHandler _twitterStatisticsHandler; #region Ctor - public UsersController(ITwitterUserService twitterUserService, IUserService userService, IStatusService statusService, InstanceSettings instanceSettings, ITwitterTweetsService twitterTweetService, ILogger logger) + public UsersController(ITwitterUserService twitterUserService, IUserService userService, IStatusService statusService, InstanceSettings instanceSettings, ITwitterTweetsService twitterTweetService, ILogger logger, ITwitterStatisticsHandler twitterStatisticsHandler) { _twitterUserService = twitterUserService; _userService = userService; @@ -42,6 +44,7 @@ namespace BirdsiteLive.Controllers _instanceSettings = instanceSettings; _twitterTweetService = twitterTweetService; _logger = logger; + _twitterStatisticsHandler = twitterStatisticsHandler; } #endregion @@ -72,12 +75,17 @@ namespace BirdsiteLive.Controllers if (!string.IsNullOrWhiteSpace(id) && UserRegexes.TwitterAccount.IsMatch(id) && id.Length <= 15) user = _twitterUserService.GetUser(id); + var isSaturated = user == null + && _twitterStatisticsHandler.GetCurrentUserCalls() >= + _twitterStatisticsHandler.GetStatistics().UserCallsMax; + var acceptHeaders = Request.Headers["Accept"]; if (acceptHeaders.Any()) { var r = acceptHeaders.First(); if (r.Contains("application/activity+json")) { + if (user == null && isSaturated) return new ObjectResult("Too Many Requests") { StatusCode = 429 }; if (user == null) return NotFound(); var apUser = _userService.GetUser(user); var jsonApUser = JsonConvert.SerializeObject(apUser); @@ -85,8 +93,9 @@ namespace BirdsiteLive.Controllers } } + if (user == null && isSaturated) return View("ApiSaturated"); if (user == null) return View("UserNotFound"); - + var displayableUser = new DisplayTwitterUser { Name = user.Name, @@ -190,7 +199,5 @@ namespace BirdsiteLive.Controllers var jsonApUser = JsonConvert.SerializeObject(followers); return Content(jsonApUser, "application/activity+json; charset=utf-8"); } - - } } \ No newline at end of file diff --git a/src/BirdsiteLive/Views/Users/ApiSaturated.cshtml b/src/BirdsiteLive/Views/Users/ApiSaturated.cshtml new file mode 100644 index 0000000..1f807aa --- /dev/null +++ b/src/BirdsiteLive/Views/Users/ApiSaturated.cshtml @@ -0,0 +1,13 @@ +@using BirdsiteLive.Controllers; +@{ + ViewData["Title"] = "Api Saturated"; +} + +
+

429 Too Many Requests

+

+
+ The API is saturated.
+ Please consider using another instance. +

+
\ No newline at end of file