示例#1
0
            public async Task UpdateStatus(string message, States newState, TraceLevel traceLevel)
            {
                this.Message = message;
                if (newState != States.Unknown)
                {
                    this.State = newState;
                }
                await AccountStatus.UpdateAccountStatus(this);

                switch (traceLevel)
                {
                case TraceLevel.Error:
                    DashTrace.TraceError(message);
                    break;

                case TraceLevel.Warning:
                    DashTrace.TraceWarning(message);
                    break;

                case TraceLevel.Info:
                case TraceLevel.Verbose:
                    DashTrace.TraceInformation(message);
                    break;
                }
            }
示例#2
0
 public async Task <HttpResponseMessage> OptionsCallAsync()
 {
     return(await DoHandlerAsync("AccountController.OptionsCallAsync", async() =>
     {
         var request = HttpContextFactory.Current.Request;
         HttpResponseMessage response;
         if (request.Headers["Origin"] != null)
         {
             DashTrace.TraceInformation("Forwarding real CORS OPTIONS request");
             Uri forwardUri = ControllerOperations.ForwardUriToNamespace(request);
             var forwardRequest = new HttpRequestMessage(HttpMethod.Options, forwardUri);
             foreach (string key in _corsOptionsHeaders)
             {
                 forwardRequest.Headers.TryAddWithoutValidation(key, request.Headers[key]);
             }
             HttpClient client = new HttpClient();
             response = await client.SendAsync(forwardRequest);
             DashTrace.TraceInformation("CORS OPTIONS response: {0}, {1}", response.StatusCode, response.ReasonPhrase);
         }
         else
         {
             response = this.Request.CreateResponse(HttpStatusCode.OK);
         }
         response.Headers.Add("x-ms-dash-client", "true");
         return response;
     }));
 }
示例#3
0
        public static string GenerateSignature(Func <string> stringToSignFactory, byte[] accountKey)
        {
            string stringToSign = stringToSignFactory();

            DashTrace.TraceInformation("Authentication signing string: {0}", stringToSign);

            var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

            using (var hmac = new HMACSHA256(accountKey))
            {
                return(Convert.ToBase64String(hmac.ComputeHash(bytesToSign)));
            }
        }
示例#4
0
        protected void Application_Start()
        {
            AzureUtils.AddAzureDiagnosticsListener();
            DashTrace.TraceInformation("Starting application instance");
            GlobalConfiguration.Configure(WebApiConfig.Register);
            // Import any statically configured accounts
            var importAccounts = DashConfiguration.ImportAccounts;

            if (importAccounts.Any())
            {
                Task.Factory.StartNew(() => AccountManager.ImportAccounts(importAccounts));
            }
        }
示例#5
0
        static void Main(string[] args)
        {
            if (!Trace.Listeners.OfType <ConsoleTraceListener>().Any())
            {
                Trace.Listeners.Add(new ConsoleTraceListener());
            }
            AzureUtils.AddAzureDiagnosticsListener();
            DashTrace.TraceInformation("DashAsync (version: {0}): Asynchronous worker starting up.", Assembly.GetEntryAssembly().GetName().Version);

            int msgProcessed = 0, msgErrors = 0;

            MessageProcessor.ProcessMessageLoop(ref msgProcessed, ref msgErrors);
            DashTrace.TraceInformation("DashAsync completed. Messages processed [{0}], messages unprocessed [{1}]", msgProcessed, msgErrors);
        }
