@@ -5,14 +5,36 @@ namespace Discord.Net | |||||
{ | { | ||||
public class HttpException : Exception | public class HttpException : Exception | ||||
{ | { | ||||
public HttpStatusCode StatusCode { get; } | |||||
public HttpStatusCode HttpCode { get; } | |||||
public int? DiscordCode { get; } | |||||
public string Reason { get; } | public string Reason { get; } | ||||
public HttpException(HttpStatusCode statusCode, string reason = null) | |||||
: base($"The server responded with error {(int)statusCode} ({statusCode}){(reason != null ? $": \"{reason}\"" : "")}") | |||||
public HttpException(HttpStatusCode httpCode, int? discordCode = null, string reason = null) | |||||
: base(CreateMessage(httpCode, discordCode, reason)) | |||||
{ | { | ||||
StatusCode = statusCode; | |||||
HttpCode = httpCode; | |||||
DiscordCode = discordCode; | |||||
Reason = reason; | Reason = reason; | ||||
} | } | ||||
private static string CreateMessage(HttpStatusCode httpCode, int? discordCode = null, string reason = null) | |||||
{ | |||||
string msg; | |||||
if (discordCode != null) | |||||
{ | |||||
if (reason != null) | |||||
msg = $"The server responded with error {(int)discordCode}: {reason}"; | |||||
else | |||||
msg = $"The server responded with error {(int)discordCode}: {httpCode}"; | |||||
} | |||||
else | |||||
{ | |||||
if (reason != null) | |||||
msg = $"The server responded with error {(int)httpCode}: {reason}"; | |||||
else | |||||
msg = $"The server responded with error {(int)httpCode}: {httpCode}"; | |||||
} | |||||
return msg; | |||||
} | |||||
} | } | ||||
} | } |
@@ -91,9 +91,9 @@ namespace Discord.Net.Queue | |||||
await Task.Delay(millis).ConfigureAwait(false); | await Task.Delay(millis).ConfigureAwait(false); | ||||
} | } | ||||
} | } | ||||
internal void PauseGlobal(RateLimitInfo info, TimeSpan lag) | |||||
internal void PauseGlobal(RateLimitInfo info) | |||||
{ | { | ||||
_waitUntil = DateTimeOffset.UtcNow.AddMilliseconds(info.RetryAfter.Value + lag.TotalMilliseconds); | |||||
_waitUntil = DateTimeOffset.UtcNow.AddMilliseconds(info.RetryAfter.Value + (info.Lag?.TotalMilliseconds ?? 0.0)); | |||||
} | } | ||||
private RequestBucket GetOrCreateBucket(string id, RestRequest request) | private RequestBucket GetOrCreateBucket(string id, RestRequest request) | ||||
@@ -54,12 +54,10 @@ namespace Discord.Net.Queue | |||||
#if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
Debug.WriteLine($"[{id}] Sending..."); | Debug.WriteLine($"[{id}] Sending..."); | ||||
#endif | #endif | ||||
TimeSpan lag = default(TimeSpan); | |||||
RateLimitInfo info = default(RateLimitInfo); | RateLimitInfo info = default(RateLimitInfo); | ||||
try | try | ||||
{ | { | ||||
var response = await request.SendAsync().ConfigureAwait(false); | var response = await request.SendAsync().ConfigureAwait(false); | ||||
lag = DateTimeOffset.UtcNow - DateTimeOffset.Parse(response.Headers["Date"]); | |||||
info = new RateLimitInfo(response.Headers); | info = new RateLimitInfo(response.Headers); | ||||
if (response.StatusCode < (HttpStatusCode)200 || response.StatusCode >= (HttpStatusCode)300) | if (response.StatusCode < (HttpStatusCode)200 || response.StatusCode >= (HttpStatusCode)300) | ||||
@@ -72,14 +70,14 @@ namespace Discord.Net.Queue | |||||
#if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
Debug.WriteLine($"[{id}] (!) 429 [Global]"); | Debug.WriteLine($"[{id}] (!) 429 [Global]"); | ||||
#endif | #endif | ||||
_queue.PauseGlobal(info, lag); | |||||
_queue.PauseGlobal(info); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
#if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
Debug.WriteLine($"[{id}] (!) 429"); | Debug.WriteLine($"[{id}] (!) 429"); | ||||
#endif | #endif | ||||
UpdateRateLimit(id, request, info, lag, true); | |||||
UpdateRateLimit(id, request, info, true); | |||||
} | } | ||||
await _queue.RaiseRateLimitTriggered(Id, info).ConfigureAwait(false); | await _queue.RaiseRateLimitTriggered(Id, info).ConfigureAwait(false); | ||||
continue; //Retry | continue; //Retry | ||||
@@ -92,6 +90,7 @@ namespace Discord.Net.Queue | |||||
continue; //Retry | continue; //Retry | ||||
default: | default: | ||||
int? code = null; | |||||
string reason = null; | string reason = null; | ||||
if (response.Stream != null) | if (response.Stream != null) | ||||
{ | { | ||||
@@ -101,12 +100,13 @@ namespace Discord.Net.Queue | |||||
using (var jsonReader = new JsonTextReader(reader)) | using (var jsonReader = new JsonTextReader(reader)) | ||||
{ | { | ||||
var json = JToken.Load(jsonReader); | var json = JToken.Load(jsonReader); | ||||
reason = json.Value<string>("message"); | |||||
try { code = json.Value<int>("code"); } catch { }; | |||||
try { reason = json.Value<string>("message"); } catch { }; | |||||
} | } | ||||
} | } | ||||
catch { } | catch { } | ||||
} | } | ||||
throw new HttpException(response.StatusCode, reason); | |||||
throw new HttpException(response.StatusCode, code, reason); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
@@ -142,7 +142,7 @@ namespace Discord.Net.Queue | |||||
}*/ | }*/ | ||||
finally | finally | ||||
{ | { | ||||
UpdateRateLimit(id, request, info, lag, false); | |||||
UpdateRateLimit(id, request, info, false); | |||||
#if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
Debug.WriteLine($"[{id}] Stop"); | Debug.WriteLine($"[{id}] Stop"); | ||||
#endif | #endif | ||||
@@ -214,7 +214,7 @@ namespace Discord.Net.Queue | |||||
} | } | ||||
} | } | ||||
private void UpdateRateLimit(int id, RestRequest request, RateLimitInfo info, TimeSpan lag, bool is429) | |||||
private void UpdateRateLimit(int id, RestRequest request, RateLimitInfo info, bool is429) | |||||
{ | { | ||||
if (WindowCount == 0) | if (WindowCount == 0) | ||||
return; | return; | ||||
@@ -250,10 +250,10 @@ namespace Discord.Net.Queue | |||||
} | } | ||||
else if (info.Reset.HasValue) | else if (info.Reset.HasValue) | ||||
{ | { | ||||
resetTick = info.Reset.Value.AddSeconds(/*1.0 +*/ lag.TotalSeconds); | |||||
resetTick = info.Reset.Value.AddSeconds(info.Lag?.TotalSeconds ?? 1.0); | |||||
int diff = (int)(resetTick.Value - DateTimeOffset.UtcNow).TotalMilliseconds; | int diff = (int)(resetTick.Value - DateTimeOffset.UtcNow).TotalMilliseconds; | ||||
#if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
Debug.WriteLine($"[{id}] X-RateLimit-Reset: {info.Reset.Value.ToUnixTimeSeconds()} ({diff} ms, {lag.TotalMilliseconds} ms lag)"); | |||||
Debug.WriteLine($"[{id}] X-RateLimit-Reset: {info.Reset.Value.ToUnixTimeSeconds()} ({diff} ms, {info.Lag?.TotalMilliseconds} ms lag)"); | |||||
#endif | #endif | ||||
} | } | ||||
else if (request.Options.IsClientBucket && request.Options.BucketId != null) | else if (request.Options.IsClientBucket && request.Options.BucketId != null) | ||||
@@ -10,6 +10,7 @@ namespace Discord.Net | |||||
public int? Remaining { get; } | public int? Remaining { get; } | ||||
public int? RetryAfter { get; } | public int? RetryAfter { get; } | ||||
public DateTimeOffset? Reset { get; } | public DateTimeOffset? Reset { get; } | ||||
public TimeSpan? Lag { get; } | |||||
internal RateLimitInfo(Dictionary<string, string> headers) | internal RateLimitInfo(Dictionary<string, string> headers) | ||||
{ | { | ||||
@@ -17,8 +18,11 @@ namespace Discord.Net | |||||
IsGlobal = headers.TryGetValue("X-RateLimit-Global", out temp) ? bool.Parse(temp) : false; | IsGlobal = headers.TryGetValue("X-RateLimit-Global", out temp) ? bool.Parse(temp) : false; | ||||
Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) ? int.Parse(temp) : (int?)null; | Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) ? int.Parse(temp) : (int?)null; | ||||
Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) ? int.Parse(temp) : (int?)null; | Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) ? int.Parse(temp) : (int?)null; | ||||
Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) ? DateTimeUtils.FromUnixSeconds(int.Parse(temp)) : (DateTimeOffset?)null; | |||||
Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) ? | |||||
DateTimeUtils.FromUnixSeconds(int.Parse(temp)) : (DateTimeOffset?)null; | |||||
RetryAfter = headers.TryGetValue("Retry-After", out temp) ? int.Parse(temp) : (int?)null; | RetryAfter = headers.TryGetValue("Retry-After", out temp) ? int.Parse(temp) : (int?)null; | ||||
Lag = headers.TryGetValue("Date", out temp) ? | |||||
DateTimeOffset.UtcNow - DateTimeOffset.Parse(temp) : (TimeSpan?)null; | |||||
} | } | ||||
} | } | ||||
} | } |