private bool IsRequestForbidden(HttpContext context) { // lock to synchronise the multiple calls on web apis. lock (_syncLock) { // read api limit rules from config or by default limit rules var apiLimitRuleDetails = GetApiLimitRuleDetails(context); // throttle cache is present means service threshold is reached, need to suspend the request. if (_cache.Get(GetThrottleBaseKey(apiLimitRuleDetails.EndPointKey)) != null) { return(true); } if (!_cache.TryGetValue(apiLimitRuleDetails.EndPointKey, out CacheSetting serviceHitCounter)) { // create service hit counter with 1 CreateOrUpdateCache(apiLimitRuleDetails.EndPointKey, _cacheSettingProvider.CreateCacheSetting(apiLimitRuleDetails.Period)); return(false); } // as long as threshold is not reached just increment the counter and update the service cache counter if (serviceHitCounter.Value < apiLimitRuleDetails.Limit) { serviceHitCounter.Value++; CreateOrUpdateCache(apiLimitRuleDetails.EndPointKey, serviceHitCounter); return(false); } // api limit threshold is reached, need to add throttle cache in the memory to suspend subsequent calls to api for particular duration CreateOrUpdateCache(GetThrottleBaseKey(apiLimitRuleDetails.EndPointKey), _cacheSettingProvider.CreateCacheSetting(apiLimitRuleDetails.SuspendPeriod)); return(true); } }
public async Task Invoke(HttpContext context) { if (IsRateLimitRequestRequired()) { _logger.LogWarning(LoggingMessages.Forbidden + context.User); // waiting for 3 seconds to serve the request. await semaphoreSlim.WaitAsync(); try { await Task.Delay(3000); } finally { semaphoreSlim.Release(); } // reset to one as we wait for 3 seconds. CreateOrUpdateCache("RequestCount", _cacheSettingProvider.CreateCacheSetting(int.MaxValue)); } await _next.Invoke(context); }
public async Task <IEnumerable <ProductViewModel> > SearchFoodProducts(ProductSearchInput input) { string url = _httpQueryBuilder.Build(input); var request = new HttpRequestMessage(HttpMethod.Get, url); // create http client for downloading the food products var client = _clientFactory.CreateClient(); // timeout 20 seconds client.Timeout = TimeSpan.FromSeconds(20); try { var response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { return(Enumerable.Empty <ProductViewModel>()); } var responseStream = await response.Content.ReadAsStringAsync(); var productDetails = JsonSerializer.Deserialize <FoodProductModel>(responseStream); // validate if the product details is null or 0 or no products if (!IsValid(productDetails)) { return(Enumerable.Empty <ProductViewModel>()); } if (!_cache.TryGetValue("PaginationCount", out CacheSetting paginationCounter)) { // create service hit pagination counter with 1 CreateOrUpdateCache("PaginationCount", _cacheSettingProvider.CreateCacheSetting(int.MaxValue, 0)); paginationCounter = _cache.Get <CacheSetting>("PaginationCount"); } // this condition to reset the counter as we reached the end of food products. if (productDetails.Products.Length < paginationCounter.Value) { paginationCounter.Value = 0; } // skipping the already served foo products & taking only 5 var foodToReturn = productDetails.Products.Skip(paginationCounter.Value).Take(5); // increment the pagination counter by 5 paginationCounter.Value += 5; // updating the counter value CreateOrUpdateCache("PaginationCount", paginationCounter); return(_mapper.Map <List <ProductViewModel> >(foodToReturn)); } catch (Exception ex) { //todo third party api is taking too much time. _logger.LogError(ex.Message, request); } return(Enumerable.Empty <ProductViewModel>()); }