示例#6
0
        public static async Task EnqueueBlobReplicationAsync(NamespaceBlob namespaceBlob, bool deleteReplica, bool saveNamespaceEntry = true)
        {
            if (!await namespaceBlob.ExistsAsync())
            {
                return;
            }
            // Trim down the namespace replication list to the first 'master' item. This is sufficient to ensure that the
            // orphaned blobs are not effectively in the account. The master blob will be replicated over the top of the
            // orphaned blobs.
            string primaryAccount = namespaceBlob.PrimaryAccountName;

            if (namespaceBlob.IsReplicated)
            {
                namespaceBlob.PrimaryAccountName = primaryAccount;
                if (saveNamespaceEntry)
                {
                    await namespaceBlob.SaveAsync();
                }
            }
            // This rest of this method does not block. Enqueueing the replication is a completely async process
            var task = Task.Factory.StartNew(() =>
            {
                var queue = new AzureMessageQueue();
                var tasks = DashConfiguration.DataAccounts
                            .Where(dataAccount => !dataAccount.Credentials.AccountName.Equals(primaryAccount, StringComparison.OrdinalIgnoreCase))
                            .Select(async dataAccount => await queue.EnqueueAsync(ConstructReplicationMessage(deleteReplica,
                                                                                                              primaryAccount,
                                                                                                              dataAccount.Credentials.AccountName,
                                                                                                              namespaceBlob.Container,
                                                                                                              namespaceBlob.BlobName,
                                                                                                              deleteReplica ? await GetBlobETagAsync(dataAccount, namespaceBlob.Container, namespaceBlob.BlobName) : null)));
                Task.WhenAll(tasks)
                .ContinueWith(antecedent =>
                {
                    if (antecedent.Exception != null)
                    {
                        DashTrace.TraceWarning("Error queueing replication message for blob: {0}. Details: {1}",
                                               PathUtils.CombineContainerAndBlob(namespaceBlob.Container, namespaceBlob.BlobName),
                                               antecedent.Exception.Flatten());
                    }
                    else
                    {
                        DashTrace.TraceInformation("Blob: {0} has been enqueued for replication.",
                                                   PathUtils.CombineContainerAndBlob(namespaceBlob.Container, namespaceBlob.BlobName));
                    }
                });
            });
        }
示例#7
0
        static bool RecoverReplicationError(StorageException ex, ICloudBlob destBlob, string exceptionMessage)
        {
            bool retval = true;

            switch ((HttpStatusCode)ex.RequestInformation.HttpStatusCode)
            {
            case HttpStatusCode.Conflict:
            case HttpStatusCode.PreconditionFailed:
                // Destination has been modified - no retry
                if (destBlob != null)
                {
                    DashTrace.TraceInformation("A pre-condition was not met attempting to replicate or cleanup blob [{0}] in account [{1}]. This operation will be aborted",
                                               destBlob.Name,
                                               destBlob.ServiceClient.Credentials.AccountName);
                }
                else
                {
                    DashTrace.TraceWarning("A pre-condition was not met attempting to replicate or cleanup an unknown blob. This operation will be aborted.");
                }
                retval = false;
                break;

            case HttpStatusCode.NotFound:
                // The source blob could not be found - delete the target to prevent orphaning
                if (destBlob != null)
                {
                    DashTrace.TraceInformation("Replication of blob [{0}] to account [{1}] cannot be completed because the source blob does not exist.",
                                               destBlob.Name,
                                               destBlob.ServiceClient.Credentials.AccountName);
                    CleanupAbortedBlobReplication(destBlob);
                }
                else
                {
                    DashTrace.TraceWarning("Replication of unknown blob cannot be completed because the source blob does not exist.");
                }
                retval = false;
                break;

            default:
                // Unexpected exceptions are warnings
                DashTrace.TraceWarning(exceptionMessage);
                break;
            }
            return(retval);
        }
