private static void ProcessStartOfRequest <T>(ExecutionState <T> executionState, string startLogMessage) { executionState.CurrentOperation = ExecutorOperation.BeginOperation; // 1. Build request Uri uri = executionState.RestCMD.StorageUri.GetUri(executionState.CurrentLocation); Uri transformedUri = executionState.RestCMD.Credentials.TransformUri(uri); Logger.LogInformational(executionState.OperationContext, startLogMessage, transformedUri); UriQueryBuilder builder = new UriQueryBuilder(executionState.RestCMD.Builder); // Note - The service accepts both api-version and x-ms-version and therefore it is ok to add x-ms-version to all requests. executionState.Req = executionState.RestCMD.BuildRequestDelegate(transformedUri, builder, executionState.Cmd.ServerTimeoutInSeconds, true, executionState.OperationContext); executionState.CancelDelegate = executionState.Req.Abort; // 2. Set Headers Executor.ApplyUserHeaders(executionState); if (executionState.RestCMD.SetHeaders != null) { executionState.RestCMD.SetHeaders(executionState.Req, executionState.OperationContext); } // Set Content-Length and do minor tweaks if (executionState.RestCMD.SendStream != null) { long streamLength = executionState.RestCMD.SendStreamLength ?? executionState.RestCMD.SendStream.Length - executionState.RestCMD.SendStream.Position; CommonUtility.ApplyRequestOptimizations(executionState.Req, streamLength); } else { CommonUtility.ApplyRequestOptimizations(executionState.Req, -1); } // Let the user know we are ready to send Executor.FireSendingRequest(executionState); // 3. Sign Request if (executionState.RestCMD.SignRequest != null) { executionState.RestCMD.SignRequest(executionState.Req, executionState.OperationContext); } #if SYNC // 4. Set timeout (this is actually not honored by asynchronous requests) executionState.Req.Timeout = (int)executionState.RemainingTimeout.TotalMilliseconds; #endif }
private static void ProcessStartOfRequest <T>(ExecutionState <T> executionState, string startLogMessage, CancellationTokenSource timeoutTokenSource = null) { RESTCommand <T> cmd = executionState.RestCMD; // 1. Build request and content executionState.CurrentOperation = ExecutorOperation.BeginOperation; // Content is re-created every retry, as HttpClient disposes it after a successful request HttpContent content = cmd.BuildContent != null?cmd.BuildContent(cmd, executionState.OperationContext) : null; // This is so the old auth header etc is cleared out, the content is where serialization occurs which is the major perf hit Uri uri = cmd.StorageUri.GetUri(executionState.CurrentLocation); Uri transformedUri = cmd.Credentials.TransformUri(uri); Logger.LogInformational(executionState.OperationContext, SR.TraceStartRequestAsync, transformedUri); UriQueryBuilder builder = new UriQueryBuilder(executionState.RestCMD.Builder); executionState.Req = cmd.BuildRequest(cmd, transformedUri, builder, content, cmd.ServerTimeoutInSeconds, executionState.OperationContext); // 2. Set Headers Executor.ApplyUserHeaders(executionState); // Let the user know we are ready to send Executor.FireSendingRequest(executionState); // 3. Sign Request is not needed, as HttpClient will call us // 4. Set timeout (this is actually not honored by asynchronous requests) if (executionState.OperationExpiryTime.HasValue) { // set the token to cancel after timing out, if the higher token hasn't already been cancelled timeoutTokenSource?.CancelAfter(executionState.RemainingTimeout); } else { // effectively prevent timeout timeoutTokenSource?.CancelAfter(int.MaxValue); } }
private static void ProcessStartOfRequest <T>(ExecutionState <T> executionState) { executionState.CurrentOperation = ExecutorOperation.BeginOperation; // 1. Build request executionState.Req = executionState.RestCMD.BuildRequestDelegate(executionState.Cmd.Uri, executionState.Cmd.Builder, executionState.Cmd.ServerTimeoutInSeconds, executionState.OperationContext); executionState.CancelDelegate = executionState.Req.Abort; // 2. Set Headers Executor.ApplyUserHeaders(executionState); if (executionState.RestCMD.SetHeaders != null) { executionState.RestCMD.SetHeaders(executionState.Req, executionState.OperationContext); } // Set Content-Length and do minor tweaks if (executionState.RestCMD.SendStream != null) { long streamLength = executionState.RestCMD.SendStreamLength ?? executionState.RestCMD.SendStream.Length - executionState.RestCMD.SendStream.Position; CommonUtility.ApplyRequestOptimizations(executionState.Req, streamLength); } else { CommonUtility.ApplyRequestOptimizations(executionState.Req, -1); } // Let the user know we are ready to send Executor.FireSendingRequest(executionState); // 3. Sign Request if (executionState.RestCMD.SignRequest != null) { executionState.RestCMD.SignRequest(executionState.Req, executionState.OperationContext); } #if SYNC // 4. Set timeout (this is actually not honored by asynchronous requests) executionState.Req.Timeout = (int)executionState.RemainingTimeout.TotalMilliseconds; #endif }
private static void ProcessStartOfRequest <T>(ExecutionState <T> executionState) { // 1. Build request executionState.Req = executionState.RestCMD.BuildRequestDelegate(executionState.Cmd.Uri, executionState.Cmd.Builder, executionState.Cmd.ServerTimeoutInSeconds, executionState.OperationContext); // User Headers Executor.ApplyUserHeaders(executionState); executionState.CancelDelegate = executionState.Req.Abort; // 2. Set Headers if (executionState.RestCMD.SetHeaders != null) { executionState.RestCMD.SetHeaders(executionState.Req, executionState.OperationContext); } if (executionState.RestCMD.SendStream != null) { CommonUtils.ApplyRequestOptimizations(executionState.Req, executionState.RestCMD.SendStream.Length - executionState.RestCMD.SendStream.Position); } else { CommonUtils.ApplyRequestOptimizations(executionState.Req, -1); } Executor.FireSendingRequest(executionState); // 3. Sign Request if (executionState.RestCMD.SignRequest != null) { executionState.RestCMD.SignRequest(executionState.Req, executionState.OperationContext); } // 4. Set timeout (this is actually not honored by asynchronous requests) executionState.Req.Timeout = executionState.RemainingTimeout; }
private async static Task <T> ExecuteAsyncInternal <T>(RESTCommand <T> cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token) { // Note all code below will reference state, not params directly, this will allow common code with multiple executors (APM, Sync, Async) using (ExecutionState <T> executionState = new ExecutionState <T>(cmd, policy, operationContext)) using (CancellationTokenSource timeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token)) { bool shouldRetry = false; TimeSpan delay = TimeSpan.Zero; // Note - The service accepts both api-version and x-ms-version and therefore it is ok to add x-ms-version to all requests. // Create a new client HttpClient client = HttpClientFactory.Instance; do { try { executionState.Init(); // 0. Begin Request Executor.StartRequestAttempt(executionState); // 1. Build request and content executionState.CurrentOperation = ExecutorOperation.BeginOperation; // Content is re-created every retry, as HttpClient disposes it after a successful request HttpContent content = cmd.BuildContent != null?cmd.BuildContent(cmd, executionState.OperationContext) : null; // This is so the old auth header etc is cleared out, the content is where serialization occurs which is the major perf hit Uri uri = cmd.StorageUri.GetUri(executionState.CurrentLocation); Uri transformedUri = cmd.Credentials.TransformUri(uri); Logger.LogInformational(executionState.OperationContext, SR.TraceStartRequestAsync, transformedUri); UriQueryBuilder builder = new UriQueryBuilder(executionState.RestCMD.Builder); executionState.Req = cmd.BuildRequest(cmd, transformedUri, builder, content, cmd.ServerTimeoutInSeconds, executionState.OperationContext); // 2. Set Headers Executor.ApplyUserHeaders(executionState); // Let the user know we are ready to send Executor.FireSendingRequest(executionState); // 3. Sign Request is not needed, as HttpClient will call us // 4. Set timeout if (executionState.OperationExpiryTime.HasValue) { // set the token to cancel after timing out, if the higher token hasn't already been cancelled timeoutTokenSource.CancelAfter(executionState.RemainingTimeout); } else { // effectively prevent timeout timeoutTokenSource.CancelAfter(int.MaxValue); } Executor.CheckTimeout <T>(executionState, true); } catch (Exception ex) { Logger.LogError(executionState.OperationContext, SR.TraceInitRequestError, ex.Message); // Store exception and throw here. All operations in this try would be non-retryable by default StorageException storageEx = StorageException.TranslateException(ex, executionState.Cmd.CurrentResult, null); storageEx.IsRetryable = false; executionState.ExceptionRef = storageEx; throw executionState.ExceptionRef; } // Enter Retryable Section of execution try { // Send Request executionState.CurrentOperation = ExecutorOperation.BeginGetResponse; Logger.LogInformational(executionState.OperationContext, SR.TraceGetResponse); executionState.Resp = await client.SendAsync(executionState.Req, HttpCompletionOption.ResponseHeadersRead, timeoutTokenSource.Token); executionState.CurrentOperation = ExecutorOperation.EndGetResponse; // Since HttpClient wont throw for non success, manually check and populate an exception if (!executionState.Resp.IsSuccessStatusCode) { // At this point, don't try to read the stream to parse the error executionState.ExceptionRef = await Exceptions.PopulateStorageExceptionFromHttpResponseMessage(executionState.Resp, executionState.Cmd.CurrentResult, executionState.Cmd.ParseError); } Logger.LogInformational(executionState.OperationContext, SR.TraceResponse, executionState.Cmd.CurrentResult.HttpStatusCode, executionState.Cmd.CurrentResult.ServiceRequestID, executionState.Cmd.CurrentResult.ContentMd5, executionState.Cmd.CurrentResult.Etag); Executor.FireResponseReceived(executionState); // 7. Do Response parsing (headers etc, no stream available here) if (cmd.PreProcessResponse != null) { executionState.CurrentOperation = ExecutorOperation.PreProcess; try { executionState.Result = cmd.PreProcessResponse(cmd, executionState.Resp, executionState.ExceptionRef, executionState.OperationContext); // clear exception executionState.ExceptionRef = null; } catch (Exception ex) { executionState.ExceptionRef = ex; } Logger.LogInformational(executionState.OperationContext, SR.TracePreProcessDone); } // 8. (Potentially reads stream from server) executionState.CurrentOperation = ExecutorOperation.GetResponseStream; cmd.ResponseStream = await executionState.Resp.Content.ReadAsStreamAsync(); // The stream is now available in ResponseStream. Use the stream to parse out the response or error if (executionState.ExceptionRef != null) { executionState.CurrentOperation = ExecutorOperation.BeginDownloadResponse; Logger.LogInformational(executionState.OperationContext, SR.TraceDownloadError); try { cmd.ErrorStream = new MemoryStream(); await cmd.ResponseStream.WriteToAsync(cmd.ErrorStream, null /* copyLength */, null /* maxLength */, false, executionState, new StreamDescriptor(), timeoutTokenSource.Token); cmd.ErrorStream.Seek(0, SeekOrigin.Begin); #if ASPNET_K || PORTABLE executionState.ExceptionRef = StorageException.TranslateExceptionWithPreBufferedStream(executionState.ExceptionRef, executionState.Cmd.CurrentResult, stream => executionState.Cmd.ParseError(stream, executionState.Resp, null), cmd.ErrorStream, executionState.Resp); #else executionState.ExceptionRef = StorageException.TranslateExceptionWithPreBufferedStream(executionState.ExceptionRef, executionState.Cmd.CurrentResult, stream => executionState.Cmd.ParseError(stream, executionState.Resp, null), cmd.ErrorStream); #endif throw executionState.ExceptionRef; } finally { cmd.ResponseStream.Dispose(); cmd.ResponseStream = null; cmd.ErrorStream.Dispose(); cmd.ErrorStream = null; } } else { if (!cmd.RetrieveResponseStream) { cmd.DestinationStream = Stream.Null; } if (cmd.DestinationStream != null) { if (cmd.StreamCopyState == null) { cmd.StreamCopyState = new StreamDescriptor(); } try { executionState.CurrentOperation = ExecutorOperation.BeginDownloadResponse; Logger.LogInformational(executionState.OperationContext, SR.TraceDownload); await cmd.ResponseStream.WriteToAsync(cmd.DestinationStream, null /* copyLength */, null /* maxLength */, cmd.CalculateMd5ForResponseStream, executionState, cmd.StreamCopyState, timeoutTokenSource.Token); } finally { cmd.ResponseStream.Dispose(); cmd.ResponseStream = null; } } } // 9. Evaluate Response & Parse Results, (Stream potentially available here) if (cmd.PostProcessResponse != null) { executionState.CurrentOperation = ExecutorOperation.PostProcess; Logger.LogInformational(executionState.OperationContext, SR.TracePostProcess); executionState.Result = await cmd.PostProcessResponse(cmd, executionState.Resp, executionState.OperationContext); } executionState.CurrentOperation = ExecutorOperation.EndOperation; Logger.LogInformational(executionState.OperationContext, SR.TraceSuccess); Executor.FinishRequestAttempt(executionState); return(executionState.Result); } catch (Exception e) { Logger.LogWarning(executionState.OperationContext, SR.TraceGenericError, e.Message); Executor.FinishRequestAttempt(executionState); if (e is TaskCanceledException && (executionState.OperationExpiryTime.HasValue && DateTime.Now.CompareTo(executionState.OperationExpiryTime.Value) > 0)) { e = new TimeoutException(SR.TimeoutExceptionMessage, e); } #if ASPNET_K || PORTABLE StorageException translatedException = StorageException.TranslateException(e, executionState.Cmd.CurrentResult, stream => executionState.Cmd.ParseError(stream, executionState.Resp, null), executionState.Resp); #else StorageException translatedException = StorageException.TranslateException(e, executionState.Cmd.CurrentResult, stream => executionState.Cmd.ParseError(stream, executionState.Resp, null)); #endif executionState.ExceptionRef = translatedException; Logger.LogInformational(executionState.OperationContext, SR.TraceRetryCheck, executionState.RetryCount, executionState.Cmd.CurrentResult.HttpStatusCode, translatedException.IsRetryable ? "yes" : "no", translatedException.Message); shouldRetry = false; if (translatedException.IsRetryable && (executionState.RetryPolicy != null)) { executionState.CurrentLocation = Executor.GetNextLocation(executionState.CurrentLocation, cmd.LocationMode); Logger.LogInformational(executionState.OperationContext, SR.TraceNextLocation, executionState.CurrentLocation); IExtendedRetryPolicy extendedRetryPolicy = executionState.RetryPolicy as IExtendedRetryPolicy; if (extendedRetryPolicy != null) { RetryContext retryContext = new RetryContext( executionState.RetryCount++, cmd.CurrentResult, executionState.CurrentLocation, cmd.LocationMode); RetryInfo retryInfo = extendedRetryPolicy.Evaluate(retryContext, executionState.OperationContext); if (retryInfo != null) { Logger.LogInformational(executionState.OperationContext, SR.TraceRetryInfo, retryInfo.TargetLocation, retryInfo.UpdatedLocationMode); shouldRetry = true; executionState.CurrentLocation = retryInfo.TargetLocation; cmd.LocationMode = retryInfo.UpdatedLocationMode; delay = retryInfo.RetryInterval; } } else { shouldRetry = executionState.RetryPolicy.ShouldRetry( executionState.RetryCount++, cmd.CurrentResult.HttpStatusCode, executionState.ExceptionRef, out delay, executionState.OperationContext); } if ((delay < TimeSpan.Zero) || (delay > Constants.MaximumRetryBackoff)) { delay = Constants.MaximumRetryBackoff; } } } finally { if (executionState.Resp != null) { executionState.Resp.Dispose(); executionState.Resp = null; } } // potentially backoff if (!shouldRetry || (executionState.OperationExpiryTime.HasValue && (DateTime.Now + delay).CompareTo(executionState.OperationExpiryTime.Value) > 0)) { Logger.LogError(executionState.OperationContext, shouldRetry ? SR.TraceRetryDecisionTimeout : SR.TraceRetryDecisionPolicy, executionState.ExceptionRef.Message); throw executionState.ExceptionRef; } else { if (cmd.RecoveryAction != null) { // I.E. Rewind stream etc. cmd.RecoveryAction(cmd, executionState.Cmd.CurrentResult.Exception, executionState.OperationContext); } if (delay > TimeSpan.Zero) { await Task.Delay(delay, token); } Logger.LogInformational(executionState.OperationContext, SR.TraceRetry); } Executor.FireRetrying(executionState); }while (shouldRetry); // should never get here throw new NotImplementedException(SR.InternalStorageError); } }
private async static Task <T> ExecuteAsyncInternal <T>(RESTCommand <T> cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token) { // Note all code below will reference state, not params directly, this will allow common code with multiple executors (APM, Sync, Async) ExecutionState <T> executionState = new ExecutionState <T>(cmd, policy, operationContext); bool shouldRetry = false; TimeSpan delay = TimeSpan.FromMilliseconds(0); // Steps 1-4 HttpClient client = cmd.BuildClient(cmd, operationContext); if (executionState.OperationExpiryTime.HasValue) { client.Timeout = executionState.OperationExpiryTime.Value - DateTime.Now; } else { client.Timeout = TimeSpan.FromDays(15); } // Setup token CancellationTokenSource cts = new CancellationTokenSource(client.Timeout); CancellationToken tokenWithTimeout = cts.Token; // Hookup users token token.Register(() => cts.Cancel()); do { Executor.StartRequestAttempt(executionState); // Enter Retryable Section of execution try { // Typically this would be out of the try, but do to the unique need for RTMD to translate exception it is included. if (executionState.OperationExpiryTime.HasValue && executionState.Cmd.CurrentResult.StartTime.CompareTo(executionState.OperationExpiryTime.Value) > 0) { throw Exceptions.GenerateTimeoutException(executionState.Cmd.CurrentResult, null); } // Content is re-created every retry, as HttpClient disposes it after a successful request HttpContent content = cmd.BuildContent != null?cmd.BuildContent(cmd, operationContext) : null; // This is so the old auth header etc is cleared out, the content is where serialization occurs which is the major perf hit executionState.Req = cmd.BuildRequest(cmd, content, operationContext); // User Headers Executor.ApplyUserHeaders(executionState); Executor.FireSendingRequest(executionState); // Send Request executionState.Resp = await client.SendAsync(executionState.Req, HttpCompletionOption.ResponseHeadersRead, tokenWithTimeout); // Since HttpClient wont throw for non success, manually check and populate an exception if (!executionState.Resp.IsSuccessStatusCode) { executionState.ExceptionRef = await Exceptions.PopulateStorageExceptionFromHttpResponseMessage(executionState.Resp, executionState.Cmd.CurrentResult); } Executor.FireResponseReceived(executionState); // 7. Do Response parsing (headers etc, no stream available here) if (cmd.PreProcessResponse != null) { executionState.Result = cmd.PreProcessResponse(cmd, executionState.Resp, executionState.ExceptionRef, executionState.OperationContext); // clear exception executionState.ExceptionRef = null; } // 8. (Potentially reads stream from server) if (cmd.RetrieveResponseStream) { cmd.ResponseStream = await executionState.Resp.Content.ReadAsStreamAsync(); } if (cmd.DestinationStream != null) { if (cmd.StreamCopyState == null) { cmd.StreamCopyState = new StreamDescriptor(); } await cmd.ResponseStream.WriteToAsync(cmd.DestinationStream, null /* MaxLength */, cmd.CalculateMd5ForResponseStream, executionState.OperationContext, cmd.StreamCopyState, tokenWithTimeout); cmd.ResponseStream.Dispose(); } // 9. Evaluate Response & Parse Results, (Stream potentially available here) if (cmd.PostProcessResponse != null) { executionState.Result = await cmd.PostProcessResponse(cmd, executionState.Resp, executionState.ExceptionRef, executionState.OperationContext); } Executor.FinishRequestAttempt(executionState); return(executionState.Result); } catch (Exception e) { Executor.FinishRequestAttempt(executionState); if (e is TaskCanceledException && (executionState.OperationExpiryTime.HasValue && DateTime.Now.CompareTo(executionState.OperationExpiryTime.Value) > 0)) { e = new TimeoutException(SR.TimeoutExceptionMessage, e); } StorageException translatedException = StorageException.TranslateException(e, executionState.Cmd.CurrentResult); executionState.ExceptionRef = translatedException; delay = TimeSpan.FromMilliseconds(0); shouldRetry = translatedException.IsRetryable && executionState.RetryPolicy != null? executionState.RetryPolicy.ShouldRetry( executionState.RetryCount++, executionState.Cmd.CurrentResult.HttpStatusCode, executionState.ExceptionRef, out delay, executionState.OperationContext) : false; delay = delay.TotalMilliseconds <0 || delay> Constants.MaximumRetryBackoff ? Constants.MaximumRetryBackoff : delay; } // potentially backoff if (!shouldRetry || (executionState.OperationExpiryTime.HasValue && (DateTime.Now + delay).CompareTo(executionState.OperationExpiryTime.Value) > 0)) { #if RTMD // Need to throw wrapped Exception with message as serialized exception info stuff. int hResult = WrappedStorageException.GenerateHResult(executionState.ExceptionRef, executionState.Cmd.CurrentResult); throw new WrappedStorageException(executionState.Cmd.CurrentResult.WriteAsXml(), executionState.ExceptionRef, hResult); #else throw executionState.ExceptionRef; // throw base exception for desktop #endif } else { if (cmd.RecoveryAction != null) { // I.E. Rewind stream etc. cmd.RecoveryAction(cmd, executionState.Cmd.CurrentResult.Exception, executionState.OperationContext); } if (delay > TimeSpan.Zero) { await Task.Delay(delay); } } }while (shouldRetry); // should never get here throw new NotImplementedException(SR.InternalStorageError); }
private async static Task <T> ExecuteAsyncInternal <T>(RESTCommand <T> cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token) { // Note all code below will reference state, not params directly, this will allow common code with multiple executors (APM, Sync, Async) using (ExecutionState <T> executionState = new ExecutionState <T>(cmd, policy, operationContext)) { bool shouldRetry = false; TimeSpan delay = TimeSpan.Zero; // Create a new client HttpClient client = cmd.BuildClient(cmd, executionState.OperationContext); client.Timeout = TimeSpan.FromMilliseconds(int.MaxValue); do { try { executionState.Init(); // 0. Begin Request Executor.StartRequestAttempt(executionState); // 1. Build request and content executionState.CurrentOperation = ExecutorOperation.BeginOperation; // Content is re-created every retry, as HttpClient disposes it after a successful request HttpContent content = cmd.BuildContent != null?cmd.BuildContent(cmd, executionState.OperationContext) : null; // This is so the old auth header etc is cleared out, the content is where serialization occurs which is the major perf hit Logger.LogInformational(executionState.OperationContext, SR.TraceStartRequestAsync, cmd.Uri); executionState.Req = cmd.BuildRequest(cmd, content, executionState.OperationContext); // 2. Set Headers Executor.ApplyUserHeaders(executionState); // Let the user know we are ready to send Executor.FireSendingRequest(executionState); // 3. Sign Request is not needed, as HttpClient will call us // 4. Set timeout if (executionState.OperationExpiryTime.HasValue) { client.Timeout = executionState.RemainingTimeout; } Executor.CheckTimeout <T>(executionState, true); } catch (Exception ex) { Logger.LogError(executionState.OperationContext, SR.TraceInitRequestError, ex.Message); // Store exception and throw here. All operations in this try would be non-retryable by default StorageException storageEx = StorageException.TranslateException(ex, executionState.Cmd.CurrentResult); storageEx.IsRetryable = false; executionState.ExceptionRef = storageEx; #if WINDOWS_RT // Need to throw wrapped Exception with message as serialized exception info stuff. int hResult = WrappedStorageException.GenerateHResult(executionState.ExceptionRef, executionState.Cmd.CurrentResult); throw new WrappedStorageException(executionState.Cmd.CurrentResult.WriteAsXml(), executionState.ExceptionRef, hResult); #else throw executionState.ExceptionRef; // throw base exception for desktop #endif } // Enter Retryable Section of execution try { // Send Request executionState.CurrentOperation = ExecutorOperation.BeginGetResponse; Logger.LogInformational(executionState.OperationContext, SR.TraceGetResponse); executionState.Resp = await client.SendAsync(executionState.Req, HttpCompletionOption.ResponseHeadersRead, token); executionState.CurrentOperation = ExecutorOperation.EndGetResponse; // Since HttpClient wont throw for non success, manually check and populate an exception if (!executionState.Resp.IsSuccessStatusCode) { executionState.ExceptionRef = await Exceptions.PopulateStorageExceptionFromHttpResponseMessage(executionState.Resp, executionState.Cmd.CurrentResult); } Logger.LogInformational(executionState.OperationContext, SR.TraceResponse, executionState.Cmd.CurrentResult.HttpStatusCode, executionState.Cmd.CurrentResult.ServiceRequestID, executionState.Cmd.CurrentResult.ContentMd5, executionState.Cmd.CurrentResult.Etag); Executor.FireResponseReceived(executionState); // 7. Do Response parsing (headers etc, no stream available here) if (cmd.PreProcessResponse != null) { executionState.CurrentOperation = ExecutorOperation.PreProcess; executionState.Result = cmd.PreProcessResponse(cmd, executionState.Resp, executionState.ExceptionRef, executionState.OperationContext); // clear exception executionState.ExceptionRef = null; Logger.LogInformational(executionState.OperationContext, SR.TracePreProcessDone); } // 8. (Potentially reads stream from server) executionState.CurrentOperation = ExecutorOperation.GetResponseStream; cmd.ResponseStream = await executionState.Resp.Content.ReadAsStreamAsync(); if (!cmd.RetrieveResponseStream) { cmd.DestinationStream = Stream.Null; } if (cmd.DestinationStream != null) { if (cmd.StreamCopyState == null) { cmd.StreamCopyState = new StreamDescriptor(); } try { executionState.CurrentOperation = ExecutorOperation.BeginDownloadResponse; Logger.LogInformational(executionState.OperationContext, SR.TraceDownload); await cmd.ResponseStream.WriteToAsync(cmd.DestinationStream, null /* copyLength */, null /* maxLength */, cmd.CalculateMd5ForResponseStream, executionState, cmd.StreamCopyState, token); } finally { cmd.ResponseStream.Dispose(); cmd.ResponseStream = null; } } // 9. Evaluate Response & Parse Results, (Stream potentially available here) if (cmd.PostProcessResponse != null) { executionState.CurrentOperation = ExecutorOperation.PostProcess; Logger.LogInformational(executionState.OperationContext, SR.TracePostProcess); executionState.Result = await cmd.PostProcessResponse(cmd, executionState.Resp, executionState.OperationContext); } executionState.CurrentOperation = ExecutorOperation.EndOperation; Logger.LogInformational(executionState.OperationContext, SR.TraceSuccess); Executor.FinishRequestAttempt(executionState); return(executionState.Result); } catch (Exception e) { Logger.LogWarning(executionState.OperationContext, SR.TraceGenericError, e.Message); Executor.FinishRequestAttempt(executionState); if (e is TaskCanceledException && (executionState.OperationExpiryTime.HasValue && DateTime.Now.CompareTo(executionState.OperationExpiryTime.Value) > 0)) { e = new TimeoutException(SR.TimeoutExceptionMessage, e); } StorageException translatedException = StorageException.TranslateException(e, executionState.Cmd.CurrentResult); executionState.ExceptionRef = translatedException; Logger.LogInformational(executionState.OperationContext, SR.TraceRetryCheck, executionState.RetryCount, executionState.Cmd.CurrentResult.HttpStatusCode, translatedException.IsRetryable ? "yes" : "no", translatedException.Message); shouldRetry = false; if (translatedException.IsRetryable && (executionState.RetryPolicy != null)) { shouldRetry = executionState.RetryPolicy.ShouldRetry( executionState.RetryCount++, executionState.Cmd.CurrentResult.HttpStatusCode, executionState.ExceptionRef, out delay, executionState.OperationContext); if ((delay < TimeSpan.Zero) || (delay > Constants.MaximumRetryBackoff)) { delay = Constants.MaximumRetryBackoff; } } } finally { if (executionState.Resp != null) { executionState.Resp.Dispose(); executionState.Resp = null; } } // potentially backoff if (!shouldRetry || (executionState.OperationExpiryTime.HasValue && (DateTime.Now + delay).CompareTo(executionState.OperationExpiryTime.Value) > 0)) { Logger.LogError(executionState.OperationContext, shouldRetry ? SR.TraceRetryDecisionTimeout : SR.TraceRetryDecisionPolicy, executionState.ExceptionRef.Message); #if WINDOWS_RT // Need to throw wrapped Exception with message as serialized exception info stuff. int hResult = WrappedStorageException.GenerateHResult(executionState.ExceptionRef, executionState.Cmd.CurrentResult); throw new WrappedStorageException(executionState.Cmd.CurrentResult.WriteAsXml(), executionState.ExceptionRef, hResult); #else throw executionState.ExceptionRef; // throw base exception for desktop #endif } else { if (cmd.RecoveryAction != null) { // I.E. Rewind stream etc. cmd.RecoveryAction(cmd, executionState.Cmd.CurrentResult.Exception, executionState.OperationContext); } if (delay > TimeSpan.Zero) { await Task.Delay(delay, token); } Logger.LogInformational(executionState.OperationContext, SR.TraceRetry); } }while (shouldRetry); // should never get here throw new NotImplementedException(SR.InternalStorageError); } }