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"));
}
}
}