示例#8
0
 static bool FinalizeBlobReplication(string dataAccount, string container, string blobName, bool deleteReplica, ICloudBlob destBlob = null)
 {
     return(NamespaceHandler.PerformNamespaceOperation(container, blobName, async(namespaceBlob) =>
     {
         bool exists = await namespaceBlob.ExistsAsync();
         if (!exists || namespaceBlob.IsMarkedForDeletion)
         {
             // It's ok for a deleted replica not to have a corresponding namespace blob
             if (!deleteReplica)
             {
                 DashTrace.TraceWarning("Replication of blob [{0}] to account [{1}] cannot be completed because the namespace blob either does not exist or is marked for deletion.",
                                        PathUtils.CombineContainerAndBlob(container, blobName),
                                        dataAccount);
                 // Attempt to not leave the replica orphaned
                 if (CleanupAbortedBlobReplication(namespaceBlob, destBlob))
                 {
                     await namespaceBlob.SaveAsync();
                 }
             }
             // Do not attempt retry in this state
             return true;
         }
         string message = "replicated to";
         bool nsDirty = false;
         if (deleteReplica)
         {
             nsDirty = namespaceBlob.RemoveDataAccount(dataAccount);
             message = "dereplicated from";
         }
         else
         {
             nsDirty = namespaceBlob.AddDataAccount(dataAccount);
         }
         if (nsDirty)
         {
             await namespaceBlob.SaveAsync();
             DashTrace.TraceInformation("Blob [{0}] has been successfully {1} account [{2}].",
                                        PathUtils.CombineContainerAndBlob(container, blobName),
                                        message,
                                        dataAccount);
         }
         return true;
     }).Result);
 }
示例#9
0
        public static bool DeleteReplica(string accountName, string container, string blobName, string eTag)
        {
            bool       retval      = false;
            ICloudBlob replicaBlob = null;

            try
            {
                DashTrace.TraceInformation("Deleting replica blob [{0}] from account [{1}]", PathUtils.CombineContainerAndBlob(container, blobName), accountName);
                var blobContainer = DashConfiguration.GetDataAccountByAccountName(accountName).CreateCloudBlobClient().GetContainerReference(container);
                replicaBlob = blobContainer.GetBlobReferenceFromServer(blobName);
                AccessCondition accessCondition = null;
                if (!String.IsNullOrWhiteSpace(eTag))
                {
                    accessCondition = AccessCondition.GenerateIfMatchCondition(eTag);
                }
                replicaBlob.Delete(DeleteSnapshotsOption.IncludeSnapshots, accessCondition);
                FinalizeBlobReplication(accountName, container, blobName, true);
                retval = true;
            }
            catch (StorageException ex)
            {
                // Classify the errors into retryable & non-retryable
                retval = !RecoverReplicationError(ex, replicaBlob,
                                                  String.Format("Storage error deleting replica blob [{0}] from account [{1}]. Details: {2}",
                                                                PathUtils.CombineContainerAndBlob(container, blobName),
                                                                accountName,
                                                                ex));
            }
            catch (Exception ex)
            {
                DashTrace.TraceWarning("Error deleting replica blob [{0}] from account [{1}]. Details: {2}",
                                       PathUtils.CombineContainerAndBlob(container, blobName),
                                       accountName,
                                       ex);
            }
            return(retval);
        }
