コード例 #1
0
        public static void DateHeader(HttpRequestMessage request, bool required)
        {
            bool standardHeader = HttpRequestParsers.GetDate(request) != null;
            bool msHeader       = HttpRequestParsers.GetHeader(request, "x-ms-date") != null;

            Assert.IsFalse(standardHeader && msHeader);
            Assert.IsFalse(required && !(standardHeader ^ msHeader));

            if (standardHeader)
            {
                try
                {
                    DateTime parsed = DateTime.Parse(HttpRequestParsers.GetDate(request)).ToUniversalTime();
                }
                catch (Exception)
                {
                    Assert.Fail();
                }
            }
            else if (msHeader)
            {
                try
                {
                    DateTime parsed = DateTime.Parse(HttpRequestParsers.GetHeader(request, "x-ms-date")).ToUniversalTime();
                }
                catch (Exception)
                {
                    Assert.Fail();
                }
            }
        }
コード例 #2
0
 public static void ContentDispositionHeader(HttpRequestMessage request, string expectedValue)
 {
     Assert.IsFalse((expectedValue != null) && (HttpRequestParsers.GetHeader(request, Constants.HeaderConstants.FileContentDispositionRequestHeader) == null));
     if (HttpRequestParsers.GetHeader(request, Constants.HeaderConstants.FileContentDispositionRequestHeader) != null)
     {
         Assert.AreEqual(expectedValue, HttpRequestParsers.GetHeader(request, Constants.HeaderConstants.FileContentDispositionRequestHeader));
     }
 }
コード例 #3
0
 public static void VersionHeader(HttpRequestMessage request, bool required)
 {
     Assert.IsFalse(required && (HttpRequestParsers.GetHeader(request, "x-ms-version") == null));
     if (HttpRequestParsers.GetHeader(request, "x-ms-version") != null)
     {
         Assert.AreEqual(Constants.HeaderConstants.TargetStorageVersion, HttpRequestParsers.GetHeader(request, "x-ms-version"));
     }
 }
コード例 #4
0
 /// <summary>
 /// Validates a break period header in a request.
 /// </summary>
 /// <param name="request">The request.</param>
 /// <param name="expectedValue">The expected value.</param>
 public static void BreakPeriodHeader(HttpRequestMessage request, string expectedValue)
 {
     Assert.IsFalse((expectedValue != null) && (HttpRequestParsers.GetHeader(request, "x-ms-lease-break-period") == null));
     if (HttpRequestParsers.GetHeader(request, "x-ms-lease-break-period") != null)
     {
         Assert.AreEqual(expectedValue, HttpRequestParsers.GetHeader(request, "x-ms-lease-break-period"));
     }
 }
コード例 #5
0
 /// <summary>
 /// Validates a lease duration header in a request.
 /// </summary>
 /// <param name="request">The request.</param>
 /// <param name="expectedValue">The expected value.</param>
 public static void LeaseDurationHeader(HttpRequestMessage request, string expectedValue)
 {
     Assert.IsFalse((expectedValue != null) && (HttpRequestParsers.GetHeader(request, "x-ms-lease-duration") == null));
     if (HttpRequestParsers.GetHeader(request, "x-ms-lease-duration") != null)
     {
         Assert.AreEqual(expectedValue, HttpRequestParsers.GetHeader(request, "x-ms-lease-duration"));
     }
 }
コード例 #6
0
 /// <summary>
 /// Validates a proposed lease ID header in a request.
 /// </summary>
 /// <param name="request">The request.</param>
 /// <param name="expectedValue">The expected value.</param>
 public static void ProposedLeaseIdHeader(HttpRequestMessage request, string expectedValue)
 {
     Assert.IsFalse((expectedValue != null) && (HttpRequestParsers.GetHeader(request, "x-ms-proposed-lease-id") == null));
     if (HttpRequestParsers.GetHeader(request, "x-ms-proposed-lease-id") != null)
     {
         Assert.AreEqual(expectedValue, HttpRequestParsers.GetHeader(request, "x-ms-proposed-lease-id"));
     }
 }
コード例 #7
0
 public static void FileTypeHeader(HttpRequestMessage request, string expectedValue)
 {
     Assert.IsFalse((expectedValue != null) && (HttpRequestParsers.GetHeader(request, "x-ms-type") == null));
     if (HttpRequestParsers.GetHeader(request, "x-ms-type") != null)
     {
         Assert.AreEqual(expectedValue, HttpRequestParsers.GetHeader(request, "x-ms-type"));
     }
 }
