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); }
private bool HandleHttpWebErrorResponse(AsyncResult asyncResult, WebException we) { asyncResult.Metrics.AddProperty(Metric.Exception, we); HttpStatusCode statusCode; AmazonServiceException errorResponseException = null; using (HttpWebResponse httpErrorResponse = we.Response as HttpWebResponse) { if (we != null && WebExceptionStatusesToThrowOn.Contains(we.Status)) { throw new AmazonServiceException("Encountered a non retryable WebException : " + we.Status, we); } if (httpErrorResponse == null || (we != null && WebExceptionStatusesToRetryOn.Contains(we.Status))) { // Abort the unsuccessful request asyncResult.RequestState.WebRequest.Abort(); if (CanRetry(asyncResult) && asyncResult.RetriesAttempt < Config.MaxErrorRetry) { pauseExponentially(asyncResult); return(true); } var errorMessage = string.Format(CultureInfo.InvariantCulture, "Encountered a WebException ({0}), the request cannot be retried. Either the maximum number of retries has been exceeded ({1}/{2}) or the request is using a non-seekable stream.", we.Status, asyncResult.RetriesAttempt, Config.MaxErrorRetry); throw new AmazonServiceException(errorMessage, we); } statusCode = httpErrorResponse.StatusCode; asyncResult.Metrics.AddProperty(Metric.StatusCode, statusCode); string redirectedLocation = httpErrorResponse.Headers[HeaderKeys.LocationHeader]; asyncResult.Metrics.AddProperty(Metric.RedirectLocation, redirectedLocation); using (httpErrorResponse) { var unmarshaller = asyncResult.Unmarshaller; var httpResponseData = new HttpWebRequestResponseData(httpErrorResponse); UnmarshallerContext errorContext = unmarshaller.CreateContext(httpResponseData, Config.LogResponse || Config.ReadEntireResponse || AWSConfigs.LoggingConfig.LogResponses != ResponseLoggingOption.Never, httpResponseData.ResponseBody.OpenResponse(), asyncResult.Metrics); errorResponseException = unmarshaller.UnmarshallException(errorContext, we, statusCode); if (Config.LogResponse || AWSConfigs.LoggingConfig.LogResponses != ResponseLoggingOption.Never) { this.logger.Error(errorResponseException, "Received error response: [{0}]", errorContext.ResponseBody); } asyncResult.Metrics.AddProperty(Metric.AWSRequestID, errorResponseException.RequestId); asyncResult.Metrics.AddProperty(Metric.AWSErrorCode, errorResponseException.ErrorCode); } asyncResult.RequestState.WebRequest.Abort(); if (CanRetry(asyncResult)) { if (isTemporaryRedirect(statusCode, redirectedLocation)) { this.logger.DebugFormat("Request {0} is being redirected to {1}.", asyncResult.RequestName, redirectedLocation); asyncResult.Request.Endpoint = new Uri(redirectedLocation); return(true); } else if (ShouldRetry(statusCode, this.Config, errorResponseException, asyncResult.RetriesAttempt)) { this.logger.DebugFormat("Retry number {0} for request {1}.", asyncResult.RetriesAttempt, asyncResult.RequestName); pauseExponentially(asyncResult); return(true); } } } if (errorResponseException != null) { this.logger.Error(errorResponseException, "Error making request {0}.", asyncResult.RequestName); throw errorResponseException; } AmazonServiceException excep = new AmazonServiceException("Unable to make request", we, statusCode); this.logger.Error(excep, "Error making request {0}.", asyncResult.RequestName); asyncResult.Metrics.AddProperty(Metric.Exception, excep); throw excep; }