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); }