コード例 #8
0
 public static void ContentEncodingHeader(HttpRequestMessage request, string expectedValue)
 {
     Assert.IsFalse((expectedValue != null) && (HttpRequestParsers.GetContentEncoding(request) == null));
     if (HttpRequestParsers.GetContentEncoding(request) != null)
     {
         Assert.AreEqual(expectedValue, HttpRequestParsers.GetContentEncoding(request));
     }
 }
コード例 #9
0
 public static void FileSizeHeader(HttpRequestMessage request, long?expectedValue)
 {
     Assert.IsFalse((expectedValue != null) && (HttpRequestParsers.GetHeader(request, "x-ms-content-length") == null));
     if (HttpRequestParsers.GetHeader(request, "x-ms-content-length") != null)
     {
         long?parsed = long.Parse(HttpRequestParsers.GetHeader(request, "x-ms-content-length"));
         Assert.IsNotNull(parsed);
         Assert.AreEqual(expectedValue, parsed);
     }
 }
コード例 #10
0
 public static void AuthorizationHeader(HttpRequestMessage request, bool required, string account)
 {
     Assert.IsFalse(required && (HttpRequestParsers.GetAuthorization(request) == null));
     if (HttpRequestParsers.GetAuthorization(request) != null)
     {
         string authorization      = HttpRequestParsers.GetAuthorization(request);
         string pattern            = String.Format("^(SharedKey|SharedKeyLite) {0}:[0-9a-zA-Z\\+/=]{{20,}}$", account);
         Regex  authorizationRegex = new Regex(pattern);
         Assert.IsTrue(authorizationRegex.IsMatch(authorization));
     }
 }
コード例 #11
0
        /// <summary>
        /// Tests for a range header in an HTTP request.
        /// </summary>
        /// <param name="request">The HTTP request.</param>
        /// <param name="expectedStart">The expected beginning of the range, or null if no range is expected.</param>
        /// <param name="expectedEnd">The expected end of the range, or null if no end is expected.</param>
        public static void RangeHeader(HttpRequestMessage request, long?expectedStart, long?expectedEnd)
        {
            // The range in "x-ms-range" is used if it exists, or else "Range" is used.
            string requestRange = HttpRequestParsers.GetHeader(request, "x-ms-range") ?? HttpRequestParsers.GetContentRange(request);

            // We should find a range if and only if we expect one.
            Assert.AreEqual(expectedStart.HasValue, requestRange != null);

            // If we expect a range, the range we find should be identical.
            if (expectedStart.HasValue)
            {
                string rangeStart    = expectedStart.Value.ToString();
                string rangeEnd      = expectedEnd.HasValue ? expectedEnd.Value.ToString() : string.Empty;
                string expectedValue = string.Format("bytes={0}-{1}", rangeStart, rangeEnd);
                Assert.AreEqual(expectedValue.ToString(), requestRange);
            }
        }
コード例 #12
0
        public static void BlobTypeHeader(HttpRequestMessage request, BlobType?expectedValue)
        {
            Assert.IsFalse((expectedValue != null) && (HttpRequestParsers.GetHeader(request, "x-ms-blob-type") == null));
            if (HttpRequestParsers.GetHeader(request, "x-ms-blob-type") != null)
            {
                string blobTypeString = HttpRequestParsers.GetHeader(request, "x-ms-blob-type");
                Assert.IsNotNull(blobTypeString);
                BlobType?blobType = null;
                switch (blobTypeString)
                {
                case "PageBlob":
                    blobType = BlobType.PageBlob;
                    break;

                case "BlockBlob":
                    blobType = BlobType.BlockBlob;
                    break;
                }

                Assert.AreEqual(expectedValue, blobType);
            }
        }
