示例#1
0
        public async Task <Stream> SendAsync(IQueuedRequest request)
        {
            while (true)
            {
                try
                {
                    return(await SendAsyncInternal(request).ConfigureAwait(false));
                }
                catch (HttpRateLimitException ex)
                {
                    //When a 429 occurs, we drop all our locks.
                    //This is generally safe though since 429s actually occuring should be very rare.
                    RequestQueueBucket bucket;
                    bool success = FindBucket(ex.BucketId, out bucket);

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

                    bucket.Pause(ex.RetryAfterMilliseconds);
                }
            }
        }
        public async Task <Stream> SendAsync(RestRequest request)
        {
            int id = Interlocked.Increment(ref nextId);

#if DEBUG_LIMITS
            Debug.WriteLine($"[{id}] Start");
#endif
            LastAttemptAt = DateTimeOffset.UtcNow;
            while (true)
            {
                await _queue.EnterGlobalAsync(id, request).ConfigureAwait(false);
                await EnterAsync(id, request).ConfigureAwait(false);

                if (_redirectBucket != null)
                {
                    return(await _redirectBucket.SendAsync(request));
                }

#if DEBUG_LIMITS
                Debug.WriteLine($"[{id}] Sending...");
#endif
                RateLimitInfo info = default(RateLimitInfo);
                try
                {
                    var response = await request.SendAsync().ConfigureAwait(false);

                    info = new RateLimitInfo(response.Headers);

                    if (response.StatusCode < (HttpStatusCode)200 || response.StatusCode >= (HttpStatusCode)300)
                    {
                        switch (response.StatusCode)
                        {
                        case (HttpStatusCode)429:
                            if (info.IsGlobal)
                            {
#if DEBUG_LIMITS
                                Debug.WriteLine($"[{id}] (!) 429 [Global]");
#endif
                                _queue.PauseGlobal(info);
                            }
                            else
                            {
#if DEBUG_LIMITS
                                Debug.WriteLine($"[{id}] (!) 429");
#endif
                                UpdateRateLimit(id, request, info, true);
                            }
                            await _queue.RaiseRateLimitTriggered(Id, info, $"{request.Method} {request.Endpoint}").ConfigureAwait(false);

                            continue;                   //Retry

                        case HttpStatusCode.BadGateway: //502
#if DEBUG_LIMITS
                            Debug.WriteLine($"[{id}] (!) 502");
#endif
                            if ((request.Options.RetryMode & RetryMode.Retry502) == 0)
                            {
                                throw new HttpException(HttpStatusCode.BadGateway, request, null);
                            }

                            continue;     //Retry

                        default:
                            int?   code   = null;
                            string reason = null;
                            if (response.Stream != null)
                            {
                                try
                                {
                                    using (var reader = new StreamReader(response.Stream))
                                        using (var jsonReader = new JsonTextReader(reader))
                                        {
                                            try
                                            {
                                                var json = await JToken.LoadAsync(jsonReader).ConfigureAwait(false);

                                                try { code = json.Value <int>("code"); } catch { }
                                                try { reason = json.Value <string>("message"); } catch { }
                                            }
                                            catch
                                            {
                                                if (response.StatusCode == HttpStatusCode.RequestEntityTooLarge)
                                                {
                                                    code   = 40005;
                                                    reason = "The server responded with error 40005: Request entity too large";
                                                }
                                                else
                                                {
                                                    code = (int)response.StatusCode;
                                                    if (response.Stream is MemoryStream ms)
                                                    {
                                                        reason = Encoding.UTF8.GetString(ms.ToArray());
                                                    }
                                                }
                                            }
                                        }
                                }
                                catch { }
                            }
                            throw new HttpException(response.StatusCode, request, code, reason);
                        }
                    }
                    else
                    {
#if DEBUG_LIMITS
                        Debug.WriteLine($"[{id}] Success");
#endif
                        return(response.Stream);
                    }
                }
                //catch (HttpException) { throw; } //Pass through
                catch (TimeoutException)
                {
#if DEBUG_LIMITS
                    Debug.WriteLine($"[{id}] Timeout");
#endif
                    if ((request.Options.RetryMode & RetryMode.RetryTimeouts) == 0)
                    {
                        throw;
                    }

                    await Task.Delay(500).ConfigureAwait(false);

                    continue; //Retry
                }

                /*catch (Exception)
                 * {
                 #if DEBUG_LIMITS
                 *  Debug.WriteLine($"[{id}] Error");
                 #endif
                 *  if ((request.Options.RetryMode & RetryMode.RetryErrors) == 0)
                 *      throw;
                 *
                 *  await Task.Delay(500);
                 *  continue; //Retry
                 * }*/
                finally
                {
                    UpdateRateLimit(id, request, info, false);
#if DEBUG_LIMITS
                    Debug.WriteLine($"[{id}] Stop");
#endif
                }
            }
        }
        public async Task <Stream> SendAsync(RestRequest request)
        {
            int id = Interlocked.Increment(ref nextId);

#if DEBUG_LIMITS
            Debug.WriteLine($"[{id}] Start");
#endif
            LastAttemptAt = DateTimeOffset.UtcNow;
            while (true)
            {
                await _queue.EnterGlobalAsync(id, request).ConfigureAwait(false);
                await EnterAsync(id, request).ConfigureAwait(false);

                if (_redirectBucket != null)
                {
                    return(await _redirectBucket.SendAsync(request));
                }

#if DEBUG_LIMITS
                Debug.WriteLine($"[{id}] Sending...");
#endif
                RateLimitInfo info = default(RateLimitInfo);
                try
                {
                    var response = await request.SendAsync().ConfigureAwait(false);

                    info = new RateLimitInfo(response.Headers, request.Endpoint);

                    request.Options.ExecuteRatelimitCallback(info);

                    if (response.StatusCode < (HttpStatusCode)200 || response.StatusCode >= (HttpStatusCode)300)
                    {
                        switch (response.StatusCode)
                        {
                        case (HttpStatusCode)429:
                            if (info.IsGlobal)
                            {
#if DEBUG_LIMITS
                                Debug.WriteLine($"[{id}] (!) 429 [Global]");
#endif
                                _queue.PauseGlobal(info);
                            }
                            else
                            {
#if DEBUG_LIMITS
                                Debug.WriteLine($"[{id}] (!) 429");
#endif
                                UpdateRateLimit(id, request, info, true, body: response.Stream);
                            }
                            await _queue.RaiseRateLimitTriggered(Id, info, $"{request.Method} {request.Endpoint}").ConfigureAwait(false);

                            continue;                   //Retry

                        case HttpStatusCode.BadGateway: //502
#if DEBUG_LIMITS
                            Debug.WriteLine($"[{id}] (!) 502");
#endif
                            if ((request.Options.RetryMode & RetryMode.Retry502) == 0)
                            {
                                throw new HttpException(HttpStatusCode.BadGateway, request, null);
                            }

                            continue;     //Retry

                        default:
                            API.DiscordError error = null;
                            if (response.Stream != null)
                            {
                                try
                                {
                                    using var reader     = new StreamReader(response.Stream);
                                    using var jsonReader = new JsonTextReader(reader);

                                    error = Discord.Rest.DiscordRestClient.Serializer.Deserialize <API.DiscordError>(jsonReader);
                                }
                                catch { }
                            }
                            throw new HttpException(
                                      response.StatusCode,
                                      request,
                                      error?.Code,
                                      error?.Message,
                                      error?.Errors.IsSpecified == true ?
                                      error.Errors.Value.Select(x => new DiscordJsonError(x.Name.GetValueOrDefault("root"), x.Errors.Select(y => new DiscordError(y.Code, y.Message)).ToArray())).ToArray() :
                                      null
                                      );
                        }
                    }
                    else
                    {
#if DEBUG_LIMITS
                        Debug.WriteLine($"[{id}] Success");
#endif
                        return(response.Stream);
                    }
                }
                //catch (HttpException) { throw; } //Pass through
                catch (TimeoutException)
                {
#if DEBUG_LIMITS
                    Debug.WriteLine($"[{id}] Timeout");
#endif
                    if ((request.Options.RetryMode & RetryMode.RetryTimeouts) == 0)
                    {
                        throw;
                    }

                    await Task.Delay(500).ConfigureAwait(false);

                    continue; //Retry
                }

                /*catch (Exception)
                 * {
                 #if DEBUG_LIMITS
                 *  Debug.WriteLine($"[{id}] Error");
                 #endif
                 *  if ((request.Options.RetryMode & RetryMode.RetryErrors) == 0)
                 *      throw;
                 *
                 *  await Task.Delay(500);
                 *  continue; //Retry
                 * }*/
                finally
                {
                    UpdateRateLimit(id, request, info, false);
#if DEBUG_LIMITS
                    Debug.WriteLine($"[{id}] Stop");
#endif
                }
            }
        }