private void ParseHeaders(WebHeaderCollection headers, RestResponse response) { string globalRetryAfterHeader = headers.Get("Retry-After"); string isGlobalRateLimitHeader = headers.Get("X-RateLimit-Global"); if (!string.IsNullOrEmpty(globalRetryAfterHeader) && !string.IsNullOrEmpty(isGlobalRateLimitHeader) && int.TryParse(globalRetryAfterHeader, out int globalRetryAfter) && bool.TryParse(isGlobalRateLimitHeader, out bool isGlobalRateLimit) && isGlobalRateLimit) { RateLimit limit = response.ParseData <RateLimit>(); if (limit.Global) { Bucket.Handler.RateLimit.ReachedRateLimit(globalRetryAfter); } } string bucketLimitHeader = headers.Get("X-RateLimit-Limit"); string bucketRemainingHeader = headers.Get("X-RateLimit-Remaining"); string bucketResetAfterHeader = headers.Get("X-RateLimit-Reset-After"); string bucketNameHeader = headers.Get("X-RateLimit-Bucket"); if (!string.IsNullOrEmpty(bucketLimitHeader) && int.TryParse(bucketLimitHeader, out int bucketLimit)) { Bucket.RateLimit = bucketLimit; } if (!string.IsNullOrEmpty(bucketRemainingHeader) && int.TryParse(bucketRemainingHeader, out int bucketRemaining)) { Bucket.RateLimitRemaining = bucketRemaining; } double timeSince = Time.TimeSinceEpoch(); if (!string.IsNullOrEmpty(bucketResetAfterHeader) && double.TryParse(bucketResetAfterHeader, out double bucketResetAfter)) { double resetTime = timeSince + bucketResetAfter; if (resetTime > Bucket.RateLimitReset) { Bucket.RateLimitReset = resetTime; } } _logger.Debug($"Method: {Method.ToString()} Route: {Route} Internal Bucket Id: {Bucket.BucketId} Limit: {Bucket.RateLimit.ToString()} Remaining: {Bucket.RateLimitRemaining.ToString()} Reset: {Bucket.RateLimitReset.ToString()} Time: {Time.TimeSinceEpoch().ToString()} Bucket: {bucketNameHeader}"); }
/// <summary> /// Fires the request off /// </summary> public void Fire() { InProgress = true; StartTime = DateTime.UtcNow; HttpWebRequest req = CreateRequest(); try { //Can timeout while writing request data WriteRequestData(req); using (HttpWebResponse response = req.GetResponse() as HttpWebResponse) { if (response != null) { ParseResponse(response); } } _success = true; Interface.Oxide.NextTick(() => { Callback?.Invoke(Response); }); Close(); } catch (WebException ex) { using (HttpWebResponse httpResponse = ex.Response as HttpWebResponse) { _lastError = new RestError(ex, req.RequestUri, Method, Data); if (httpResponse == null) { Bucket.ErrorDelayUntil = Time.TimeSinceEpoch() + 1; Close(false); _logger.Exception($"A web request exception occured (internal error) [RETRY={_retries.ToString()}/3].\nRequest URL: [{req.Method}] {req.RequestUri}", ex); return; } int statusCode = (int)httpResponse.StatusCode; _lastError.HttpStatusCode = statusCode; string message = ParseResponse(ex.Response); _lastError.Message = message; bool isRateLimit = statusCode == 429; if (isRateLimit) { _logger.Warning($"Discord rate limit reached. (Rate limit info: remaining: [{req.Method}] Route:{req.RequestUri} Remaining: {Bucket.RateLimitRemaining.ToString()} Limit: {Bucket.RateLimit.ToString()}, Reset In: {Bucket.RateLimitReset.ToString()}, Current Time: {Time.TimeSinceEpoch().ToString()}"); Close(false); return; } DiscordApiError apiError = Response.ParseData <DiscordApiError>(); _lastError.DiscordError = apiError; if (apiError != null && apiError.Code != 0) { _logger.Error($"Discord API has returned error Discord Code: {apiError.Code.ToString()} Discord Error: {apiError.Message} Request: [{req.Method}] {req.RequestUri} (Response Code: {httpResponse.StatusCode.ToString()})" + $"\nDiscord Errors: {apiError.Errors}" + $"\nRequest Body:\n{(Contents != null ? Encoding.UTF8.GetString(Contents) : "Contents is null")}"); } else { _logger.Error($"An error occured whilst submitting a request: Exception Status: {ex.Status.ToString()} Request: [{req.Method}] {req.RequestUri} (Response Code: {httpResponse.StatusCode.ToString()}): {message}"); } Close(); } } catch (Exception ex) { _logger.Exception($"An exception occured for request: [{req.Method}] {req.RequestUri}", ex); Close(); } }