Exemplo n.º 1
0
        /// <summary>
        /// Send a CDM request with the retry logic helper function.
        /// </summary>
        /// <param name="cdmRequest">The CDM Http request.</param>
        /// <param name="callback">The callback that gets executed after the request finishes.</param>
        /// <returns>The <see cref="Task"/>, representing CDM Http response.</returns>
        private async Task <CdmHttpResponse> SendAsyncHelper(CdmHttpRequest cdmRequest, Callback callback = null, CdmCorpusContext ctx = null)
        {
            string fullUrl;

            if (isApiEndpointSet)
            {
                fullUrl = Combine(this.apiEndpoint, cdmRequest.RequestedUrl);
            }
            else
            {
                fullUrl = cdmRequest.RequestedUrl;
            }

            // If the number of retries is 0, we only try once, otherwise we retry the specified number of times.
            for (int retryNumber = 0; retryNumber <= cdmRequest.NumberOfRetries; retryNumber++)
            {
                var requestMessage = new HttpRequestMessage(cdmRequest.Method, fullUrl);

                foreach (var item in cdmRequest.Headers)
                {
                    requestMessage.Headers.Add(item.Key, item.Value);
                }

                // GET requests might not have any content.
                if (cdmRequest.Content != null)
                {
                    requestMessage.Content = new StringContent(cdmRequest.Content, Encoding.UTF8, cdmRequest.ContentType);
                }

                CdmHttpResponse cdmHttpResponse = null;
                var             hasFailed       = false;
                try
                {
                    Task <HttpResponseMessage> request;

                    DateTimeOffset startTime = DateTimeOffset.UtcNow;

                    if (ctx != null)
                    {
                        Logger.Info(nameof(CdmHttpClient), ctx, $"Sending request {cdmRequest.RequestId}, request type: {requestMessage.Method}, request url: {cdmRequest.StripSasSig()}, retry number: {retryNumber}.", nameof(SendAsyncHelper));
                    }

                    // The check is added to fix a known issue in .net http client when reading HEAD request > 2GB.
                    // .net http client tries to write content even when the request is HEAD request.
                    if (cdmRequest.Method.Equals(HttpMethod.Head))
                    {
                        request = Task.Run(async() => await this.client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead));
                    }
                    else
                    {
                        request = Task.Run(async() => await this.client.SendAsync(requestMessage));
                    }

                    if (!request.Wait((TimeSpan)cdmRequest.Timeout))
                    {
                        if (ctx != null && cdmRequest.Timeout != null)
                        {
                            Logger.Info(nameof(CdmHttpClient), ctx, $"Request {cdmRequest.RequestId} timeout after {cdmRequest.Timeout?.Seconds} s.", nameof(SendAsyncHelper));
                        }

                        throw new CdmTimedOutException("Request timeout.");
                    }

                    HttpResponseMessage response = request.Result;

                    if (ctx != null)
                    {
                        DateTimeOffset endTime = DateTimeOffset.UtcNow;
                        Logger.Info(nameof(CdmHttpClient), ctx, $"Response for request {cdmRequest.RequestId} received, elapsed time: {endTime.Subtract(startTime).TotalMilliseconds} ms.", nameof(SendAsyncHelper));
                    }

                    if (response != null)
                    {
                        cdmHttpResponse = new CdmHttpResponse(response.StatusCode)
                        {
                            Reason       = response.ReasonPhrase,
                            Content      = response.Content,
                            IsSuccessful = response.IsSuccessStatusCode
                        };

                        foreach (var item in response.Headers)
                        {
                            cdmHttpResponse.ResponseHeaders.Add(item.Key, string.Join(",", item.Value));
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (ex is AggregateException aggrEx)
                    {
                        ex = aggrEx.InnerException;
                    }

                    hasFailed = true;

                    // Only throw an exception if another retry is not expected anymore.
                    if (callback == null || retryNumber == cdmRequest.NumberOfRetries)
                    {
                        if (retryNumber != 0)
                        {
                            throw new CdmNumberOfRetriesExceededException(ex.Message);
                        }
                        else
                        {
                            throw ex;
                        }
                    }
                }

                // Check whether we have a callback function set and whether this is not our last retry.
                if (callback != null && retryNumber != cdmRequest.NumberOfRetries)
                {
                    // Call the callback function with the retry numbers starting from 1.
                    var waitTime = callback(cdmHttpResponse, hasFailed, retryNumber + 1);

                    // Callback returned back that we do not want to retry anymore (probably successful request, client can set up what they want here).
                    if (waitTime == null)
                    {
                        return(cdmHttpResponse);
                    }
                    else
                    {
                        // Sleep time specified by the callback.
                        Thread.Sleep((int)waitTime.Value.TotalMilliseconds);
                    }
                }
                else
                {
                    // CDM Http Response exists, could be successful or bad (e.g. 403/404), it is up to caller to deal with it.
                    if (cdmHttpResponse != null)
                    {
                        return(cdmHttpResponse);
                    }
                    else
                    {
                        if (retryNumber == 0)
                        {
                            return(null);
                        }
                        else
                        {
                            // If response doesn't exist repeatedly, just throw that the number of retries has exceeded (we don't have any other information).
                            throw new CdmNumberOfRetriesExceededException();
                        }
                    }
                }
            }

            // Should never come here, but just in case throw this exception.
            throw new CdmNumberOfRetriesExceededException();
        }
Exemplo n.º 2
0
        /// <summary>
        /// Send a CDM request with the retry logic helper function.
        /// </summary>
        /// <param name="cdmRequest">The CDM Http request.</param>
        /// <param name="callback">The callback that gets executed after the request finishes.</param>
        /// <returns>The <see cref="Task"/>, representing CDM Http response.</returns>
        private async Task <CdmHttpResponse> SendAsyncHelper(CdmHttpRequest cdmRequest, Callback callback = null)
        {
            string fullUrl;

            if (isApiEndpointSet)
            {
                fullUrl = Combine(this.apiEndpoint, cdmRequest.RequestedUrl);
            }
            else
            {
                fullUrl = cdmRequest.RequestedUrl;
            }

            // If the number of retries is 0, we only try once, otherwise we retry the specified number of times.
            for (int retryNumber = 0; retryNumber <= cdmRequest.NumberOfRetries; retryNumber++)
            {
                var requestMessage = new HttpRequestMessage(cdmRequest.Method, fullUrl);

                foreach (var item in cdmRequest.Headers)
                {
                    requestMessage.Headers.Add(item.Key, item.Value);
                }

                // GET requests might not have any content.
                if (cdmRequest.Content != null)
                {
                    requestMessage.Content = new StringContent(cdmRequest.Content, Encoding.UTF8, cdmRequest.ContentType);
                }

                HttpResponseMessage response        = null;
                CdmHttpResponse     cdmHttpResponse = null;
                var cts = new CancellationTokenSource();
                cts.CancelAfter((TimeSpan)cdmRequest.Timeout);
                var hasFailed = false;
                try
                {
                    response = await this.client.SendAsync(requestMessage);

                    cts.Token.ThrowIfCancellationRequested();

                    if (response != null)
                    {
                        cdmHttpResponse = new CdmHttpResponse(response.StatusCode)
                        {
                            Reason       = response.ReasonPhrase,
                            Content      = response.Content,
                            IsSuccessful = response.IsSuccessStatusCode
                        };

                        foreach (var item in response.Headers)
                        {
                            cdmHttpResponse.ResponseHeaders.Add(item.Key, string.Join(",", item.Value));
                        }
                    }
                }
                catch (Exception ex)
                {
                    hasFailed = true;

                    // Only throw an exception if another retry is not expected anymore.
                    if (callback == null || retryNumber == cdmRequest.NumberOfRetries)
                    {
                        if (retryNumber != 0)
                        {
                            throw new CdmNumberOfRetriesExceededException(ex.Message);
                        }
                        else
                        {
                            throw (ex is OperationCanceledException) ? new CdmTimedOutException(ex.Message) : ex;
                        }
                    }
                }

                // Check whether we have a callback function set and whether this is not our last retry.
                if (callback != null && retryNumber != cdmRequest.NumberOfRetries)
                {
                    // Call the callback function with the retry numbers starting from 1.
                    var waitTime = callback(cdmHttpResponse, hasFailed, retryNumber + 1);

                    // Callback returned back that we do not want to retry anymore (probably successful request, client can set up what they want here).
                    if (waitTime == null)
                    {
                        return(cdmHttpResponse);
                    }
                    else
                    {
                        // Sleep time specified by the callback.
                        System.Threading.Thread.Sleep((int)waitTime.Value.TotalMilliseconds);
                    }
                }
                else
                {
                    // CDM Http Response exists, could be successful or bad (e.g. 403/404), it is up to caller to deal with it.
                    if (cdmHttpResponse != null)
                    {
                        return(cdmHttpResponse);
                    }
                    else
                    {
                        if (retryNumber == 0)
                        {
                            return(null);
                        }
                        else
                        {
                            // If response doesn't exist repeatedly, just throw that the number of retries has exceeded (we don't have any other information).
                            throw new CdmNumberOfRetriesExceededException();
                        }
                    }
                }
            }

            // Should never come here, but just in case throw this exception.
            throw new CdmNumberOfRetriesExceededException();
        }