示例#10
0
        public static async Task ImportAccountAsync(string accountName)
        {
            await OperationRunner.DoActionAsync(String.Format("Importing data account: {0}", accountName), async() =>
            {
                // This method will only import the blobs into the namespace. A future task may be
                // to redistribute the blobs to balance the entire virtual account.
                var account = DashConfiguration.GetDataAccountByAccountName(accountName);
                if (account == null)
                {
                    DashTrace.TraceWarning("Failure importing storage account: {0}. The storage account has not been configured as part of this virtual account",
                                           accountName);
                    return;
                }
                // Check if we've already imported this account
                var accountClient     = account.CreateCloudBlobClient();
                var namespaceClient   = DashConfiguration.NamespaceAccount.CreateCloudBlobClient();
                var accountContainers = await ListContainersAsync(accountClient);
                var status            = await AccountStatus.GetAccountStatus(accountName);
                await status.UpdateStatusInformation(AccountStatus.States.Healthy, "Importing storage account: {0} into virtual account", accountName);
                bool alreadyImported = false;
                await GetAccountBlobs(accountClient, async(blobItem) =>
                {
                    var blob          = (ICloudBlob)blobItem;
                    var namespaceBlob = await NamespaceBlob.FetchForBlobAsync(
                        (CloudBlockBlob)NamespaceHandler.GetBlobByName(DashConfiguration.NamespaceAccount, blob.Container.Name, blob.Name, blob.IsSnapshot ? blob.SnapshotTime.ToString() : String.Empty));
                    alreadyImported = await namespaceBlob.ExistsAsync(true) &&
                                      namespaceBlob.DataAccounts.Contains(accountName, StringComparer.OrdinalIgnoreCase);
                    return(false);
                });
                if (alreadyImported)
                {
                    await status.UpdateStatusWarning("Importing storage account: {0} has already been imported. This account cannot be imported again.", accountName);
                    return;
                }
                // Sync the container structure first - add containers in the imported account to the virtual account
                await status.UpdateStatusInformation("Importing storage account: {0}. Synchronizing container structure", accountName);
                int containersAddedCount = 0, containersWarningCount = 0;
                var namespaceContainers  = await ListContainersAsync(namespaceClient);
                await ProcessContainerDifferencesAsync(accountContainers, namespaceContainers, async(newContainerName, accountContainer) =>
                {
                    var createContainerResult = await ContainerHandler.DoForAllContainersAsync(newContainerName,
                                                                                               HttpStatusCode.Created,
                                                                                               async newContainer => await CopyContainer(accountContainer, newContainer),
                                                                                               true,
                                                                                               new[] { account });
                    if (createContainerResult.StatusCode < HttpStatusCode.OK || createContainerResult.StatusCode >= HttpStatusCode.Ambiguous)
                    {
                        await status.UpdateStatusWarning("Importing storage account: {0}. Failed to create container: {1} in virtual account. Details: {2}, {3}",
                                                         accountName,
                                                         newContainerName,
                                                         createContainerResult.StatusCode.ToString(),
                                                         createContainerResult.ReasonPhrase);
                        containersWarningCount++;
                    }
                    else
                    {
                        containersAddedCount++;
                    }
                },
                                                       (newContainerName, ex) =>
                {
                    status.UpdateStatusWarning("Importing storage account: {0}. Error processing container {1}. Details: {2}",
                                               accountName,
                                               newContainerName,
                                               ex.ToString()).Wait();
                    containersWarningCount++;
                });
                // Sync the other way
                await ProcessContainerDifferencesAsync(namespaceContainers, accountContainers, async(newContainerName, namespaceContainer) =>
                {
                    await CopyContainer(namespaceContainer, accountClient.GetContainerReference(newContainerName));
                },
                                                       (newContainerName, ex) =>
                {
                    status.UpdateStatusWarning("Importing storage account: {0}. Error replicating container {1} to imported account. Details: {2}",
                                               accountName,
                                               newContainerName,
                                               ex.ToString()).Wait();
                    containersWarningCount++;
                });
                DashTrace.TraceInformation("Importing storage account: {0}. Synchronized containers structure. {1} containers added to virtual account. {2} failures/warnings.",
                                           accountName,
                                           containersAddedCount,
                                           containersWarningCount);

                // Start importing namespace entries
                await status.UpdateStatusInformation("Importing storage account: {0}. Adding blob entries to namespace", accountName);
                int blobsAddedCount = 0, warningCount = 0, duplicateCount = 0;
                await GetAccountBlobs(accountClient, async(blobItem) =>
                {
                    var blob = (ICloudBlob)blobItem;
                    try
                    {
                        var namespaceBlob = await NamespaceBlob.FetchForBlobAsync(
                            (CloudBlockBlob)NamespaceHandler.GetBlobByName(DashConfiguration.NamespaceAccount, blob.Container.Name, blob.Name, blob.IsSnapshot ? blob.SnapshotTime.ToString() : String.Empty));
                        if (await namespaceBlob.ExistsAsync())
                        {
                            if (!String.Equals(namespaceBlob.PrimaryAccountName, accountName, StringComparison.OrdinalIgnoreCase))
                            {
                                await status.UpdateStatusWarning("Importing storage account: {0}. Adding blob: {1}/{2} would result in a duplicate blob entry. This blob will NOT be imported into the virtual account. Manually add the contents of this blob to the virtual account.",
                                                                 accountName,
                                                                 blob.Container.Name,
                                                                 blob.Name);
                                duplicateCount++;
                            }
                        }
                        else
                        {
                            namespaceBlob.PrimaryAccountName  = accountName;
                            namespaceBlob.Container           = blob.Container.Name;
                            namespaceBlob.BlobName            = blob.Name;
                            namespaceBlob.IsMarkedForDeletion = false;
                            await namespaceBlob.SaveAsync();
                            blobsAddedCount++;
                        }
                    }
                    catch (StorageException ex)
                    {
                        status.UpdateStatusWarning("Importing storage account: {0}. Error importing blob: {0}/{1} into virtual namespace. Details: {3}",
                                                   accountName,
                                                   blob.Container.Name,
                                                   blob.Name,
                                                   ex.ToString()).Wait();
                        warningCount++;
                    }
                    return(true);
                });

                if (status.State < AccountStatus.States.Warning)
                {
                    await status.UpdateStatus(String.Empty, AccountStatus.States.Unknown, TraceLevel.Off);
                }
                DashTrace.TraceInformation("Successfully imported the contents of storage account: '{0}' into the virtual namespace. Blobs added: {1}, duplicates detected: {2}, errors encountered: {3}",
                                           accountName, blobsAddedCount, duplicateCount, warningCount);
            },
                                                ex =>
            {
                var status = AccountStatus.GetAccountStatus(accountName).Result;
                status.UpdateStatusWarning("Error importing storage account: {0} into virtual account. Details: {1}", accountName, ex.ToString()).Wait();
            }, false, true);
        }
