using CryptoExchange.Net.Objects; using CryptoExchange.Net.RateLimiting.Interfaces; using CryptoExchange.Net.RateLimiting.Trackers; using System; using System.Collections.Generic; using System.Security; namespace CryptoExchange.Net.RateLimiting.Guards { /// /// Rate limit guard for a per endpoint limit /// public class SingleLimitGuard : IRateLimitGuard { /// /// Default endpoint limit /// public static Func Default { get; } = new Func((def, host, key) => def.Path + def.Method); /// /// Endpoint limit per API key /// public static Func PerApiKey { get; } = new Func((def, host, key) => def.Path + def.Method); private readonly Dictionary _trackers; private readonly RateLimitWindowType _windowType; private readonly double? _decayRate; private readonly int _limit; private readonly TimeSpan _period; private readonly Func _keySelector; /// public string Name => "EndpointLimitGuard"; /// public string Description => $"Limit requests to endpoint"; /// /// ctor /// public SingleLimitGuard( int limit, TimeSpan period, RateLimitWindowType windowType, double? decayRate = null, Func? keySelector = null) { _limit = limit; _period = period; _windowType = windowType; _decayRate = decayRate; _keySelector = keySelector ?? Default; _trackers = new Dictionary(); } /// public LimitCheck Check(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight) { var key = _keySelector(definition, host, apiKey); if (!_trackers.TryGetValue(key, out var tracker)) { tracker = CreateTracker(); _trackers.Add(key, tracker); } var delay = tracker.GetWaitTime(requestWeight); if (delay == default) return LimitCheck.NotNeeded; return LimitCheck.Needed(delay, _limit, _period, tracker.Current); } /// public RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight) { var key = _keySelector(definition, host, apiKey); var tracker = _trackers[key]; tracker.ApplyWeight(requestWeight); return RateLimitState.Applied(_limit, _period, tracker.Current); } /// /// Create a new WindowTracker /// /// protected IWindowTracker CreateTracker() { return _windowType == RateLimitWindowType.Sliding ? new SlidingWindowTracker(_limit, _period) : _windowType == RateLimitWindowType.Fixed ? new FixedWindowTracker(_limit, _period) : new DecayWindowTracker(_limit, _period, _decayRate ?? throw new InvalidOperationException("Decay rate not provided")); } } }