예제 #1
0
        protected override async Task <HttpResponseMessage> SendRequestAsync(IRequestInfo request, bool readBody)
        {
            var bucketId = GenerateBucketId(request);

            while (true)
            {
                await _rateLimiter.EnterLockAsync(bucketId, request.CancellationToken).ConfigureAwait(false);

                bool allowAnyStatus = request.AllowAnyStatusCode;
                ((RequestInfo)request).AllowAnyStatusCode = true;
                var response = await base.SendRequestAsync(request, readBody).ConfigureAwait(false);

                var info = new RateLimitInfo(response.Headers);
                if (response.IsSuccessStatusCode)
                {
                    _rateLimiter.UpdateLimit(bucketId, info);
                    return(response);
                }

                switch (response.StatusCode)
                {
                case (HttpStatusCode)429:
                    _rateLimiter.UpdateLimit(bucketId, info);
                    continue;

                case HttpStatusCode.BadGateway:     //502
                    await Task.Delay(250, request.CancellationToken).ConfigureAwait(false);

                    continue;

                default:
                    if (allowAnyStatus)
                    {
                        return(response);
                    }
                    // TODO: Does this allocate?
                    var bytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

                    if (bytes.Length > 0)
                    {
                        RestError error = null;
                        try { error = _serializer.Read <RestError>(bytes.AsSpan()); } catch { }
                        if (error != null)
                        {
                            throw new WumpusRestException(response.StatusCode, error.Code, error.Message);
                        }

                        Utf8String msg = null;
                        try { msg = new Utf8String(bytes); } catch { }
                        if (!(msg is null))
                        {
                            throw new WumpusRestException(response.StatusCode, null, msg);
                        }
                    }
                    throw new WumpusRestException(response.StatusCode);
                }
            }
        }
예제 #2
0
 public virtual void UpdateLimit(string bucketId, RateLimitInfo info)
 {
     if (info.IsGlobal)
     {
         _globalWaitUntil = DateTimeOffset.UtcNow.AddMilliseconds(info.RetryAfter.Value + (info.Lag?.TotalMilliseconds ?? 0.0));
     }
     else
     {
         var bucket = _buckets.GetOrAdd(bucketId, x => new RequestBucket(this));
         bucket.UpdateRateLimit(info);
     }
 }
예제 #3
0
        public void UpdateRateLimit(RateLimitInfo info)
        {
            if (WindowCount == 0)
            {
                return;
            }

            lock (_lock)
            {
                bool hasQueuedReset = _resetsAt != null;
                if (info.Limit.HasValue && WindowCount != info.Limit.Value)
                {
                    WindowCount = info.Limit.Value;
                    _semaphore  = info.Remaining.Value;
                }

                long           now      = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
                DateTimeOffset?resetsAt = null;

                //Using X-RateLimit-Remaining causes a race condition

                /*if (info.Remaining.HasValue)
                 *  _semaphore = info.Remaining.Value;*/
                if (info.RetryAfter.HasValue) //Millis
                {
                    resetsAt = DateTimeOffset.UtcNow.AddMilliseconds(info.RetryAfter.Value);
                }
                else if (info.Reset.HasValue) //Secs
                {
                    resetsAt = info.Reset.Value.AddSeconds(info.Lag?.TotalSeconds ?? 1.0);
                }

                if (resetsAt == null)
                {
                    WindowCount = 0; //No rate limit info, disable limits on this bucket (should only ever happen with a user token)
                    return;
                }

                if (!hasQueuedReset || resetsAt > _resetsAt)
                {
                    _resetsAt     = resetsAt;
                    LastAttemptAt = resetsAt.Value; //Make sure we dont destroy this until after its been reset

                    if (!hasQueuedReset)
                    {
                        int millis = (int)Math.Ceiling((_resetsAt.Value - DateTimeOffset.UtcNow).TotalMilliseconds);
                        var _      = QueueReset(millis);
                    }
                }
            }
        }