示例#11
0
        static bool ProcessBlobCopyStatus(ICloudBlob destBlob, string sourceAccount, string copyId, int?waitDelay = null)
        {
            bool   retval      = false;
            string destAccount = destBlob.ServiceClient.Credentials.AccountName;
            Uri    sourceUri   = destBlob.CopyState.Source;

            // If the blob has moved on to another copy, just assume that it overrode our copy
            if (destBlob.CopyState.CopyId == copyId)
            {
                switch (destBlob.CopyState.Status)
                {
                case CopyStatus.Aborted:
                    // Copy has been abandoned - we don't automatically come back from here
                    DashTrace.TraceWarning("Replicating blob [{0}] to account [{1}]. Copy Id [{3}] has been aborted.",
                                           sourceUri, destAccount, copyId);
                    // Make sure we don't orphan the replica
                    CleanupAbortedBlobReplication(destBlob);
                    retval = true;
                    break;

                case CopyStatus.Failed:
                case CopyStatus.Invalid:
                    // Possibly temporaral issues - allow the message to retry after a period
                    DashTrace.TraceWarning("Replicating blob [{0}] to account [{1}]. Copy Id [{3}] has been failed or is invalid.",
                                           sourceUri, destAccount, copyId);
                    // Make sure we don't orphan the replica
                    CleanupAbortedBlobReplication(destBlob);
                    retval = false;
                    break;

                case CopyStatus.Pending:
                    // Enqueue a new message to check on the copy status
                    DashTrace.TraceInformation("Replicating blob [{0}] to account [{1}]. Copy Id [{2}] is pending. Copied [{3}]/[{4}] bytes. Enqueing progress message.",
                                               sourceUri, destBlob.ServiceClient.Credentials.AccountName, copyId, destBlob.CopyState.BytesCopied, destBlob.CopyState.TotalBytes);
                    new AzureMessageQueue().Enqueue(new QueueMessage(MessageTypes.ReplicateProgress,
                                                                     new Dictionary <string, string>
                    {
                        { ReplicateProgressPayload.Source, sourceAccount },
                        { ReplicateProgressPayload.Destination, destAccount },
                        { ReplicateProgressPayload.Container, destBlob.Container.Name },
                        { ReplicateProgressPayload.BlobName, destBlob.Name },
                        { ReplicateProgressPayload.CopyID, copyId },
                    },
                                                                     DashTrace.CorrelationId),
                                                    waitDelay ?? (DashConfiguration.WorkerQueueInitialDelay + 10));
                    retval = true;
                    break;

                case CopyStatus.Success:
                    retval = FinalizeBlobReplication(destAccount, destBlob.Container.Name, destBlob.Name, false, destBlob);
                    break;
                }
            }
            else
            {
                DashTrace.TraceInformation("Replication of blob [{0}] to account [{1}] has been aborted as the destination blob has a different copy id. Expected [{2}], actual [{3}]",
                                           sourceUri, destAccount, copyId, destBlob.CopyState.CopyId);
                // Return true to indicate that this operation shouldn't be retried
                retval = true;
            }
            return(retval);
        }
