Ejemplo n.º 1
0
        private async Task DoDownloadRangeToByteArrayNegativeTestsAsync(ICloudBlob blob)
        {
            int blobLength    = 1024;
            int resultBufSize = 1024;

            byte[] buffer       = GetRandomBuffer(blobLength);
            byte[] resultBuffer = new byte[resultBufSize];

            using (MemoryStream stream = new MemoryStream(buffer))
            {
                await blob.UploadFromStreamAsync(stream.AsInputStream());

                OperationContext context = new OperationContext();
                await TestHelper.ExpectedExceptionAsync(async() => await blob.DownloadRangeToByteArrayAsync(resultBuffer, 0, 1024, 1, null, null, context), context, "Try invalid length", HttpStatusCode.RequestedRangeNotSatisfiable);

                WrappedStorageException ex = await TestHelper.ExpectedExceptionAsync <WrappedStorageException>(async() => await blob.DownloadToByteArrayAsync(resultBuffer, 1024), "Provide invalid offset");

                Assert.IsInstanceOfType(ex.InnerException.InnerException, typeof(NotSupportedException));
                ex = await TestHelper.ExpectedExceptionAsync <WrappedStorageException>(async() => await blob.DownloadRangeToByteArrayAsync(resultBuffer, 1023, 0, 2), "Should fail when offset + length required is greater than size of the buffer");

                Assert.IsInstanceOfType(ex.InnerException.InnerException, typeof(NotSupportedException));
                ex = await TestHelper.ExpectedExceptionAsync <WrappedStorageException>(async() => await blob.DownloadRangeToByteArrayAsync(resultBuffer, 0, 0, -10), "Fail when a negative length is specified");

                Assert.IsInstanceOfType(ex.InnerException.InnerException, typeof(ArgumentOutOfRangeException));
                await TestHelper.ExpectedExceptionAsync <ArgumentOutOfRangeException>(async() => await blob.DownloadRangeToByteArrayAsync(resultBuffer, -10, 0, 20), "Fail if a negative offset is provided");

                ex = await TestHelper.ExpectedExceptionAsync <WrappedStorageException>(async() => await blob.DownloadRangeToByteArrayAsync(resultBuffer, 0, -10, 20), "Fail if a negative blob offset is provided");

                Assert.IsInstanceOfType(ex.InnerException.InnerException, typeof(ArgumentOutOfRangeException));
            }
        }
Ejemplo n.º 2
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)
            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);
        }
Ejemplo n.º 3
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))
            {
                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.BuildClient(cmd, cmd.Handler, true, 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
                        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)
                        {
                            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, null);
                        storageEx.IsRetryable       = false;
                        executionState.ExceptionRef = storageEx;

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

                    // 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)
                        {
                            // At this point, don't try to read the stream to parse the error
                            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;

                            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(), token);

                                cmd.ErrorStream.Seek(0, SeekOrigin.Begin);
#if ASPNET_K || PORTABLE
                                executionState.ExceptionRef = StorageException.TranslateExceptionWithPreBufferedStream(executionState.ExceptionRef, executionState.Cmd.CurrentResult, null /* parseError */, cmd.ErrorStream, executionState.Resp);
#else
                                executionState.ExceptionRef = StorageException.TranslateExceptionWithPreBufferedStream(executionState.ExceptionRef, executionState.Cmd.CurrentResult, null /* parseError */, 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, 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, null, executionState.Resp);
#else
                        StorageException translatedException = StorageException.TranslateException(e, executionState.Cmd.CurrentResult, 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);
#if WINDOWS_RT || ASPNET_K || PORTABLE
                        // 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);
                    }

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

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