public async Task MultipleHandlers_CanReexecuteSendAsync_FirstResponseDisposed()
    {
        // Arrange
        var policy1 = HttpPolicyExtensions.HandleTransientHttpError()
                      .RetryAsync(retryCount: 1);
        var policy2 = HttpPolicyExtensions.HandleTransientHttpError()
                      .CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 2, durationOfBreak: TimeSpan.FromSeconds(10));

        var callCount     = 0;
        var fakeContent   = new FakeContent();
        var firstResponse = new HttpResponseMessage()
        {
            StatusCode = System.Net.HttpStatusCode.InternalServerError,
            Content    = fakeContent,
        };
        var expected = new HttpResponseMessage();

        var handler1 = new PolicyHttpMessageHandler(policy1);
        var handler2 = new PolicyHttpMessageHandler(policy2);

        handler1.InnerHandler = handler2;
        handler2.InnerHandler = new TestHandler()
        {
            OnSendAsync = (req, ct) =>
            {
                if (callCount == 0)
                {
                    callCount++;
                    return(Task.FromResult(firstResponse));
                }
                else if (callCount == 1)
                {
                    callCount++;
                    return(Task.FromResult(expected));
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
        };
        var invoke = new HttpMessageInvoker(handler1);

        // Act
        var response = await invoke.SendAsync(new HttpRequestMessage(), CancellationToken.None);

        // Assert
        Assert.Equal(2, callCount);
        Assert.Same(expected, response);
        Assert.True(fakeContent.Disposed);
    }
    public async Task SendAsync_StaticPolicy_PolicyTriggers_CanReexecuteSendAsync_FirstResponseDisposed()
    {
        // Arrange
        var policy = HttpPolicyExtensions.HandleTransientHttpError()
                     .RetryAsync(retryCount: 1);

        var callCount     = 0;
        var fakeContent   = new FakeContent();
        var firstResponse = new HttpResponseMessage()
        {
            StatusCode = System.Net.HttpStatusCode.InternalServerError,
            Content    = fakeContent,
        };
        var expected = new HttpResponseMessage();

        var handler = new PolicyHttpMessageHandler(policy);

        handler.InnerHandler = new TestHandler()
        {
            OnSendAsync = (req, ct) =>
            {
                if (callCount == 0)
                {
                    callCount++;
                    return(Task.FromResult(firstResponse));
                }
                else if (callCount == 1)
                {
                    callCount++;
                    return(Task.FromResult(expected));
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
        };
        var invoke = new HttpMessageInvoker(handler);

        // Act
        var response = await invoke.SendAsync(new HttpRequestMessage(), CancellationToken.None);

        // Assert
        Assert.Equal(2, callCount);
        Assert.Same(expected, response);
        Assert.True(fakeContent.Disposed);
    }
Exemple #3
0
        private async Task <VssConnection> ConnectToAzureDevOpsAsync(WorkItemEventContext eventContext, VssCredentials clientCredentials, CancellationToken cancellationToken)
        {
            // see https://docs.microsoft.com/en-us/azure/devops/integrate/concepts/rate-limits#api-client-experience
            var policy = Policy
                         .Handle <HttpRequestException>()
                         // https://github.com/App-vNext/Polly/wiki/Retry#retryafter-when-the-response-specifies-how-long-to-wait
                         .OrResult <HttpResponseMessage>(r => r.StatusCode == (HttpStatusCode)429)
                         .WaitAndRetryAsync(
                retryCount: MaxRetries,
                sleepDurationProvider: (retryCount, response, context) => {
                return(response.Result?.Headers.RetryAfter.Delta.Value
                       ?? TimeSpan.FromSeconds(BaseRetryInterval * retryCount));
            },
#pragma warning disable CS1998
                onRetryAsync: async(response, timespan, retryCount, context) => {
                logger.WriteInfo($"{Environment.NewLine}Waiting {timespan} before retrying (attemp #{retryCount}/{MaxRetries})...");
            }
#pragma warning restore CS1998
                );
            var handler = new PolicyHttpMessageHandler(policy);

            var vssHandler = new VssHttpMessageHandler(clientCredentials, VssClientHttpRequestSettings.Default.Clone());

            var devops = new VssConnection(eventContext.CollectionUri, vssHandler, new DelegatingHandler[] { handler });

            try
            {
                await devops.ConnectAsync(cancellationToken);

                logger.WriteInfo($"Connected to Azure DevOps");
            }
            catch (System.Exception ex)
            {
                logger.WriteError(ex.Message);
                if (ex.InnerException != null)
                {
                    logger.WriteError(ex.InnerException.Message);
                }
                throw;
            }

            return(devops);
        }
Exemple #4
0
        public RetryEnabledHttpClientProxy(HttpMessageHandler httpMessageHandler)
        {
            AsyncRetryPolicy <HttpResponseMessage> retryPolicy = HttpPolicyExtensions
                                                                 .HandleTransientHttpError() // HttpRequestException, 5XX and 408
                                                                 .OrResult(r => r.StatusCode == HttpStatusCode.NotFound)
                                                                 .WaitAndRetryAsync(new[] {
                TimeSpan.FromSeconds(1),
                TimeSpan.FromSeconds(5),
                TimeSpan.FromSeconds(10)
            }, (x, i) => x.Result.Dispose());
            var policyHttpMessageHandler = new PolicyHttpMessageHandler(retryPolicy);

            try {
                var timeoutHandler = new TimeoutHandler {
                    InnerHandler = httpMessageHandler
                };
                policyHttpMessageHandler.InnerHandler = timeoutHandler;
                _httpClient = new HttpClient(policyHttpMessageHandler);
            } catch {
                policyHttpMessageHandler.Dispose();
                throw;
            }
        }
        private static async Task TestRetry(string collectionUrl, string devOpsToken, int worktItemId, CancellationToken cancellationToken)
        {
            // see https://docs.microsoft.com/en-us/azure/devops/integrate/concepts/rate-limits#api-client-experience
            int MaxRetries = 3;
            var policy     = Policy
                             .Handle <HttpRequestException>()
                             // https://github.com/App-vNext/Polly/wiki/Retry#retryafter-when-the-response-specifies-how-long-to-wait
                             .OrResult <HttpResponseMessage>(r => r.StatusCode == (HttpStatusCode)429)
                             .WaitAndRetryAsync(
                retryCount: MaxRetries,
                sleepDurationProvider: (retryCount, response, context) => {
                return(response.Result?.Headers.RetryAfter.Delta.Value
                       ?? TimeSpan.FromSeconds(30 * retryCount));
            },
                onRetryAsync: async(response, timespan, retryCount, context) => {
                await Console.Out.WriteLineAsync($"{Environment.NewLine}Waiting {timespan} before retrying (attemp #{retryCount}/{MaxRetries})...");
            }
                );
            var handler = new PolicyHttpMessageHandler(policy);

            var clientCredentials = new VssBasicCredential("", devOpsToken);
            var vssHandler        = new VssHttpMessageHandler(clientCredentials, VssClientHttpRequestSettings.Default.Clone());

            using var devops = new VssConnection(new Uri(collectionUrl), vssHandler, new DelegatingHandler[] { handler });
            await devops.ConnectAsync(cancellationToken);

            using var witClient = await devops.GetClientAsync <WorkItemTrackingHttpClient>(cancellationToken);

            for (int i = 0; i < 10; i++)
            {
                _ = await witClient.GetWorkItemAsync(worktItemId);

                Console.Write('+');
            }

            devops.Disconnect();
        }