示例#12
0
        public static bool BeginBlobReplication(string sourceAccount, string destAccount, string container, string blobName, int?waitDelay = null)
        {
            bool       retval   = false;
            ICloudBlob destBlob = null;

            try
            {
                // Process is:
                //  - start the copy
                //  - wait around for a little while to see if it finishes - if so, we're done - update the namespace
                //  - if the copy is still in progress, enqueue a ReplicateProgress message to revisit the progress & update the namespace

                // Attempt to acquire a reference to the specified destination first, because if the source no longer exists we must cleanup this orphaned replica
                var destContainer = DashConfiguration.GetDataAccountByAccountName(destAccount).CreateCloudBlobClient().GetContainerReference(container);
                try
                {
                    destBlob = destContainer.GetBlobReferenceFromServer(blobName);
                }
                catch
                {
                    destBlob = null;
                }
                var sourceClient = DashConfiguration.GetDataAccountByAccountName(sourceAccount).CreateCloudBlobClient();
                var sourceBlob   = sourceClient.GetContainerReference(container).GetBlobReferenceFromServer(blobName);
                if (destBlob == null)
                {
                    if (sourceBlob.BlobType == BlobType.PageBlob)
                    {
                        destBlob = destContainer.GetPageBlobReference(blobName);
                    }
                    else
                    {
                        destBlob = destContainer.GetBlockBlobReference(blobName);
                    }
                }
                DashTrace.TraceInformation("Replicating blob [{0}] to account [{1}]", sourceBlob.Uri, destAccount);
                // If the source is still being copied to (we kicked off the replication as the result of a copy operation), then just recycle
                // this message (don't return false as that may ultimately cause us to give up replicating).
                if (sourceBlob.CopyState != null && sourceBlob.CopyState.Status == CopyStatus.Pending)
                {
                    DashTrace.TraceInformation("Waiting for replication source [{0}] to complete copy.", sourceBlob.Uri);
                    new AzureMessageQueue().Enqueue(
                        BlobReplicationHandler.ConstructReplicationMessage(
                            false,
                            sourceAccount,
                            destAccount,
                            container,
                            blobName,
                            String.Empty));
                    return(true);
                }
                var sasUri = new UriBuilder(sourceBlob.Uri);
                sasUri.Query = sourceBlob.GetSharedAccessSignature(new SharedAccessBlobPolicy
                {
                    SharedAccessStartTime  = DateTime.UtcNow.AddMinutes(-5),
                    SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(60),
                    Permissions            = SharedAccessBlobPermissions.Read,
                }).TrimStart('?');
                string   copyId = destBlob.StartCopyFromBlob(sasUri.Uri);
                DateTime waitForCompleteGiveUp = DateTime.UtcNow.AddSeconds(waitDelay ?? (DashConfiguration.AsyncWorkerTimeout / 2));
                while (DateTime.UtcNow < waitForCompleteGiveUp)
                {
                    destBlob.FetchAttributes();
                    if (destBlob.CopyState.CopyId != copyId || destBlob.CopyState.Status != CopyStatus.Pending)
                    {
                        break;
                    }
                    Thread.Sleep(1000);
                }
                retval = ProcessBlobCopyStatus(destBlob, sourceAccount, copyId, waitDelay);
            }
            catch (StorageException ex)
            {
                // Classify the errors into retryable & non-retryable
                retval = !RecoverReplicationError(ex, destBlob,
                                                  String.Format("Storage error initiating replication for blob [{0}][{1}] to account [{2}]. Details: {3}",
                                                                sourceAccount,
                                                                PathUtils.CombineContainerAndBlob(container, blobName),
                                                                destAccount,
                                                                ex));
            }
            catch (Exception ex)
            {
                DashTrace.TraceWarning("Error initiating replication for blob [{0}][{1}] to account [{2}]. Details: {3}",
                                       sourceAccount,
                                       PathUtils.CombineContainerAndBlob(container, blobName),
                                       destAccount,
                                       ex);
            }
            return(retval);
        }