コード例 #13
0
        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 = cmd.HttpClient ?? 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 = await StorageException.TranslateExceptionAsync(ex, executionState.Cmd.CurrentResult, null, timeoutTokenSource.Token, executionState.Resp).ConfigureAwait(false);

                            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).ConfigureAwait(false);

                            executionState.CurrentOperation = ExecutorOperation.EndGetResponse;

                            // Check that echoed client ID matches the one we sent
                            var clientRequestId       = HttpRequestParsers.GetHeader(executionState.Req, Constants.HeaderConstants.ClientRequestIdHeader);
                            var echoedClientRequestId = HttpResponseParsers.GetHeader(executionState.Resp, Constants.HeaderConstants.ClientRequestIdHeader);

                            if (echoedClientRequestId != null && echoedClientRequestId != clientRequestId)
                            {
                                var requestId = HttpResponseParsers.GetHeader(executionState.Resp, Constants.HeaderConstants.RequestIdHeader);
                                var storageEx = new StorageException($"Echoed client request ID: {echoedClientRequestId} does not match sent client request ID: {clientRequestId}.  Service request ID: {requestId}")
                                {
                                    IsRetryable = false
                                };
                                executionState.ExceptionRef = storageEx;
                                throw storageEx;
                            }

                            // 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).ConfigureAwait(false);
                            }

                            Logger.LogInformational(executionState.OperationContext, SR.TraceResponse, executionState.Cmd.CurrentResult.HttpStatusCode, executionState.Cmd.CurrentResult.ServiceRequestID, executionState.Cmd.CurrentResult.ContentMd5, executionState.Cmd.CurrentResult.ContentCrc64, 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;
                            var responseStream = await executionState.Resp.Content.ReadAsStreamAsync().ConfigureAwait(false);

                            if (cmd.NetworkTimeout.HasValue)
                            {
                                responseStream = new TimeoutStream(responseStream, cmd.NetworkTimeout.Value);
                            }
                            cmd.ResponseStream = responseStream;

                            // 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, default(IBufferManager), null /* copyLength */, null /* maxLength */, ChecksumRequested.None, executionState, new StreamDescriptor(), timeoutTokenSource.Token).ConfigureAwait(false);

                                    cmd.ErrorStream.Seek(0, SeekOrigin.Begin);
                                    executionState.ExceptionRef = StorageException.TranslateExceptionWithPreBufferedStream(executionState.ExceptionRef, executionState.Cmd.CurrentResult, stream => executionState.Cmd.ParseError(stream, executionState.Resp, null), cmd.ErrorStream, executionState.Resp);
                                    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, default(IBufferManager), null /* copyLength */, null /* maxLength */, cmd.ChecksumRequestedForResponseStream, executionState, cmd.StreamCopyState, timeoutTokenSource.Token).ConfigureAwait(false);
                                    }
                                    finally
                                    {
                                        cmd.ResponseStream.Dispose();
                                        cmd.ResponseStream = null;
                                    }
                                }
                            }

                            // 9. Evaluate Response & Parse Results, (Stream potentially available here)
                            if (cmd.PostProcessResponseAsync != null)
                            {
                                executionState.CurrentOperation = ExecutorOperation.PostProcess;
                                Logger.LogInformational(executionState.OperationContext, SR.TracePostProcess);
                                executionState.Result = await cmd.PostProcessResponseAsync(cmd, executionState.Resp, executionState.OperationContext, timeoutTokenSource.Token).ConfigureAwait(false);
                            }

                            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 NETCORE || WINDOWS_RT
                            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).ConfigureAwait(false);
                            }

                            Logger.LogInformational(executionState.OperationContext, SR.TraceRetry);
                        }

                        Executor.FireRetrying(executionState);
                    }while (shouldRetry);

                    // should never get here
                    throw new NotImplementedException(SR.InternalStorageError);
                }
        }
