Exemple #1
0
        static RestClient()
        {
            Version version = Assembly.Load(new AssemblyName("Discore")).GetName().Version;

            // Don't include revision since Discore uses the Major.Minor.Patch semantic.
            discoreVersion = $"{version.Major}.{version.Minor}.{version.Build}";

            globalRateLimitLock = new RateLimitLock();
            routeRateLimitLocks = new ConcurrentDictionary <string, RateLimitLock>();
        }
Exemple #2
0
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <DiscordApiData> Send(Func <HttpRequestMessage> requestCreate, string rateLimitRoute,
                                                CancellationToken?cancellationToken = null)
        {
            CancellationToken ct = cancellationToken ?? CancellationToken.None;

            // Get or create the rate limit lock for the specified route
            RateLimitLock routeLock;

            if (!routeRateLimitLocks.TryGetValue(rateLimitRoute, out routeLock))
            {
                routeLock = new RateLimitLock();
                routeRateLimitLocks[rateLimitRoute] = routeLock;
            }

            // Acquire route-specific lock
            using (await routeLock.LockAsync(ct).ConfigureAwait(false))
            {
                HttpResponseMessage response;
                RateLimitHeaders    rateLimitHeaders;

                bool retry    = false;
                int  attempts = 0;

                IDisposable globalLock = null;

                do
                {
                    retry = false;
                    attempts++;

                    // If the route-specific lock requires a wait, delay before continuing
                    await routeLock.WaitAsync(ct).ConfigureAwait(false);

                    // If we don't already have the global lock and the global rate limit is active, acquire it.
                    if (globalLock == null && globalRateLimitLock.RequiresWait)
                    {
                        globalLock = await globalRateLimitLock.LockAsync(ct).ConfigureAwait(false);
                    }

                    bool keepGlobalLock = false;

                    try
                    {
                        // If we have the global lock, delay if it requires a wait
                        if (globalLock != null)
                        {
                            await globalRateLimitLock.WaitAsync(ct).ConfigureAwait(false);
                        }

                        // Send request
                        if (globalHttpClient != null)
                        {
                            response = await globalHttpClient.SendAsync(requestCreate(), ct).ConfigureAwait(false);
                        }
                        else
                        {
                            using (HttpClient http = CreateHttpClient())
                            {
                                response = await http.SendAsync(requestCreate(), ct).ConfigureAwait(false);
                            }
                        }

                        // Check rate limit headers
                        rateLimitHeaders = RateLimitHeaders.ParseOrNull(response.Headers);
                        if (rateLimitHeaders != null)
                        {
                            if ((int)response.StatusCode == 429)
                            {
                                // Tell the appropriate lock to wait
                                if (rateLimitHeaders.IsGlobal)
                                {
                                    globalRateLimitLock.ResetAfter(rateLimitHeaders.RetryAfter.Value);
                                }
                                else
                                {
                                    routeLock.ResetAfter(rateLimitHeaders.RetryAfter.Value);
                                }

                                retry = RetryOnRateLimit && attempts < 20;

                                // If we are retrying from a global rate limit, don't release the lock
                                // so this request doesn't go to the back of the queue.
                                keepGlobalLock = retry && rateLimitHeaders.IsGlobal;
                            }
                            else
                            {
                                // If the request succeeded but we are out of calls, set the route lock
                                // to wait until the reset time.
                                if (rateLimitHeaders.Remaining == 0)
                                {
                                    routeLock.ResetAt(rateLimitHeaders.Reset);
                                }
                            }
                        }

                        // Check if the request received a bad gateway
                        if (response.StatusCode == HttpStatusCode.BadGateway)
                        {
                            retry = attempts < 10;
                        }

                        // Release the global lock if necessary
                        if (globalLock != null && !keepGlobalLock)
                        {
                            globalLock.Dispose();
                        }
                    }
                    catch
                    {
                        // Always release the global lock if we ran into an exception
                        globalLock?.Dispose();

                        // Don't suppress the exception
                        throw;
                    }
                }while (retry);

                return(await ParseResponse(response, rateLimitHeaders).ConfigureAwait(false));
            }
        }