示例#13
0
        public static async Task <HandlerResult> HandlePrePipelineOperationAsync(IHttpRequestWrapper requestWrapper)
        {
            string containerName = requestWrapper.UriParts.Container;
            string blobName      = requestWrapper.UriParts.BlobName;
            StorageOperationTypes  requestOperation = StorageOperations.GetBlobOperation(requestWrapper);
            DashClientCapabilities client           = DashClientDetector.DetectClient(requestWrapper);
            HandlerResult          result           = null;
            bool servePrimaryOnly = false;

            switch (requestOperation)
            {
            case StorageOperationTypes.GetBlobProperties:
            case StorageOperationTypes.SetBlobProperties:
            case StorageOperationTypes.GetBlobMetadata:
            case StorageOperationTypes.SetBlobMetadata:
            case StorageOperationTypes.LeaseBlob:
            case StorageOperationTypes.GetBlockList:
                servePrimaryOnly = true;
                // Fall through
                goto case StorageOperationTypes.GetBlob;

            case StorageOperationTypes.GetBlob:
            case StorageOperationTypes.SnapshotBlob:
            case StorageOperationTypes.GetPageRanges:
                if (client.HasFlag(DashClientCapabilities.FollowRedirects))
                {
                    // If the client has specified a concurrent operation, then we always serve the primary account
                    // as that is where the concurrency controls exist
                    if (!servePrimaryOnly && requestWrapper.Headers.IsConcurrentRequest())
                    {
                        servePrimaryOnly = true;
                    }
                    result = await BlobHandler.BasicBlobAsync(requestWrapper,
                                                              containerName,
                                                              blobName,
                                                              servePrimaryOnly,
                                                              BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation));
                }
                break;

            case StorageOperationTypes.PutPage:
                if (client.HasFlag(DashClientCapabilities.NoPayloadToDash))
                {
                    result = await BlobHandler.BasicBlobAsync(requestWrapper,
                                                              containerName,
                                                              blobName,
                                                              true,
                                                              BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation));
                }
                break;

            case StorageOperationTypes.PutBlob:
            case StorageOperationTypes.PutBlock:
            case StorageOperationTypes.PutBlockList:
                if (client.HasFlag(DashClientCapabilities.NoPayloadToDash))
                {
                    result = await BlobHandler.PutBlobAsync(requestWrapper,
                                                            containerName,
                                                            blobName,
                                                            BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation));
                }
                break;

            default:
                // All other operations flow through to the controller action
                break;
            }
            DashTrace.TraceInformation("Operation: {0}, client capability: {1}, action: {2}",
                                       requestOperation,
                                       client,
                                       result == null ? "Forward" : "Redirect");
            return(result);
        }