コード例 #14
0
        public async Task UseTransactionalMD5PutTestAsync()
        {
            BlobRequestOptions optionsWithNoMD5 = new BlobRequestOptions()
            {
                UseTransactionalMD5 = false,
            };
            BlobRequestOptions optionsWithMD5 = new BlobRequestOptions()
            {
                UseTransactionalMD5 = true,
            };

            byte[] buffer = GetRandomBuffer(1024);
            MD5    hasher = MD5.Create();
            string md5    = Convert.ToBase64String(hasher.ComputeHash(buffer));

            string           lastCheckMD5          = null;
            int              checkCount            = 0;
            OperationContext opContextWithMD5Check = new OperationContext();

            opContextWithMD5Check.SendingRequest += (_, args) =>
            {
                if (HttpRequestParsers.GetContentLength(args.Request) >= buffer.Length)
                {
                    lastCheckMD5 = HttpRequestParsers.GetContentMD5(args.Request);
                    checkCount++;
                }
            };

            CloudBlobContainer container = GetRandomContainerReference();

            try
            {
                await container.CreateAsync();

                CloudBlockBlob blockBlob = container.GetBlockBlobReference("blob1");
                List <string>  blockIds  = GetBlockIdList(3);
                checkCount = 0;
                using (Stream blockData = new MemoryStream(buffer))
                {
                    lastCheckMD5 = "invalid_md5";
                    await blockBlob.PutBlockAsync(blockIds[0], blockData, null, null, optionsWithNoMD5, opContextWithMD5Check);

                    Assert.IsNull(lastCheckMD5);

                    lastCheckMD5 = "invalid_md5";
                    blockData.Seek(0, SeekOrigin.Begin);
                    await blockBlob.PutBlockAsync(blockIds[1], blockData, null, null, optionsWithMD5, opContextWithMD5Check);

                    Assert.AreEqual(md5, lastCheckMD5);

                    lastCheckMD5 = "invalid_md5";
                    blockData.Seek(0, SeekOrigin.Begin);
                    await blockBlob.PutBlockAsync(blockIds[2], blockData, md5, null, optionsWithNoMD5, opContextWithMD5Check);

                    Assert.AreEqual(md5, lastCheckMD5);
                }

                Assert.AreEqual(3, checkCount);

                checkCount = 0;
                CloudAppendBlob appendBlob = container.GetAppendBlobReference("blob2");
                await appendBlob.CreateOrReplaceAsync();

                checkCount = 0;
                using (Stream blockData = new MemoryStream(buffer))
                {
                    lastCheckMD5 = "invalid_md5";
                    await appendBlob.AppendBlockAsync(blockData, null, null, optionsWithNoMD5, opContextWithMD5Check);

                    Assert.IsNull(lastCheckMD5);

                    lastCheckMD5 = "invalid_md5";
                    blockData.Seek(0, SeekOrigin.Begin);
                    await appendBlob.AppendBlockAsync(blockData, null, null, optionsWithMD5, opContextWithMD5Check);

                    Assert.AreEqual(md5, lastCheckMD5);

                    lastCheckMD5 = "invalid_md5";
                    blockData.Seek(0, SeekOrigin.Begin);
                    await appendBlob.AppendBlockAsync(blockData, md5, null, optionsWithNoMD5, opContextWithMD5Check);

                    Assert.AreEqual(md5, lastCheckMD5);
                }

                Assert.AreEqual(3, checkCount);

                CloudPageBlob pageBlob = container.GetPageBlobReference("blob3");
                await pageBlob.CreateAsync(buffer.Length);

                checkCount = 0;
                using (Stream pageData = new MemoryStream(buffer))
                {
                    lastCheckMD5 = "invalid_md5";
                    await pageBlob.WritePagesAsync(pageData, 0, null, null, optionsWithNoMD5, opContextWithMD5Check);

                    Assert.IsNull(lastCheckMD5);

                    lastCheckMD5 = "invalid_md5";
                    pageData.Seek(0, SeekOrigin.Begin);
                    await pageBlob.WritePagesAsync(pageData, 0, null, null, optionsWithMD5, opContextWithMD5Check);

                    Assert.AreEqual(md5, lastCheckMD5);

                    lastCheckMD5 = "invalid_md5";
                    pageData.Seek(0, SeekOrigin.Begin);
                    await pageBlob.WritePagesAsync(pageData, 0, md5, null, optionsWithNoMD5, opContextWithMD5Check);

                    Assert.AreEqual(md5, lastCheckMD5);
                }

                Assert.AreEqual(3, checkCount);

                lastCheckMD5 = null;
                blockBlob    = container.GetBlockBlobReference("blob4");
                checkCount   = 0;
                using (Stream blobStream = await blockBlob.OpenWriteAsync(null, optionsWithMD5, opContextWithMD5Check))
                {
                    blobStream.Write(buffer, 0, buffer.Length);
                    blobStream.Write(buffer, 0, buffer.Length);
                }
                Assert.IsNotNull(lastCheckMD5);
                Assert.AreEqual(1, checkCount);

                lastCheckMD5 = "invalid_md5";
                blockBlob    = container.GetBlockBlobReference("blob5");
                checkCount   = 0;
                using (Stream blobStream = await blockBlob.OpenWriteAsync(null, optionsWithNoMD5, opContextWithMD5Check))
                {
                    blobStream.Write(buffer, 0, buffer.Length);
                    blobStream.Write(buffer, 0, buffer.Length);
                }
                Assert.IsNull(lastCheckMD5);
                Assert.AreEqual(1, checkCount);

                lastCheckMD5 = null;
                pageBlob     = container.GetPageBlobReference("blob6");
                checkCount   = 0;
                using (Stream blobStream = await pageBlob.OpenWriteAsync(buffer.Length * 3, null, optionsWithMD5, opContextWithMD5Check))
                {
                    blobStream.Write(buffer, 0, buffer.Length);
                    blobStream.Write(buffer, 0, buffer.Length);
                }
                Assert.IsNotNull(lastCheckMD5);
                Assert.AreEqual(1, checkCount);

                lastCheckMD5 = "invalid_md5";
                pageBlob     = container.GetPageBlobReference("blob7");
                checkCount   = 0;
                using (Stream blobStream = await pageBlob.OpenWriteAsync(buffer.Length * 3, null, optionsWithNoMD5, opContextWithMD5Check))
                {
                    blobStream.Write(buffer, 0, buffer.Length);
                    blobStream.Write(buffer, 0, buffer.Length);
                }
                Assert.IsNull(lastCheckMD5);
                Assert.AreEqual(1, checkCount);
            }
            finally
            {
                await container.DeleteIfExistsAsync();
            }
        }
