예제 #1
0
        private async Task <AmazonWebServiceResponse> HandleHttpContentAsync(WebRequestState state, HttpResponseMessage httpResponse)
        {
            LogResponse(state.Metrics, state.Request, httpResponse.StatusCode);
            AmazonWebServiceResponse response     = null;
            HttpClientResponseData   responseData = new HttpClientResponseData(httpResponse);
            UnmarshallerContext      context      = null;

            try
            {
                var responseStream = await responseData.OpenResponseAsync()
                                     .ConfigureAwait(continueOnCapturedContext: false);

                context = state.Unmarshaller.CreateContext(responseData,
                                                           this.SupportResponseLogging &&
                                                           (Config.LogResponse || Config.ReadEntireResponse || AWSConfigs.ResponseLogging != ResponseLoggingOption.Never),
                                                           responseStream,
                                                           state.Metrics);

                using (state.Metrics.StartEvent(Metric.ResponseUnmarshallTime))
                {
                    response = state.Unmarshaller.Unmarshall(context);
                }
                context.ValidateCRC32IfAvailable();

                var contentHeaders = httpResponse.Content.Headers as HttpContentHeaders;
                if (contentHeaders != null)
                {
                    response.ContentLength  = contentHeaders.ContentLength.GetValueOrDefault();
                    response.HttpStatusCode = httpResponse.StatusCode;
                }

                if (response.ResponseMetadata != null)
                {
                    state.Metrics.AddProperty(Metric.AWSRequestID, response.ResponseMetadata.RequestId);
                }
                LogFinishedResponse(state.Metrics, context, response.ContentLength);

                return(response);
            }
            finally
            {
                if (!state.Unmarshaller.HasStreamingProperty)
                {
                    httpResponse.Dispose();
                }

                ProcessResponseHandlers(response, state.Request, responseData);
            }
        }
        private async Task <bool> HandleHttpErrorResponseAsync(WebRequestState state, HttpResponseMessage httpErrorResponse, HttpClientResponseData responseData, CancellationToken cancellationToken)
        {
            HttpStatusCode         statusCode;
            AmazonServiceException errorResponseException = null;

            UnmarshallerContext errorContext = null;

            statusCode = httpErrorResponse.StatusCode;
            state.Metrics.AddProperty(Metric.StatusCode, statusCode);
            string redirectedLocation = responseData.GetHeaderValue("location");

            state.Metrics.AddProperty(Metric.RedirectLocation, redirectedLocation);

            var responseStream = await responseData.ResponseBody.OpenResponseAsync()
                                 .ConfigureAwait(continueOnCapturedContext: false);

            errorContext = state.Unmarshaller.CreateContext(responseData,
                                                            Config.LogResponse || Config.ReadEntireResponse || AWSConfigs.LoggingConfig.LogResponses != ResponseLoggingOption.Never,
                                                            responseStream,
                                                            state.Metrics);
            errorResponseException = state.Unmarshaller.UnmarshallException(errorContext, null, statusCode);
            if (Config.LogResponse || AWSConfigs.LoggingConfig.LogResponses != ResponseLoggingOption.Never)
            {
                this.logger.Error(errorResponseException, "Received error response: [{0}]", errorContext.ResponseBody);
            }
            state.Metrics.AddProperty(Metric.AWSRequestID, errorResponseException.RequestId);
            state.Metrics.AddProperty(Metric.AWSErrorCode, errorResponseException.ErrorCode);

            if (CanRetry(state))
            {
                if (isTemporaryRedirect(statusCode, redirectedLocation))
                {
                    this.logger.DebugFormat("Request {0} is being redirected to {1}.", state.Request.RequestName, redirectedLocation);
                    state.Request.Endpoint = new Uri(redirectedLocation);
                    return(true);
                }
                else if (ShouldRetry(statusCode, this.Config, errorResponseException, state.RetriesAttempt))
                {
                    this.logger.DebugFormat("Retry number {0} for request {1}.", state.RetriesAttempt, state.Request.RequestName);
                    pauseExponentially(state);
                    cancellationToken.ThrowIfCancellationRequested();
                    return(true);
                }
            }

            if (errorResponseException != null)
            {
                this.logger.Error(errorResponseException, "Error making request {0}.", state.Request.RequestName);
                state.Metrics.AddProperty(Metric.Exception, errorResponseException);
                throw errorResponseException;
            }


            AmazonServiceException excep = new AmazonServiceException("Unable to make request", null, statusCode);

            this.logger.Error(excep, "Error making request {0}.", state.Request.RequestName);
            state.Metrics.AddProperty(Metric.Exception, excep);
            throw excep;
        }
        private async Task <T> InvokeConfiguredRequest <T>(WebRequestState state, CancellationToken cancellationToken) where T : AmazonWebServiceResponse
        {
            int  currentRetries   = state.RetriesAttempt;
            T    response         = null;
            bool shouldRetry      = false;
            bool responseReceived = false;
            HttpResponseMessage    responseMessage = null;
            HttpClientResponseData responseData    = null;
            var requestMessage = ConfigureRequestMessage(state);

            try
            {
                try
                {
                    SetContent(requestMessage, state);

                    using (state.Metrics.StartEvent(Metric.HttpRequestTime))
                    {
                        responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
                                          .ConfigureAwait(continueOnCapturedContext: false);

                        responseReceived = true;
                    }

                    responseData = new HttpClientResponseData(responseMessage);
                    if (!IsErrorResponse(responseMessage) ||
                        responseMessage.StatusCode == HttpStatusCode.NotFound && state.Request.Suppress404Exceptions)
                    {
                        using (state.Metrics.StartEvent(Metric.ResponseProcessingTime))
                        {
                            response = (T) await HandleHttpContentAsync(state, responseMessage, responseData)
                                       .ConfigureAwait(continueOnCapturedContext: false);
                        }
                    }
                    else
                    {
                        bool retry = await HandleHttpErrorResponseAsync(state, responseMessage, responseData, cancellationToken)
                                     .ConfigureAwait(continueOnCapturedContext: false);

                        if (retry)
                        {
                            shouldRetry = true;
                        }
                    }
                }
                catch (HttpRequestException e)
                {
                    var we = e.InnerException as WebException;

                    if (we != null)
                    {
                        if (WebExceptionStatusesToThrowOn.Contains(we.Status))
                        {
                            throw new AmazonServiceException("Encountered a non retryable WebException : " + we.Status, we);
                        }

                        // The WinRT framework doesn't break down errors, not all status values are available for WinRT.
                        if (
#if WIN_RT
                            (we.Status == WebExceptionStatus.UnknownError || WebExceptionStatusesToRetryOn.Contains(we.Status))
#else
                            WebExceptionStatusesToRetryOn.Contains(we.Status)
#endif
                            )
                        {
                            shouldRetry = RetryOrThrow(state, e);
                        }
                    }

                    var ioe = e.InnerException as IOException;
                    if (ioe != null)
                    {
#if !WIN_RT
                        if (IsInnerExceptionThreadAbort(ioe))
                        {
                            throw new AmazonServiceException(e);
                        }
#endif
                        shouldRetry = RetryOrThrow(state, e);
                    }

                    // Check if response is null at the end as
                    // it can be null for both WebException and IOException.
                    if (!shouldRetry && response == null)
                    {
                        shouldRetry = RetryOrThrow(state, e);
                    }

                    // If shouldRetry is not set by any of the above checks,
                    // re-throw the exception.
                    if (!shouldRetry)
                    {
                        throw new AmazonServiceException(e);
                    }
                }
                catch (TaskCanceledException taskCancelledException)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        throw;
                    }
                    else
                    {
                        // It's a timeout exception.
                        throw new AmazonServiceException(taskCancelledException);
                    }
                }
                catch (OperationCanceledException operationCancelledException)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        // Throw an exception with the original cancellation token.
                        throw new OperationCanceledException(operationCancelledException.Message, cancellationToken);
                    }
                    else
                    {
                        // It's a timeout exception.
                        throw new AmazonServiceException(operationCancelledException);
                    }
                }
                finally
                {
                    if (responseMessage != null && (response == null || !state.Unmarshaller.HasStreamingProperty))
                    {
                        responseMessage.Dispose();
                        responseMessage = null;
                    }
                }

                if (shouldRetry)
                {
                    pauseExponentially(state);
                    cancellationToken.ThrowIfCancellationRequested();
                    state.RetriesAttempt++;
                    var retryResponse = await InvokeHelper <T>(state, cancellationToken)
                                        .ConfigureAwait(continueOnCapturedContext: false);

                    return((T)retryResponse);
                }
                else
                {
                    LogFinalMetrics(state.Metrics);
                }
            }
            catch (Exception e)
            {
                // On errors that are passed to the client, invoke exception handlers
                if (!shouldRetry)
                {
                    ProcessExceptionHandlers(e, state.Request);
                }
                throw;
            }
            finally
            {
                // ProcessResponseHandlers is called only if a response was received from the server (success or error).
                // It's not called in case of client errors like timeout or proxy error.
                if (!shouldRetry && responseReceived)
                {
                    ProcessResponseHandlers(response, state.Request, responseData);
                }
            }

            return(response);
        }