/// <summary>Dispatch an HTTP request.</summary> /// <param name="request">The response message to validate.</param> /// <param name="dispatcher">A method which executes the request.</param> /// <returns>The final HTTP response.</returns> public async Task <HttpResponseMessage> ExecuteAsync(IRequest request, Func <IRequest, Task <HttpResponseMessage> > dispatcher) { int attempt = 0; while (true) { // dispatch request attempt++; var response = await DispatchRequest(request, dispatcher).ConfigureAwait(false); // check if the token needs to be refreshed if (response.StatusCode == HttpStatusCode.Unauthorized) { var responseContent = await response.Content.ReadAsStringAsync(null).ConfigureAwait(false); var jObject = JObject.Parse(responseContent); var message = jObject.GetPropertyValue("message", string.Empty); if (message.StartsWith("access token is expired", StringComparison.OrdinalIgnoreCase)) { var refreshedToken = _tokenHandler.RefreshTokenIfNecessary(true); request = request.WithBearerAuthentication(refreshedToken); response = await DispatchRequest(request, dispatcher); } } // find the applicable retry configuration, if any IRetryConfig retryConfig = RetryConfig.None(); bool shouldRetry = false; foreach (var config in _retryConfigs) { if (config.ShouldRetry(response)) { retryConfig = config; shouldRetry = true; break; } } // exit if there is no need to retry the request if (!shouldRetry) { return(response); } int maxAttempt = 1 + retryConfig?.MaxRetries ?? 0; // throw an exception if we have reached the maximum number of retries if (attempt > maxAttempt) { throw new ApiException(new Response(response, request.Formatters), $"The HTTP request failed, and the retry coordinator gave up after the maximum {maxAttempt} retries"); } // set up retry TimeSpan delay = retryConfig.GetDelay(attempt, response); if (delay.TotalMilliseconds > 0) { await Task.Delay(delay).ConfigureAwait(false); } } }