Improved 429 handling
This commit is contained in:
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
public HttpRateLimitException(string bucketId, int retryAfterMilliseconds, string reason)
|
||||||
: base((HttpStatusCode)429)
|
: base((HttpStatusCode)429, reason)
|
||||||
{
|
{
|
||||||
|
BucketId = bucketId;
|
||||||
RetryAfterMilliseconds = retryAfterMilliseconds;
|
RetryAfterMilliseconds = retryAfterMilliseconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
string reason = null;
|
||||||
|
JToken content = null;
|
||||||
|
if (response.Content.Headers.GetValues("content-type").FirstOrDefault() == "application/json")
|
||||||
{
|
{
|
||||||
//TODO: Include bucket info
|
try
|
||||||
int retryAfterMillis = int.Parse(response.Headers.GetValues("retry-after").First());
|
{
|
||||||
throw new HttpRateLimitException(retryAfterMillis);
|
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?
|
||||||
}
|
}
|
||||||
|
|
||||||
string reason = null;
|
if (statusCode == 429 && content != null)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
//TODO: Include bucket info
|
||||||
using (var reader = new StreamReader(stream))
|
string bucketId = content.Value<string>("bucket");
|
||||||
using (var json = new JsonTextReader(reader))
|
int retryAfterMillis = content.Value<int>("retry_after");
|
||||||
{
|
throw new HttpRateLimitException(bucketId, retryAfterMillis, reason);
|
||||||
reason = (_errorDeserializer.Deserialize(json) as JToken).Value<string>("message");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch { } //Might have been HTML
|
else
|
||||||
throw new HttpException(response.StatusCode, reason);
|
throw new HttpException(response.StatusCode, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (headerOnly)
|
if (headerOnly)
|
||||||
|
|||||||
Reference in New Issue
Block a user