using System; using System.Collections.Concurrent; using System.Linq; using System.Net; using System.Threading.Tasks; namespace EmbedIO.Security { /// /// Represents a maximun requests per second criterion for . /// /// public class IPBanningRequestsCriterion : IIPBanningCriterion { /// /// The default maximum request per second. /// public const int DefaultMaxRequestsPerSecond = 50; private static readonly ConcurrentDictionary> Requests = new ConcurrentDictionary>(); private readonly int _maxRequestsPerSecond; private bool _disposed; internal IPBanningRequestsCriterion(int maxRequestsPerSecond) { _maxRequestsPerSecond = maxRequestsPerSecond; } /// /// Finalizes an instance of the class. /// ~IPBanningRequestsCriterion() { Dispose(false); } /// public Task ValidateIPAddress(IPAddress address) { Requests.GetOrAdd(address, new ConcurrentBag()).Add(DateTime.Now.Ticks); var lastSecond = DateTime.Now.AddSeconds(-1).Ticks; var lastMinute = DateTime.Now.AddMinutes(-1).Ticks; var shouldBan = Requests.TryGetValue(address, out var attempts) && (attempts.Count(x => x >= lastSecond) >= _maxRequestsPerSecond || (attempts.Count(x => x >= lastMinute) / 60) >= _maxRequestsPerSecond); return Task.FromResult(shouldBan); } /// public void ClearIPAddress(IPAddress address) => Requests.TryRemove(address, out _); /// public void PurgeData() { var minTime = DateTime.Now.AddMinutes(-1).Ticks; foreach (var k in Requests.Keys) { if (!Requests.TryGetValue(k, out var requests)) continue; var recentRequests = new ConcurrentBag(requests.Where(x => x >= minTime)); if (!recentRequests.Any()) Requests.TryRemove(k, out _); else Requests.AddOrUpdate(k, recentRequests, (x, y) => recentRequests); } } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { Requests.Clear(); } _disposed = true; } } }