Browse Source

Improved 429 handling

pull/108/head
RogueException 9 years ago
parent
commit
741f10d9b1
3 changed files with 30 additions and 22 deletions
  1. +6
    -6
      src/Discord.Net/Net/Queue/RequestQueueBucket.cs
  2. +3
    -2
      src/Discord.Net/Net/RateLimitException.cs
  3. +21
    -14
      src/Discord.Net/Net/Rest/DefaultRestClient.cs

+ 6
- 6
src/Discord.Net/Net/Queue/RequestQueueBucket.cs View File

@@ -47,7 +47,7 @@ namespace Discord.Net.Queue
RequestQueueBucket bucket; RequestQueueBucket bucket;
bool success = FindBucket(ex.BucketId, out bucket); bool success = FindBucket(ex.BucketId, out bucket);


await _queue.RaiseRateLimitTriggered(ex.BucketId, success ? bucket.Definition : (Bucket)null, ex.RetryAfterMilliseconds).ConfigureAwait(false);
await _queue.RaiseRateLimitTriggered(ex.BucketId, success ? bucket.Definition : null, ex.RetryAfterMilliseconds).ConfigureAwait(false);


bucket.Pause(ex.RetryAfterMilliseconds); bucket.Pause(ex.RetryAfterMilliseconds);
} }
@@ -67,6 +67,7 @@ namespace Discord.Net.Queue
//Get our 429 state //Get our 429 state
Task notifier; Task notifier;
int resumeTime; int resumeTime;
lock (_pauseLock) lock (_pauseLock)
{ {
notifier = _resumeNotifier.Task; notifier = _resumeNotifier.Task;
@@ -133,14 +134,14 @@ namespace Discord.Net.Queue
{ {
_resumeNotifier = new TaskCompletionSource<byte>(); _resumeNotifier = new TaskCompletionSource<byte>();
_pauseEndTick = unchecked(Environment.TickCount + milliseconds); _pauseEndTick = unchecked(Environment.TickCount + milliseconds);
QueueResumeAsync(milliseconds);
QueueResumeAsync(_resumeNotifier, milliseconds);
} }
} }
} }
private async Task QueueResumeAsync(int millis)
private async Task QueueResumeAsync(TaskCompletionSource<byte> resumeNotifier, int millis)
{ {
await Task.Delay(millis).ConfigureAwait(false); await Task.Delay(millis).ConfigureAwait(false);
_resumeNotifier.SetResult(0);
resumeNotifier.SetResult(0);
} }


private async Task EnterAsync(int? endTick) private async Task EnterAsync(int? endTick)
@@ -151,8 +152,7 @@ namespace Discord.Net.Queue
if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false)) if (millis <= 0 || !await _semaphore.WaitAsync(millis).ConfigureAwait(false))
throw new TimeoutException(); throw new TimeoutException();
} }
else
await _semaphore.WaitAsync().ConfigureAwait(false);
await _semaphore.WaitAsync().ConfigureAwait(false);
} }
private async Task QueueExitAsync() private async Task QueueExitAsync()
{ {


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

@@ -7,9 +7,10 @@ namespace Discord.Net
public string BucketId { get; } public string BucketId { get; }
public int RetryAfterMilliseconds { get; } public int RetryAfterMilliseconds { get; }


public HttpRateLimitException(int retryAfterMilliseconds)
: base((HttpStatusCode)429)
public HttpRateLimitException(string bucketId, int retryAfterMilliseconds, string reason)
: base((HttpStatusCode)429, reason)
{ {
BucketId = bucketId;
RetryAfterMilliseconds = retryAfterMilliseconds; RetryAfterMilliseconds = retryAfterMilliseconds;
} }
} }


+ 21
- 14
src/Discord.Net/Net/Rest/DefaultRestClient.cs View File

@@ -124,25 +124,32 @@ namespace Discord.Net.Rest
int statusCode = (int)response.StatusCode; int statusCode = (int)response.StatusCode;
if (statusCode < 200 || statusCode >= 300) //2xx = Success if (statusCode < 200 || statusCode >= 300) //2xx = Success
{ {
if (statusCode == 429)
{
//TODO: Include bucket info
int retryAfterMillis = int.Parse(response.Headers.GetValues("retry-after").First());
throw new HttpRateLimitException(retryAfterMillis);
}

string reason = null; string reason = null;
try
JToken content = null;
if (response.Content.Headers.GetValues("content-type").FirstOrDefault() == "application/json")
{ {
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var reader = new StreamReader(stream))
using (var json = new JsonTextReader(reader))
try
{ {
reason = (_errorDeserializer.Deserialize(json) as JToken).Value<string>("message");
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var reader = new StreamReader(stream))
using (var json = new JsonTextReader(reader))
{
content = _errorDeserializer.Deserialize<JToken>(json);
reason = content.Value<string>("message");
}
} }
catch { } //Might have been HTML Should we check for content-type?
}

if (statusCode == 429 && content != null)
{
//TODO: Include bucket info
string bucketId = content.Value<string>("bucket");
int retryAfterMillis = content.Value<int>("retry_after");
throw new HttpRateLimitException(bucketId, retryAfterMillis, reason);
} }
catch { } //Might have been HTML
throw new HttpException(response.StatusCode, reason);
else
throw new HttpException(response.StatusCode, reason);
} }


if (headerOnly) if (headerOnly)


Loading…
Cancel
Save