Compare commits

...

4 Commits

Author SHA1 Message Date
  Christopher F 0924f43db0
Merge branch 'dev' into feature/reset-fater 5 years ago
  Christopher Felegy b4ae580d8d
patch: update Reset-After parsing precision 5 years ago
  Christopher Felegy 246d6a61e9
patch: wire new config properties to ApiClient 5 years ago
  Christopher Felegy bda427a963
feature: support X-RateLimit-Reset-After 5 years ago
8 changed files with 55 additions and 5 deletions
Split View
  1. +18
    -0
      src/Discord.Net.Core/DiscordConfig.cs
  2. +12
    -0
      src/Discord.Net.Core/RequestOptions.cs
  3. +5
    -1
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  4. +5
    -1
      src/Discord.Net.Rest/DiscordRestClient.cs
  5. +7
    -0
      src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs
  6. +3
    -0
      src/Discord.Net.Rest/Net/RateLimitInfo.cs
  7. +2
    -1
      src/Discord.Net.WebSocket/BaseSocketClient.cs
  8. +3
    -2
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs

+ 18
- 0
src/Discord.Net.Core/DiscordConfig.cs View File

@@ -152,5 +152,23 @@ namespace Discord
/// The currently set <see cref="RateLimitPrecision"/>.
/// </returns>
public RateLimitPrecision RateLimitPrecision { get; set; } = RateLimitPrecision.Millisecond;

/// <summary>
/// Gets or sets whether or not rate-limits should use the system clock.
/// </summary>
/// <remarks>
/// If set to <c>false</c>, we will use the X-RateLimit-Reset-After header
/// to determine when a rate-limit expires, rather than comparing the
/// X-RateLimit-Reset timestamp to the system time.
///
/// This should only be changed to false if the system is known to have
/// a clock that is out of sync. Relying on the Reset-After header will
/// incur network lag.
///
/// Regardless of this property, we still rely on the system's wall-clock
/// to determine if a bucket is rate-limited; we do not use any monotonic
/// clock. Your system will still need a stable clock.
/// </remarks>
public bool UseSystemClock { get; set; } = true;
}
}

+ 12
- 0
src/Discord.Net.Core/RequestOptions.cs View File

@@ -44,6 +44,18 @@ namespace Discord
/// to all actions.
/// </remarks>
public string AuditLogReason { get; set; }
/// <summary>
/// Gets or sets whether or not this request should use the system
/// clock for rate-limiting. Defaults to <c>true</c>.
/// </summary>
/// <remarks>
/// This property can also be set in <see cref="DiscordConfig">.
///
/// On a per-request basis, the system clock should only be disabled
/// when millisecond precision is especially important, and the
/// hosting system is known to have a desynced clock.
/// </remarks>
public bool? UseSystemClock { get; set; }

internal bool IgnoreState { get; set; }
internal string BucketId { get; set; }


+ 5
- 1
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -46,18 +46,20 @@ namespace Discord.API
internal IRestClient RestClient { get; private set; }
internal ulong? CurrentUserId { get; set; }
public RateLimitPrecision RateLimitPrecision { get; private set; }
internal bool UseSystemClock { get; set; }
internal JsonSerializer Serializer => _serializer;

/// <exception cref="ArgumentException">Unknown OAuth token type.</exception>
public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry,
JsonSerializer serializer = null, RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second)
JsonSerializer serializer = null, RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second, bool useSystemClock = true)
{
_restClientProvider = restClientProvider;
UserAgent = userAgent;
DefaultRetryMode = defaultRetryMode;
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
RateLimitPrecision = rateLimitPrecision;
UseSystemClock = useSystemClock;

RequestQueue = new RequestQueue();
_stateLock = new SemaphoreSlim(1, 1);
@@ -265,6 +267,8 @@ namespace Discord.API
CheckState();
if (request.Options.RetryMode == null)
request.Options.RetryMode = DefaultRetryMode;
if (request.Options.UseSystemClock == null)
request.Options.UseSystemClock = UseSystemClock;

var stopwatch = Stopwatch.StartNew();
var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false);