コード例 #15
0
 public static void ContentLengthHeader(HttpRequestMessage request, long expectedValue)
 {
     Assert.AreEqual(expectedValue, HttpRequestParsers.GetContentLength(request));
 }
コード例 #16
0
        public async Task UseTransactionalCRC64PutTestAsync()
        {
            BlobRequestOptions optionsWithNoCRC64 = new BlobRequestOptions()
            {
                ChecksumOptions = new ChecksumOptions {
                    UseTransactionalCRC64 = false
                }
            };
            BlobRequestOptions optionsWithCRC64 = new BlobRequestOptions()
            {
                ChecksumOptions = new ChecksumOptions {
                    UseTransactionalCRC64 = true
                }
            };

            byte[]       buffer = GetRandomBuffer(1024);
            Crc64Wrapper hasher = new Crc64Wrapper();

            hasher.UpdateHash(buffer, 0, buffer.Length);
            string crc64 = hasher.ComputeHash();

            string           lastCheckCRC64          = null;
            int              checkCount              = 0;
            OperationContext opContextWithCRC64Check = new OperationContext();

            opContextWithCRC64Check.SendingRequest += (_, args) =>
            {
                if (HttpRequestParsers.GetContentLength(args.Request) >= buffer.Length)
                {
                    lastCheckCRC64 = HttpRequestParsers.GetContentCRC64(args.Request);
                    checkCount++;
                }
            };

            OperationContext opContextWithCRC64CheckAndInjectedFailure = new OperationContext();

            opContextWithCRC64CheckAndInjectedFailure.SendingRequest += (_, args) =>
            {
                args.Response.Headers.Remove(Constants.HeaderConstants.ContentCrc64Header);
                args.Request.Headers.TryAddWithoutValidation(Constants.HeaderConstants.ContentCrc64Header, "dummy");
                if (HttpRequestParsers.GetContentLength(args.Request) >= buffer.Length)
                {
                    lastCheckCRC64 = HttpRequestParsers.GetContentCRC64(args.Request);
                    checkCount++;
                }
            };

            CloudBlobContainer container = GetRandomContainerReference();

            try
            {
                await container.CreateAsync();

                CloudBlockBlob blockBlob = container.GetBlockBlobReference("blob1");
                List <string>  blockIds  = GetBlockIdList(3);
                checkCount = 0;
                using (Stream blockData = new MemoryStream(buffer))
                {
                    lastCheckCRC64 = "invalid_CRC64";
                    await blockBlob.PutBlockAsync(blockIds[0], blockData, null, null, optionsWithNoCRC64, opContextWithCRC64Check);

                    Assert.IsNull(lastCheckCRC64);

                    lastCheckCRC64 = "invalid_CRC64";
                    blockData.Seek(0, SeekOrigin.Begin);
                    await blockBlob.PutBlockAsync(blockIds[1], blockData, null, null, optionsWithCRC64, opContextWithCRC64Check);

                    Assert.AreEqual(crc64, lastCheckCRC64);

                    blockData.Seek(0, SeekOrigin.Begin);
                    await TestHelper.ExpectedExceptionAsync <StorageException>(
                        () => blockBlob.PutBlockAsync(blockIds[1], blockData, null, null, optionsWithCRC64, opContextWithCRC64CheckAndInjectedFailure),
                        "Calculated CRC64 does not match existing property"
                        );

                    lastCheckCRC64 = "invalid_CRC64";
                    blockData.Seek(0, SeekOrigin.Begin);
                    await blockBlob.PutBlockAsync(blockIds[2], blockData, new Checksum(crc64 : crc64), null, optionsWithNoCRC64, opContextWithCRC64Check);

                    Assert.AreEqual(crc64, lastCheckCRC64);
                }

                Assert.AreEqual(3, checkCount);

                checkCount = 0;
                CloudAppendBlob appendBlob = container.GetAppendBlobReference("blob2");
                await appendBlob.CreateOrReplaceAsync();

                checkCount = 0;
                using (Stream blockData = new MemoryStream(buffer))
                {
                    lastCheckCRC64 = "invalid_CRC64";
                    await appendBlob.AppendBlockAsync(blockData, null, null, optionsWithNoCRC64, opContextWithCRC64Check);

                    Assert.IsNull(lastCheckCRC64);

                    lastCheckCRC64 = "invalid_CRC64";
                    blockData.Seek(0, SeekOrigin.Begin);
                    await appendBlob.AppendBlockAsync(blockData, null, null, optionsWithCRC64, opContextWithCRC64Check);

                    Assert.AreEqual(crc64, lastCheckCRC64);

                    blockData.Seek(0, SeekOrigin.Begin);
                    await TestHelper.ExpectedExceptionAsync <StorageException>(
                        () => appendBlob.AppendBlockAsync(blockData, null, null, optionsWithCRC64, opContextWithCRC64CheckAndInjectedFailure),
                        "Calculated CRC64 does not match existing property"
                        );

                    lastCheckCRC64 = "invalid_CRC64";
                    blockData.Seek(0, SeekOrigin.Begin);
                    await appendBlob.AppendBlockAsync(blockData, new Checksum(crc64 : crc64), null, optionsWithNoCRC64, opContextWithCRC64Check);

                    Assert.AreEqual(crc64, lastCheckCRC64);
                }

                Assert.AreEqual(3, checkCount);

                CloudPageBlob pageBlob = container.GetPageBlobReference("blob3");
                await pageBlob.CreateAsync(buffer.Length);

                checkCount = 0;
                using (Stream pageData = new MemoryStream(buffer))
                {
                    lastCheckCRC64 = "invalid_CRC64";
                    await pageBlob.WritePagesAsync(pageData, 0, null, null, optionsWithNoCRC64, opContextWithCRC64Check);

                    Assert.IsNull(lastCheckCRC64);

                    lastCheckCRC64 = "invalid_CRC64";
                    pageData.Seek(0, SeekOrigin.Begin);
                    await pageBlob.WritePagesAsync(pageData, 0, null, null, optionsWithCRC64, opContextWithCRC64Check);

                    Assert.AreEqual(crc64, lastCheckCRC64);

                    pageData.Seek(0, SeekOrigin.Begin);
                    await TestHelper.ExpectedExceptionAsync <StorageException>(
                        () => pageBlob.WritePagesAsync(pageData, 0, null, null, optionsWithCRC64, opContextWithCRC64CheckAndInjectedFailure),
                        "Calculated CRC64 does not match existing property"
                        );

                    lastCheckCRC64 = "invalid_CRC64";
                    pageData.Seek(0, SeekOrigin.Begin);
                    await pageBlob.WritePagesAsync(pageData, 0, new Checksum(crc64 : crc64), null, optionsWithNoCRC64, opContextWithCRC64Check);

                    Assert.AreEqual(crc64, lastCheckCRC64);
                }

                Assert.AreEqual(3, checkCount);

                lastCheckCRC64 = null;
                blockBlob      = container.GetBlockBlobReference("blob4");
                checkCount     = 0;
                using (Stream blobStream = await blockBlob.OpenWriteAsync(null, optionsWithCRC64, opContextWithCRC64Check))
                {
                    blobStream.Write(buffer, 0, buffer.Length);
                    blobStream.Write(buffer, 0, buffer.Length);
                }
                Assert.IsNotNull(lastCheckCRC64);
                Assert.AreEqual(1, checkCount);

                lastCheckCRC64 = "invalid_CRC64";
                blockBlob      = container.GetBlockBlobReference("blob5");
                checkCount     = 0;
                using (Stream blobStream = await blockBlob.OpenWriteAsync(null, optionsWithNoCRC64, opContextWithCRC64Check))
                {
                    blobStream.Write(buffer, 0, buffer.Length);
                    blobStream.Write(buffer, 0, buffer.Length);
                }
                Assert.IsNull(lastCheckCRC64);
                Assert.AreEqual(1, checkCount);

                lastCheckCRC64 = null;
                pageBlob       = container.GetPageBlobReference("blob6");
                checkCount     = 0;
                using (Stream blobStream = await pageBlob.OpenWriteAsync(buffer.Length * 3, null, optionsWithCRC64, opContextWithCRC64Check))
                {
                    blobStream.Write(buffer, 0, buffer.Length);
                    blobStream.Write(buffer, 0, buffer.Length);
                }
                Assert.IsNotNull(lastCheckCRC64);
                Assert.AreEqual(1, checkCount);

                lastCheckCRC64 = "invalid_CRC64";
                pageBlob       = container.GetPageBlobReference("blob7");
                checkCount     = 0;
                using (Stream blobStream = await pageBlob.OpenWriteAsync(buffer.Length * 3, null, optionsWithNoCRC64, opContextWithCRC64Check))
                {
                    blobStream.Write(buffer, 0, buffer.Length);
                    blobStream.Write(buffer, 0, buffer.Length);
                }
                Assert.IsNull(lastCheckCRC64);
                Assert.AreEqual(1, checkCount);
            }
            finally
            {
                await container.DeleteIfExistsAsync();
            }
        }
