private static void EndSendStreamCopy <T>(ExecutionState <T> executionState) { lock (executionState.CancellationLockerObject) { if (Executor.CheckCancellation(executionState)) { if (executionState.Req != null) { try { executionState.Req.Abort(); } catch (Exception) { // No op } } } if (executionState.ExceptionRef != null) { EndOperation(executionState); } else { BeginGetResponse(executionState); } } }
// Note for future maintainers: // In EndOperation<T> above, this method is called and sync-Wait()'d on, with runPostProcess set to false. // This means that, if 'runPostProcess' is ever false, this method must never hit an await point. // This restriction is irrelevant in future versions of the library that only have an async executor. private static async Task EndOperationAsync <T>(ExecutionState <T> executionState, bool runPostProcess) { Executor.FinishRequestAttempt(executionState); try { // If an operation has been canceled of timed out this should overwrite any exception Executor.CheckCancellation(executionState); Executor.CheckTimeout(executionState, true); // Success if (executionState.ExceptionRef == null) { if (runPostProcess) { // Step 9 - This will not be called if an exception is raised during stream copying await Executor.RunPostProcessAsync(executionState); } DisposeAndEnd <T>(executionState); executionState.OnComplete(); return; } } catch (Exception ex) { Logger.LogWarning(executionState.OperationContext, SR.TracePostProcessError, ex.Message); executionState.ExceptionRef = ExecutorBase.TranslateExceptionBasedOnParseError(ex, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseError); } finally { try { if (executionState.ReqStream != null) { executionState.ReqStream.Dispose(); executionState.ReqStream = null; } if (executionState.Resp != null) { executionState.Resp.Close(); executionState.Resp = null; } } catch (Exception) { // no op } } ConsiderRetry <T>(executionState); }
public static void InitRequest <T>(ExecutionState <T> executionState) { try { executionState.Init(); // 0. Begin Request Executor.StartRequestAttempt(executionState); // Steps 1 - 4 Logger.LogInformational(executionState.OperationContext, SR.TraceStartRequestAsync, executionState.Cmd.Uri); Executor.ProcessStartOfRequest(executionState); if (Executor.CheckTimeout <T>(executionState, false)) { Executor.EndOperation(executionState); return; } lock (executionState.CancellationLockerObject) { if (Executor.CheckCancellation(executionState)) { Executor.EndOperation(executionState); return; } // 5. potentially upload data if (executionState.RestCMD.SendStream != null) { Executor.BeginGetRequestStream(executionState); } else { Executor.BeginGetResponse(executionState); } } } catch (Exception ex) { Logger.LogError(executionState.OperationContext, SR.TraceInitRequestError, ex.Message); // Store exception and invoke callback 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; executionState.OnComplete(); } }
private static void EndSendStreamCopy <T>(ExecutionState <T> executionState) { lock (executionState.CancellationLockerObject) { if (Executor.CheckCancellation(executionState)) { if (executionState.Req != null) { try { executionState.Req.Abort(); } catch (Exception) { // No op } } } if (executionState.ExceptionRef != null) { EndOperation(executionState); } else { try { executionState.ReqStream.Flush(); executionState.ReqStream.Dispose(); executionState.ReqStream = null; } catch (Exception) { // If we could not flush/dispose the request stream properly, // BeginGetResponse will fail with a more meaningful error anyway. } BeginGetResponse(executionState); } } }
private static void EndSendStreamCopy <T>(ExecutionState <T> executionState) { executionState.CurrentOperation = ExecutorOperation.EndUploadRequest; Executor.CheckCancellation(executionState); if (executionState.ExceptionRef != null) { try { executionState.Req.Abort(); } catch (Exception) { // No op } Executor.EndOperation(executionState); } else { try { executionState.ReqStream.Flush(); executionState.ReqStream.Dispose(); executionState.ReqStream = null; } catch (Exception) { // If we could not flush/dispose the request stream properly, // BeginGetResponse will fail with a more meaningful error anyway. } Executor.BeginGetResponse(executionState); } }
private static void EndOperation <T>(ExecutionState <T> executionState) { Executor.FinishRequestAttempt(executionState); try { // If an operation has been canceled of timed out this should overwrite any exception Executor.CheckCancellation(executionState); Executor.CheckTimeout(executionState, true); // Success if (executionState.ExceptionRef == null) { // Step 9 - This will not be called if an exception is raised during stream copying Executor.ProcessEndOfRequest(executionState); executionState.OnComplete(); return; } } catch (Exception ex) { Logger.LogWarning(executionState.OperationContext, SR.TracePostProcessError, ex.Message); executionState.ExceptionRef = ExecutorBase.TranslateExceptionBasedOnParseError(ex, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseError); } finally { try { if (executionState.ReqStream != null) { executionState.ReqStream.Dispose(); executionState.ReqStream = null; } if (executionState.Resp != null) { executionState.Resp.Close(); executionState.Resp = null; } } catch (Exception) { // no op } } // Handle Retry try { StorageException translatedException = TranslateExceptionBasedOnParseError(executionState.ExceptionRef, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseError); executionState.ExceptionRef = translatedException; Logger.LogInformational(executionState.OperationContext, SR.TraceRetryCheck, executionState.RetryCount, executionState.Cmd.CurrentResult.HttpStatusCode, translatedException.IsRetryable ? "yes" : "no", translatedException.Message); bool shouldRetry = false; TimeSpan delay = TimeSpan.Zero; if (translatedException.IsRetryable && (executionState.RetryPolicy != null)) { executionState.CurrentLocation = Executor.GetNextLocation(executionState.CurrentLocation, executionState.RestCMD.LocationMode); Logger.LogInformational(executionState.OperationContext, SR.TraceNextLocation, executionState.CurrentLocation); IExtendedRetryPolicy extendedRetryPolicy = executionState.RetryPolicy as IExtendedRetryPolicy; if (extendedRetryPolicy != null) { RetryContext retryContext = new RetryContext( executionState.RetryCount++, executionState.Cmd.CurrentResult, executionState.CurrentLocation, executionState.RestCMD.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; executionState.RestCMD.LocationMode = retryInfo.UpdatedLocationMode; delay = retryInfo.RetryInterval; } } else { 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; } } 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); // No Retry executionState.OnComplete(); } else { if (executionState.Cmd.RecoveryAction != null) { // I.E. Rewind stream etc. executionState.Cmd.RecoveryAction(executionState.Cmd, executionState.ExceptionRef, executionState.OperationContext); } if (delay > TimeSpan.Zero) { Logger.LogInformational(executionState.OperationContext, SR.TraceRetryDelay, (int)delay.TotalMilliseconds); executionState.UpdateCompletedSynchronously(false); if (executionState.BackoffTimer == null) { executionState.BackoffTimer = new Timer( Executor.RetryRequest <T>, executionState, (int)delay.TotalMilliseconds, Timeout.Infinite); } else { executionState.BackoffTimer.Change((int)delay.TotalMilliseconds, Timeout.Infinite); } executionState.CancelDelegate = () => { // Disabling the timer here, but there is still a scenario where the user calls cancel after // the timer starts the next retry but before it sets the CancelDelegate back to null. However, even // if that happens, next retry will start and then stop immediately because of the cancelled flag. Timer backoffTimer = executionState.BackoffTimer; if (backoffTimer != null) { executionState.BackoffTimer = null; backoffTimer.Dispose(); } Logger.LogWarning(executionState.OperationContext, SR.TraceAbortRetry); Executor.CheckCancellation(executionState); executionState.OnComplete(); }; } else { // Start next request immediately Executor.RetryRequest <T>(executionState); } } } catch (Exception ex) { // Catch all ( i.e. users retry policy throws etc.) Logger.LogWarning(executionState.OperationContext, SR.TraceRetryError, ex.Message); executionState.ExceptionRef = ExecutorBase.TranslateExceptionBasedOnParseError(ex, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseError); executionState.OnComplete(); } }
private static void EndGetResponse <T>(IAsyncResult getResponseResult) { ExecutionState <T> executionState = (ExecutionState <T>)getResponseResult.AsyncState; executionState.CurrentOperation = ExecutorOperation.EndGetResponse; try { executionState.UpdateCompletedSynchronously(getResponseResult.CompletedSynchronously); try { executionState.Resp = executionState.Req.EndGetResponse(getResponseResult) as HttpWebResponse; } catch (WebException ex) { Logger.LogWarning(executionState.OperationContext, SR.TraceGetResponseError, ex.Message); executionState.Resp = (HttpWebResponse)ex.Response; if (ex.Status == WebExceptionStatus.Timeout || executionState.ReqTimedOut) { throw new TimeoutException(); } if (executionState.Resp == null) { throw; } else { // Store this exception for now. It will be parsed/thrown after the stream is read in step 8 executionState.ExceptionRef = ex; } } 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 (executionState.RestCMD.PreProcessResponse != null) { executionState.CurrentOperation = ExecutorOperation.PreProcess; try { executionState.Result = executionState.RestCMD.PreProcessResponse(executionState.RestCMD, executionState.Resp, executionState.ExceptionRef, executionState.OperationContext); // clear exception executionState.ExceptionRef = null; Logger.LogInformational(executionState.OperationContext, SR.TracePreProcessDone); } catch (Exception ex) { executionState.ExceptionRef = ex; } } Executor.CheckCancellation(executionState); executionState.CurrentOperation = ExecutorOperation.GetResponseStream; executionState.RestCMD.ResponseStream = executionState.Resp.GetResponseStream(); // 8. (Potentially reads stream from server) if (executionState.ExceptionRef != null) { executionState.CurrentOperation = ExecutorOperation.BeginDownloadResponse; Logger.LogInformational(executionState.OperationContext, SR.TraceDownloadError); executionState.RestCMD.ErrorStream = new MemoryStream(); executionState.RestCMD.ResponseStream.WriteToAsync(executionState.RestCMD.ErrorStream, null /* copyLength */, null /* maxLength */, false /* calculateMd5 */, executionState, new StreamDescriptor(), EndResponseStreamCopy); } else { if (!executionState.RestCMD.RetrieveResponseStream) { executionState.RestCMD.DestinationStream = Stream.Null; } if (executionState.RestCMD.DestinationStream != null) { if (executionState.RestCMD.StreamCopyState == null) { executionState.RestCMD.StreamCopyState = new StreamDescriptor(); } executionState.CurrentOperation = ExecutorOperation.BeginDownloadResponse; Logger.LogInformational(executionState.OperationContext, SR.TraceDownload); executionState.RestCMD.ResponseStream.WriteToAsync(executionState.RestCMD.DestinationStream, null /* copyLength */, null /* maxLength */, executionState.RestCMD.CalculateMd5ForResponseStream, executionState, executionState.RestCMD.StreamCopyState, EndResponseStreamCopy); } else { // Dont want to copy stream, just want to consume it so end Executor.EndOperation(executionState); } } } catch (Exception ex) { Logger.LogWarning(executionState.OperationContext, SR.TracePreProcessError, ex.Message); executionState.ExceptionRef = ExecutorBase.TranslateExceptionBasedOnParseError(ex, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseError); Executor.EndOperation(executionState); } }
private static void EndOperation <T>(ExecutionState <T> executionState) { Executor.FinishRequestAttempt(executionState); lock (executionState.CancellationLockerObject) { try { // If an operation has been canceled of timed out this should overwtie any exception Executor.CheckCancellation(executionState); Executor.CheckTimeout(executionState, true); // Success if (executionState.ExceptionRef == null) { // Step 9 - This will not be called if an exception is raised during stream copying Executor.ProcessEndOfRequest(executionState, executionState.ExceptionRef); executionState.OnComplete(); return; } } catch (Exception ex) { executionState.ExceptionRef = StorageException.TranslateException(ex, executionState.Cmd.CurrentResult); } finally { try { if (executionState.Resp != null) { executionState.Resp.Close(); executionState.Resp = null; } } catch (Exception) { // no op } } } // Handle Retry try { StorageException translatedException = StorageException.TranslateException(executionState.ExceptionRef, executionState.Cmd.CurrentResult); executionState.ExceptionRef = translatedException; TimeSpan delay = TimeSpan.FromMilliseconds(0); bool 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; if (!shouldRetry || (executionState.OperationExpiryTime.HasValue && (DateTime.Now + delay).CompareTo(executionState.OperationExpiryTime.Value) > 0)) { // No Retry executionState.OnComplete(); } else { if (executionState.Cmd.RecoveryAction != null) { // I.E. Rewind stream etc. executionState.Cmd.RecoveryAction(executionState.Cmd, executionState.ExceptionRef, executionState.OperationContext); } if (delay > TimeSpan.Zero) { Timer backoffTimer = null; backoffTimer = new Timer( (obj) => { executionState.CompletedSynchronously = false; backoffTimer.Change(Timeout.Infinite, Timeout.Infinite); backoffTimer.Dispose(); InitRequest(executionState); }, null /* state */, (int)delay.TotalMilliseconds, Timeout.Infinite); } else { // Start Next Request Immediately InitRequest(executionState); } } } catch (Exception ex) { // Catch all ( i.e. users retry policy throws etc.) executionState.ExceptionRef = StorageException.TranslateException(ex, executionState.Cmd.CurrentResult); executionState.OnComplete(); } }
private static void BeginGetRequestStream <T>(ExecutionState <T> executionState) { try { APMWithTimeout.RunWithTimeout( executionState.Req.BeginGetRequestStream, (getRequestStreamResp) => { executionState.CompletedSynchronously = executionState.CompletedSynchronously && getRequestStreamResp.CompletedSynchronously; try { executionState.ReqStream = executionState.Req.EndGetRequestStream(getRequestStreamResp); // don't calculate md5 here as we should have already set this for auth purposes executionState.RestCMD.SendStream.WriteToAsync(executionState.ReqStream, null /* maxLength */, executionState.OperationExpiryTime, false, executionState, executionState.OperationContext, null /* streamCopyState */, EndSendStreamCopy); } catch (WebException ex) { if (ex.Status == WebExceptionStatus.RequestCanceled) { // If the below condition is true, ExceptionRef is set anyway, so don't touch it if (!Executor.CheckCancellation(executionState)) { executionState.ExceptionRef = Exceptions.GenerateTimeoutException(executionState.Cmd.CurrentResult, null); } } else { executionState.ExceptionRef = StorageException.TranslateException(ex, executionState.Cmd.CurrentResult); } EndOperation(executionState); } catch (Exception ex) { executionState.ExceptionRef = StorageException.TranslateException(ex, executionState.Cmd.CurrentResult); EndOperation(executionState); } }, null, executionState.RemainingTimeout, (getRequestStreamResp) => { try { executionState.ReqTimedOut = true; executionState.Req.Abort(); } catch (Exception) { // no op } }); } catch (Exception ex) { executionState.ExceptionRef = StorageException.TranslateException(ex, executionState.Cmd.CurrentResult); EndOperation(executionState); } }