/// <summary>
        /// Gets the request identity key.
        /// </summary>
        /// <returns>The request identity key.</returns>
        /// <param name="requestIdentity">Request identity.</param>
        private string GetRequestIdentityKey(RequestIdentity requestIdentity, CounterRule rule)
        {
            var key = $"{requestIdentity.HttpVerb}:{requestIdentity.Path}:{requestIdentity.ClientId}:{rule.Period}";

            var idBytes = System.Text.Encoding.UTF8.GetBytes(key);

            byte[] hashBytes;

            using (var algorithm = System.Security.Cryptography.SHA1.Create())
            {
                hashBytes = algorithm.ComputeHash(idBytes);
            }

            return(BitConverter.ToString(hashBytes).Replace("-", string.Empty));
        }
        /// <summary>
        /// Handles the rate limit async.
        /// </summary>
        /// <returns>The rate limit async.</returns>
        /// <param name="httpContext">Http context.</param>
        /// <param name="requestIdentity">Request identity.</param>
        private Task <bool> HandleRateLimitAsync(HttpContext httpContext, RequestIdentity requestIdentity)
        {
            var limitRules = _options.ClientRules.Where(x => x.ClientId == requestIdentity.ClientId).SelectMany(x => x.CounterRules);

            var limitRule = limitRules.FirstOrDefault(x => x.EndPoint.Equals(requestIdentity.Path));

            if (limitRule != null)
            {
                // increment counter
                var counter = _rateLimiter.Process(requestIdentity, limitRule);

                // check if limit is reached
                if (counter.TotalRequests > limitRule.LimitCount)
                {
                    //will be blocked
                    _logger.LogInformation($"Request {requestIdentity.ClientId},{requestIdentity.HttpVerb},{requestIdentity.Path} has been blocked!");
                    return(Task.FromResult(true));
                }
            }

            return(Task.FromResult(false));
        }
        public RateLimitCounter Process(RequestIdentity requestIdentity, CounterRule rule)
        {
            var counter = new RateLimitCounter
            {
                Timestamp     = DateTime.UtcNow,
                TotalRequests = 1
            };

            var counterId = GetRequestIdentityKey(requestIdentity, rule);

            // serial reads and writes
            lock (_processLocker)
            {
                var entry = _store.Get(counterId);
                if (entry.HasValue)
                {
                    // entry has not expired
                    if (entry.Value.Timestamp.AddSeconds(rule.Period) >= DateTime.UtcNow)
                    {
                        // increment request count
                        var totalRequests = entry.Value.TotalRequests + 1;

                        // deep copy
                        counter = new RateLimitCounter
                        {
                            Timestamp     = entry.Value.Timestamp,
                            TotalRequests = totalRequests
                        };
                    }
                }

                // stores: id (string) - timestamp (datetime) - total_requests (long)
                _store.Set(counterId, counter, TimeSpan.FromSeconds(rule.Period));
            }

            return(counter);
        }