コード例 #17
0
        public void FileUseTransactionalMD5PutTestAPM()
        {
            FileRequestOptions optionsWithNoMD5 = new FileRequestOptions()
            {
                UseTransactionalMD5 = false,
            };
            FileRequestOptions optionsWithMD5 = new FileRequestOptions()
            {
                UseTransactionalMD5 = true,
            };

            byte[] buffer = GetRandomBuffer(1024);
            MD5    hasher = MD5.Create();
            string md5    = Convert.ToBase64String(hasher.ComputeHash(buffer));

            string           lastCheckMD5          = null;
            int              checkCount            = 0;
            OperationContext opContextWithMD5Check = new OperationContext();

            opContextWithMD5Check.SendingRequest += (_, args) =>
            {
                if (HttpRequestParsers.GetContentLength(args.Request) >= buffer.Length)
                {
                    lastCheckMD5 = HttpRequestParsers.GetContentMD5(args.Request);
                    checkCount++;
                }
            };

            CloudFileShare share = GetRandomShareReference();

            try
            {
                share.Create();

                using (AutoResetEvent waitHandle = new AutoResetEvent(false))
                {
                    IAsyncResult result;
                    CloudFile    file = share.GetRootDirectoryReference().GetFileReference("file2");
                    file.Create(buffer.Length);
                    checkCount = 0;
                    using (Stream fileData = new MemoryStream(buffer))
                    {
                        result = file.BeginWriteRange(fileData, 0, null, null, optionsWithNoMD5, opContextWithMD5Check,
                                                      ar => waitHandle.Set(),
                                                      null);
                        waitHandle.WaitOne();
                        file.EndWriteRange(result);
                        Assert.IsNull(lastCheckMD5);

                        fileData.Seek(0, SeekOrigin.Begin);
                        result = file.BeginWriteRange(fileData, 0, null, null, optionsWithMD5, opContextWithMD5Check,
                                                      ar => waitHandle.Set(),
                                                      null);
                        waitHandle.WaitOne();
                        file.EndWriteRange(result);
                        Assert.AreEqual(md5, lastCheckMD5);

                        fileData.Seek(0, SeekOrigin.Begin);
                        result = file.BeginWriteRange(fileData, 0, md5, null, optionsWithNoMD5, opContextWithMD5Check,
                                                      ar => waitHandle.Set(),
                                                      null);
                        waitHandle.WaitOne();
                        file.EndWriteRange(result);
                        Assert.AreEqual(md5, lastCheckMD5);
                    }
                    Assert.AreEqual(3, checkCount);
                }
            }
            finally
            {
                share.DeleteIfExists();
            }
        }
