public void SendAsyncRequest(TaskListener <HttpResponseMessage> listener, HttpRequestMessage request, Logger logger,
                                     BackoffProvider backoffProvider, TimeSpan?timeout = null)
        {
            int timeoutMS = timeout == null ? (int)Timeout.TotalMilliseconds : (int)timeout.Value.TotalMilliseconds;
            CancellationToken timeoutCancelationToken = new CancellationTokenSource(timeoutMS).Token;
            IAsyncResult      asyncResult             = GetHttpClient().SendAsync(request, timeoutCancelationToken)
                                                        .AsApm(ar => GetResponseCallBack_(listener, ar, request, logger, backoffProvider), request, logger);

            if (asyncResult != null && asyncResult.CompletedSynchronously)
            {
                logger.Log(TraceLevel.Notice, Stage.General,
                           new { message = "request.BeginGetResponse completed synchronously" });
                GetResponseCallBack_(listener, asyncResult, request, logger, backoffProvider);
            }
        }
        private bool MakeAnotherAttempt_(TaskListener <HttpResponseMessage> listener, HttpRequestMessage originalRequest, Logger logger, BackoffProvider backoffProvider)
        {
            if (!backoffProvider.ShouldWait)
            {
                return(false);
            }

            logger.Log(TraceLevel.Warn, Stage.General, StageType.Retry);
            backoffProvider.Wait();
            HttpRequestMessage request = CloneRequest_(originalRequest);

            SendAsyncRequest(listener, request, logger, backoffProvider);
            return(true);
        }
        private void GetResponseCallBack_(TaskListener <HttpResponseMessage> listener, IAsyncResult result,
                                          HttpRequestMessage originalRequest, Logger logger, BackoffProvider backoffProvider)
        {
            if (!result.IsCompleted)
            {
                logger.Log(TraceLevel.Warn, Stage.General, StageType.RequestFailed,
                           new { message = "result is not complete.", result });
                return;
            }
            try
            {
                Task <HttpResponseMessage> resultAsTask = (Task <HttpResponseMessage>)result;
                if (resultAsTask.IsFaulted)
                {
                    if (MakeAnotherAttempt_(listener, originalRequest, logger, backoffProvider))
                    {
                        return;
                    }

                    listener.OnFail(
                        new EyesException($"HttpRequestMessage request failed: {originalRequest.Method} {originalRequest.RequestUri}",
                                          resultAsTask.Exception));
                    return;
                }
                if (resultAsTask.IsCanceled)
                {
                    if (MakeAnotherAttempt_(listener, originalRequest, logger, backoffProvider))
                    {
                        return;
                    }

                    listener.OnFail(
                        new TimeoutException($"HttpRequestMessage request timed out: {originalRequest.Method} {originalRequest.RequestUri}"));
                    return;
                }
                HttpResponseMessage response = resultAsTask.Result;
                if (response.StatusCode >= HttpStatusCode.Ambiguous)
                {
                    listener.OnFail(new EyesException($"Wrong response status: {response.StatusCode} {response.ReasonPhrase}"));
                }
                else
                {
                    listener.OnComplete(response);
                }
                response.Dispose();
            }
            catch (WebException ex)
            {
                if (MakeAnotherAttempt_(listener, originalRequest, logger, backoffProvider))
                {
                    return;
                }
                listener.OnFail(ex);
            }
        }