+ 5
- 1
src/Discord.Net.Rest/DiscordRestClient.cs View File

@@ -28,7 +28,11 @@ namespace Discord.Rest
internal DiscordRestClient(DiscordRestConfig config, API.DiscordRestApiClient api) : base(config, api) { }

private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config)
=> new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent);
=> new API.DiscordRestApiClient(config.RestClientProvider,
DiscordRestConfig.UserAgent,
rateLimitPrecision: config.RateLimitPrecision,
useSystemClock: config.UseSystemClock);

internal override void Dispose(bool disposing)
{
if (disposing)


+ 7
- 0
src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs View File

@@ -247,12 +247,19 @@ namespace Discord.Net.Queue
Debug.WriteLine($"[{id}] Retry-After: {info.RetryAfter.Value} ({info.RetryAfter.Value} ms)");
#endif
}
else if (info.ResetAfter.HasValue && (request.Options.UseSystemClock.HasValue ? !request.Options.UseSystemClock.Value : false))
{
resetTick = DateTimeOffset.Now.Add(info.ResetAfter.Value);
}
else if (info.Reset.HasValue)
{
resetTick = info.Reset.Value.AddSeconds(info.Lag?.TotalSeconds ?? 1.0);

/* millisecond precision makes this unnecessary, retaining in case of regression

if (request.Options.IsReactionBucket)
resetTick = DateTimeOffset.Now.AddMilliseconds(250);
*/

int diff = (int)(resetTick.Value - DateTimeOffset.UtcNow).TotalMilliseconds;
#if DEBUG_LIMITS


+ 3
- 0
src/Discord.Net.Rest/Net/RateLimitInfo.cs View File

@@ -11,6 +11,7 @@ namespace Discord.Net
public int? Remaining { get; }
public int? RetryAfter { get; }
public DateTimeOffset? Reset { get; }
public TimeSpan? ResetAfter { get; }
public TimeSpan? Lag { get; }

internal RateLimitInfo(Dictionary<string, string> headers)
@@ -25,6 +26,8 @@ namespace Discord.Net
double.TryParse(temp, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var reset) ? DateTimeOffset.FromUnixTimeMilliseconds((long)(reset * 1000)) : (DateTimeOffset?)null;
RetryAfter = headers.TryGetValue("Retry-After", out temp) &&
int.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out var retryAfter) ? retryAfter : (int?)null;
ResetAfter = headers.TryGetValue("X-RateLimit-Reset-After", out temp) &&
float.TryParse(temp, out var resetAfter) ? TimeSpan.FromMilliseconds((long)(resetAfter * 1000)) : (TimeSpan?)null;
Lag = headers.TryGetValue("Date", out temp) &&
DateTimeOffset.TryParse(temp, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date) ? DateTimeOffset.UtcNow - date : (TimeSpan?)null;
}


+ 2
- 1
src/Discord.Net.WebSocket/BaseSocketClient.cs View File

@@ -81,7 +81,8 @@ namespace Discord.WebSocket
: base(config, client) => BaseConfig = config;
private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config)
=> new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent,
rateLimitPrecision: config.RateLimitPrecision);
rateLimitPrecision: config.RateLimitPrecision,
useSystemClock: config.UseSystemClock);

/// <summary>
/// Gets a Discord application information for the logged-in user.


+ 3
- 2
src/Discord.Net.WebSocket/DiscordSocketApiClient.cs View File

@@ -39,8 +39,9 @@ namespace Discord.API

public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent,
string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null,
RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second)
: base(restClientProvider, userAgent, defaultRetryMode, serializer, rateLimitPrecision)
RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second,
bool useSystemClock = true)
: base(restClientProvider, userAgent, defaultRetryMode, serializer, rateLimitPrecision, useSystemClock)
{
_gatewayUrl = url;
if (url != null)


Loading…
Cancel
Save