public async Task TimeoutUtility_SucceedsWithoutResult()
        {
            // Arrange
            CancellationToken timeoutToken             = CancellationToken.None;
            Func <CancellationToken, Task> actionAsync = token =>
            {
                timeoutToken = token;
                return(Task.FromResult(0));
            };

            // Act
            await TimeoutUtility.StartWithTimeout(
                actionAsync,
                TimeSpan.FromSeconds(1),
                "message",
                CancellationToken.None);

            // Assert
            Assert.False(timeoutToken.IsCancellationRequested);
        }
        public async Task TimeoutUtility_FailsWithResult()
        {
            // Arrange
            var expected = new Exception();
            CancellationToken timeoutToken = CancellationToken.None;
            Func <CancellationToken, Task <int> > actionAsync = token =>
            {
                timeoutToken = token;
                throw expected;
            };

            // Act & Assert
            var actual = await Assert.ThrowsAsync <Exception>(() => TimeoutUtility.StartWithTimeout(
                                                                  actionAsync,
                                                                  TimeSpan.FromSeconds(1),
                                                                  "message",
                                                                  CancellationToken.None));

            Assert.Same(expected, actual);
            Assert.False(timeoutToken.IsCancellationRequested);
        }
        public async Task TimeoutUtility_TimesOutWithoutResult()
        {
            // Arrange
            var expected = "timeout message";
            CancellationToken timeoutToken             = CancellationToken.None;
            Func <CancellationToken, Task> actionAsync = async token =>
            {
                timeoutToken = token;
                await Task.Delay(TimeSpan.FromMilliseconds(250));
            };

            // Act & Assert
            var exception = await Assert.ThrowsAsync <TimeoutException>(() => TimeoutUtility.StartWithTimeout(
                                                                            actionAsync,
                                                                            TimeSpan.FromTicks(1),
                                                                            expected,
                                                                            CancellationToken.None));

            Assert.Equal(expected, exception.Message);
            Assert.True(timeoutToken.IsCancellationRequested);
        }
        public async Task TimeoutUtility_SucceedsWithResult()
        {
            // Arrange
            var expected = 23;
            CancellationToken timeoutToken = CancellationToken.None;
            Func <CancellationToken, Task <int> > actionAsync = token =>
            {
                timeoutToken = token;
                return(Task.FromResult(expected));
            };

            // Act
            var actual = await TimeoutUtility.StartWithTimeout(
                actionAsync,
                TimeSpan.FromSeconds(1),
                "message",
                CancellationToken.None);

            // Assert
            Assert.Equal(expected, actual);
            Assert.False(timeoutToken.IsCancellationRequested);
        }
        partial void OnCreated()
        {
            TimeSpan lTimeSpan = new TimeSpan(0, 10, 0);

            TimeoutUtility.ChangeSentTimeout(this, lTimeSpan);
        }
        /// <summary>
        /// Make an HTTP request while retrying after failed attempts or timeouts.
        /// </summary>
        /// <remarks>
        /// This method accepts a factory to create instances of the <see cref="HttpRequestMessage"/> because
        /// requests cannot always be used. For example, suppose the request is a POST and contains content
        /// of a stream that can only be consumed once.
        /// </remarks>
        public async Task <HttpResponseMessage> SendAsync(
            HttpRetryHandlerRequest request,
            ILogger log,
            CancellationToken cancellationToken)
        {
            var tries = 0;
            HttpResponseMessage response = null;
            var success = false;

            while (tries < request.MaxTries && !success)
            {
                if (tries > 0)
                {
                    await Task.Delay(request.RetryDelay, cancellationToken);
                }

                tries++;
                success = true;

                using (var requestMessage = request.RequestFactory())
                {
                    var stopwatch  = Stopwatch.StartNew();
                    var requestUri = requestMessage.RequestUri.ToString();

                    try
                    {
                        // The only time that we will be disposing this existing response is if we have
                        // successfully fetched an HTTP response but the response has an status code indicating
                        // failure (i.e. HTTP status code >= 500).
                        //
                        // If we don't even get an HTTP response message because an exception is thrown, then there
                        // is no response instance to dispose. Additionally, we cannot use a finally here because
                        // the caller needs the response instance returned in a non-disposed state.
                        //
                        // Also, remember that if an HTTP server continuously returns a failure status code (like
                        // 500 Internal Server Error), we will retry some number of times but eventually return the
                        // response as-is, expecting the caller to check the status code as well. This results in the
                        // success variable being set to false but the response being returned to the caller without
                        // disposing it.
                        response?.Dispose();

                        log.LogInformation("  " + string.Format(
                                               CultureInfo.InvariantCulture,
                                               "{0} {1}",
                                               requestMessage.Method,
                                               requestUri));

                        // Issue the request.
                        var timeoutMessage = string.Format(
                            CultureInfo.CurrentCulture,
                            "The HTTP request to '{0} {1}' has timed out after {2}ms.",
                            requestMessage.Method,
                            requestUri,
                            (int)request.RequestTimeout.TotalMilliseconds);
                        response = await TimeoutUtility.StartWithTimeout(
                            timeoutToken => request.HttpClient.SendAsync(requestMessage, request.CompletionOption, timeoutToken),
                            request.RequestTimeout,
                            timeoutMessage,
                            cancellationToken);

                        // Wrap the response stream so that the download can timeout.
                        if (response.Content != null)
                        {
                            var networkStream = await response.Content.ReadAsStreamAsync();

                            var contentLength = response.Content.Headers.ContentLength;
                            // Only bother to report if size is big enough (1MB)
                            if (contentLength != null && contentLength.Value >= 1024 * 1024)
                            {
                                networkStream = new NugetDownloadProgressStream(networkStream, contentLength.Value, downloadProgress);
                            }

                            var newContent = new DownloadTimeoutStreamContent(
                                requestUri,
                                networkStream,
                                request.DownloadTimeout);

                            // Copy over the content headers since we are replacing the HttpContent instance associated
                            // with the response message.
                            foreach (var header in response.Content.Headers)
                            {
                                newContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
                            }

                            response.Content = newContent;
                        }

                        log.LogInformation("  " + string.Format(
                                               CultureInfo.InvariantCulture,
                                               "{0} {1} {2}ms",
                                               response.StatusCode,
                                               requestUri,
                                               stopwatch.ElapsedMilliseconds));

                        if ((int)response.StatusCode >= 500)
                        {
                            success = false;
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        response?.Dispose();

                        throw;
                    }
                    catch (Exception e)
                    {
                        success = false;

                        response?.Dispose();

                        if (tries >= request.MaxTries)
                        {
                            // Note: we override nuget default behavior by ignoring http errors (NoContent is ignored in V2FeedParser)
                            //throw;
                            return(new HttpResponseMessage(HttpStatusCode.NoContent));
                        }

                        log.LogInformation(string.Format(
                                               CultureInfo.CurrentCulture,
                                               "An error was encountered when fetching '{0} {1}'. The request will now be retried.",
                                               requestMessage.Method,
                                               requestUri,
                                               requestMessage)
                                           + Environment.NewLine
                                           + ExceptionUtilities.DisplayMessage(e));
                    }
                }
            }

            return(response);
        }