public async Task <RateLimitingValidationResponse> ValidateContext(RateLimitingValidationContext rateLimitingValidationContext) { var rateLimitingCounter = new RateLimitingCounter { Timestamp = DateTime.UtcNow, Count = 1 }; var expirationTime = rateLimitingValidationContext.Period.ToTimeSpan(); var counterKey = await _rateLimitingCounterKeyBuilder.BuildCounterKey(rateLimitingValidationContext); var rateLimitingCounterEntry = await _rateLimitingCounterStore.GetCounter(counterKey); if (rateLimitingCounterEntry != null) { rateLimitingCounter = rateLimitingCounterEntry; rateLimitingCounter.Count += 1; var expirationDateTime = rateLimitingCounterEntry.Timestamp + rateLimitingValidationContext.Period.ToTimeSpan(); expirationTime = expirationDateTime - DateTime.UtcNow; } await _rateLimitingCounterStore.SetCounter(counterKey, rateLimitingCounterEntry, expirationTime); var rateLimitingValidationResponse = new RateLimitingValidationResponse { RateLimitingValidationContext = rateLimitingValidationContext, RateLimitingCounter = rateLimitingCounter }; return(rateLimitingValidationResponse); }
public Task <string> BuildCounterKey(RateLimitingValidationContext rateLimitingValidationContext) { var counterKeyTemplate = $"{rateLimitingValidationContext.TrafficInitiatorId}_{rateLimitingValidationContext.Path}" + $"{rateLimitingValidationContext.Method}_{rateLimitingValidationContext.Limit}" + $"{rateLimitingValidationContext.Period}"; var counterKeyBytes = Encoding.UTF8.GetBytes(counterKeyTemplate); using var hashingAlgorithm = SHA1.Create(); var counterKeyHashBytes = hashingAlgorithm.ComputeHash(counterKeyBytes); var counterKey = BitConverter.ToString(counterKeyHashBytes) .Replace("-", string.Empty); return(Task.FromResult(counterKey)); }
public async Task Invoke(ITrafficContext trafficContext, BrolicTrafficDelegate next) { var trafficInitiatorId = await _trafficInitiatorIdentifier.IdentifyTrafficInitiator(trafficContext); var httpContext = trafficContext.HttpContext; var httpMethod = httpContext.Request.Method; var rateLimitingRouteOptions = trafficContext.Route.GetRateLimitingRouteOptions(); var applicableRateLimitingRules = rateLimitingRouteOptions.Rules .Where(r => !r.Methods.Any() || r.Methods.Contains(httpMethod)); var rateLimitingValidationResponses = new List <RateLimitingValidationResponse>(); foreach (var applicableRateLimitingRule in applicableRateLimitingRules) { var path = httpContext.Request.Path; var parsedMethod = !applicableRateLimitingRule.Methods.Any() ? "Any" : applicableRateLimitingRule.Methods.Aggregate(string.Empty, (current, method) => { current += method; return(current); }); var rateLimitingValidationContext = new RateLimitingValidationContext { TrafficInitiatorId = trafficInitiatorId, Path = path, Method = parsedMethod, Limit = applicableRateLimitingRule.Limit, Period = applicableRateLimitingRule.Period }; var rateLimitingValidationResponse = await _rateLimitingValidationStrategy.ValidateContext(rateLimitingValidationContext); rateLimitingValidationResponses.Add(rateLimitingValidationResponse); } if (rateLimitingValidationResponses.Any(r => !r.IsValid)) { httpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; return; } await next(trafficContext); }