コード例 #1
0
        /// <summary>
        /// Returns a copy of this lock with the same reset time.
        /// </summary>
        public RateLimitLock Clone()
        {
            var clone = new RateLimitLock();

            clone.resetAtEpochMilliseconds = resetAtEpochMilliseconds;

            return(clone);
        }
コード例 #2
0
ファイル: RestClient.cs プロジェクト: Francessco121/Discore
        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>();
            routesToBuckets      = new ConcurrentDictionary <string, string>();
            bucketRateLimitLocks = new ConcurrentDictionary <string, RateLimitLock>();
        }
コード例 #3
0
ファイル: RestClient.cs プロジェクト: Francessco121/Discore
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <DiscordApiData> Send(Func <HttpRequestMessage> requestCreate, string rateLimitRoute,
                                                CancellationToken?cancellationToken = null)
        {
            CancellationToken ct = cancellationToken ?? CancellationToken.None;

            // Get the rate limit lock for the route
            RateLimitLock routeLock = null;

            if (routesToBuckets.TryGetValue(rateLimitRoute, out string rateLimitBucket))
            {
                // Use the bucket for the route if it exists
                routeLock = bucketRateLimitLocks[rateLimitBucket];
            }

            if (routeLock == null)
            {
                // Get or create the rate limit lock for the specified route
                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 * 1000);
                                }
                            }

                            if (rateLimitHeaders.Bucket != null)
                            {
                                // Create the bucket this route is apparently in if not already created
                                if (!bucketRateLimitLocks.ContainsKey(rateLimitHeaders.Bucket))
                                {
                                    bucketRateLimitLocks.TryAdd(rateLimitHeaders.Bucket, routeLock.Clone());
                                }

                                // Link the route to the bucket if not already linked
                                routesToBuckets[rateLimitRoute] = rateLimitHeaders.Bucket;
                            }
                            else
                            {
                                // If the route was previously in a rate-limit bucket, but isn't anymore, remove the link
                                if (routesToBuckets.TryRemove(rateLimitRoute, out string bucket))
                                {
                                    // Additionally remove the bucket if no routes are linked to it
                                    if (!routesToBuckets.Values.Contains(bucket))
                                    {
                                        bucketRateLimitLocks.TryRemove(bucket, out _);
                                    }
                                }
                            }
                        }

                        // 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));
            }
        }