コード例 #18
0
        public static async Task <T> ExecuteAsync <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 async executor
            using (ExecutionState <T> executionState = new ExecutionState <T>(cmd, policy, operationContext))
                using (CancellationTokenSource timeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token))
                {
                    bool     shouldRetry = false;
                    TimeSpan delay       = TimeSpan.Zero;

                    // Create a new client
                    HttpClient client = cmd.HttpClient ?? HttpClientFactory.Instance;

                    do
                    {
                        try
                        {
                            executionState.Init();

                            // 0. Begin Request
                            Executor.StartRequestAttempt(executionState);

                            // Steps 1-4: Build Content/SetHeaders/
                            Executor.ProcessStartOfRequest(executionState, SR.TraceStartRequestAsync, timeoutTokenSource);
                            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. At this point, the request is not even made.
                            // Therefore, we will not get extended error info. Hence ParseError doesn't matter here.
                            StorageException storageEx = await ExecutorBase.TranslateExceptionBasedOnParseErrorAsync(ex, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseErrorAsync, token).ConfigureAwait(false);

                            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).ConfigureAwait(false);

                            executionState.CurrentOperation = ExecutorOperation.EndGetResponse;

                            // Check that echoed client ID matches the one we sent
                            var clientRequestId       = HttpRequestParsers.GetHeader(executionState.Req, Constants.HeaderConstants.ClientRequestIdHeader);
                            var echoedClientRequestId = HttpResponseParsers.GetHeader(executionState.Resp, Constants.HeaderConstants.ClientRequestIdHeader);

                            if (echoedClientRequestId != null && echoedClientRequestId != clientRequestId)
                            {
                                var requestId = HttpResponseParsers.GetHeader(executionState.Resp, Constants.HeaderConstants.RequestIdHeader);
                                var storageEx = new StorageException($"Echoed client request ID: {echoedClientRequestId} does not match sent client request ID: {clientRequestId}.  Service request ID: {requestId}")
                                {
                                    IsRetryable = false
                                };
                                executionState.ExceptionRef = storageEx;
                                throw storageEx;
                            }

                            // 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, token, executionState.Cmd.ParseErrorAsync).ConfigureAwait(false);
                            }

                            Logger.LogInformational(executionState.OperationContext, SR.TraceResponse, executionState.Cmd.CurrentResult.HttpStatusCode, executionState.Cmd.CurrentResult.ServiceRequestID, executionState.Cmd.CurrentResult.ContentMd5, executionState.Cmd.CurrentResult.ContentCrc64, 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;
                            var responseStream = await executionState.Resp.Content.ReadAsStreamAsync().ConfigureAwait(false);

                            if (cmd.NetworkTimeout.HasValue)
                            {
                                responseStream = new TimeoutStream(responseStream, cmd.NetworkTimeout.Value);
                            }
                            cmd.ResponseStream = responseStream;

                            // 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, default(IBufferManager), null /* copyLength */, null /* maxLength */, ChecksumRequested.None, executionState, new StreamDescriptor(), timeoutTokenSource.Token).ConfigureAwait(false);

                                    cmd.ErrorStream.Seek(0, SeekOrigin.Begin);
                                    executionState.ExceptionRef = StorageException.TranslateExceptionWithPreBufferedStream(executionState.ExceptionRef, executionState.Cmd.CurrentResult, stream => executionState.Cmd.ParseError(stream, executionState.Resp, null), cmd.ErrorStream, executionState.Resp);
                                    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, default(IBufferManager), null /* copyLength */, null /* maxLength */, cmd.ChecksumRequestedForResponseStream, executionState, cmd.StreamCopyState, timeoutTokenSource.Token).ConfigureAwait(false);
                                    }
                                    finally
                                    {
                                        cmd.ResponseStream.Dispose();
                                        cmd.ResponseStream = null;
                                    }
                                }
                            }

                            await Executor.ProcessEndOfRequestAsync(executionState, token).ConfigureAwait(false);

                            Executor.FinishRequestAttempt(executionState);

                            return(executionState.Result);
                        }
                        catch (Exception e)
                        {
                            Logger.LogWarning(executionState.OperationContext, SR.TraceGenericError, e.Message);
                            Executor.FinishRequestAttempt(executionState);

                            if (e is OperationCanceledException && (executionState.OperationExpiryTime.HasValue && DateTime.Now.CompareTo(executionState.OperationExpiryTime.Value) > 0))
                            {
                                e = new TimeoutException(SR.TimeoutExceptionMessage, e);
                            }

                            StorageException translatedException = await ExecutorBase.TranslateExceptionBasedOnParseErrorAsync(e, executionState.Cmd.CurrentResult, executionState.Resp, executionState.Cmd.ParseErrorAsync, token).ConfigureAwait(false);

                            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
                        {
                            // I.E. Rewind stream etc.
                            cmd.RecoveryAction?.Invoke(cmd, executionState.Cmd.CurrentResult.Exception, executionState.OperationContext);

                            if (delay > TimeSpan.Zero)
                            {
                                await Task.Delay(delay, token).ConfigureAwait(false);
                            }

                            Logger.LogInformational(executionState.OperationContext, SR.TraceRetry);
                        }

                        Executor.FireRetrying(executionState);
                    }while (shouldRetry);

                    // should never get here
                    throw new NotImplementedException(SR.InternalStorageError);
                }
        }