diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000000000..d645695673349
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000..eeca3cf539abd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,90 @@
+
Windows Azure SDK for .NET
+This SDK allows you to build Windows Azure applications that take advantage of
+Azure scalable cloud computing resources: table and blob storage, messaging through
+Service Bus, distributed caching through cache.
+For documentation please see the
+Windows Azure .NET Developer Center.
+
+Features
+
+ - Tables
+
+ - Create/Delete Tables
+ - Query/Create/Read/Update/Delete Entities
+
+ - BLOBs
+
+ - Create/Read/Update/Delete BLOBs
+
+ - Queues
+
+ - Create/Delete Queues
+ - Insert/Peek Queue Messages
+ - Advanced Queue Operations
+
+
+
+Getting Started
+Download
+
+Option 1: Via Git
+To get the source code of the SDK via git just type:
+
git clone git://github.com/WindowsAzure/azure-sdk-for-net.git
+cd ./azure-sdk-for-net
+
+Option 2: Via NuGet
+To get the binaries of this library as distributed by Microsoft, ready for use
+within your project you can also have them installed by the .NET package manager NuGet.
+
Install-Package WindowsAzure.Storage
+
+Requirements
+
+ - Account: To use this SDK to call Windows Azure services, you need to first
+ create an account.
+ - Hosting: To host your Java code in Windows Azure, you additionally need
+ to download the full Windows Azure SDK for .NET - which includes packaging,
+ emulation, and deployment tools.
+ - .NET Framework 3.5 or higher
+
+
+Code Samples
+Note:
+
+
+First, include the classes you need (in this case we'll include the StorageClient
+and further demonstrate creating a table):
+
using Microsoft.WindowsAzure;
+using Microsoft.WindowsAzure.StorageClient;
+
+To perform an operation on any Windows Azure resource you will first instantiate
+a client which allows performing actions on it. The resource is known as an
+entity. To do so for Table you also have to authenticate your request:
+
var storageAccount =
+ CloudStorageAccount.FromConfigurationSetting("StorageConnectionString");
+var tableClient = storageAccount.CreateCloudTableClient();
+
+Now, to create a table entity using the client:
+
tableClient.CreateTable("People");
+
+Need Help?
+Be sure to check out the Windows Azure
+Developer Forums on MSDN if you have trouble with the provided code.
+
+Feedback
+For feedback related specificically to this SDK, please use the Issues
+section of the repository.
+For general suggestions about Windows Azure please use our
+UserVoice forum.
+
+Learn More
+
diff --git a/microsoft-azure-api/AssemblyInfo.cs b/microsoft-azure-api/AssemblyInfo.cs
new file mode 100644
index 0000000000000..6283e4af1ac98
--- /dev/null
+++ b/microsoft-azure-api/AssemblyInfo.cs
@@ -0,0 +1,96 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains assembly information.
+//
+//-----------------------------------------------------------------------
+
+using System;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Security;
+
+#if UNSIGNED
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.StorageClient.Internal")]
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.StorageClient.ConvenienceTests")]
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.StorageClient.Protocol.Tests")]
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Diagnostics")]
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Diagnostics.Internal")]
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.CloudDrive")]
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.InternalAccessor")]
+#else
+[assembly: InternalsVisibleTo(
+ "Microsoft.WindowsAzure.StorageClient.Internal, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67" +
+ "871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0b" +
+ "d333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307" +
+ "e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3" +
+ "08055da9")]
+[assembly: InternalsVisibleTo(
+ "StorageClientConvenienceTests, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67" +
+ "871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0b" +
+ "d333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307" +
+ "e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3" +
+ "08055da9")]
+[assembly: InternalsVisibleTo(
+ "StorageClientProtocolTests, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67" +
+ "871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0b" +
+ "d333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307" +
+ "e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3" +
+ "08055da9")]
+[assembly: InternalsVisibleTo(
+ "StorageClientProtocolBillingTests, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67" +
+ "871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0b" +
+ "d333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307" +
+ "e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3" +
+ "08055da9")]
+[assembly: InternalsVisibleTo(
+ "Microsoft.WindowsAzure.Diagnostics, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67" +
+ "871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0b" +
+ "d333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307" +
+ "e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3" +
+ "08055da9")]
+[assembly: InternalsVisibleTo(
+ "Microsoft.WindowsAzure.Diagnostics.Internal, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67" +
+ "871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0b" +
+ "d333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307" +
+ "e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3" +
+ "08055da9")]
+[assembly: InternalsVisibleTo(
+ "Microsoft.WindowsAzure.CloudDrive, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67" +
+ "871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0b" +
+ "d333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307" +
+ "e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3" +
+ "08055da9")]
+[assembly: InternalsVisibleTo(
+ "Microsoft.WindowsAzure.InternalAccessor, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67" +
+ "871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0b" +
+ "d333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307" +
+ "e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3" +
+ "08055da9")]
+
+#endif
+[assembly: CLSCompliant(true)]
+[assembly: AllowPartiallyTrustedCallers]
+[assembly: SecurityTransparent]
+[assembly: NeutralResourcesLanguageAttribute("en-US")]
diff --git a/microsoft-azure-api/CloudStorageAccount.cs b/microsoft-azure-api/CloudStorageAccount.cs
new file mode 100644
index 0000000000000..f7d62db6c77e2
--- /dev/null
+++ b/microsoft-azure-api/CloudStorageAccount.cs
@@ -0,0 +1,945 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudStorageAccount class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using Microsoft.WindowsAzure.StorageClient;
+ using AccountSetting = System.Collections.Generic.KeyValuePair>;
+
+ ///
+ /// Represents a Windows Azure storage account.
+ ///
+ public sealed class CloudStorageAccount
+ {
+ ///
+ /// The setting name for using the development storage.
+ ///
+ internal const string UseDevelopmentStorageName = "UseDevelopmentStorage";
+
+ ///
+ /// The setting name for specifying a development storage proxy Uri.
+ ///
+ internal const string DevelopmentStorageProxyUriName = "DevelopmentStorageProxyUri";
+
+ ///
+ /// The setting name for using the default storage endpoints with the specified protocol.
+ ///
+ internal const string DefaultEndpointsProtocolName = "DefaultEndpointsProtocol";
+
+ ///
+ /// The setting name for the account name.
+ ///
+ internal const string AccountNameName = "AccountName";
+
+ ///
+ /// The setting name for the account key.
+ ///
+ internal const string AccountKeyName = "AccountKey";
+
+ ///
+ /// The setting name for a custom blob storage endpoint.
+ ///
+ internal const string BlobEndpointName = "BlobEndpoint";
+
+ ///
+ /// The setting name for a custom queue endpoint.
+ ///
+ internal const string QueueEndpointName = "QueueEndpoint";
+
+ ///
+ /// The setting name for a custom table storage endpoint.
+ ///
+ internal const string TableEndpointName = "TableEndpoint";
+
+ ///
+ /// The setting name for a shared access key.
+ ///
+ internal const string SharedAccessSignatureName = "SharedAccessSignature";
+
+ ///
+ /// The default account name for the development storage.
+ ///
+ private const string DevstoreAccountName = "devstoreaccount1";
+
+ ///
+ /// The default account key for the development storage.
+ ///
+ private const string DevstoreAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
+
+ ///
+ /// The credentials string used to test for the development storage credentials.
+ ///
+ private const string DevstoreCredentialInString =
+ CloudStorageAccount.AccountNameName + "=" + DevstoreAccountName + ";" +
+ CloudStorageAccount.AccountKeyName + "=" + DevstoreAccountKey;
+
+ ///
+ /// The root blob storage DNS name.
+ ///
+ private const string BlobBaseDnsName = "blob.core.windows.net";
+
+ ///
+ /// The root queue DNS name.
+ ///
+ private const string QueueBaseDnsName = "queue.core.windows.net";
+
+ ///
+ /// The root table storage DNS name.
+ ///
+ private const string TableBaseDnsName = "table.core.windows.net";
+
+ ///
+ /// Validator for the UseDevelopmentStorage setting. Must be "true".
+ ///
+ private static readonly AccountSetting UseDevelopmentStorageSetting = Setting(UseDevelopmentStorageName, "true");
+
+ ///
+ /// Validator for the DevelopmentStorageProxyUri setting. Must be a valid Uri.
+ ///
+ private static readonly AccountSetting DevelopmentStorageProxyUriSetting = Setting(DevelopmentStorageProxyUriName, IsValidUri);
+
+ ///
+ /// Validator for the DefaultEndpointsProtocol setting. Must be either "http" or "https".
+ ///
+ private static readonly AccountSetting DefaultEndpointsProtocolSetting = Setting(DefaultEndpointsProtocolName, "http", "https");
+
+ ///
+ /// Validator for the AccountName setting. No restrictions.
+ ///
+ private static readonly AccountSetting AccountNameSetting = Setting(AccountNameName);
+
+ ///
+ /// Validator for the AccountKey setting. Must be a valid base64 string.
+ ///
+ private static readonly AccountSetting AccountKeySetting = Setting(AccountKeyName, IsValidBase64String);
+
+ ///
+ /// Validator for the BlobEndpoint setting. Must be a valid Uri.
+ ///
+ private static readonly AccountSetting BlobEndpointSetting = Setting(BlobEndpointName, IsValidUri);
+
+ ///
+ /// Validator for the QueueEndpoint setting. Must be a valid Uri.
+ ///
+ private static readonly AccountSetting QueueEndpointSetting = Setting(QueueEndpointName, IsValidUri);
+
+ ///
+ /// Validator for the TableEndpoint setting. Must be a valid Uri.
+ ///
+ private static readonly AccountSetting TableEndpointSetting = Setting(TableEndpointName, IsValidUri);
+
+ ///
+ /// Validator for the SharedAccessSignature setting. No restrictions.
+ ///
+ private static readonly AccountSetting SharedAccessSignatureSetting = Setting(SharedAccessSignatureName);
+
+ ///
+ /// Stores the user-specified configuration setting publisher.
+ ///
+ private static Action> configurationSettingPublisher;
+
+ ///
+ /// Singleton instance for the development storage account.
+ ///
+ private static CloudStorageAccount devStoreAccount;
+
+ ///
+ /// Initializes a new instance of the class using the specified
+ /// account credentials and service endpoints.
+ ///
+ /// The account credentials.
+ /// The Blob service endpoint.
+ /// The Queue service endpoint.
+ /// The Table service endpoint.
+ public CloudStorageAccount(
+ StorageCredentials storageCredentials,
+ Uri blobEndpoint,
+ Uri queueEndpoint,
+ Uri tableEndpoint)
+ {
+ this.Credentials = storageCredentials;
+ this.BlobEndpoint = blobEndpoint;
+ this.QueueEndpoint = queueEndpoint;
+ this.TableEndpoint = tableEndpoint;
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified
+ /// account credentials and the default service endpoints.
+ ///
+ /// An object of type that
+ /// specifies the account name and account key for the storage account.
+ /// True to use HTTPS to connect to storage service endpoints; otherwise, false.
+ public CloudStorageAccount(StorageCredentialsAccountAndKey storageCredentialsAccountAndKey, bool useHttps)
+ : this(
+ storageCredentialsAccountAndKey,
+ new Uri(GetDefaultBlobEndpoint(useHttps ? "https" : "http", storageCredentialsAccountAndKey.AccountName)),
+ new Uri(GetDefaultQueueEndpoint(useHttps ? "https" : "http", storageCredentialsAccountAndKey.AccountName)),
+ new Uri(GetDefaultTableEndpoint(useHttps ? "https" : "http", storageCredentialsAccountAndKey.AccountName)))
+ {
+ }
+
+ ///
+ /// Gets a object that references the development storage account.
+ ///
+ /// A reference to the development storage account.
+ public static CloudStorageAccount DevelopmentStorageAccount
+ {
+ get
+ {
+ if (devStoreAccount == null)
+ {
+ devStoreAccount = GetDevelopmentStorageAccount(new Uri("http://127.0.0.1"));
+ }
+
+ return devStoreAccount;
+ }
+ }
+
+ ///
+ /// Gets the endpoint for the Blob service, as configured for the storage account.
+ ///
+ /// The Blob service endpoint.
+ public Uri BlobEndpoint { get; private set; }
+
+ ///
+ /// Gets the endpoint for the Queue service, as configured for the storage account.
+ ///
+ /// The Queue service endpoint.
+ public Uri QueueEndpoint { get; private set; }
+
+ ///
+ /// Gets the endpoint for the Table service, as configured for the storage account.
+ ///
+ /// The Table service endpoint.
+ public Uri TableEndpoint { get; private set; }
+
+ ///
+ /// Gets the credentials used to create this object.
+ ///
+ /// The credentials used to create the object.
+ public StorageCredentials Credentials { get; private set; }
+
+ ///
+ /// Parses a connection string and returns a created
+ /// from the connection string.
+ ///
+ /// A valid connection string.
+ /// Thrown if is null or empty.
+ /// Thrown if is not a valid connection string.
+ /// Thrown if cannot be parsed.
+ /// A object constructed from the values provided in the connection string.
+ public static CloudStorageAccount Parse(string value)
+ {
+ CloudStorageAccount ret;
+
+ if (String.IsNullOrEmpty(value))
+ {
+ throw new ArgumentNullException("value");
+ }
+
+ if (TryParse(value, out ret, err => { throw new FormatException(err); }))
+ {
+ return ret;
+ }
+
+ throw new ArgumentException("Error parsing", "value");
+ }
+
+ ///
+ /// Create a new instance of a object from a specified configuration
+ /// setting. This method may be called only after the
+ /// method has been called to configure the global configuration setting publisher.
+ ///
+ /// The name of the configuration setting.
+ /// Thrown if the global configuration setting
+ /// publisher has not been configured, or if the configuration setting cannot be found.
+ /// A constructed from the values in the configuration string.
+ public static CloudStorageAccount FromConfigurationSetting(string settingName)
+ {
+ if (configurationSettingPublisher == null)
+ {
+ throw new InvalidOperationException(SR.ConfigurationSettingPublisherError);
+ }
+
+ return (new StorageAccountConfigurationSetting(settingName)).CloudStorageAccount;
+ }
+
+ ///
+ /// Indicates whether a connection string can be parsed to return a object.
+ ///
+ /// The connection string to parse.
+ /// A object to hold the instance returned if
+ /// the connection string can be parsed.
+ /// true if the connection string was successfully parsed; otherwise, false.
+ public static bool TryParse(string value, out CloudStorageAccount account)
+ {
+ if (String.IsNullOrEmpty(value))
+ {
+ account = null;
+
+ return false;
+ }
+
+ return TryParse(value, out account, err => { });
+ }
+
+ ///
+ /// Sets the global configuration setting publisher for the storage account, which will be called when
+ /// the account access keys are updated in the service configuration file.
+ ///
+ /// The configuration setting publisher for the storage account.
+ public static void SetConfigurationSettingPublisher(Action> configurationSettingPublisher)
+ {
+ CloudStorageAccount.configurationSettingPublisher = configurationSettingPublisher;
+ }
+
+ ///
+ /// Returns a connection string for this storage account, without sensitive data.
+ ///
+ /// A connection string.
+ public override string ToString()
+ {
+ return this.ToString(false);
+ }
+
+ ///
+ /// Returns a connection string for the storage account, optionally with sensitive data.
+ ///
+ /// True to include sensitive data in the string; otherwise, false.
+ /// A connection string.
+ public string ToString(bool exportSecrets)
+ {
+ var settings = new List();
+
+ if (this == DevelopmentStorageAccount)
+ {
+ settings.Add(String.Format("{0}=true", UseDevelopmentStorageName));
+ }
+ else if (this.Credentials != null &&
+ this.Credentials.AccountName == DevstoreAccountName &&
+ this.Credentials.ToString(true) == DevstoreCredentialInString &&
+ this.BlobEndpoint != null && this.QueueEndpoint != null && this.TableEndpoint != null &&
+ this.BlobEndpoint.Host == this.QueueEndpoint.Host &&
+ this.QueueEndpoint.Host == this.TableEndpoint.Host &&
+ this.BlobEndpoint.Scheme == this.QueueEndpoint.Scheme &&
+ this.QueueEndpoint.Scheme == this.TableEndpoint.Scheme)
+ {
+ settings.Add(String.Format("{0}=true", UseDevelopmentStorageName));
+ settings.Add(String.Format("{0}={1}://{2}", DevelopmentStorageProxyUriName, this.BlobEndpoint.Scheme, this.BlobEndpoint.Host));
+ }
+ else if (this.BlobEndpoint != null && this.QueueEndpoint != null && this.TableEndpoint != null &&
+ this.BlobEndpoint.Host.EndsWith(BlobBaseDnsName) &&
+ this.QueueEndpoint.Host.EndsWith(QueueBaseDnsName) &&
+ this.TableEndpoint.Host.EndsWith(TableBaseDnsName) &&
+ this.BlobEndpoint.Scheme == this.QueueEndpoint.Scheme &&
+ this.QueueEndpoint.Scheme == this.TableEndpoint.Scheme)
+ {
+ settings.Add(String.Format("{0}={1}", DefaultEndpointsProtocolName, this.BlobEndpoint.Scheme));
+
+ if (this.Credentials != null)
+ {
+ settings.Add(this.Credentials.ToString(exportSecrets));
+ }
+ }
+ else
+ {
+ if (this.BlobEndpoint != null)
+ {
+ settings.Add(String.Format("{0}={1}", BlobEndpointName, this.BlobEndpoint));
+ }
+
+ if (this.QueueEndpoint != null)
+ {
+ settings.Add(String.Format("{0}={1}", QueueEndpointName, this.QueueEndpoint));
+ }
+
+ if (this.TableEndpoint != null)
+ {
+ settings.Add(String.Format("{0}={1}", TableEndpointName, this.TableEndpoint));
+ }
+
+ if (this.Credentials != null)
+ {
+ settings.Add(this.Credentials.ToString(exportSecrets));
+ }
+ }
+
+ return String.Join(";", settings.ToArray());
+ }
+
+ ///
+ /// Returns a with development storage credentials using the specified proxy Uri.
+ ///
+ /// The proxy endpoint to use.
+ /// The new .
+ internal static CloudStorageAccount GetDevelopmentStorageAccount(Uri proxyUri)
+ {
+ if (proxyUri == null)
+ {
+ return DevelopmentStorageAccount;
+ }
+
+ string prefix = proxyUri.Scheme + "://" + proxyUri.Host;
+
+ return new CloudStorageAccount(
+ new StorageCredentialsAccountAndKey(DevstoreAccountName, DevstoreAccountKey),
+ new Uri(prefix + ":10000/devstoreaccount1"),
+ new Uri(prefix + ":10001/devstoreaccount1"),
+ new Uri(prefix + ":10002/devstoreaccount1"));
+ }
+
+ ///
+ /// Internal implementation of Parse/TryParse.
+ ///
+ /// The string to parse.
+ /// The to return.
+ /// A callback for reporting errors.
+ /// If true, the parse was successful. Otherwise, false.
+ internal static bool TryParse(string s, out CloudStorageAccount accountInformation, Action error)
+ {
+ var settings = ParseStringIntoSettings(s, error);
+
+ // malformed settings string
+ if (settings == null)
+ {
+ accountInformation = null;
+
+ return false;
+ }
+
+ // devstore case
+ if (MatchesSpecification(
+ settings,
+ AllRequired(UseDevelopmentStorageSetting),
+ Optional(DevelopmentStorageProxyUriSetting)))
+ {
+ var proxyUri = settings[DevelopmentStorageProxyUriName];
+
+ accountInformation = GetDevelopmentStorageAccount(proxyUri == null ? null : new Uri(proxyUri));
+
+ return true;
+ }
+
+ // automatic case
+ if (MatchesSpecification(
+ settings,
+ AllRequired(DefaultEndpointsProtocolSetting, AccountNameSetting, AccountKeySetting),
+ Optional(BlobEndpointSetting, QueueEndpointSetting, TableEndpointSetting)))
+ {
+ var blobEndpoint = settings[BlobEndpointName];
+ var queueEndpoint = settings[QueueEndpointName];
+ var tableEndpoint = settings[TableEndpointName];
+
+ accountInformation = new CloudStorageAccount(
+ GetCredentials(settings),
+ new Uri(blobEndpoint ?? GetDefaultBlobEndpoint(settings)),
+ new Uri(queueEndpoint ?? GetDefaultQueueEndpoint(settings)),
+ new Uri(tableEndpoint ?? GetDefaultTableEndpoint(settings)));
+
+ return true;
+ }
+
+ // explicit case
+ if (MatchesSpecification(
+ settings,
+ AtLeastOne(BlobEndpointSetting, QueueEndpointSetting, TableEndpointSetting),
+ ValidCredentials()))
+ {
+ var blobUri = settings[BlobEndpointName] == null ? null : new Uri(settings[BlobEndpointName]);
+ var queueUri = settings[QueueEndpointName] == null ? null : new Uri(settings[QueueEndpointName]);
+ var tableUri = settings[TableEndpointName] == null ? null : new Uri(settings[TableEndpointName]);
+
+ accountInformation = new CloudStorageAccount(GetCredentials(settings), blobUri, queueUri, tableUri);
+
+ return true;
+ }
+
+ // not valid
+ accountInformation = null;
+
+ error("No valid combination of account information found.");
+
+ return false;
+ }
+
+ ///
+ /// Tokenizes input and stores name/value pairs in a NameValueCollection.
+ ///
+ /// The string to parse.
+ /// Error reporting delegate.
+ /// Tokenized collection.
+ private static NameValueCollection ParseStringIntoSettings(string s, Action error)
+ {
+ var settings = new NameValueCollection();
+ var pos = 0;
+
+ while (pos < s.Length)
+ {
+ var equalsPos = s.IndexOf('=', pos);
+
+ if (equalsPos == -1)
+ {
+ error("Settings must be of the form \"name=value\".");
+ return null;
+ }
+
+ var name = s.Substring(pos, equalsPos - pos).Trim();
+
+ if (settings[name] != null)
+ {
+ error(String.Format("Duplicate setting '{0}' found.", name));
+ return null;
+ }
+
+ var semiPos = s.IndexOf(';', equalsPos);
+
+ if (semiPos == -1)
+ {
+ // add 1 to move past the '='
+ settings.Add(name, s.Substring(equalsPos + 1).Trim());
+
+ return settings;
+ }
+ else
+ {
+ // add 1 to move past the '=', subtract one to miss the ';'
+ settings.Add(name, s.Substring(equalsPos + 1, semiPos - equalsPos - 1));
+ }
+
+ // add 1 to move past the ';'
+ pos = semiPos + 1;
+ }
+
+ error("Invalid account string.");
+ return null;
+ }
+
+ ///
+ /// Encapsulates a validation rule for an enumeration based account setting.
+ ///
+ /// The name of the setting.
+ /// A list of valid values for the setting.
+ /// An representing the enumeration constraint.
+ private static AccountSetting Setting(string name, params string[] validValues)
+ {
+ return new AccountSetting(
+ name,
+ (settingValue) =>
+ {
+ if (validValues.Length == 0)
+ {
+ return true;
+ }
+
+ foreach (var validValue in validValues)
+ {
+ if (settingValue == validValue)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ });
+ }
+
+ ///
+ /// Encapsulates a validation rule using a func.
+ ///
+ /// The name of the setting.
+ /// A func that determines if the value is valid.
+ /// An representing the constraint.
+ private static AccountSetting Setting(string name, Func isValid)
+ {
+ return new AccountSetting(name, isValid);
+ }
+
+ ///
+ /// Determines whether the specified setting value is a valid base64 string.
+ ///
+ /// The setting value.
+ /// true if the specified setting value is a valid base64 string; otherwise, false.
+ private static bool IsValidBase64String(string settingValue)
+ {
+ try
+ {
+ Convert.FromBase64String(settingValue);
+
+ return true;
+ }
+ catch (FormatException)
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Validation function that validates Uris.
+ ///
+ /// Value to validate.
+ /// true if the specified setting value is a valid Uri; otherwise, false.
+ private static bool IsValidUri(string settingValue)
+ {
+ return Uri.IsWellFormedUriString(settingValue, UriKind.Absolute);
+ }
+
+ ///
+ /// Settings filter that requires all specified settings be present and valid.
+ ///
+ /// A list of settings that must be present.
+ /// The remaining settings or null if the filter's requirement is not satisfied.
+ private static Func AllRequired(params AccountSetting[] requiredSettings)
+ {
+ return (settings) =>
+ {
+ var result = new NameValueCollection(settings);
+
+ foreach (var requirement in requiredSettings)
+ {
+ if (result[requirement.Key] != null && requirement.Value(result[requirement.Key]))
+ {
+ result.Remove(requirement.Key);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ return result;
+ };
+ }
+
+ ///
+ /// Settings filter that removes optional values.
+ ///
+ /// A list of settings that are optional.
+ /// The remaining settings or null if the filter's requirement is not satisfied.
+ private static Func Optional(params AccountSetting[] optionalSettings)
+ {
+ return (settings) =>
+ {
+ var result = new NameValueCollection(settings);
+
+ foreach (var requirement in optionalSettings)
+ {
+ if (result[requirement.Key] != null && requirement.Value(result[requirement.Key]))
+ {
+ result.Remove(requirement.Key);
+ }
+ }
+
+ return result;
+ };
+ }
+
+ ///
+ /// Settings filter that ensures that at least one setting is present.
+ ///
+ /// A list of settings of which one must be present.
+ /// The remaining settings or null if the filter's requirement is not satisfied.
+ private static Func AtLeastOne(params AccountSetting[] atLeastOneSettings)
+ {
+ return (settings) =>
+ {
+ var result = new NameValueCollection(settings);
+ bool foundOne = false;
+
+ foreach (var requirement in atLeastOneSettings)
+ {
+ if (result[requirement.Key] != null && requirement.Value(result[requirement.Key]))
+ {
+ result.Remove(requirement.Key);
+ foundOne = true;
+ }
+ }
+
+ return foundOne ? result : null;
+ };
+ }
+
+ ///
+ /// Settings filter that ensures that a valid combination of credentials is present.
+ ///
+ /// The remaining settings or null if the filter's requirement is not satisfied.
+ private static Func ValidCredentials()
+ {
+ return (settings) =>
+ {
+ var accountName = settings[AccountNameName];
+ var accountKey = settings[AccountKeyName];
+ var sharedAccessSignature = settings[SharedAccessSignatureName];
+ var result = settings;
+
+ if (accountName != null && !AccountNameSetting.Value(accountName))
+ {
+ return null;
+ }
+
+ if (accountKey != null && !AccountKeySetting.Value(accountKey))
+ {
+ return null;
+ }
+
+ if (sharedAccessSignature != null && !SharedAccessSignatureSetting.Value(sharedAccessSignature))
+ {
+ return null;
+ }
+
+ result.Remove(AccountNameName);
+ result.Remove(AccountKeyName);
+ result.Remove(SharedAccessSignatureName);
+
+ // AccountAndKey
+ if (accountName != null && accountKey != null && sharedAccessSignature == null)
+ {
+ return result;
+ }
+
+ // SharedAccessSignature
+ if (accountName == null && accountKey == null && sharedAccessSignature != null)
+ {
+ return result;
+ }
+
+ // Anonymous
+ if (accountName == null && accountKey == null && sharedAccessSignature == null)
+ {
+ return result;
+ }
+
+ return null;
+ };
+ }
+
+ ///
+ /// Tests to see if a given list of settings matches a set of filters exactly.
+ ///
+ /// The settings to check.
+ /// A list of filters to check.
+ ///
+ /// If any filter returns null, false.
+ /// If there are any settings left over after all filters are processed, false.
+ /// Otherwise true.
+ ///
+ private static bool MatchesSpecification(
+ NameValueCollection settings,
+ params Func[] constraints)
+ {
+ foreach (var constraint in constraints)
+ {
+ var remainingSettings = constraint(settings);
+
+ if (remainingSettings == null)
+ {
+ return false;
+ }
+ else
+ {
+ settings = remainingSettings;
+ }
+ }
+
+ if (settings.Count == 0)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Gets a StorageCredentials object corresponding to whatever credentials are supplied in the given settings.
+ ///
+ /// The settings to check.
+ /// The StorageCredentials object specified in the settings.
+ private static StorageCredentials GetCredentials(NameValueCollection settings)
+ {
+ var accountName = settings[AccountNameName];
+ var accountKey = settings[AccountKeyName];
+ var sharedAccessSignature = settings[SharedAccessSignatureName];
+
+ if (accountName != null && accountKey != null && sharedAccessSignature == null)
+ {
+ return new StorageCredentialsAccountAndKey(accountName, Convert.FromBase64String(accountKey));
+ }
+
+ if (accountName == null && accountKey == null && sharedAccessSignature != null)
+ {
+ return new StorageCredentialsSharedAccessSignature(sharedAccessSignature);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets the default blob endpoint using specified settings.
+ ///
+ /// The settings to use.
+ /// The default blob endpoint.
+ private static string GetDefaultBlobEndpoint(NameValueCollection settings)
+ {
+ return GetDefaultBlobEndpoint(settings[DefaultEndpointsProtocolName], settings[AccountNameName]);
+ }
+
+ ///
+ /// Gets the default blob endpoint using the specified protocol and account name.
+ ///
+ /// The protocol to use.
+ /// The name of the storage account.
+ /// The default blob endpoint.
+ private static string GetDefaultBlobEndpoint(string scheme, string accountName)
+ {
+ return String.Format("{0}://{1}.{2}", scheme, accountName, BlobBaseDnsName);
+ }
+
+ ///
+ /// Gets the default queue endpoint using the specified settings.
+ ///
+ /// The settings.
+ /// The default queue endpoint.
+ private static string GetDefaultQueueEndpoint(NameValueCollection settings)
+ {
+ return GetDefaultQueueEndpoint(settings[DefaultEndpointsProtocolName], settings[AccountNameName]);
+ }
+
+ ///
+ /// Gets the default queue endpoint using the specified protocol and account name.
+ ///
+ /// The protocol to use.
+ /// The name of the storage account.
+ /// The default queue endpoint.
+ private static string GetDefaultQueueEndpoint(string scheme, string accountName)
+ {
+ return String.Format("{0}://{1}.{2}", scheme, accountName, QueueBaseDnsName);
+ }
+
+ ///
+ /// Gets the default table endpoint using the specified settings.
+ ///
+ /// The settings.
+ /// The default table endpoint.
+ private static string GetDefaultTableEndpoint(NameValueCollection settings)
+ {
+ return GetDefaultTableEndpoint(settings[DefaultEndpointsProtocolName], settings[AccountNameName]);
+ }
+
+ ///
+ /// Gets the default table endpoint using the specified protocol and account name.
+ ///
+ /// The protocol to use.
+ /// The name of the storage account.
+ /// The default table endpoint.
+ private static string GetDefaultTableEndpoint(string scheme, string accountName)
+ {
+ return String.Format("{0}://{1}.{2}", scheme, accountName, TableBaseDnsName);
+ }
+
+ ///
+ /// Encapsulates a mutable storage credentials object.
+ ///
+ internal class StorageAccountConfigurationSetting
+ {
+ ///
+ /// Stores the mutable storage credentials.
+ ///
+ private MutableStorageCredentials credentials;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Name of the configuration setting.
+ public StorageAccountConfigurationSetting(string configurationSettingName)
+ {
+ this.ConfigurationSettingName = configurationSettingName;
+ this.CloudStorageAccount = null;
+
+ CloudStorageAccount.configurationSettingPublisher(configurationSettingName, this.SetConfigurationValue);
+ }
+
+ ///
+ /// Gets or sets the name of the configuration setting from which we retrieve storage account information.
+ ///
+ public string ConfigurationSettingName { get; internal set; }
+
+ ///
+ /// Gets or sets the cloud storage account.
+ ///
+ /// The cloud storage account.
+ public CloudStorageAccount CloudStorageAccount { get; internal set; }
+
+ ///
+ /// Sets the configuration value.
+ ///
+ /// The value.
+ /// true if the value was set; otherwise, false.
+ private bool SetConfigurationValue(string value)
+ {
+ if (this.CloudStorageAccount == null)
+ {
+ // first time
+ CloudStorageAccount initialAccount;
+
+ if (CloudStorageAccount.TryParse(value, out initialAccount))
+ {
+ this.credentials = new MutableStorageCredentials(initialAccount.Credentials);
+
+ this.CloudStorageAccount = new CloudStorageAccount(
+ this.credentials,
+ initialAccount.BlobEndpoint,
+ initialAccount.QueueEndpoint,
+ initialAccount.TableEndpoint);
+ }
+ else
+ {
+ System.Diagnostics.Trace.TraceError("Error parsing storage account information - staying uninitialized");
+ }
+ }
+ else
+ {
+ // key rotation
+ CloudStorageAccount newAccount;
+ if (CloudStorageAccount.TryParse(value, out newAccount))
+ {
+ if ((newAccount.BlobEndpoint != CloudStorageAccount.BlobEndpoint) ||
+ (newAccount.QueueEndpoint != CloudStorageAccount.QueueEndpoint) ||
+ (newAccount.TableEndpoint != CloudStorageAccount.TableEndpoint))
+ {
+ System.Diagnostics.Trace.TraceWarning("Rejecting change: Endpoint(s) changed when updating storage account.");
+ return false;
+ }
+
+ this.credentials.UpdateWith(newAccount.Credentials);
+ }
+ else
+ {
+ System.Diagnostics.Trace.WriteLine("Error parsing storage account information - staying unchanged");
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+}
diff --git a/microsoft-azure-api/MutableStorageCredentials.cs b/microsoft-azure-api/MutableStorageCredentials.cs
new file mode 100644
index 0000000000000..cc6b5c5c2dc5f
--- /dev/null
+++ b/microsoft-azure-api/MutableStorageCredentials.cs
@@ -0,0 +1,184 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the MutableStorageCredentials class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using Microsoft.WindowsAzure.StorageClient;
+
+ ///
+ /// Represents a object that is mutable to support key rotation.
+ ///
+ internal sealed class MutableStorageCredentials : StorageCredentials
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MutableStorageCredentials()
+ {
+ this.Current = StorageCredentialsAnonymous.Anonymous;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The initial credentials.
+ public MutableStorageCredentials(StorageCredentials initialCredentials)
+ {
+ this.Current = initialCredentials;
+ }
+
+ ///
+ /// Gets the name of the storage account associated with the specified credentials.
+ ///
+ /// The account name.
+ public override string AccountName
+ {
+ get { return this.Current.AccountName; }
+ }
+
+ ///
+ /// Gets a value indicating whether the method will return a valid
+ /// HMAC-encoded signature string when called with the specified credentials.
+ ///
+ ///
+ /// Returns true if these credentials will yield a valid signature string; otherwise, false.
+ ///
+ public override bool CanComputeHmac
+ {
+ get { return this.Current.CanComputeHmac; }
+ }
+
+ ///
+ /// Gets a value indicating whether a request can be signed under the Shared Key authentication
+ /// scheme using the specified credentials.
+ ///
+ ///
+ /// Returns true if a request can be signed with these credentials; otherwise, false.
+ ///
+ public override bool CanSignRequest
+ {
+ get { return this.Current.CanSignRequest; }
+ }
+
+ ///
+ /// Gets a value indicating whether a request can be signed under the Shared Key Lite authentication
+ /// scheme using the specified credentials.
+ ///
+ ///
+ /// Returns true if a request can be signed with these credentials; otherwise, false.
+ ///
+ public override bool CanSignRequestLite
+ {
+ get { return this.Current.CanSignRequestLite; }
+ }
+
+ ///
+ /// Gets a value indicating whether the method must be called
+ /// before generating a signature string with the specified credentials.
+ ///
+ ///
+ /// Returns true if [needs transform Uri]; otherwise, false. If false,
+ /// calling returns the original, unmodified Uri.
+ ///
+ public override bool NeedsTransformUri
+ {
+ get { return this.Current.NeedsTransformUri; }
+ }
+
+ ///
+ /// Gets or sets the current object that this instance represents.
+ ///
+ internal StorageCredentials Current { get; set; }
+
+ ///
+ /// Updates the object with new credentials.
+ ///
+ /// The new credentials.
+ public void UpdateWith(StorageCredentials newCredentials)
+ {
+ this.Current = newCredentials;
+ }
+
+ ///
+ /// Computes the HMAC signature of the specified string.
+ ///
+ /// The string to sign.
+ /// The computed signature.
+ public override string ComputeHmac(string stringToSign)
+ {
+ return this.Current.ComputeHmac(stringToSign);
+ }
+
+ ///
+ /// Signs a request using the specified credentials under the Shared Key authentication scheme.
+ ///
+ /// The request to be signed.
+ public override void SignRequest(HttpWebRequest request)
+ {
+ this.Current.SignRequest(request);
+ }
+
+ ///
+ /// Signs a request using the specified credentials under the Shared Key Lite authentication scheme.
+ ///
+ /// The request to be signed.
+ public override void SignRequestLite(HttpWebRequest request)
+ {
+ this.Current.SignRequestLite(request);
+ }
+
+ ///
+ /// Transforms the Uri.
+ ///
+ /// The resource Uri.
+ /// The transformed Uri.
+ public override string TransformUri(string resourceURI)
+ {
+ return this.Current.TransformUri(resourceURI);
+ }
+
+ ///
+ /// Computes the 512-bit HMAC signature of the specified string.
+ ///
+ /// The string to sign.
+ /// The computed signature.
+ protected internal override string ComputeHmac512(string stringToSign)
+ {
+ return this.Current.ComputeHmac512(stringToSign);
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ /// If set to true the string exposes key information.
+ ///
+ /// A that represents this instance.
+ ///
+ protected internal override string ToString(bool exportSecrets)
+ {
+ return this.Current.ToString(exportSecrets);
+ }
+ }
+}
diff --git a/microsoft-azure-api/SR.Designer.cs b/microsoft-azure-api/SR.Designer.cs
new file mode 100644
index 0000000000000..ac775624734ba
--- /dev/null
+++ b/microsoft-azure-api/SR.Designer.cs
@@ -0,0 +1,545 @@
+//------------------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.5403
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class SR {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal SR() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.WindowsAzure.SR", typeof(SR).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The argument must not be empty string..
+ ///
+ internal static string ArgumentEmptyError {
+ get {
+ return ResourceManager.GetString("ArgumentEmptyError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The argument is out of range.
+ ///
+ internal static string ArgumentOutOfRangeError {
+ get {
+ return ResourceManager.GetString("ArgumentOutOfRangeError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The argument '{0}' is larger than maximum of '{1}'.
+ ///
+ internal static string ArgumentTooLargeError {
+ get {
+ return ResourceManager.GetString("ArgumentTooLargeError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The argument '{0}' is smaller than minimum of '{1}'.
+ ///
+ internal static string ArgumentTooSmallError {
+ get {
+ return ResourceManager.GetString("ArgumentTooSmallError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot attach to a TableStorageDataServiceContext object. These objects already contain the functionality for accessing the table storage service..
+ ///
+ internal static string AttachToTableServiceContext {
+ get {
+ return ResourceManager.GetString("AttachToTableServiceContext", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to EncodeMessage should be true for binary message..
+ ///
+ internal static string BinaryMessageShouldUseBase64Encoding {
+ get {
+ return ResourceManager.GetString("BinaryMessageShouldUseBase64Encoding", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to "Versions before 2009-09-19 do not support Shared Key Lite for Blob And Queue, current target version '{0}'".
+ ///
+ internal static string BlobQSharedKeyLiteUnsuppported {
+ get {
+ return ResourceManager.GetString("BlobQSharedKeyLiteUnsuppported", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot change size below currently written size.
+ ///
+ internal static string BlobSizeReductonError {
+ get {
+ return ResourceManager.GetString("BlobSizeReductonError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to A stream blob must have a blob size of 0..
+ ///
+ internal static string BlobSizeTypeMismatch {
+ get {
+ return ResourceManager.GetString("BlobSizeTypeMismatch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The blob is larger than maximum supported size '{0}'.
+ ///
+ internal static string BlobTooLargeError {
+ get {
+ return ResourceManager.GetString("BlobTooLargeError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to BlobType of the blob reference doesn't match BlobType of the blob..
+ ///
+ internal static string BlobTypeMismatchExceptionMessage {
+ get {
+ return ResourceManager.GetString("BlobTypeMismatchExceptionMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Data already uploaded.
+ ///
+ internal static string BlocksExistError {
+ get {
+ return ResourceManager.GetString("BlocksExistError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The block size must be positive value.
+ ///
+ internal static string BlocksTooSmallError {
+ get {
+ return ResourceManager.GetString("BlocksTooSmallError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Block size can not be larger than '{0}'.
+ ///
+ internal static string BlockTooLargeError {
+ get {
+ return ResourceManager.GetString("BlockTooLargeError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot create Shared Access Signature for snapshots. Perform the operation on the root blob instead..
+ ///
+ internal static string CannotCreateSASForSnapshot {
+ get {
+ return ResourceManager.GetString("CannotCreateSASForSnapshot", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot create Shared Access Signature as the credentials does not have account name information. Please check that the credentials used support creating Shared Access Signature..
+ ///
+ internal static string CannotCreateSASSignatureForGivenCred {
+ get {
+ return ResourceManager.GetString("CannotCreateSASSignatureForGivenCred", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot create Shared Access Signature unless the Account Key credentials are used by the BlobServiceClient..
+ ///
+ internal static string CannotCreateSASWithoutAccountKey {
+ get {
+ return ResourceManager.GetString("CannotCreateSASWithoutAccountKey", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot perform this operation on a blob representing a snapshot..
+ ///
+ internal static string CannotModifySnapshot {
+ get {
+ return ResourceManager.GetString("CannotModifySnapshot", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot retry operation using a source stream which does not support seek. To avoid this exception set the RetryPolicy to NoRetry or use a seekable stream..
+ ///
+ internal static string CannotRetryNonSeekableStreamError {
+ get {
+ return ResourceManager.GetString("CannotRetryNonSeekableStreamError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Server operation did not finish within user specified timeout '{0}' seconds, check if operation is valid or try increasing the timeout..
+ ///
+ internal static string ClientSideTimeoutError {
+ get {
+ return ResourceManager.GetString("ClientSideTimeoutError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to If-Modified-Since and If-Unmodified-Since require a DateTime value..
+ ///
+ internal static string ConditionalRequiresDateTime {
+ get {
+ return ResourceManager.GetString("ConditionalRequiresDateTime", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to If-Match and If-None-Match require an ETag value..
+ ///
+ internal static string ConditionalRequiresETag {
+ get {
+ return ResourceManager.GetString("ConditionalRequiresETag", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The conditionals specified for this operation did not match server..
+ ///
+ internal static string ConditionNotMatchedError {
+ get {
+ return ResourceManager.GetString("ConditionNotMatchedError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to SetConfigurationSettingPublisher needs to be called before FromConfigurationSetting can be used.
+ ///
+ internal static string ConfigurationSettingPublisherError {
+ get {
+ return ResourceManager.GetString("ConfigurationSettingPublisherError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The supplied credentials '{0'} cannot be used to sign request.
+ ///
+ internal static string CredentialsCantSignRequest {
+ get {
+ return ResourceManager.GetString("CredentialsCantSignRequest", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The option '{0}' must be 'None' to delete a specific snapshot specified by '{1}'.
+ ///
+ internal static string DeleteSnapshotsNotValidError {
+ get {
+ return ResourceManager.GetString("DeleteSnapshotsNotValidError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot combine incompatible absolute Uris base '{0}' relative '{1}'.When trying to combine 2 absolute Uris, the base uri should be a valid base of the relative Uri..
+ ///
+ internal static string IncompatibleAddressesProvided {
+ get {
+ return ResourceManager.GetString("IncompatibleAddressesProvided", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid acl public access type returned '{0}'. Expected blob or container..
+ ///
+ internal static string InvalidAclType {
+ get {
+ return ResourceManager.GetString("InvalidAclType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The continuation type passed in is unexpected. Please verify that the correct continuation type is passed in. Expected {0}, found {1}.
+ ///
+ internal static string InvalidContinuationType {
+ get {
+ return ResourceManager.GetString("InvalidContinuationType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid query parameters inside Blob address '{0}'..
+ ///
+ internal static string InvalidQueryParametersInsideBlobAddress {
+ get {
+ return ResourceManager.GetString("InvalidQueryParametersInsideBlobAddress", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Listing snapshots is only supported in flat mode (no delimiter). Consider setting BlobRequestOptions.UseFlatBlobListing property to true..
+ ///
+ internal static string ListSnapshotsWithDelimiterError {
+ get {
+ return ResourceManager.GetString("ListSnapshotsWithDelimiterError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Calculated MD5 does not match existing property.
+ ///
+ internal static string MD5MismatchError {
+ get {
+ return ResourceManager.GetString("MD5MismatchError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Messages cannot be larger than {0} bytes..
+ ///
+ internal static string MessageTooLarge {
+ get {
+ return ResourceManager.GetString("MessageTooLarge", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot find account information inside Uri '{0}'.
+ ///
+ internal static string MissingAccountInformationInUri {
+ get {
+ return ResourceManager.GetString("MissingAccountInformationInUri", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid blob address '{0}', missing container information.
+ ///
+ internal static string MissingContainerInformation {
+ get {
+ return ResourceManager.GetString("MissingContainerInformation", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Missing mandatory parameters for valid Shared Access Signature.
+ ///
+ internal static string MissingMandatoryParamtersForSAS {
+ get {
+ return ResourceManager.GetString("MissingMandatoryParamtersForSAS", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Canonicalization did not find a non empty x-ms-date header in the WebRequest. Please use a WebRequest with a valid x-ms-date header in RFC 123 format (example request.Headers["x-ms-date"] = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture)).
+ ///
+ internal static string MissingXmsDateInHeader {
+ get {
+ return ResourceManager.GetString("MissingXmsDateInHeader", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot provide credentials as part of the address and as constructor parameter. Either pass in the address or use a different constructor..
+ ///
+ internal static string MultipleCredentialsProvided {
+ get {
+ return ResourceManager.GetString("MultipleCredentialsProvided", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Multiple different snapshot times provided as part of query '{0}' and as constructor parameter '{1}'..
+ ///
+ internal static string MultipleSnapshotTimesProvided {
+ get {
+ return ResourceManager.GetString("MultipleSnapshotTimesProvided", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to EndMoveNextSegment must be called before the Current property can be accessed..
+ ///
+ internal static string MustCallEndMoveNextSegmentFirst {
+ get {
+ return ResourceManager.GetString("MustCallEndMoveNextSegmentFirst", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The segment cursor has no more results..
+ ///
+ internal static string NoMoreResultsForSegmentCursor {
+ get {
+ return ResourceManager.GetString("NoMoreResultsForSegmentCursor", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to This operation is not supported for creating a PageBlob. Use other operations to create a PageBlob..
+ ///
+ internal static string NotSupportedForPageBlob {
+ get {
+ return ResourceManager.GetString("NotSupportedForPageBlob", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Missing account name information inside path style uri. Path style uris should be of the form http://<IPAddressPlusPort>/<accountName>.
+ ///
+ internal static string PathStyleUriMissingAccountNameInformation {
+ get {
+ return ResourceManager.GetString("PathStyleUriMissingAccountNameInformation", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Address '{0}' is not an absolute address. Relative addresses are not permitted in here..
+ ///
+ internal static string RelativeAddressNotPermitted {
+ get {
+ return ResourceManager.GetString("RelativeAddressNotPermitted", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Attempting to seek past the end of the stream.
+ ///
+ internal static string SeekTooFarError {
+ get {
+ return ResourceManager.GetString("SeekTooFarError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Attempting to seek before the start of the stream.
+ ///
+ internal static string SeekTooLowError {
+ get {
+ return ResourceManager.GetString("SeekTooLowError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Server returned more that MaxResults requested.
+ ///
+ internal static string ServerReturnedMoreThanMaxResults {
+ get {
+ return ResourceManager.GetString("ServerReturnedMoreThanMaxResults", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Snapshot query parameter is already defined in the blobUri. Either pass in a snapshotTime parameter or use a full URL with a snapshot query parameter..
+ ///
+ internal static string SnapshotTimePassedTwice {
+ get {
+ return ResourceManager.GetString("SnapshotTimePassedTwice", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to '{0}' is not a valid table name..
+ ///
+ internal static string TableNameInvalid {
+ get {
+ return ResourceManager.GetString("TableNameInvalid", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The number of blocks is larger than the maximum of '{0}'.
+ ///
+ internal static string TooManyBlocksError {
+ get {
+ return ResourceManager.GetString("TooManyBlocksError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Too many '{0}' shared access policy identifiers provided. Server does not support setting more than '{1}' on a single container..
+ ///
+ internal static string TooManyPolicyIdentifiers {
+ get {
+ return ResourceManager.GetString("TooManyPolicyIdentifiers", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The blob type cannot be undefined..
+ ///
+ internal static string UndefinedBlobType {
+ get {
+ return ResourceManager.GetString("UndefinedBlobType", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/microsoft-azure-api/SR.resx b/microsoft-azure-api/SR.resx
new file mode 100644
index 0000000000000..3b87ad0c907d4
--- /dev/null
+++ b/microsoft-azure-api/SR.resx
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Cannot change size below currently written size
+
+
+ A stream blob must have a blob size of 0.
+
+
+ The blob is larger than maximum supported size '{0}'
+
+
+ Data already uploaded
+
+
+ Block size can not be larger than '{0}'
+
+
+ If-Modified-Since and If-Unmodified-Since require a DateTime value.
+
+
+ If-Match and If-None-Match require an ETag value.
+
+
+ Calculated MD5 does not match existing property
+
+
+ The blob type cannot be undefined.
+
+
+ The block size must be positive value
+
+
+ The argument must not be empty string.
+
+
+ Invalid blob address '{0}', missing container information
+
+
+ The argument is out of range
+
+
+ The argument '{0}' is larger than maximum of '{1}'
+
+
+ The argument '{0}' is smaller than minimum of '{1}'
+
+
+ Attempting to seek past the end of the stream
+
+
+ Attempting to seek before the start of the stream
+
+
+ Address '{0}' is not an absolute address. Relative addresses are not permitted in here.
+
+
+ The number of blocks is larger than the maximum of '{0}'
+
+
+ The segment cursor has no more results.
+
+
+ '{0}' is not a valid table name.
+
+
+ Cannot attach to a TableStorageDataServiceContext object. These objects already contain the functionality for accessing the table storage service.
+
+
+ Cannot create Shared Access Signature unless the Account Key credentials are used by the BlobServiceClient.
+
+
+ Cannot perform this operation on a blob representing a snapshot.
+
+
+ Invalid query parameters inside Blob address '{0}'.
+
+
+ Missing mandatory parameters for valid Shared Access Signature
+
+
+ Cannot provide credentials as part of the address and as constructor parameter. Either pass in the address or use a different constructor.
+
+
+ Multiple different snapshot times provided as part of query '{0}' and as constructor parameter '{1}'.
+
+
+ Too many '{0}' shared access policy identifiers provided. Server does not support setting more than '{1}' on a single container.
+
+
+ Messages cannot be larger than {0} bytes.
+
+
+ Invalid acl public access type returned '{0}'. Expected blob or container.
+
+
+ Cannot combine incompatible absolute Uris base '{0}' relative '{1}'.When trying to combine 2 absolute Uris, the base uri should be a valid base of the relative Uri.
+
+
+ Cannot find account information inside Uri '{0}'
+
+
+ Missing account name information inside path style uri. Path style uris should be of the form http://<IPAddressPlusPort>/<accountName>
+
+
+ Cannot create Shared Access Signature as the credentials does not have account name information. Please check that the credentials used support creating Shared Access Signature.
+
+
+ Server operation did not finish within user specified timeout '{0}' seconds, check if operation is valid or try increasing the timeout.
+
+
+ EndMoveNextSegment must be called before the Current property can be accessed.
+
+
+ "Versions before 2009-09-19 do not support Shared Key Lite for Blob And Queue, current target version '{0}'"
+
+
+ Canonicalization did not find a non empty x-ms-date header in the WebRequest. Please use a WebRequest with a valid x-ms-date header in RFC 123 format (example request.Headers["x-ms-date"] = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture))
+
+
+ This operation is not supported for creating a PageBlob. Use other operations to create a PageBlob.
+
+
+ The option '{0}' must be 'None' to delete a specific snapshot specified by '{1}'
+
+
+ The supplied credentials '{0'} cannot be used to sign request
+
+
+ Cannot create Shared Access Signature for snapshots. Perform the operation on the root blob instead.
+
+
+ The conditionals specified for this operation did not match server.
+
+
+ SetConfigurationSettingPublisher needs to be called before FromConfigurationSetting can be used
+
+
+ Listing snapshots is only supported in flat mode (no delimiter). Consider setting BlobRequestOptions.UseFlatBlobListing property to true.
+
+
+ Server returned more that MaxResults requested
+
+
+ The continuation type passed in is unexpected. Please verify that the correct continuation type is passed in. Expected {0}, found {1}
+
+
+ BlobType of the blob reference doesn't match BlobType of the blob.
+
+
+ EncodeMessage should be true for binary message.
+
+
+ Cannot retry operation using a source stream which does not support seek. To avoid this exception set the RetryPolicy to NoRetry or use a seekable stream.
+
+
+ Snapshot query parameter is already defined in the blobUri. Either pass in a snapshotTime parameter or use a full URL with a snapshot query parameter.
+
+
\ No newline at end of file
diff --git a/microsoft-azure-api/Settings.SourceAnalysis b/microsoft-azure-api/Settings.SourceAnalysis
new file mode 100644
index 0000000000000..975146b8930ca
--- /dev/null
+++ b/microsoft-azure-api/Settings.SourceAnalysis
@@ -0,0 +1,40 @@
+
+
+
+ %StyleCopLocation%\bin\Settings.SourceAnalysis
+
+
+
+
+ True
+
+
+
+
+
+
+
+ md
+
+
+
+
+
+ Microsoft
+ Copyright (c)2010 Microsoft. All rights reserved.
+
+
+
+
\ No newline at end of file
diff --git a/microsoft-azure-api/Settings.StyleCop b/microsoft-azure-api/Settings.StyleCop
new file mode 100644
index 0000000000000..4abf2f98f0cee
--- /dev/null
+++ b/microsoft-azure-api/Settings.StyleCop
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+ at
+ io
+ md
+ x
+
+
+
+
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+ True
+
+
+
+
+ True
+
+
+
+
+ True
+
+
+
+
+ True
+
+
+
+
+ True
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/microsoft-azure-api/SharedAccessSignatureHelper.cs b/microsoft-azure-api/SharedAccessSignatureHelper.cs
new file mode 100644
index 0000000000000..14244f7e77e39
--- /dev/null
+++ b/microsoft-azure-api/SharedAccessSignatureHelper.cs
@@ -0,0 +1,233 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the SharedAccessSignatureHelper.cs class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure
+{
+ using System;
+ using System.Collections.Specialized;
+ using System.Globalization;
+ using System.Web;
+ using Microsoft.WindowsAzure.StorageClient;
+ using Microsoft.WindowsAzure.StorageClient.Protocol;
+
+ ///
+ /// Contains helper methods for implementing shared access signatures.
+ ///
+ internal static class SharedAccessSignatureHelper
+ {
+ ///
+ /// Get the signature hash embedded inside the Shared Access Signature.
+ ///
+ /// The shared access policy to hash.
+ /// An optional identifier for the policy.
+ /// The canonical resource string, unescaped.
+ /// The client whose credentials are to be used for signing.
+ /// The signed hash.
+ internal static string GetSharedAccessSignatureHashImpl(
+ SharedAccessPolicy policy,
+ string groupPolicyIdentifier,
+ string resourceName,
+ CloudBlobClient client)
+ {
+ CommonUtils.AssertNotNull("policy", policy);
+ CommonUtils.AssertNotNullOrEmpty("resourceName", resourceName);
+ CommonUtils.AssertNotNull("client", client);
+
+ ////StringToSign = signedpermissions + "\n"
+ //// signedstart + "\n"
+ //// signedexpiry + "\n"
+ //// canonicalizedresource + "\n"
+ //// signedidentifier
+ ////HMAC-SHA256(URL.Decode(UTF8.Encode(StringToSign)))
+
+ string stringToSign = string.Format(
+ "{0}\n{1}\n{2}\n{3}\n{4}",
+ SharedAccessPolicy.PermissionsToString(policy.Permissions),
+ GetDateTimeOrEmpty(policy.SharedAccessStartTime),
+ GetDateTimeOrEmpty(policy.SharedAccessExpiryTime),
+ resourceName,
+ groupPolicyIdentifier);
+
+ string signature = client.Credentials.ComputeHmac(stringToSign);
+
+ return signature;
+ }
+
+ ///
+ /// Get the complete query builder for creating the Shared Access Signature query.
+ ///
+ /// The shared access policy to hash.
+ /// An optional identifier for the policy.
+ /// Either "b" for blobs or "c" for containers.
+ /// The signature to use.
+ /// The finished query builder.
+ internal static UriQueryBuilder GetShareAccessSignatureImpl(
+ SharedAccessPolicy policy,
+ string groupPolicyIdentifier,
+ string resourceType,
+ string signature)
+ {
+ CommonUtils.AssertNotNull("policy", policy);
+ CommonUtils.AssertNotNullOrEmpty("resourceType", resourceType);
+ CommonUtils.AssertNotNull("signature", signature);
+
+ UriQueryBuilder builder = new UriQueryBuilder();
+
+ // FUTURE blob for blob and container for container
+ string permissions = SharedAccessPolicy.PermissionsToString(policy.Permissions);
+ if (String.IsNullOrEmpty(permissions))
+ {
+ permissions = null;
+ }
+
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedStart, GetDateTimeOrNull(policy.SharedAccessStartTime));
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedExpiry, GetDateTimeOrNull(policy.SharedAccessExpiryTime));
+ builder.Add(Constants.QueryConstants.SignedResource, resourceType);
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedPermissions, permissions);
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedIdentifier, groupPolicyIdentifier);
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.Signature, signature);
+
+ return builder;
+ }
+
+ ///
+ /// Converts the specified value to either a string representation or .
+ ///
+ /// The value to convert.
+ /// A string representing the specified value.
+ internal static string GetDateTimeOrEmpty(DateTime? value)
+ {
+ string result = GetDateTimeOrNull(value) ?? string.Empty;
+ return result;
+ }
+
+ ///
+ /// Converts the specified value to either a string representation or null.
+ ///
+ /// The value to convert.
+ /// A string representing the specified value.
+ internal static string GetDateTimeOrNull(DateTime? value)
+ {
+ string result = value != null ? value.Value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") : null;
+ return result;
+ }
+
+ ///
+ /// Escapes and adds the specified name/value pair to the query builder if it is not null.
+ ///
+ /// The builder to add the value to.
+ /// The name of the pair.
+ /// The value to be escaped.
+ internal static void AddEscapedIfNotNull(UriQueryBuilder builder, string name, string value)
+ {
+ if (value != null)
+ {
+ builder.Add(name, value);
+ }
+ }
+
+ ///
+ /// Parses the query.
+ ///
+ /// The query parameters.
+ /// The credentials.
+ internal static void ParseQuery(NameValueCollection queryParameters, out StorageCredentialsSharedAccessSignature credentials)
+ {
+ string signature = null;
+ string signedStart = null;
+ string signedExpiry = null;
+ string signedResource = null;
+ string sigendPermissions = null;
+ string signedIdentifier = null;
+ string signedVersion = null;
+
+ bool sasParameterFound = false;
+
+ credentials = null;
+
+ foreach (var key in queryParameters.AllKeys)
+ {
+ switch (key.ToLower())
+ {
+ case Constants.QueryConstants.SignedStart:
+ signedStart = queryParameters[key];
+ sasParameterFound = true;
+ break;
+
+ case Constants.QueryConstants.SignedExpiry:
+ signedExpiry = queryParameters[key];
+ sasParameterFound = true;
+ break;
+
+ case Constants.QueryConstants.SignedPermissions:
+ sigendPermissions = queryParameters[key];
+ sasParameterFound = true;
+ break;
+
+ case Constants.QueryConstants.SignedResource:
+ signedResource = queryParameters[key];
+ sasParameterFound = true;
+ break;
+
+ case Constants.QueryConstants.SignedIdentifier:
+ signedIdentifier = queryParameters[key];
+ sasParameterFound = true;
+ break;
+
+ case Constants.QueryConstants.Signature:
+ signature = queryParameters[key];
+ sasParameterFound = true;
+ break;
+
+ case Constants.QueryConstants.SignedVersion:
+ signedVersion = queryParameters[key];
+ sasParameterFound = true;
+ break;
+
+ default:
+ break;
+ //// string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.InvalidQueryParametersInsideBlobAddress, key.ToLower());
+ //// throw new ArgumentException(errorMessage);
+ }
+ }
+
+ if (sasParameterFound)
+ {
+ if (signature == null || signedResource == null)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.MissingMandatoryParamtersForSAS);
+ throw new ArgumentException(errorMessage);
+ }
+
+ UriQueryBuilder builder = new UriQueryBuilder();
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedStart, signedStart);
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedExpiry, signedExpiry);
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedPermissions, sigendPermissions);
+ builder.Add(Constants.QueryConstants.SignedResource, signedResource);
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedIdentifier, signedIdentifier);
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.SignedVersion, signedVersion);
+ AddEscapedIfNotNull(builder, Constants.QueryConstants.Signature, signature);
+
+ string token = builder.ToString();
+ credentials = new StorageCredentialsSharedAccessSignature(token);
+ }
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient.csproj b/microsoft-azure-api/StorageClient.csproj
new file mode 100644
index 0000000000000..2c65c540b052d
--- /dev/null
+++ b/microsoft-azure-api/StorageClient.csproj
@@ -0,0 +1,266 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {C6F30C10-E1C2-4327-BB6B-3160B479CCA1}
+ Library
+ Microsoft.WindowsAzure
+ Microsoft.WindowsAzure.StorageClient
+ v3.5
+
+
+ 3.5
+
+ false
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ TRACE;DEBUG;CODE_ANALYSIS;UNSIGNED
+ prompt
+ 4
+ bin\Debug\Microsoft.WindowsAzure.StorageClient.XML
+ false
+
+
+ AllRules.ruleset
+ true
+
+
+ pdbonly
+ true
+ bin\Release\
+
+
+ prompt
+ 4
+ AllRules.ruleset
+ true
+ false
+
+
+ false
+
+
+
+
+
+ 3.5
+
+
+
+ 3.5
+
+
+
+ 3.5
+
+
+
+
+
+
+
+
+
+ True
+ True
+ SR.resx
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ SR.Designer.cs
+ Designer
+
+
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ true
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
\ No newline at end of file
diff --git a/microsoft-azure-api/StorageClient/AccessCondition.cs b/microsoft-azure-api/StorageClient/AccessCondition.cs
new file mode 100644
index 0000000000000..0677e59cc2797
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/AccessCondition.cs
@@ -0,0 +1,204 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the AccessCondition struct.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Globalization;
+ using System.Net;
+ using Protocol;
+
+ ///
+ /// Represents a set of access conditions to be used for operations against the storage services.
+ ///
+ public struct AccessCondition
+ {
+ ///
+ /// Indicates that no access condition is set.
+ ///
+ public static readonly AccessCondition None = new AccessCondition();
+
+ ///
+ /// Gets or sets the header of the request to be set.
+ ///
+ /// The access condition header.
+ private HttpRequestHeader? AccessConditionHeader { get; set; }
+
+ ///
+ /// Gets or sets the value of the access condition header.
+ ///
+ /// The access condition header value.
+ private string AccessConditionValue { get; set; }
+
+ ///
+ /// Returns an access condition such that an operation will be performed only if the resource has been modified since the specified time.
+ ///
+ /// The last-modified time for the resource, expressed as a UTC value.
+ /// A structure specifying the if-modified-since condition.
+ public static AccessCondition IfModifiedSince(DateTime lastModifiedUtc)
+ {
+ lastModifiedUtc = lastModifiedUtc.ToUniversalTime();
+ return new AccessCondition
+ {
+ AccessConditionHeader = HttpRequestHeader.IfModifiedSince,
+
+ // convert Date to String using RFC 1123 pattern
+ AccessConditionValue = lastModifiedUtc.ToString("R", CultureInfo.InvariantCulture)
+ };
+ }
+
+ ///
+ /// Returns an access condition such that an operation will be performed only if the resource has not been modified since the specified time.
+ ///
+ /// The last-modified time for the resource, expressed as a UTC value.
+ /// A structure specifying the if-not-modified-since condition.
+ public static AccessCondition IfNotModifiedSince(DateTime lastModifiedUtc)
+ {
+ lastModifiedUtc = lastModifiedUtc.ToUniversalTime();
+ return new AccessCondition
+ {
+ AccessConditionHeader = HttpRequestHeader.IfUnmodifiedSince,
+
+ // convert Date to String using RFC 1123 pattern
+ AccessConditionValue = Request.ConvertDateTimeToHttpString(lastModifiedUtc)
+ };
+ }
+
+ ///
+ /// Returns an access condition such that an operation will be performed only if the resource's ETag value matches the ETag value provided.
+ ///
+ /// The ETag value to check.
+ /// A structure specifying the if-match condition.
+ public static AccessCondition IfMatch(string etag)
+ {
+ return new AccessCondition { AccessConditionHeader = HttpRequestHeader.IfMatch, AccessConditionValue = etag };
+ }
+
+ ///
+ /// Returns an access condition such that an operation will be performed only if the resource's ETag value does not match the ETag value provided.
+ ///
+ /// The ETag value to check.
+ /// A structure specifying the if-none-match condition.
+ public static AccessCondition IfNoneMatch(string etag)
+ {
+ return new AccessCondition { AccessConditionHeader = HttpRequestHeader.IfNoneMatch, AccessConditionValue = etag };
+ }
+
+ ///
+ /// Converts AccessCondition into a type for use as a source conditional to Copy.
+ ///
+ /// The original condition.
+ /// The resulting header for the condition.
+ /// The value for the condition.
+ internal static void GetSourceConditions(
+ AccessCondition condition,
+ out Protocol.ConditionHeaderKind header,
+ out string value)
+ {
+ header = Protocol.ConditionHeaderKind.None;
+ value = null;
+
+ if (condition.AccessConditionHeader != null)
+ {
+ switch (condition.AccessConditionHeader.GetValueOrDefault())
+ {
+ case HttpRequestHeader.IfMatch:
+ header = Protocol.ConditionHeaderKind.IfMatch;
+ break;
+ case HttpRequestHeader.IfNoneMatch:
+ header = Protocol.ConditionHeaderKind.IfNoneMatch;
+ break;
+ case HttpRequestHeader.IfModifiedSince:
+ header = Protocol.ConditionHeaderKind.IfModifiedSince;
+ break;
+ case HttpRequestHeader.IfUnmodifiedSince:
+ header = Protocol.ConditionHeaderKind.IfUnmodifiedSince;
+ break;
+ default:
+ CommonUtils.ArgumentOutOfRange("condition", condition);
+ break;
+ }
+
+ value = condition.AccessConditionValue;
+ }
+ }
+
+ ///
+ /// Applies the condition to the web request.
+ ///
+ /// The request to be modified.
+ internal void ApplyCondition(HttpWebRequest request)
+ {
+ if (this.AccessConditionHeader != null)
+ {
+ if (this.AccessConditionHeader.Equals(HttpRequestHeader.IfModifiedSince))
+ {
+ // Not using this property will cause Restricted property exception to be thrown
+ request.IfModifiedSince = DateTime.Parse(
+ this.AccessConditionValue,
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.AdjustToUniversal);
+ }
+ else
+ {
+ request.Headers[(HttpRequestHeader)this.AccessConditionHeader] = this.AccessConditionValue;
+ }
+ }
+ }
+
+ ///
+ /// Verifies the condition is satisfied.
+ ///
+ /// The ETag to check.
+ /// The last modified time UTC.
+ /// true if the condition is satisfied, otherwise false.
+ internal bool VerifyConditionHolds(string etag, DateTime lastModifiedTimeUtc)
+ {
+ switch (this.AccessConditionHeader.GetValueOrDefault())
+ {
+ case HttpRequestHeader.IfMatch:
+ return this.AccessConditionValue == etag || String.Equals(this.AccessConditionValue, "*");
+ case HttpRequestHeader.IfNoneMatch:
+ return this.AccessConditionValue != etag;
+ case HttpRequestHeader.IfModifiedSince:
+ {
+ var conditional = DateTime.Parse(
+ this.AccessConditionValue,
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.AdjustToUniversal);
+ return lastModifiedTimeUtc > conditional;
+ }
+
+ case HttpRequestHeader.IfUnmodifiedSince:
+ {
+ var conditional = DateTime.Parse(
+ this.AccessConditionValue,
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.AdjustToUniversal);
+ return lastModifiedTimeUtc <= conditional;
+ }
+
+ default:
+ CommonUtils.ArgumentOutOfRange("AccessConditionHeader", this.AccessConditionHeader);
+ return false;
+ }
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobAttributes.cs b/microsoft-azure-api/StorageClient/BlobAttributes.cs
new file mode 100644
index 0000000000000..06c556b54cb8c
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobAttributes.cs
@@ -0,0 +1,86 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobAttributes class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Specialized;
+ using System.ComponentModel;
+ using Protocol;
+
+ ///
+ /// Represents a blob's attributes.
+ ///
+ public class BlobAttributes
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BlobAttributes()
+ {
+ this.Properties = new BlobProperties();
+ this.Metadata = new NameValueCollection();
+ }
+
+ ///
+ /// Initializes a new instance of the class from an existing object.
+ ///
+ /// The set of blob attributes to clone.
+ public BlobAttributes(BlobAttributes other)
+ {
+ this.Properties = new BlobProperties(other.Properties);
+
+ if (other.Metadata != null)
+ {
+ this.Metadata = new NameValueCollection(other.Metadata);
+ }
+
+ this.Snapshot = other.Snapshot;
+ this.Uri = other.Uri;
+ }
+
+ ///
+ /// Gets the blob's system properties.
+ ///
+ /// The blob's properties.
+ public BlobProperties Properties { get; internal set; }
+
+ ///
+ /// Gets the user-defined metadata for the blob.
+ ///
+ /// The blob's metadata, as a collection of name-value pairs.
+ public NameValueCollection Metadata { get; internal set; }
+
+ ///
+ /// Gets the blob's URI.
+ ///
+ /// The absolute URI to the blob.
+ public Uri Uri { get; internal set; }
+
+ ///
+ /// Gets the date and time that the blob snapshot was taken, if this blob is a snapshot.
+ ///
+ /// The blob's snapshot time if the blob is a snapshot; otherwise, null.
+ ///
+ /// If the blob is not a snapshot, the value of this property is null.
+ ///
+ public DateTime? Snapshot { get; internal set; }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobContainerAttributes.cs b/microsoft-azure-api/StorageClient/BlobContainerAttributes.cs
new file mode 100644
index 0000000000000..efd43b4f3716c
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobContainerAttributes.cs
@@ -0,0 +1,64 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobContainerAttributes class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Specialized;
+
+ ///
+ /// Represents a container's attributes, including its properties and metadata.
+ ///
+ public class BlobContainerAttributes
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BlobContainerAttributes()
+ {
+ this.Metadata = new NameValueCollection();
+ this.Properties = new BlobContainerProperties();
+ }
+
+ ///
+ /// Gets the user-defined metadata for the container.
+ ///
+ /// The container's metadata, as a collection of name-value pairs.
+ public NameValueCollection Metadata { get; internal set; }
+
+ ///
+ /// Gets the container's system properties.
+ ///
+ /// The container's properties.
+ public BlobContainerProperties Properties { get; internal set; }
+
+ ///
+ /// Gets the name of the container.
+ ///
+ /// The container's name.
+ public string Name { get; internal set; }
+
+ ///
+ /// Gets the container's URI.
+ ///
+ /// The absolute URI to the container.
+ public Uri Uri { get; internal set; }
+ }
+}
\ No newline at end of file
diff --git a/microsoft-azure-api/StorageClient/BlobContainerPermissions.cs b/microsoft-azure-api/StorageClient/BlobContainerPermissions.cs
new file mode 100644
index 0000000000000..c317c29dc85a3
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobContainerPermissions.cs
@@ -0,0 +1,52 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobContainerPermissions class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Represents the permissions for a container.
+ ///
+ public class BlobContainerPermissions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BlobContainerPermissions()
+ {
+ this.PublicAccess = BlobContainerPublicAccessType.Off;
+ SharedAccessPolicies = new SharedAccessPolicies();
+ }
+
+ ///
+ /// Gets or sets the public access setting for the container.
+ ///
+ /// The public access setting for the container.
+ public BlobContainerPublicAccessType PublicAccess { get; set; }
+
+ ///
+ /// Gets the set of shared access policies for the container.
+ ///
+ /// The set of shared access policies for the container.
+ public SharedAccessPolicies SharedAccessPolicies { get; private set; }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobContainerProperties.cs b/microsoft-azure-api/StorageClient/BlobContainerProperties.cs
new file mode 100644
index 0000000000000..35fba435a1f9b
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobContainerProperties.cs
@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobContainerProperties class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Specialized;
+
+ ///
+ /// Represents the system properties for a container.
+ ///
+ public class BlobContainerProperties
+ {
+ ///
+ /// Gets the ETag value for the container.
+ ///
+ /// The container's ETag value.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Naming",
+ "CA1702:CompoundWordsShouldBeCasedCorrectly",
+ MessageId = "ETag",
+ Justification = "ETag is the correct capitalization.")]
+ public string ETag { get; internal set; }
+
+ ///
+ /// Gets the container's last-modified time, expressed as a UTC value.
+ ///
+ /// The container's last-modified time.
+ public DateTime LastModifiedUtc { get; internal set; }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobContainerPublicAccessType.cs b/microsoft-azure-api/StorageClient/BlobContainerPublicAccessType.cs
new file mode 100644
index 0000000000000..10d18364468bf
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobContainerPublicAccessType.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobContainerPublicAccessType enumeration.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ ///
+ /// Specifies the level of public access that is allowed on the container.
+ ///
+ public enum BlobContainerPublicAccessType
+ {
+ ///
+ /// No public access. Only the account owner can read resources in this container.
+ ///
+ Off,
+
+ ///
+ /// Container-level public access. Anonymous clients can read container and blob data.
+ ///
+ Container,
+
+ ///
+ /// Blob-level public access. Anonymous clients can read only blob data within this container.
+ ///
+ Blob
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobErrorCodeStrings.cs b/microsoft-azure-api/StorageClient/BlobErrorCodeStrings.cs
new file mode 100644
index 0000000000000..55b586913d2d9
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobErrorCodeStrings.cs
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobErrorCodeStrings class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ ///
+ /// Provides error code strings that are specific to the Blob service.
+ ///
+ public static class BlobErrorCodeStrings
+ {
+ ///
+ /// Error code that may be returned when a block ID is invalid.
+ ///
+ public const string InvalidBlockId = "InvalidBlockId";
+
+ ///
+ /// Error code that may be returned when a blob with the specified address cannot be found.
+ ///
+ public const string BlobNotFound = "BlobNotFound";
+
+ ///
+ /// Error code that may be returned when a client attempts to create a blob that already exists.
+ ///
+ public const string BlobAlreadyExists = "BlobAlreadyExists";
+
+ ///
+ /// Error code that may be returned when the specified block or blob is invalid.
+ ///
+ public const string InvalidBlobOrBlock = "InvalidBlobOrBlock";
+
+ ///
+ /// Error code that may be returned when a block list is invalid.
+ ///
+ public const string InvalidBlockList = "InvalidBlockList";
+ }
+}
\ No newline at end of file
diff --git a/microsoft-azure-api/StorageClient/BlobListingDetails.cs b/microsoft-azure-api/StorageClient/BlobListingDetails.cs
new file mode 100644
index 0000000000000..a5942cf383116
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobListingDetails.cs
@@ -0,0 +1,56 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobListingDetails enumeration.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+
+ ///
+ /// Specifies which items to include when listing a set of blobs.
+ ///
+ [Flags]
+ public enum BlobListingDetails
+ {
+ ///
+ /// List only committed blobs, and do not return blob metadata.
+ ///
+ None = 0x0,
+
+ ///
+ /// List committed blobs and blob snapshots.
+ ///
+ Snapshots = 0x1,
+
+ ///
+ /// Retrieve blob metadata for each blob returned in the listing.
+ ///
+ Metadata = 0x2,
+
+ ///
+ /// List committed and uncommitted blobs.
+ ///
+ UncommittedBlobs = 0x4,
+
+ ///
+ /// List all available committed blobs, uncommitted blobs, and snapshots, and return all metadata for those blobs.
+ ///
+ All = 0x7
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobProperties.cs b/microsoft-azure-api/StorageClient/BlobProperties.cs
new file mode 100644
index 0000000000000..cfe582e792c9f
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobProperties.cs
@@ -0,0 +1,130 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobProperties class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Specialized;
+ using System.ComponentModel;
+ using Protocol;
+
+ ///
+ /// Represents the system properties for a blob.
+ ///
+ public class BlobProperties
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BlobProperties()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class based on an existing instance.
+ ///
+ /// The set of properties to clone.
+ public BlobProperties(BlobProperties other)
+ {
+ this.BlobType = other.BlobType;
+ this.ContentEncoding = other.ContentEncoding;
+ this.ContentLanguage = other.ContentLanguage;
+ this.Length = other.Length;
+ this.ContentType = other.ContentType;
+ this.ETag = other.ETag;
+ this.LastModifiedUtc = other.LastModifiedUtc;
+ this.LeaseStatus = other.LeaseStatus;
+ }
+
+ ///
+ /// Gets or sets the cache-control value stored for the blob.
+ ///
+ /// The blob's cache-control value.
+ public string CacheControl { get; set; }
+
+ ///
+ /// Gets or sets the content-encoding value stored for the blob.
+ ///
+ /// The blob's content-encoding value.
+ ///
+ /// If this property has not been set for the blob, it returns null.
+ ///
+ public string ContentEncoding { get; set; }
+
+ ///
+ /// Gets or sets the content-language value stored for the blob.
+ ///
+ /// The blob's content-language value.
+ ///
+ /// If this property has not been set for the blob, it returns null.
+ ///
+ public string ContentLanguage { get; set; }
+
+ ///
+ /// Gets the size of the blob, in bytes.
+ ///
+ /// The blob's size in bytes.
+ public long Length { get; internal set; }
+
+ ///
+ /// Gets or sets the content-MD5 value stored for the blob.
+ ///
+ /// The blob's content-MD5 hash.
+ public string ContentMD5 { get; set; }
+
+ ///
+ /// Gets or sets the content-type value stored for the blob.
+ ///
+ /// The blob's content-type value.
+ ///
+ /// If this property has not been set for the blob, it returns null.
+ ///
+ public string ContentType { get; set; }
+
+ ///
+ /// Gets the blob's ETag value.
+ ///
+ /// The blob's ETag value.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Naming",
+ "CA1702:CompoundWordsShouldBeCasedCorrectly",
+ MessageId = "ETag",
+ Justification = "ETag is the proper capitalization.")]
+ public string ETag { get; internal set; }
+
+ ///
+ /// Gets the the last-modified time for the blob, expressed as a UTC value.
+ ///
+ /// The blob's last-modified time, in UTC format.
+ public DateTime LastModifiedUtc { get; internal set; }
+
+ ///
+ /// Gets the type of the blob.
+ ///
+ /// A object that indicates the type of the blob.
+ public BlobType BlobType { get; internal set; }
+
+ ///
+ /// Gets the blob's lease status.
+ ///
+ /// A object that indicates the blob's lease status.
+ public LeaseStatus LeaseStatus { get; internal set; }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobReadStream.cs b/microsoft-azure-api/StorageClient/BlobReadStream.cs
new file mode 100644
index 0000000000000..451719f47fa6d
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobReadStream.cs
@@ -0,0 +1,1047 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobReadStream class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Security.Cryptography;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// This class represents a seekable, read-only stream on a blob.
+ ///
+ internal class BlobReadStream : BlobStream
+ {
+ ///
+ /// The threshold beyond which we start a new read-Ahead.
+ ///
+ private const double ReadAheadThreshold = 0.25;
+
+ ///
+ /// The current position with the stream.
+ ///
+ private long position;
+
+ ///
+ /// The number of bytes to read forward on every request.
+ ///
+ private long readAheadSize;
+
+ ///
+ /// The options applied to the stream.
+ ///
+ private BlobRequestOptions options;
+
+ ///
+ /// True if the AccessCondition has been changed to match a single ETag.
+ ///
+ private bool setEtagCondition;
+
+ ///
+ /// The list of blocks for this blob.
+ ///
+ private List blockList;
+
+ ///
+ /// The already available blocks for reading. This member is the only possible point of thread contention between the user's requests and ReadAhead async work.
+ /// At any particular time, the ReadAhead thread may be adding more items into the list. The second thread will never remove/modify an existing item within the list.
+ ///
+ private DownloadedBlockCollection downloadedBlocksList;
+
+ ///
+ /// A handle to the parallel download of data for ReadAhead.
+ ///
+ private IAsyncResult readAheadResult;
+
+ ///
+ /// Initializes a new instance of the BlobReadStream class.
+ ///
+ /// The blob used for downloads.
+ /// Modifiers to be applied to the blob. After first request, the ETag is always applied.
+ /// The number of bytes to read ahead.
+ /// Controls whether block's signatures are verified.
+ internal BlobReadStream(CloudBlob blob, BlobRequestOptions options, long readAheadInBytes, bool verifyBlocks)
+ {
+ CommonUtils.AssertNotNull("blob", blob);
+ CommonUtils.AssertNotNull("options", options);
+ CommonUtils.AssertInBounds("readAheadInBytes", readAheadInBytes, 0, Protocol.Constants.MaxBlobSize);
+
+ this.Blob = blob;
+ this.IntegrityControlVerificationEnabled = verifyBlocks;
+ this.options = options;
+ this.ReadAheadSize = readAheadInBytes;
+
+ this.downloadedBlocksList = new DownloadedBlockCollection();
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports reading.
+ ///
+ /// Returns true if the stream supports reading; otherwise, false.
+ public override bool CanRead
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports seeking.
+ ///
+ /// Returns true if the stream supports seeking; otherwise, false.
+ public override bool CanSeek
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports writing.
+ ///
+ /// Returns true if the stream supports writing; otherwise, false.
+ public override bool CanWrite
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream can time out.
+ ///
+ /// A value that determines whether the current stream can time out.
+ public override bool CanTimeout
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to read before timing out.
+ ///
+ /// A value, in miliseconds, that determines how long the stream will attempt to read before timing out.
+ public override int ReadTimeout
+ {
+ get
+ {
+ return this.options.Timeout.RoundUpToMilliseconds();
+ }
+
+ set
+ {
+ this.options.Timeout = TimeSpan.FromMilliseconds(value);
+ }
+ }
+
+ ///
+ /// Gets the length of the blob.
+ ///
+ /// May need to do a roundtrip to retrieve it.
+ public override long Length
+ {
+ get
+ {
+ if (!this.LengthAvailable)
+ {
+ this.RetrieveSize();
+ }
+
+ return this.Blob.Properties.Length;
+ }
+ }
+
+ ///
+ /// Gets or sets the position within the stream.
+ ///
+ public override long Position
+ {
+ get
+ {
+ return this.position;
+ }
+
+ set
+ {
+ this.Seek(value, SeekOrigin.Begin);
+ }
+ }
+
+ ///
+ /// Gets or sets the number of bytes to read ahead.
+ ///
+ public override long ReadAheadSize
+ {
+ get
+ {
+ return this.readAheadSize;
+ }
+
+ set
+ {
+ this.readAheadSize = value;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the signature of each downloaded block should be verified.
+ ///
+ /// This requires having a blocklist, having blockIDs to be in the appropriate format. This causes all reads to be rounded to the nearest block.
+ public override bool IntegrityControlVerificationEnabled { get; internal set; }
+
+ ///
+ /// Gets the number of bytes that are cached locally but not yet read by user.
+ ///
+ internal long BufferedDataLength
+ {
+ get
+ {
+ long start, end;
+ this.downloadedBlocksList.CalculateGapLength(this.position, this.readAheadSize, out start, out end);
+ return start - this.position;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether length is available.
+ ///
+ private bool LengthAvailable
+ {
+ get
+ {
+ return (!string.IsNullOrEmpty(this.Blob.Properties.ETag) && (this.Blob.Properties.Length > 0)) || this.setEtagCondition;
+ }
+ }
+
+ ///
+ /// Setting the length of the blob is not supported.
+ ///
+ /// The desired length.
+ /// Always thrown.
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Write is not supported.
+ ///
+ /// An array of bytes. This method copies count bytes from buffer to the current stream.
+ /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream.
+ /// The number of bytes to be written to the current stream.
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Flush is not supported on read-only stream.
+ ///
+ public override void Flush()
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Seeks to the desired position. Any seek outside of the buffered/read-ahead data will cancel read-ahead and clear the buffered data.
+ ///
+ /// A byte offset relative to the origin parameter.
+ /// A value of type System.IO.SeekOrigin indicating the reference point used to obtain the new position.
+ /// The new position within the current stream.
+ /// Thrown if offset is invalid for SeekOrigin
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ switch (origin)
+ {
+ case SeekOrigin.Begin:
+ this.CheckBounds(offset);
+
+ this.position = offset;
+ break;
+ case SeekOrigin.Current:
+ this.CheckBounds(this.position + offset);
+
+ this.position += offset;
+ break;
+ case SeekOrigin.End:
+ this.CheckBounds(this.Length + offset);
+
+ this.position = this.Length + offset;
+ break;
+ }
+
+ if (this.readAheadResult != null)
+ {
+ if (!this.readAheadResult.IsCompleted)
+ {
+ this.readAheadResult.AsyncWaitHandle.WaitOne();
+ }
+
+ this.readAheadResult = null;
+ }
+
+ // Check any blocks that are not useful anymore
+ this.downloadedBlocksList.RemoveExtraBlocks(this.position, this.readAheadSize);
+ return this.position;
+ }
+
+ ///
+ /// Copies the specified amount of data from internal buffers to the buffer and advances the position.
+ ///
+ /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values
+ /// between offset and (offset + count - 1) replaced by the bytes read from the current source.
+ /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ /// The total number of bytes read into the buffer. This can be less than the number of bytes requested
+ /// if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
+ ///
+ /// The sum of offset and count is larger than the buffer length.
+ /// The buffer parameter is null.
+ /// The offset or count parameters are negative.
+ /// An I/O error occurs.
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return TaskImplHelper.ExecuteImpl((result) => { return this.ReadImplWrapper(buffer, offset, count, result); });
+ }
+
+ ///
+ /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
+ ///
+ /// The unsigned byte cast to an Int32, or -1 if at the end of the stream.
+ public override int ReadByte()
+ {
+ return base.ReadByte();
+ }
+
+ ///
+ /// Begins an asynchronous read operation.
+ ///
+ /// The buffer to read the data into.
+ /// The byte offset in at which to begin writing data read from the stream.
+ /// The maximum number of bytes to read.
+ /// An optional asynchronous callback, to be called when the read is complete.
+ /// A user-provided object that distinguishes this particular asynchronous read request from other requests.
+ ///
+ /// An that represents the asynchronous read, which could still be pending.
+ ///
+ ///
+ /// Attempted an asynchronous read past the end of the stream, or a disk error occurs.
+ ///
+ ///
+ /// One or more of the arguments is invalid.
+ ///
+ ///
+ /// Methods were called after the stream was closed.
+ ///
+ ///
+ /// The current Stream implementation does not support the read operation.
+ ///
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImpl((result) => this.ReadImplWrapper(buffer, offset, count, result), callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous read operation.
+ ///
+ /// The reference to the pending asynchronous request to finish.
+ ///
+ /// The number of bytes read from the stream, between zero (0) and the number of bytes you requested.
+ /// Streams return zero (0) only at the end of the stream, otherwise, they should block until at least one byte is available.
+ ///
+ ///
+ /// is null.
+ ///
+ ///
+ /// did not originate from a method on the current stream.
+ ///
+ ///
+ /// The stream is closed or an internal error has occurred.
+ ///
+ public override int EndRead(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Wraps the Read operation to remap any exceptions into IOException.
+ ///
+ /// The buffer to read the data into.
+ /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ /// The action to be done upon completion to return the number of bytes read.
+ /// A task sequence representing the operation.
+ /// The sum of offset and count is larger than the buffer length.
+ /// The buffer parameter is null.
+ /// The offset or count parameters are negative
+ /// An I/O error occurs.
+ private TaskSequence ReadImplWrapper(byte[] buffer, int offset, int count, Action setResult)
+ {
+ StreamUtilities.CheckBufferArguments(buffer, offset, count);
+ try
+ {
+ return this.ReadImpl(buffer, offset, count, setResult);
+ }
+ catch (System.Exception ex)
+ {
+ throw new IOException("Error while reading blob", ex);
+ }
+ }
+
+ ///
+ /// Performs the read operation.
+ ///
+ /// The buffer to read the data into.
+ /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ /// The action to be done upon completion to return the number of bytes read.
+ /// A task sequence representing the operation.
+ ///
+ /// If verification is on, retrieve the blocklist.
+ /// If there are downloaded blocks, read from there.
+ /// Otherwise, if there's an outstanding request, wait for completion and read from there.
+ /// Otherwise perform a new request.
+ ///
+ private TaskSequence ReadImpl(byte[] buffer, int offset, int count, Action setResult)
+ {
+ if (this.Blob.Properties.BlobType == BlobType.Unspecified)
+ {
+ this.Blob.FetchAttributes(this.options);
+ }
+
+ var origCount = count;
+
+ // If we don't have a blocklist and need it, get it.
+ if (this.Blob.Properties.BlobType == BlobType.BlockBlob && this.IntegrityControlVerificationEnabled && this.blockList == null)
+ {
+ var blockListTask = TaskImplHelper.GetRetryableAsyncTask>(
+ (result) =>
+ {
+ return this.Blob.ToBlockBlob.GetDownloadBlockList(
+ BlockListingFilter.Committed,
+ this.options,
+ result);
+ },
+ this.options.RetryPolicy);
+
+ yield return blockListTask;
+
+ this.blockList = blockListTask.Result.ToList();
+
+ // Verify we got a blocklist and it's in valid state. This will disable verification if it's invalid state
+ this.VerifyBlocks();
+ }
+
+ bool readComplete = false;
+
+ // Clear any pending read-aheads
+ if (this.readAheadResult != null && this.readAheadResult.IsCompleted)
+ {
+ this.readAheadResult = null;
+ }
+
+ // If we have any data, read existing data
+ if (this.downloadedBlocksList.Any())
+ {
+ readComplete = this.ReadBufferedData(buffer, ref offset, ref count);
+ }
+
+ // If we didn't complete our read and have outstanding readAhead, wait on it
+ if (!readComplete && this.readAheadResult != null && !this.readAheadResult.IsCompleted)
+ {
+ this.readAheadResult.AsyncWaitHandle.WaitOne();
+ this.readAheadResult = null;
+
+ // We now have more data, so we can do some read
+ readComplete = this.ReadBufferedData(buffer, ref offset, ref count);
+ }
+
+ long gapStart, gapEnd;
+ this.downloadedBlocksList.CalculateGapLength(this.position, count + this.readAheadSize, out gapStart, out gapEnd);
+
+ // Figure out if we need to do a server request. There are two reasons:
+ // * We have satisfied the read request, but the remaining data is low enough to warrant a ReadAhead
+ // * We didn't satisfy any of the read request and therefore must do it now
+ if (this.CalculateIfBufferTooLow(gapStart) || !readComplete)
+ {
+ long startReadAhead;
+ long readAheadCount;
+ this.CalculateReadAheadBounds(gapStart, gapEnd, count, out startReadAhead, out readAheadCount);
+
+ var blocksLeft = startReadAhead != -1;
+ var outsideBounds = this.LengthAvailable && (this.Position >= this.Length || startReadAhead >= this.Length);
+
+ // If we didn't find any blocks, that means there's no data left.
+ // If we have length, we can ensure we are within bounds. This will prevent extra round trips
+ if (!blocksLeft || outsideBounds)
+ {
+ setResult(origCount - count);
+ yield break;
+ }
+
+ // If we are doing an optional read-ahead, save the async result for future use
+ if (readComplete)
+ {
+ // We should only have a single outstanding ReadAhed
+ if (this.readAheadResult == null)
+ {
+ this.readAheadResult = TaskImplHelper.BeginImplWithRetry(
+ () =>
+ {
+ return this.ReadAheadImpl(startReadAhead, readAheadCount);
+ },
+ this.options.RetryPolicy,
+ (result) =>
+ {
+ this.EndReadAhead(result);
+ },
+ null);
+ }
+ }
+ else
+ {
+ var task = TaskImplHelper.GetRetryableAsyncTask(() => this.ReadAheadImpl(startReadAhead, readAheadCount), this.options.RetryPolicy);
+
+ yield return task;
+
+ var scratch = task.Result;
+
+ // We now have data, so we read it
+ readComplete = this.ReadBufferedData(buffer, ref offset, ref count);
+ }
+ }
+
+ setResult(origCount - count);
+ }
+
+ ///
+ /// Locks download to a specific ETag.
+ ///
+ private void LockToEtag()
+ {
+ // Add the ETag
+ if (this.setEtagCondition == false && !string.IsNullOrEmpty(this.Blob.Properties.ETag))
+ {
+ this.setEtagCondition = true;
+
+ // If we have any existing conditions, see if they failed.
+ if (!this.options.AccessCondition.Equals(AccessCondition.None))
+ {
+ if (!this.options.AccessCondition.VerifyConditionHolds(
+ this.Blob.Properties.ETag,
+ this.Blob.Properties.LastModifiedUtc))
+ {
+ throw new StorageClientException(
+ StorageErrorCode.ConditionFailed,
+ SR.ConditionNotMatchedError,
+ System.Net.HttpStatusCode.PreconditionFailed,
+ null,
+ null);
+ }
+ }
+
+ this.options.AccessCondition = AccessCondition.IfMatch(this.Blob.Properties.ETag);
+ }
+ }
+
+ ///
+ /// Reads the data from the service starting at the specified location. Verifies the block's signature (if required) and adds it to the buffered data.
+ ///
+ /// The starting position of the read-ahead.
+ /// The number of bytes to read ahead.
+ /// An TaskSequence that represents the asynchronous read action.
+ private TaskSequence ReadAheadImpl(long startPosition, long length)
+ {
+ var webResponseTask = new InvokeTaskSequenceTask((result) => { return this.Blob.GetStreamImpl(options, startPosition, length, result); });
+ yield return webResponseTask;
+
+ using (var stream = webResponseTask.Result)
+ {
+ this.LockToEtag();
+
+ if (this.IntegrityControlVerificationEnabled && this.Blob.Properties.BlobType == BlobType.BlockBlob)
+ {
+ long blockStartPosition = 0;
+ foreach (var block in this.blockList)
+ {
+ var blockSize = block.Size;
+
+ // Find the starting block
+ if (blockStartPosition < startPosition)
+ {
+ blockStartPosition += blockSize;
+ continue;
+ }
+
+ // Start creating blocks
+ var memoryStream = new SmallBlockMemoryStream(Constants.DefaultBufferSize);
+ var md5Check = MD5.Create();
+ int totalCopied = 0, numRead = 0;
+ do
+ {
+ byte[] buffer = new byte[Constants.DefaultBufferSize];
+ var numToRead = (int)Math.Min(buffer.Length, blockSize - totalCopied);
+ var readTask = stream.ReadAsync(buffer, 0, numToRead);
+ yield return readTask;
+
+ numRead = readTask.Result;
+
+ if (numRead != 0)
+ {
+ // Verify the content
+ StreamUtilities.ComputeHash(buffer, 0, numRead, md5Check);
+
+ var writeTask = memoryStream.WriteAsync(buffer, 0, numRead);
+ yield return writeTask;
+ var scratch = writeTask.Result; // Materialize any exceptions
+ totalCopied += numRead;
+ }
+ }
+ while (numRead != 0 && totalCopied < blockSize);
+
+ // If we read something, act on it
+ if (totalCopied != 0)
+ {
+ // Verify the hash
+ string blockNameMD5Value = Utilities.ExtractMD5ValueFromBlockID(block.Name);
+
+ if (blockNameMD5Value != StreamUtilities.GetHashValue(md5Check))
+ {
+ throw new InvalidDataException("Blob data corrupted (integrity check failed)");
+ }
+
+ memoryStream.Position = 0; // Rewind the stream to allow for reading
+
+ this.downloadedBlocksList.Add(new DownloadedBlock(startPosition, memoryStream));
+
+ startPosition += blockSize;
+ blockStartPosition += blockSize;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ var memoryStream = new SmallBlockMemoryStream(Constants.DefaultBufferSize);
+ var copyTask = new InvokeTaskSequenceTask(() => { return stream.WriteTo(memoryStream); });
+ yield return copyTask;
+
+ var scratch = copyTask.Result; // Materialize any errors
+
+ memoryStream.Position = 0; // Rewind the stream to allow for reading
+ this.downloadedBlocksList.Add(new DownloadedBlock(startPosition, memoryStream));
+ }
+ }
+ }
+
+ ///
+ /// Ends an asynchronous read-ahead operation.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// asyncResult is null
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ private void EndReadAhead(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Retrieves the size of the blob.
+ ///
+ /// If verification is on, it will retrieve the blocklist as well
+ private void RetrieveSize()
+ {
+ try
+ {
+ this.Blob.FetchAttributes(this.options);
+ }
+ catch (System.Exception ex)
+ {
+ throw new IOException("Error while retrieving size", ex);
+ }
+ }
+
+ ///
+ /// Verifies if the blocks are in expected format. Disables the integrity control if they are not appropriate for validation.
+ ///
+ private void VerifyBlocks()
+ {
+ this.LockToEtag();
+
+ if (this.blockList == null || this.blockList.Count == 0)
+ {
+ this.IntegrityControlVerificationEnabled = false;
+ return;
+ }
+
+ if (this.blockList[0].Name.Length == Constants.V2MD5blockIdExpectedLength &&
+ this.blockList[0].Name.StartsWith(Constants.V2blockPrefix))
+ {
+ // New V2 style Block IDs
+ foreach (var block in this.blockList)
+ {
+ if (block.Name.Length != Constants.V2MD5blockIdExpectedLength ||
+ !block.Name.StartsWith(Constants.V2blockPrefix))
+ {
+ this.IntegrityControlVerificationEnabled = false;
+ break;
+ }
+ }
+ }
+ else if (this.blockList[0].Name.Length == Constants.V1MD5blockIdExpectedLength && this.blockList[0].Name.StartsWith(Constants.V1BlockPrefix))
+ {
+ // Old 1.3 and lower style Block IDs
+ foreach (var block in this.blockList)
+ {
+ if (block.Name.Length != Constants.V1MD5blockIdExpectedLength || !block.Name.StartsWith(Constants.V1BlockPrefix))
+ {
+ this.IntegrityControlVerificationEnabled = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ this.IntegrityControlVerificationEnabled = false;
+ }
+ }
+
+ ///
+ /// Calculates the ReadAhead bounds (start and count) as required by the existing data.
+ ///
+ /// The desired start position.
+ /// The end of the existing gap.
+ /// The desired number of bytes.
+ /// The start position rounded down to the nearest block start.
+ /// The number of bytes with readAheadSize included and rounded up to the end of the nearest block.
+ /// This calculates the bounds based on the blocklist, not any existing data.
+ private void CalculateReadAheadBounds(long gapStart, long gapEnd, int count, out long startReadAhead, out long readAheadCount)
+ {
+ readAheadCount = startReadAhead = -1;
+ var minReadCount = Math.Min(gapEnd - gapStart, count + this.readAheadSize);
+
+ if (this.IntegrityControlVerificationEnabled && this.Blob.Properties.BlobType == BlobType.BlockBlob)
+ {
+ long blockStart = 0;
+ foreach (var block in this.blockList)
+ {
+ long blockEnd = blockStart + block.Size;
+
+ // If the start hasn't been set, calculate it
+ if (startReadAhead == -1)
+ {
+ if (gapStart >= blockStart
+ && gapStart < blockEnd)
+ {
+ startReadAhead = blockStart;
+ readAheadCount = blockEnd - startReadAhead;
+ }
+ }
+
+ // Start is set, so calculate the end
+ if (startReadAhead != -1)
+ {
+ var desiredEnd = gapStart + minReadCount;
+ if (desiredEnd >= blockStart
+ && desiredEnd < blockEnd)
+ {
+ readAheadCount = blockEnd - startReadAhead;
+ break;
+ }
+ }
+
+ blockStart = blockEnd;
+ }
+ }
+ else
+ {
+ startReadAhead = gapStart;
+ var endReadAheadPosition = startReadAhead + minReadCount;
+ readAheadCount = endReadAheadPosition - startReadAhead;
+ }
+ }
+
+ ///
+ /// Reads from the verified blocks as much data as is available and needed.
+ ///
+ /// The buffer to read the data into.
+ /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ /// True if there was any data read.
+ private bool ReadBufferedData(byte[] buffer, ref int offset, ref int count)
+ {
+ bool readCompleated = false;
+ while (count != 0 && this.downloadedBlocksList.Any())
+ {
+ var currentBlock = this.downloadedBlocksList.GetBlockByPosition(this.position);
+ if (currentBlock == null)
+ {
+ return readCompleated;
+ }
+
+ // Make sure the block's position is correct relative to global position
+ currentBlock.BlockContent.Position = this.position - currentBlock.StartOffset;
+
+ var blockRemainingSize = currentBlock.BlockContent.Length - currentBlock.BlockContent.Position;
+
+ var numRead = currentBlock.BlockContent.Read(buffer, offset, (int)Math.Min(count, blockRemainingSize));
+ readCompleated = true;
+ this.position += numRead;
+ offset += numRead;
+ count -= numRead;
+
+ // Check if the block is exhausted
+ if (currentBlock.BlockContent.Position == currentBlock.BlockContent.Length)
+ {
+ lock (this.downloadedBlocksList)
+ {
+ this.downloadedBlocksList.Remove(currentBlock.StartOffset);
+ }
+ }
+ }
+
+ return readCompleated;
+ }
+
+ ///
+ /// Calculates if the currently held data is less than percentage depleted.
+ ///
+ /// The start position for any ReadAhead based on the last block.
+ /// True if more data is needed.
+ private bool CalculateIfBufferTooLow(long gapStart)
+ {
+ var remainingSize = gapStart - this.position;
+ return remainingSize < (ReadAheadThreshold * this.readAheadSize);
+ }
+
+ ///
+ /// Verifies if the given offset is within the bounds of the stream.
+ ///
+ /// The offset to be checked.
+ /// This may do a server round-trip if the length is not known.
+ private void CheckBounds(long offset)
+ {
+ if (offset < 0)
+ {
+ throw new ArgumentException(SR.SeekTooLowError, "offset");
+ }
+ }
+
+ ///
+ /// Represents a single block of data that was downloaded.
+ ///
+ internal class DownloadedBlock
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The start offset.
+ /// The content.
+ internal DownloadedBlock(long startOffset, Stream content)
+ {
+ this.StartOffset = startOffset;
+ this.BlockContent = content;
+ }
+
+ ///
+ /// Gets the starting location of the block.
+ ///
+ public long StartOffset { get; private set; }
+
+ ///
+ /// Gets the content of the block.
+ ///
+ public Stream BlockContent { get; private set; }
+ }
+
+ ///
+ /// Encapsulates the collection of downloaded blocks and provides appropriate locking mechanism.
+ ///
+ internal class DownloadedBlockCollection
+ {
+ ///
+ /// Holds the downloaded blocks.
+ ///
+ private SortedList downloadedBlocksList = new SortedList();
+
+ ///
+ /// Finds the block that contains the data for the .
+ ///
+ /// The position which is requested.
+ /// A block that contains the data for the or null.
+ internal DownloadedBlock GetBlockByPosition(long desiredPosition)
+ {
+ // Need to lock the list to make sure it's not modified while traversing
+ lock (this.downloadedBlocksList)
+ {
+ foreach (var kvp in this.downloadedBlocksList)
+ {
+ var block = kvp.Value;
+ if (desiredPosition >= block.StartOffset)
+ {
+ // If we are within the block, return it. Otherwise we don't have a valid block. That shouldn't happen.
+ if ((desiredPosition - block.StartOffset) < block.BlockContent.Length)
+ {
+ return block;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Removes any downloaded blocks that are outside the current bounds (position and readAheadSize).
+ ///
+ /// The position before which all blocks should be purged.
+ /// Size of the read-ahead beyond position for blocks to be kept.
+ internal void RemoveExtraBlocks(long position, long bufferedLength)
+ {
+ if (this.downloadedBlocksList.Count != 0)
+ {
+ var farPoint = position + bufferedLength;
+ lock (this.downloadedBlocksList)
+ {
+ for (int blockID = 0; blockID < this.downloadedBlocksList.Count; blockID++)
+ {
+ var block = this.downloadedBlocksList.Values[blockID];
+
+ // Check for a block that's already passed
+ if (position >= block.StartOffset + block.BlockContent.Length)
+ {
+ this.downloadedBlocksList.Remove(block.StartOffset);
+ blockID--;
+ continue;
+ }
+
+ // Remove the downloaded blocks after rewinding.
+ if (position < block.StartOffset)
+ {
+ this.downloadedBlocksList.Remove(block.StartOffset);
+ blockID--;
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Calculates the length of the gap relative to .
+ ///
+ /// The position from which to find the gap.
+ /// Size of the desired.
+ /// The gap start.
+ /// The gap end.
+ internal void CalculateGapLength(long position, long desiredLength, out long gapStart, out long gapEnd)
+ {
+ var readAheadStartPosition = position;
+
+ if (this.Any())
+ {
+ gapStart = gapEnd = -1;
+ DownloadedBlock block = null;
+ lock (this.downloadedBlocksList)
+ {
+ // Walk all blocks and find the next gap starting
+ foreach (var kvp in this.downloadedBlocksList)
+ {
+ // If we aren't beyond a block, we need to read from there.
+ block = kvp.Value;
+ if (gapStart == -1 && readAheadStartPosition < block.StartOffset)
+ {
+ gapStart = readAheadStartPosition;
+ gapEnd = block.StartOffset - 1;
+ break;
+ }
+
+ readAheadStartPosition = block.StartOffset + block.BlockContent.Length;
+ }
+ }
+
+ // If we walked the whole list and didn't find a gap, we are close to the end.
+ if (gapStart == -1)
+ {
+ gapStart = block.StartOffset + block.BlockContent.Length;
+ gapEnd = gapStart + desiredLength;
+ }
+ }
+ else
+ {
+ gapStart = position;
+ gapEnd = position + desiredLength;
+ }
+
+ System.Diagnostics.Debug.Assert(gapStart != -1, "Ensure that the gap bounds are not negative.");
+ System.Diagnostics.Debug.Assert(gapEnd != -1, "Ensure that the gap bounds are not negative.");
+ }
+
+ ///
+ /// Tells if there are any blocks available.
+ ///
+ /// Returns true if there are any blocks; otherwise, false.
+ internal bool Any()
+ {
+ return this.downloadedBlocksList.Count != 0;
+ }
+
+ ///
+ /// Adds the specified downloaded block.
+ ///
+ /// The downloaded block.
+ internal void Add(DownloadedBlock downloadedBlock)
+ {
+ lock (this.downloadedBlocksList)
+ {
+ this.downloadedBlocksList.Add(downloadedBlock.StartOffset, downloadedBlock);
+ }
+ }
+
+ ///
+ /// Removes the specified block based on the startOffset key.
+ ///
+ /// The key for the block.
+ internal void Remove(long key)
+ {
+ lock (this.downloadedBlocksList)
+ {
+ this.downloadedBlocksList.Remove(key);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/microsoft-azure-api/StorageClient/BlobRequestOptions.cs b/microsoft-azure-api/StorageClient/BlobRequestOptions.cs
new file mode 100644
index 0000000000000..e03f322d13b7b
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobRequestOptions.cs
@@ -0,0 +1,164 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobRequestOptions.cs class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+
+ ///
+ /// Represents a set of options that may be specified on a request.
+ ///
+ public class BlobRequestOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BlobRequestOptions()
+ {
+ this.AccessCondition = AccessCondition.None;
+ }
+
+ ///
+ /// Initializes a new instance of the class based on an
+ /// existing instance.
+ ///
+ /// The set of request options to clone.
+ public BlobRequestOptions(BlobRequestOptions other)
+ {
+ this.Timeout = other.Timeout;
+ this.RetryPolicy = other.RetryPolicy;
+ this.AccessCondition = other.AccessCondition;
+
+ this.CopySourceAccessCondition = other.CopySourceAccessCondition;
+ this.DeleteSnapshotsOption = other.DeleteSnapshotsOption;
+ this.BlobListingDetails = other.BlobListingDetails;
+ this.UseFlatBlobListing = other.UseFlatBlobListing;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An object of type .
+ internal BlobRequestOptions(CloudBlobClient service)
+ : this()
+ {
+ this.ApplyDefaults(service);
+ }
+
+ ///
+ /// Gets or sets the retry policy for the request.
+ ///
+ /// The retry policy delegate.
+ public RetryPolicy RetryPolicy { get; set; }
+
+ ///
+ /// Gets or sets the server and client timeout for the request.
+ ///
+ /// The server and client timeout interval for the request.
+ public TimeSpan? Timeout { get; set; }
+
+ ///
+ /// Gets or sets the access condition for the request.
+ ///
+ /// A structure that specifies any conditional parameters on the request.
+ public AccessCondition AccessCondition { get; set; }
+
+ ///
+ /// Gets or sets the access condition on the source blob, when the request is to copy a blob.
+ ///
+ /// A structure that specifies any conditional parameters on the request.
+ ///
+ /// This property is applicable only to a request that will copy a blob.
+ ///
+ public AccessCondition CopySourceAccessCondition { get; set; }
+
+ ///
+ /// Gets or sets options for deleting snapshots when a blob is to be deleted.
+ ///
+ /// One of the enumeration values that specifies whether to delete blobs and snapshots, delete blobs only, or delete snapshots only.
+ ///
+ /// This property is applicable only to a request that will delete a blob.
+ ///
+ public DeleteSnapshotsOption DeleteSnapshotsOption { get; set; }
+
+ ///
+ /// Gets or sets options for listing blobs.
+ ///
+ /// One of the enumeration values that indicates what items a listing operation will return.
+ ///
+ /// This property is applicable only to a request to list blobs.
+ ///
+ public BlobListingDetails BlobListingDetails { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the blob listing operation will list all blobs in a container in a flat listing,
+ /// or whether it will list blobs hierarchically, by virtual directory.
+ ///
+ /// True if blobs will be listed in a flat listing; otherwise, false. The default value is false.
+ ///
+ /// This property is applicable only to a request to list blobs.
+ ///
+ public bool UseFlatBlobListing { get; set; }
+
+ ///
+ /// Creates the full modifier.
+ ///
+ /// The type of the options.
+ /// The service.
+ /// An object that specifies any additional options for the request.
+ /// A full modifier of the requested type.
+ internal static T CreateFullModifier(CloudBlobClient service, T options)
+ where T : BlobRequestOptions, new()
+ {
+ T fullModifier = null;
+
+ if (options != null)
+ {
+ fullModifier = options.Clone() as T;
+ }
+ else
+ {
+ fullModifier = new T();
+ }
+
+ fullModifier.ApplyDefaults(service);
+ return fullModifier;
+ }
+
+ ///
+ /// Clones this instance.
+ ///
+ /// A clone of the instance.
+ internal virtual BlobRequestOptions Clone()
+ {
+ return new BlobRequestOptions(this);
+ }
+
+ ///
+ /// Applies the defaults.
+ ///
+ /// The service.
+ internal virtual void ApplyDefaults(CloudBlobClient service)
+ {
+ this.Timeout = this.Timeout ?? service.Timeout;
+ this.RetryPolicy = this.RetryPolicy ?? service.RetryPolicy;
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobStream.cs b/microsoft-azure-api/StorageClient/BlobStream.cs
new file mode 100644
index 0000000000000..6ae71387bbefd
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobStream.cs
@@ -0,0 +1,126 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobStream class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.IO;
+
+ ///
+ /// Represents a stream for reading and writing to a blob.
+ ///
+ public abstract class BlobStream : Stream
+ {
+ ///
+ /// Gets a reference to the blob on which the stream is opened.
+ ///
+ /// The blob this stream accesses.
+ public CloudBlob Blob { get; internal set; }
+
+ ///
+ /// Gets a value indicating whether the signature of each block should be verified.
+ ///
+ ///
+ /// Returns true if integrity control verification is enabled; otherwise, false.
+ ///
+ public virtual bool IntegrityControlVerificationEnabled
+ {
+ get
+ {
+ return true;
+ }
+
+ internal set
+ {
+ }
+ }
+
+ ///
+ /// Gets or sets the number of bytes to read ahead.
+ ///
+ /// The number of bytes to read ahead.
+ ///
+ /// This operation is not currently supported.
+ ///
+ public virtual long ReadAheadSize
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Gets or sets the block size.
+ ///
+ /// The size of the block.
+ ///
+ /// This operation is not currently supported.
+ ///
+ public virtual long BlockSize
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ ///
+ /// Aborts the operation to write to the blob.
+ ///
+ public virtual void Abort()
+ {
+ }
+
+ ///
+ /// Begins an asynchronous operation to commit a blob.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ ///
+ /// This operation is not currently supported.
+ ///
+ public virtual IAsyncResult BeginCommit(AsyncCallback callback, object state)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Ends an asynchronous operation to commit the blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// asyncResult is null
+ ///
+ /// This operation is not currently supported.
+ ///
+ public virtual void EndCommit(IAsyncResult asyncResult)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Commits the blob.
+ ///
+ ///
+ /// This operation is not currently supported.
+ ///
+ public virtual void Commit()
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobType.cs b/microsoft-azure-api/StorageClient/BlobType.cs
new file mode 100644
index 0000000000000..71f2eab35db05
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobType.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobType enumeration.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ ///
+ /// The type of a blob.
+ ///
+ public enum BlobType
+ {
+ ///
+ /// Not specified.
+ ///
+ Unspecified,
+
+ ///
+ /// A page blob.
+ ///
+ PageBlob,
+
+ ///
+ /// A block blob.
+ ///
+ BlockBlob,
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/BlobWriteStream.cs b/microsoft-azure-api/StorageClient/BlobWriteStream.cs
new file mode 100644
index 0000000000000..6b392e3a14b0e
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlobWriteStream.cs
@@ -0,0 +1,689 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlobWriteStream class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Security.Cryptography;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// The class is an append-only stream for writing into storage.
+ ///
+ internal class BlobWriteStream : BlobStream
+ {
+ ///
+ /// Internal Block ID sequence number. Used in generating Block IDs.
+ ///
+ private long blockIdSequenceNumber = -1;
+
+ ///
+ /// The stream is writable until committed/close.
+ ///
+ private bool canWrite;
+
+ ///
+ /// The current position within the blob.
+ ///
+ private long position;
+
+ ///
+ /// The size of the blocks to use.
+ ///
+ private long blockSize;
+
+ ///
+ /// The list of uploaded blocks.
+ ///
+ private List blockList;
+
+ ///
+ /// A memory stream holding the current block information.
+ ///
+ private SmallBlockMemoryStream blockBuffer;
+
+ ///
+ /// The hash of the current block.
+ ///
+ private HashAlgorithm blockHash;
+
+ ///
+ /// The ongoing blob hash.
+ ///
+ private HashAlgorithm blobHash;
+
+ ///
+ /// The set of options applying to the current blob.
+ ///
+ private BlobRequestOptions currentModifier;
+
+ ///
+ /// Initializes a new instance of the BlobWriteStream class.
+ ///
+ /// The blob used for uploads.
+ /// The options used for the stream.
+ /// The size of the blocks to use.
+ internal BlobWriteStream(CloudBlockBlob blob, BlobRequestOptions options, long blockSize)
+ {
+ CommonUtils.AssertNotNull("blob", blob);
+ CommonUtils.AssertNotNull("options", options);
+
+ this.Blob = blob;
+ this.blobHash = MD5.Create();
+ ((BlobWriteStream)this).BlockSize = blockSize;
+ this.blockList = new List();
+ this.currentModifier = options;
+ this.canWrite = true;
+
+ Random rand = new Random();
+ this.blockIdSequenceNumber = (long)rand.Next() << 32;
+ this.blockIdSequenceNumber += rand.Next();
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports reading.
+ ///
+ /// Returns true if the stream supports reading; otherwise, false.
+ public override bool CanRead
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports seeking.
+ ///
+ /// Returns true if the stream supports seeking; otherwise, false.
+ public override bool CanSeek
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports writing.
+ ///
+ /// Returns true if the stream supports writing; otherwise, false.
+ public override bool CanWrite
+ {
+ get
+ {
+ return this.canWrite;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream can time out.
+ ///
+ /// A value that determines whether the current stream can time out.
+ public override bool CanTimeout
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to write before timing out.
+ ///
+ public override int WriteTimeout
+ {
+ get
+ {
+ return this.currentModifier.Timeout.RoundUpToMilliseconds();
+ }
+
+ set
+ {
+ this.currentModifier.Timeout = TimeSpan.FromMilliseconds(value);
+ }
+ }
+
+ ///
+ /// Gets the current length (equal to the position).
+ ///
+ ///
+ /// A long value representing the length of the stream in bytes.
+ ///
+ ///
+ /// A class derived from Stream does not support seeking.
+ ///
+ ///
+ /// Methods were called after the stream was closed.
+ ///
+ public override long Length
+ {
+ get
+ {
+ return this.Position;
+ }
+ }
+
+ ///
+ /// Gets or sets the current position.
+ ///
+ ///
+ /// The current position within the stream.
+ ///
+ ///
+ /// An I/O error occurs.
+ ///
+ ///
+ /// The stream does not support seeking.
+ ///
+ ///
+ /// Methods were called after the stream was closed.
+ ///
+ public override long Position
+ {
+ get
+ {
+ return this.position;
+ }
+
+ set
+ {
+ this.Seek(value, SeekOrigin.Begin);
+ }
+ }
+
+ ///
+ /// Gets or sets the block size.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Usage",
+ "CA2208:InstantiateArgumentExceptionsCorrectly",
+ Justification = "This is for BlockSize variable itself")]
+ public override long BlockSize
+ {
+ get
+ {
+ return this.blockSize;
+ }
+
+ set
+ {
+ // Prevent changing to no-blocklist once a block is written
+ if (this.blockList != null && this.blockList.Count != 0 && value == 0)
+ {
+ throw new InvalidOperationException(SR.BlocksExistError);
+ }
+
+ if (this.blockBuffer != null && this.blockBuffer.Length >= value)
+ {
+ throw new ArgumentOutOfRangeException("BlockSize", SR.BlobSizeReductonError);
+ }
+
+ if (value > Protocol.Constants.MaxBlockSize)
+ {
+ throw new ArgumentOutOfRangeException("BlockSize", String.Format(CultureInfo.CurrentCulture, SR.BlockTooLargeError, Protocol.Constants.MaxBlockSize));
+ }
+
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("BlockSize", SR.BlocksTooSmallError);
+ }
+
+ this.blockSize = value;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the upload is done in blocks.
+ ///
+ /// Returns true if blocks are to be used; otherwise, false.
+ private bool UseBlocks
+ {
+ get
+ {
+ return this.BlockSize != 0;
+ }
+ }
+
+ ///
+ /// The stream does not support reading.
+ ///
+ /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values
+ /// between offset and (offset + count - 1) replaced by the bytes read from the current source.
+ /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ /// The total number of bytes read into the buffer. This can be less than the number of bytes requested
+ /// if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
+ ///
+ /// Not supported operation as this is a write-only stream
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// The stream does not support seeking.
+ ///
+ /// A byte offset relative to the origin parameter.
+ /// A value of type System.IO.SeekOrigin indicating the reference point used to obtain the new position.
+ /// The new position within the current stream.
+ /// Not supported operation as this is a write-only stream
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// The stream does not support setting of length.
+ ///
+ /// The desired length of the current stream in bytes.
+ /// Growing a stream is not possible without writing.
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Write the provided data into the underlying stream.
+ ///
+ /// An array of bytes. This method copies count bytes from buffer to the current stream.
+ /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream.
+ /// The number of bytes to be written to the current stream.
+ /// is null.
+ /// offset or count is negative.
+ /// Thrown if blob is already committed/closed
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ this.EndWrite(this.BeginWrite(buffer, offset, count, null, null));
+ }
+
+ ///
+ /// Copies a single byte into the stream.
+ ///
+ /// The byte of data to be written.
+ public override void WriteByte(byte value)
+ {
+ base.WriteByte(value);
+ }
+
+ ///
+ /// Begins an asynchronous write operation.
+ ///
+ /// The buffer to write data from.
+ /// The byte offset in buffer from which to begin writing.
+ /// The number of bytes to write.
+ /// An optional asynchronous callback, to be called when the write is complete.
+ /// A user-provided object that distinguishes this particular asynchronous write request from other requests.
+ /// An IAsyncResult that represents the asynchronous write, which could still be pending.
+ /// is null.
+ /// offset or count is negative.
+ /// Thrown if blob is already committed/closed
+ /// The operation will be completed synchronously if the buffer is not filled
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ this.CheckWriteState();
+ StreamUtilities.CheckBufferArguments(buffer, offset, count);
+
+ Task task = null;
+
+ if (this.UseBlocks)
+ {
+ task = new InvokeTaskSequenceTask(() =>
+ {
+ return this.WriteBlockBlobImpl(buffer, offset, count);
+ });
+ }
+ else
+ {
+ task = this.WriteNonBlockedBlobImpl(buffer, offset, count);
+ }
+
+ return task.ToAsyncResult(callback, state);
+ }
+
+ /// B
+ /// Ends an asynchronous write operation.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// asyncResult is null
+ public override void EndWrite(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Causes any buffered data to be written to the remote storage. If the blob is not using blocks, the blob is fully committed.
+ ///
+ /// An I/O error occurs while writing to storage.
+ public override void Flush()
+ {
+ this.CheckWriteState();
+
+ TaskImplHelper.ExecuteImpl(this.FlushInternal);
+ }
+
+ ///
+ /// Aborts the upload of the blob.
+ ///
+ public override void Abort()
+ {
+ this.blockList = null;
+ this.blobHash = null;
+ this.canWrite = false;
+ this.ResetBlock();
+ }
+
+ ///
+ /// Begins an asynchronous operation to commit the blob.
+ ///
+ /// An optional asynchronous callback, to be called when the commit is complete.
+ /// A user-provided object that distinguishes this particular asynchronous commit request from other requests.
+ /// An IAsyncResult that represents the asynchronous commit, which could still be pending.
+ public override IAsyncResult BeginCommit(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImpl(this.CommitImpl, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to commit the blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// asyncResult is null
+ public override void EndCommit(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Commits the blob on the server.
+ ///
+ public override void Commit()
+ {
+ TaskImplHelper.ExecuteImpl(this.CommitImpl);
+ }
+
+ ///
+ /// Implements the disposing logic of committing the blob.
+ ///
+ /// True to release both managed and unmanaged resources; false to release only unmanaged resources.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && this.CanWrite)
+ {
+ this.Commit();
+ this.ResetBlock();
+ this.canWrite = false;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// Generates a blockID using the block's hash and a prefix.
+ ///
+ /// The base64 encoded blockID.
+ private string GetBlockID()
+ {
+ this.blockIdSequenceNumber += 1;
+ return Utilities.GenerateBlockIDWithHash(StreamUtilities.GetHashValue(this.blockHash), this.blockIdSequenceNumber);
+ }
+
+ ///
+ /// Implements the block writing task.
+ ///
+ /// The buffer to write data from.
+ /// The byte offset in buffer from which to begin writing.
+ /// The number of bytes to write.
+ /// The sequence representing the uploading of the blocks.
+ private TaskSequence WriteBlockBlobImpl(byte[] buffer, int offset, int count)
+ {
+ if (this.blockList.Count + (count / this.blockSize) > Constants.MaxBlockNumber)
+ {
+ throw new ArgumentOutOfRangeException("count", String.Format(CultureInfo.CurrentCulture, SR.TooManyBlocksError, Constants.MaxBlockNumber));
+ }
+
+ while (count != 0)
+ {
+ // Create a buffer if we don't have one
+ if (this.blockBuffer == null)
+ {
+ this.CreateNewBlock();
+ }
+
+ // Copy enough data to fill the buffer.
+ int numCopied = (int)Math.Min(count, this.blockSize - this.blockBuffer.Length);
+ this.blockBuffer.Write(buffer, offset, numCopied);
+
+ StreamUtilities.ComputeHash(buffer, offset, numCopied, this.blockHash);
+ StreamUtilities.ComputeHash(buffer, offset, numCopied, this.blobHash);
+
+ // Advance the location
+ this.position += numCopied;
+ offset += numCopied;
+ count -= numCopied;
+
+ // If buffer is full, flush
+ if (this.blockBuffer.Length == this.blockSize)
+ {
+ var newTask = new InvokeTaskSequenceTask(this.FlushInternal);
+ yield return newTask;
+
+ // Make sure task completed successfully by materializing the exception
+ var result = newTask.Result;
+ }
+ }
+ }
+
+ ///
+ /// Creates a task that writes data for a full blob.
+ ///
+ /// The buffer to write data from.
+ /// The byte offset in buffer from which to begin writing.
+ /// The number of bytes to write.
+ /// The (synchronous) sequence that copies the data into a buffer.
+ /// Thrown if the buffer is larger than maximum blob size.
+ /// Since a non-block based blob is always fully buffered before upload, this task is a synchronous copy.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Usage",
+ "CA2208:InstantiateArgumentExceptionsCorrectly",
+ Justification = "Param name is count")]
+ private SynchronousTask WriteNonBlockedBlobImpl(byte[] buffer, int offset, int count)
+ {
+ return new SynchronousTask(() =>
+ {
+ if (this.blockBuffer == null)
+ {
+ this.CreateNewBlock();
+ }
+
+ // Verify we don't go beyond single-upload size
+ if (this.Length + count > Protocol.Constants.MaxSingleUploadBlobSize)
+ {
+ throw new ArgumentOutOfRangeException("count", String.Format(CultureInfo.CurrentCulture, SR.BlobTooLargeError, Protocol.Constants.MaxSingleUploadBlobSize));
+ }
+
+ this.blockBuffer.Write(buffer, offset, count);
+ this.position += count;
+ StreamUtilities.ComputeHash(buffer, offset, count, this.blockHash);
+ StreamUtilities.ComputeHash(buffer, offset, count, this.blobHash); // Update both, in case someone changes their mind mid-stream
+ });
+ }
+
+ ///
+ /// Implements the flushing sequence.
+ ///
+ /// The sequence of events for a flush.
+ private TaskSequence FlushInternal()
+ {
+ // If there's nothing to flush, just return;
+ if (this.blockBuffer == null)
+ {
+ yield break;
+ }
+
+ // Rewind the stream before upload
+ this.blockBuffer.Position = 0;
+
+ InvokeTaskSequenceTask newTask;
+
+ if (this.UseBlocks)
+ {
+ newTask = new InvokeTaskSequenceTask(this.UploadBlock);
+ }
+ else
+ {
+ newTask = new InvokeTaskSequenceTask(this.UploadBlob);
+ }
+
+ yield return newTask;
+ var result = newTask.Result; // Materialize the results to ensure exception propagation
+ }
+
+ ///
+ /// Commits the blob by uploading any remaining data and the blocklist asynchronously.
+ ///
+ /// A sequence of events required for commit.
+ private TaskSequence CommitImpl()
+ {
+ this.CheckWriteState();
+
+ if (this.blockBuffer != null && this.blockBuffer.Length != 0)
+ {
+ var task = new InvokeTaskSequenceTask(this.FlushInternal);
+ yield return task;
+ var result = task.Result; // Materialize the errors
+ }
+
+ // If all blocks are uploaded, commit
+ if (this.UseBlocks)
+ {
+ this.SetBlobMD5();
+
+ var task = TaskImplHelper.GetRetryableAsyncTask(
+ () =>
+ {
+ // At the convenience layer we always upload uncommitted blocks
+ List putBlockList = new List();
+ foreach (var id in this.blockList)
+ {
+ putBlockList.Add(new PutBlockListItem(id, BlockSearchMode.Uncommitted));
+ }
+
+ return this.Blob.ToBlockBlob.UploadBlockList(putBlockList, this.currentModifier);
+ },
+ this.currentModifier.RetryPolicy);
+ yield return task;
+ var result = task.Result;
+ }
+
+ // Now we can set the full size.
+ this.Blob.Properties.Length = this.Length;
+
+ // Clear the internal state
+ this.Abort();
+ }
+
+ ///
+ /// Implements a sequence of events to upload a full blob.
+ ///
+ /// The sequence of events required to upload the blob.
+ private TaskSequence UploadBlob()
+ {
+ this.SetBlobMD5();
+
+ var task = this.Blob.ToBlockBlob.UploadFullBlobWithRetryImpl(this.blockBuffer, this.currentModifier);
+ yield return task;
+ var result = task.Result;
+
+ // Reset all of the state
+ this.Abort();
+ }
+
+ ///
+ /// Sets the MD5 of the blob.
+ ///
+ private void SetBlobMD5()
+ {
+ var hashValue = StreamUtilities.GetHashValue(this.blobHash);
+
+ this.Blob.Properties.ContentMD5 = hashValue;
+ }
+
+ ///
+ /// Implements a sequence to upload a block.
+ ///
+ /// The sequence of events for upload.
+ private TaskSequence UploadBlock()
+ {
+ // Calculate the MD5 of the buffer
+ var blockID = this.GetBlockID();
+
+ // Upload the current data as block
+ string hash = Utilities.ExtractMD5ValueFromBlockID(blockID);
+ var task = this.Blob.ToBlockBlob.UploadBlockWithRetry(this.blockBuffer, blockID, hash, this.currentModifier);
+ yield return task;
+ var result = task.Result;
+
+ // Add the block to the list of blocks.
+ this.blockList.Add(blockID);
+ this.ResetBlock();
+ }
+
+ ///
+ /// Verifies that the blob is in writable state.
+ ///
+ /// Thrown if stream is non-writable.
+ private void CheckWriteState()
+ {
+ if (!this.CanWrite)
+ {
+ throw new ObjectDisposedException("BlobWriteStream", "Blob is in non-writable state");
+ }
+ }
+
+ ///
+ /// Resets the block and the block hash.
+ ///
+ private void ResetBlock()
+ {
+ if (this.blockBuffer != null)
+ {
+ this.blockBuffer.Dispose();
+ }
+
+ this.blockBuffer = null;
+ this.blockHash = null;
+ }
+
+ ///
+ /// Creates the new block and the block hash.
+ ///
+ private void CreateNewBlock()
+ {
+ this.blockBuffer = new SmallBlockMemoryStream(Constants.DefaultBufferSize);
+ this.blockHash = MD5.Create();
+ }
+ }
+}
\ No newline at end of file
diff --git a/microsoft-azure-api/StorageClient/BlockListingFilter.cs b/microsoft-azure-api/StorageClient/BlockListingFilter.cs
new file mode 100644
index 0000000000000..5141e8b27180f
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/BlockListingFilter.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the BlockListingFilter enumeration.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ ///
+ /// Indicates whether to list only committed blocks, only uncommitted blocks, or all blocks.
+ ///
+ public enum BlockListingFilter
+ {
+ ///
+ /// Committed blocks.
+ ///
+ Committed,
+
+ ///
+ /// Uncommitted blocks.
+ ///
+ Uncommitted,
+
+ ///
+ /// Both committed and uncommitted blocks.
+ ///
+ All
+ }
+}
\ No newline at end of file
diff --git a/microsoft-azure-api/StorageClient/CloudBlob.cs b/microsoft-azure-api/StorageClient/CloudBlob.cs
new file mode 100644
index 0000000000000..80fbcd4bf9ab9
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudBlob.cs
@@ -0,0 +1,2072 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudBlob class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Specialized;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using System.Web;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// Represents a Windows Azure blob.
+ ///
+ public class CloudBlob : IListBlobItem
+ {
+ ///
+ /// Stores the blob's attributes.
+ ///
+ private readonly BlobAttributes attributes;
+
+ ///
+ /// Stores the that contains this blob.
+ ///
+ private CloudBlobContainer container;
+
+ ///
+ /// Stores the name of this blob.
+ ///
+ private string name;
+
+ ///
+ /// Stores the blob's parent .
+ ///
+ private CloudBlobDirectory parent;
+
+ ///
+ /// Stores the blob's transformed address.
+ ///
+ private Uri transformedAddress;
+
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob.
+ ///
+ /// The absolute URI to the blob.
+ public CloudBlob(string blobAbsoluteUri)
+ : this(null, blobAbsoluteUri)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob
+ /// and a set of credentials.
+ ///
+ /// The absolute URI to the blob.
+ /// The account credentials.
+ public CloudBlob(string blobAbsoluteUri, StorageCredentials credentials)
+ : this(blobAbsoluteUri, new CloudBlobClient(NavigationHelper.GetServiceClientBaseAddress(blobAbsoluteUri, null), credentials))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob, and the snapshot timestamp,
+ /// if the blob is a snapshot.
+ ///
+ /// The absolute URI to the blob.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// The account credentials.
+ public CloudBlob(string blobAbsoluteUri, DateTime? snapshotTime, StorageCredentials credentials)
+ : this(blobAbsoluteUri, snapshotTime, new CloudBlobClient(NavigationHelper.GetServiceClientBaseAddress(blobAbsoluteUri, null), credentials))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using a relative URI to the blob, and the snapshot timestamp,
+ /// if the blob is a snapshot.
+ ///
+ /// The relative URI to the blob, beginning with the container name.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudBlob(string blobUri, DateTime? snapshotTime, CloudBlobClient serviceClient)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobAbsoluteUri", blobUri);
+ CommonUtils.AssertNotNull("serviceClient", serviceClient);
+
+ this.attributes = new BlobAttributes();
+ this.ServiceClient = serviceClient;
+
+ var completeUri = NavigationHelper.AppendPathToUri(this.ServiceClient.BaseUri, blobUri);
+
+ this.ParseQueryAndVerify(completeUri, this.ServiceClient, this.ServiceClient.UsePathStyleUris);
+
+ if (snapshotTime != null)
+ {
+ if (this.SnapshotTime != null)
+ {
+ throw new ArgumentException(SR.SnapshotTimePassedTwice);
+ }
+ else
+ {
+ this.SnapshotTime = snapshotTime;
+ }
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class using a relative URI to the blob.
+ ///
+ /// The relative URI to the blob, beginning with the container name.
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudBlob(string blobUri, CloudBlobClient serviceClient)
+ : this(blobUri, null, serviceClient)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class based on an existing instance.
+ ///
+ /// An existing reference to a blob.
+ public CloudBlob(CloudBlob cloudBlob)
+ {
+ this.attributes = cloudBlob.Attributes;
+ this.container = cloudBlob.container;
+ this.parent = cloudBlob.parent;
+
+ this.ServiceClient = cloudBlob.ServiceClient;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Absolute Uri.
+ /// True to use path style Uri.
+ ///
+ /// Any authentication information inside the address will be used.
+ /// Otherwise a blob for anonymous access is created.
+ /// Any snapshot information as part of the address will be recorded
+ /// Explicity specify whether to use host style or path style Uri
+ ///
+ internal CloudBlob(string blobAbsoluteUri, bool usePathStyleUris)
+ : this(usePathStyleUris, blobAbsoluteUri)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class creating a new service client.
+ ///
+ /// Complete Uri.
+ /// Storage credentials.
+ /// True to use path style Uris.
+ internal CloudBlob(string blobAbsoluteUri, StorageCredentials credentials, bool usePathStyleUris)
+ : this(blobAbsoluteUri, new CloudBlobClient(NavigationHelper.GetServiceClientBaseAddress(blobAbsoluteUri, usePathStyleUris), credentials))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the given absolute Uri.
+ ///
+ /// True to use path style Uris.
+ /// The absolute blob Uri.
+ internal CloudBlob(bool? usePathStyleUris, string blobAbsoluteUri)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobAbsoluteUriString", blobAbsoluteUri);
+
+ this.attributes = new BlobAttributes();
+
+ var completeUri = new Uri(blobAbsoluteUri);
+
+ this.ParseQueryAndVerify(completeUri, null, usePathStyleUris);
+ }
+
+ ///
+ /// Initializes a new instance of the class with existing attributes.
+ ///
+ /// The attributes (NOTE: Saved by reference, does not make a copy).
+ /// The service client.
+ /// The snapshot time.
+ internal CloudBlob(BlobAttributes attributes, CloudBlobClient serviceClient, string snapshotTime)
+ {
+ this.attributes = attributes;
+ this.ServiceClient = serviceClient;
+
+ this.ParseQueryAndVerify(attributes.Uri, this.ServiceClient, this.ServiceClient.UsePathStyleUris);
+
+ if (!string.IsNullOrEmpty(snapshotTime))
+ {
+ this.SnapshotTime = this.ParseSnapshotTime(snapshotTime);
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified blob Uri.
+ /// Note that this is just a reference to a blob instance and no requests are issued to the service
+ /// yet to update the blob properties, attribute or metaddata. FetchAttributes is the API that
+ /// issues such request to the service.
+ ///
+ /// A relative Uri to the blob.
+ /// A object that specifies the endpoint for the Blob service.
+ /// The reference to the parent container.
+ internal CloudBlob(string blobUri, CloudBlobClient serviceClient, CloudBlobContainer containerReference)
+ : this(blobUri, null, serviceClient)
+ {
+ this.container = containerReference;
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified relative blob Uri.
+ /// If snapshotTime is not null, the blob instance represents a Snapshot.
+ /// Note that this is just a reference to a blob instance and no requests are issued to the service
+ /// yet to update the blob properties, attribute or metaddata. FetchAttributes is the API that
+ /// issues such request to the service.
+ ///
+ /// A relative Uri to the blob.
+ /// Snapshot time in case the blob is a snapshot.
+ /// A object that specifies the endpoint for the Blob service.
+ /// The reference to the parent container.
+ internal CloudBlob(string blobUri, DateTime? snapshotTime, CloudBlobClient serviceClient, CloudBlobContainer containerReference)
+ : this(blobUri, snapshotTime, serviceClient)
+ {
+ this.container = containerReference;
+ }
+
+ ///
+ /// Gets the value that uniquely identifies the snapshot, if this blob is a snapshot.
+ ///
+ /// A value that uniquely identfies the snapshot.
+ ///
+ /// If the blob is not a snapshot, this property returns null.
+ ///
+ public DateTime? SnapshotTime
+ {
+ get { return this.attributes.Snapshot; }
+ internal set { this.attributes.Snapshot = value; }
+ }
+
+ ///
+ /// Gets the object that represents the Blob service.
+ ///
+ /// A client object that specifies the Blob service endpoint.
+ public CloudBlobClient ServiceClient { get; private set; }
+
+ ///
+ /// Gets the URI that identifies the blob.
+ ///
+ /// The address of the blob.
+ public Uri Uri
+ {
+ get
+ {
+ return this.attributes.Uri;
+ }
+ }
+
+ ///
+ /// Gets the object that represents the blob's attributes.
+ ///
+ /// The blob's attributes.
+ public BlobAttributes Attributes
+ {
+ get { return this.attributes; }
+ }
+
+ ///
+ /// Gets the blob's user-defined metadata.
+ ///
+ /// The blob's metadata.
+ public NameValueCollection Metadata
+ {
+ get
+ {
+ return this.attributes.Metadata;
+ }
+
+ internal set
+ {
+ this.attributes.Metadata = value;
+ }
+ }
+
+ ///
+ /// Gets the blob's system properties.
+ ///
+ /// The blob's system properties.
+ public BlobProperties Properties
+ {
+ get
+ {
+ return this.attributes.Properties;
+ }
+
+ internal set
+ {
+ this.attributes.Properties = value;
+ }
+ }
+
+ ///
+ /// Gets the blob's name.
+ ///
+ /// The blob's name.
+ public string Name
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(this.name))
+ {
+ this.name = Uri.UnescapeDataString(NavigationHelper.GetBlobName(this.Uri, this.ServiceClient.UsePathStyleUris));
+ }
+
+ return this.name;
+ }
+ }
+
+ ///
+ /// Gets a object representing the blob's container.
+ ///
+ /// The blob's container.
+ public CloudBlobContainer Container
+ {
+ get
+ {
+ if (this.container == null)
+ {
+ this.container = new CloudBlobContainer(
+ NavigationHelper.GetContainerAddress(this.Uri, this.ServiceClient.UsePathStyleUris),
+ this.ServiceClient.Credentials);
+ }
+
+ return this.container;
+ }
+ }
+
+ ///
+ /// Gets the object representing the
+ /// virtual parent directory for the blob.
+ ///
+ /// The blob's virtual parent directory.
+ public CloudBlobDirectory Parent
+ {
+ get
+ {
+ if (this.parent == null)
+ {
+ this.parent = new CloudBlobDirectory(
+ NavigationHelper.GetParentAddress(
+ this.Uri,
+ this.ServiceClient.DefaultDelimiter,
+ this.ServiceClient.UsePathStyleUris),
+ this.ServiceClient);
+ }
+
+ return this.parent;
+ }
+ }
+
+ ///
+ /// Gets a object based on this blob.
+ ///
+ /// A reference to a page blob.
+ public CloudPageBlob ToPageBlob
+ {
+ get
+ {
+ var pageBlob = this as CloudPageBlob;
+
+ if (pageBlob != null)
+ {
+ return pageBlob;
+ }
+
+ return new CloudPageBlob(this);
+ }
+ }
+
+ ///
+ /// Gets a object based on this blob.
+ ///
+ /// A reference to a block blob.
+ public CloudBlockBlob ToBlockBlob
+ {
+ get
+ {
+ var blockBlob = this as CloudBlockBlob;
+
+ if (blockBlob != null)
+ {
+ return blockBlob;
+ }
+
+ return new CloudBlockBlob(this);
+ }
+ }
+
+ ///
+ /// Gets the Uri after applying authentication transformation.
+ ///
+ /// The transformed address.
+ internal Uri TransformedAddress
+ {
+ get
+ {
+ if (this.ServiceClient.Credentials.NeedsTransformUri)
+ {
+ // This is required to support key rotation
+ // Potential Improvement: Could get the latest credentials and compare against a cached
+ this.transformedAddress = new Uri(this.ServiceClient.Credentials.TransformUri(this.Uri.AbsoluteUri));
+
+ return this.transformedAddress;
+ }
+ else
+ {
+ return this.Uri;
+ }
+ }
+ }
+
+ ///
+ /// Populates a blob's properties and metadata.
+ ///
+ public void FetchAttributes()
+ {
+ this.FetchAttributes(null);
+ }
+
+ ///
+ /// Populates a blob's properties and metadata.
+ ///
+ /// An object that specifies any additional options for the request.
+ public void FetchAttributes(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.FetchAttributesImpl(fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to populate the blob's properties and metadata.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginFetchAttributes(AsyncCallback callback, object state)
+ {
+ return this.BeginFetchAttributes(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to populate the blob's properties and metadata.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginFetchAttributes(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.FetchAttributesImpl(fullModifiers), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to populate the blob's properties and metadata.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public void EndFetchAttributes(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Updates the blob's metadata.
+ ///
+ public void SetMetadata()
+ {
+ this.SetMetadata(null);
+ }
+
+ ///
+ /// Updates the blob's metadata.
+ ///
+ /// An object that specifies any additional options for the request.
+ public void SetMetadata(BlobRequestOptions options)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.SetMetadataImpl(fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to update the blob's metadata.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetMetadata(AsyncCallback callback, object state)
+ {
+ return this.BeginSetMetadata(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to update the blob's metadata.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetMetadata(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.SetMetadataImpl(fullModifiers), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to update the blob's metadata.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public void EndSetMetadata(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Updates the blob's properties.
+ ///
+ public void SetProperties()
+ {
+ this.SetProperties(null);
+ }
+
+ ///
+ /// Updates the blob's properties.
+ ///
+ /// An object that specifies any additional options for the request.
+ public void SetProperties(BlobRequestOptions options)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.SetPropertiesImpl(fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to update the blob's properties.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetProperties(AsyncCallback callback, object state)
+ {
+ return this.BeginSetProperties(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to update the blob's properties.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetProperties(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.SetPropertiesImpl(fullModifiers), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to update the blob's properties.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public void EndSetProperties(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Copies an existing blob's contents, properties, and metadata to a new blob.
+ ///
+ /// The source blob.
+ public void CopyFromBlob(CloudBlob source)
+ {
+ this.CopyFromBlob(source, null);
+ }
+
+ ///
+ /// Copies a blob's contents, properties, and metadata to a new blob.
+ ///
+ /// The source blob.
+ /// An object that specifies any additional options for the request.
+ public void CopyFromBlob(CloudBlob source, BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.CopyFromBlobImpl(source, fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to copy a blob's contents, properties, and metadata to a new blob.
+ ///
+ /// The source blob.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCopyFromBlob(CloudBlob source, AsyncCallback callback, object state)
+ {
+ return this.BeginCopyFromBlob(source, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to copy another blob's contents, properties, and metadata to the blob referenced by this object.
+ ///
+ /// The source blob.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCopyFromBlob(CloudBlob source, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(
+ () => this.CopyFromBlobImpl(source, fullModifiers),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to copy a blob's contents, properties, and metadata to a new blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public void EndCopyFromBlob(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Deletes the blob.
+ ///
+ public void Delete()
+ {
+ this.Delete(null);
+ }
+
+ ///
+ /// Deletes the blob.
+ ///
+ /// An object that specifies any additional options for the request.
+ public void Delete(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.DeleteBlobImpl(fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete the blob.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDelete(AsyncCallback callback, object state)
+ {
+ return this.BeginDelete(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete the blob.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDelete(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.DeleteBlobImpl(fullModifiers), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to delete the blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public void EndDelete(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Deletes the blob if it exists.
+ ///
+ /// true if the blob was deleted; otherwise, false.
+ public bool DeleteIfExists()
+ {
+ return this.DeleteIfExists(null);
+ }
+
+ ///
+ /// Deletes the blob if it exists.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// true if the blob was deleted; otherwise, false.
+ public bool DeleteIfExists(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry((setResult) => this.DeleteBlobIfExistsImpl(fullModifiers, setResult), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete the blob if it exists.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDeleteIfExists(AsyncCallback callback, object state)
+ {
+ return this.BeginDeleteIfExists(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete the blob if it exists.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDeleteIfExists(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry((setResult) => this.DeleteBlobIfExistsImpl(fullModifiers, setResult), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to delete the blob if it exists.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// true if the blob was successfully deleted; otherwise, false.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public bool EndDeleteIfExists(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Opens a stream for writing to the blob.
+ ///
+ /// A stream to be used for writing to the blob.
+ public virtual BlobStream OpenWrite()
+ {
+ return this.OpenWrite(null);
+ }
+
+ ///
+ /// Opens a stream for writing to the blob.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A stream to be used for writing to the blob.
+ public virtual BlobStream OpenWrite(BlobRequestOptions options)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return new BlobWriteStream(this.ToBlockBlob, fullModifier, this.ServiceClient.WriteBlockSizeInBytes);
+ }
+
+ ///
+ /// Uploads a stream to a block blob.
+ ///
+ /// The stream providing the blob content.
+ public virtual void UploadFromStream(Stream source)
+ {
+ this.UploadFromStream(source, null);
+ }
+
+ ///
+ /// Uploads a stream to a block blob.
+ ///
+ /// The stream providing the blob content.
+ /// An object that specifies any additional options for the request.
+ public virtual void UploadFromStream(Stream source, BlobRequestOptions options)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImpl(() => { return UploadFromStreamDispatch(source, fullModifier); });
+ }
+
+ ///
+ /// Begins an asynchronous operation to upload a stream to a block blob.
+ ///
+ /// The stream providing the blob content.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public virtual IAsyncResult BeginUploadFromStream(Stream source, AsyncCallback callback, object state)
+ {
+ return this.BeginUploadFromStream(source, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to upload a stream to a block blob.
+ ///
+ /// The stream providing the blob content.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public virtual IAsyncResult BeginUploadFromStream(Stream source, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImpl(() => { return this.UploadFromStreamDispatch(source, fullModifier); }, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to upload a stream to a block blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ public virtual void EndUploadFromStream(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Uploads a string of text to a block blob.
+ ///
+ /// The text to upload, encoded as a UTF-8 string.
+ public virtual void UploadText(string content)
+ {
+ this.UploadText(content, Encoding.UTF8, null);
+ }
+
+ ///
+ /// Uploads a string of text to a block blob.
+ ///
+ /// The text to upload.
+ /// An object that indicates the text encoding to use.
+ /// An object that specifies any additional options for the request.
+ public virtual void UploadText(string content, Encoding encoding, BlobRequestOptions options)
+ {
+ this.UploadByteArray(encoding.GetBytes(content), options);
+ }
+
+ ///
+ /// Uploads a file from the file system to a block blob.
+ ///
+ /// The path and file name of the file to upload.
+ public virtual void UploadFile(string fileName)
+ {
+ this.UploadFile(fileName, null);
+ }
+
+ ///
+ /// Uploads a file from the file system to a block blob.
+ ///
+ /// The path and file name of the file to upload.
+ /// An object that specifies any additional options for the request.
+ public virtual void UploadFile(string fileName, BlobRequestOptions options)
+ {
+ using (var data = File.OpenRead(fileName))
+ {
+ this.UploadFromStream(data, options);
+ }
+ }
+
+ ///
+ /// Uploads an array of bytes to a block blob.
+ ///
+ /// The array of bytes to upload.
+ public virtual void UploadByteArray(byte[] content)
+ {
+ this.UploadByteArray(content, null);
+ }
+
+ ///
+ /// Uploads an array of bytes to a blob.
+ ///
+ /// The array of bytes to upload.
+ /// An object that specifies any additional options for the request.
+ public virtual void UploadByteArray(byte[] content, BlobRequestOptions options)
+ {
+ using (var data = new MemoryStream(content))
+ {
+ this.UploadFromStream(data, options);
+ }
+ }
+
+ ///
+ /// Opens a stream for reading the blob's contents.
+ ///
+ /// A stream to use for reading from the blob.
+ public BlobStream OpenRead()
+ {
+ return this.OpenRead(null);
+ }
+
+ ///
+ /// Opens a stream for reading the blob's contents.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A stream to use for reading from the blob.
+ public BlobStream OpenRead(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+ return new BlobReadStream(this, fullModifiers, this.ServiceClient.ReadAheadInBytes, this.ServiceClient.UseIntegrityControlForStreamReading);
+ }
+
+ ///
+ /// Downloads the contents of a blob to a stream.
+ ///
+ /// The target stream.
+ public void DownloadToStream(Stream target)
+ {
+ this.DownloadToStream(target, null);
+ }
+
+ ///
+ /// Downloads the contents of a blob to a stream.
+ ///
+ /// The target stream.
+ /// An object that specifies any additional options for the request.
+ public void DownloadToStream(Stream target, BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+ TaskImplHelper.ExecuteSyncTaskWithRetry(this.DownloadToStreamSyncImpl(target, fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to download the contents of a blob to a stream.
+ ///
+ /// The target stream.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDownloadToStream(Stream target, AsyncCallback callback, object state)
+ {
+ return this.BeginDownloadToStream(target, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to download the contents of a blob to a stream.
+ ///
+ /// The target stream.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDownloadToStream(Stream target, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+ return TaskImplHelper.BeginImplWithRetry(() => this.DownloadToStreamImpl(target, fullModifiers), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to download the contents of a blob to a stream.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public void EndDownloadToStream(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Downloads the blob's contents.
+ ///
+ /// The contents of the blob, as a string.
+ public string DownloadText()
+ {
+ return this.DownloadText(null);
+ }
+
+ ///
+ /// Downloads the blob's contents.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The contents of the blob, as a string.
+ public string DownloadText(BlobRequestOptions options)
+ {
+ Encoding encoding = GetDefaultEncoding();
+
+ byte[] array = this.DownloadByteArray(options);
+
+ return encoding.GetString(array);
+ }
+
+ ///
+ /// Downloads the blob's contents to a file.
+ ///
+ /// The path and file name of the target file.
+ public void DownloadToFile(string fileName)
+ {
+ this.DownloadToFile(fileName, null);
+ }
+
+ ///
+ /// Downloads the blob's contents to a file.
+ ///
+ /// The path and file name of the target file.
+ /// An object that specifies any additional options for the request.
+ public void DownloadToFile(string fileName, BlobRequestOptions options)
+ {
+ using (var fileStream = File.Create(fileName))
+ {
+ this.DownloadToStream(fileStream, options);
+ }
+ }
+
+ ///
+ /// Downloads the blob's contents as an array of bytes.
+ ///
+ /// The contents of the blob, as an array of bytes.
+ public byte[] DownloadByteArray()
+ {
+ return this.DownloadByteArray(null);
+ }
+
+ ///
+ /// Downloads the blob's contents as an array of bytes.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The contents of the blob, as an array of bytes.
+ public byte[] DownloadByteArray(BlobRequestOptions options)
+ {
+ using (var memoryStream = new MemoryStream())
+ {
+ this.DownloadToStream(memoryStream, options);
+
+ return memoryStream.ToArray();
+ }
+ }
+
+ ///
+ /// Creates a snapshot of the blob.
+ ///
+ /// A blob snapshot.
+ public CloudBlob CreateSnapshot()
+ {
+ return this.CreateSnapshot(null, null);
+ }
+
+ ///
+ /// Creates a snapshot of the blob.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A blob snapshot.
+ public CloudBlob CreateSnapshot(BlobRequestOptions options)
+ {
+ return this.CreateSnapshot(null, options);
+ }
+
+ ///
+ /// Creates a snapshot of the blob.
+ ///
+ /// A collection of name-value pairs defining the metadata of the snapshot.
+ /// An object that specifies any additional options for the request, or null.
+ /// A blob snapshot.
+ public CloudBlob CreateSnapshot(NameValueCollection metadata, BlobRequestOptions options)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ BlobRequestOptions fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry(
+ (setResult) => this.CreateSnapshotImpl(metadata, fullModifiers, setResult),
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a snapshot of the blob.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreateSnapshot(AsyncCallback callback, object state)
+ {
+ return this.BeginCreateSnapshot(null, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a snapshot of the blob.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreateSnapshot(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ return this.BeginCreateSnapshot(null, options, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a snapshot of the blob.
+ ///
+ /// A collection of name-value pairs defining the metadata of the snapshot.
+ /// An object that specifies any additional options for the request, or null.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreateSnapshot(NameValueCollection metadata, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ this.VerifyNoWriteOperationForSnapshot();
+
+ BlobRequestOptions fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(
+ (setResult) => this.CreateSnapshotImpl(metadata, fullModifiers, setResult),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to create a snapshot of the blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A blob snapshot.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public CloudBlob EndCreateSnapshot(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Returns a shared access signature for the blob.
+ ///
+ /// The access policy for the shared access signature.
+ /// A shared access signature.
+ /// Thrown if the current credentials don't support creating a shared access signature.
+ /// Thrown if blob is a snapshot.
+ public string GetSharedAccessSignature(SharedAccessPolicy policy)
+ {
+ return this.GetSharedAccessSignature(policy, null);
+ }
+
+ ///
+ /// Returns a shared access signature for the blob.
+ ///
+ /// The access policy for the shared access signature.
+ /// A container-level access policy.
+ /// A shared access signature.
+ /// Thrown if the current credentials don't support creating a shared access signature.
+ /// Thrown if blob is a snapshot.
+ public string GetSharedAccessSignature(SharedAccessPolicy policy, string groupPolicyIdentifier)
+ {
+ if (!this.ServiceClient.Credentials.CanSignRequest)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.CannotCreateSASWithoutAccountKey);
+ throw new InvalidOperationException(errorMessage);
+ }
+
+ if (this.SnapshotTime != null)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.CannotCreateSASForSnapshot);
+ throw new NotSupportedException(errorMessage);
+ }
+
+ string resourceName = this.GetCanonicalName(true);
+
+ string signature = SharedAccessSignatureHelper.GetSharedAccessSignatureHashImpl(policy, groupPolicyIdentifier, resourceName, this.ServiceClient);
+
+ // Future resource type changes from "b" => "blob"
+ var builder = SharedAccessSignatureHelper.GetShareAccessSignatureImpl(policy, groupPolicyIdentifier, "b", signature);
+
+ return builder.ToString();
+ }
+
+ ///
+ /// Dispatches the stream upload to a specific implementation method.
+ ///
+ /// The source stream.
+ /// An object that specifies any additional options for the request.
+ /// A that uploads the stream.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Design",
+ "CA1031:DoNotCatchGeneralExceptionTypes",
+ Justification = "Stream provides no way to determine if Length is accessible so we must catch all exceptions.")]
+ internal TaskSequence UploadFromStreamDispatch(Stream source, BlobRequestOptions options)
+ {
+ options = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+ var blockSize = this.ServiceClient.WriteBlockSizeInBytes;
+ long streamLength = -1;
+
+ // If the stream is unseekable, buffer it for retries
+ if (!source.CanSeek)
+ {
+ if (this.ServiceClient.ParallelOperationThreadCount == 1)
+ {
+ return this.UploadBlobWithBuffering(source, options, blockSize);
+ }
+ else
+ {
+ return this.ParallelUploadBlobWithBlocks(source, blockSize, options);
+ }
+ }
+
+ // Check if the source stream has length. If length is unavailable, buffer it up for sending.
+ // Otherwise set streamLength as delta between lenght and position
+ try
+ {
+ streamLength = source.Length - source.Position;
+ }
+ catch (System.Exception)
+ {
+ if (this.ServiceClient.ParallelOperationThreadCount == 1)
+ {
+ return this.UploadUnknownSizeStream(source, options, blockSize);
+ }
+ else
+ {
+ return this.ParallelUploadBlobWithBlocks(source, blockSize, options);
+ }
+ }
+
+ // If the blob is below the upload threshold, upload it as single version
+ if (streamLength < this.ServiceClient.SingleBlobUploadThresholdInBytes)
+ {
+ return this.UploadFullBlobWithRetrySequenceImpl(source, options);
+ }
+
+ if (this.ServiceClient.ParallelOperationThreadCount == 1)
+ {
+ return this.UploadBlobWithBlocks(source, options, blockSize);
+ }
+ else
+ {
+ return this.ParallelUploadBlobWithBlocks(source, blockSize, options);
+ }
+ }
+
+ ///
+ /// Uploads the blob with buffering.
+ ///
+ /// The source stream.
+ /// An object that specifies any additional options for the request.
+ /// The size of the block.
+ /// A that uploads the blob.
+ internal TaskSequence UploadBlobWithBuffering(Stream source, BlobRequestOptions options, long blockSize)
+ {
+ CommonUtils.AssertNotNull("modifers", options);
+
+ var target = this.OpenWrite(options);
+
+ target.BlockSize = blockSize;
+ return source.WriteToAndCloseOutput(target);
+ }
+
+ ///
+ /// Uploads the full blob with retry.
+ ///
+ /// The source stream.
+ /// An object that specifies any additional options for the request.
+ /// A that uploads the blob.
+ internal Task UploadFullBlobWithRetryImpl(Stream source, BlobRequestOptions options)
+ {
+ var position = source.Position;
+
+ // The stream should be always seekable, since they are under our control
+ var retryPolicy = source.CanSeek ? options.RetryPolicy : RetryPolicies.NoRetry();
+
+ return TaskImplHelper.GetRetryableAsyncTask(
+ () =>
+ {
+ // This shouldn't ever be false happen, since source streams are under our control
+ if (source.CanSeek)
+ {
+ source.Position = position;
+ }
+
+ return UploadFullBlobImpl(source, options);
+ },
+ retryPolicy);
+ }
+
+ ///
+ /// Uploads the full blob.
+ ///
+ /// The source stream.
+ /// An object that specifies any additional options for the request.
+ /// A that uploads the blob.
+ internal TaskSequence UploadFullBlobImpl(Stream source, BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("modifers", options);
+
+ this.Properties.BlobType = BlobType.BlockBlob;
+ var request = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.Put(this.TransformedAddress, timeout, this.Properties, this.Properties.BlobType, null, 0));
+ BlobRequest.AddMetadata(request, this.Metadata);
+ var length = source.Length - source.Position;
+ CommonUtils.ApplyRequestOptimizations(request, length);
+ this.ServiceClient.Credentials.SignRequest(request);
+
+ var uploadTask = new InvokeTaskSequenceTask((result) =>
+ {
+ return UploadData(request, source, result);
+ });
+ yield return uploadTask;
+
+ using (var response = uploadTask.Result as HttpWebResponse)
+ {
+ // Retrieve the ETag/LastModified
+ this.ParseSizeAndLastModified(response);
+
+ this.Properties.Length = length;
+ }
+ }
+
+ ///
+ /// Uploads the blob in parallel with blocks.
+ ///
+ /// The source stream.
+ /// The size of the block.
+ /// An object that specifies any additional options for the request.
+ /// A that uploads the blob.
+ internal TaskSequence ParallelUploadBlobWithBlocks(Stream source, long blockSize, BlobRequestOptions options)
+ {
+ ParallelUpload uploader = new ParallelUpload(source, options, blockSize, this.ToBlockBlob);
+
+ return uploader.ParallelExecute(this.UploadBlockWithRetry);
+ }
+
+ ///
+ /// Uploads the block with retry.
+ ///
+ /// The source stream.
+ /// The block IS.
+ /// The content MD5.
+ /// An object that specifies any additional options for the request.
+ /// A that uploads the block.
+ internal Task UploadBlockWithRetry(Stream source, string blockId, string contentMD5, BlobRequestOptions options)
+ {
+ // Used by our internal code. We always try to set the blockId as MD5
+ var position = source.Position;
+ var retryPolicy = source.CanSeek ? options.RetryPolicy : RetryPolicies.NoRetry();
+
+ return TaskImplHelper.GetRetryableAsyncTask(
+ () =>
+ {
+ if (source.CanSeek)
+ {
+ source.Position = position;
+ }
+
+ return UploadBlock(source, blockId, contentMD5, options);
+ },
+ retryPolicy);
+ }
+
+ ///
+ /// Uploads the block.
+ ///
+ /// The source stream.
+ /// The block ID.
+ /// The content MD5.
+ /// An object that specifies any additional options for the request.
+ /// A that uploads the block.
+ internal TaskSequence UploadBlock(Stream source, string blockId, string contentMD5, BlobRequestOptions options)
+ {
+ int id = Environment.TickCount;
+ TraceHelper.WriteLine("Starting upload for id {0}", id);
+ CommonUtils.AssertNotNull("options", options);
+
+ var request = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => BlobRequest.PutBlock(this.TransformedAddress, timeout, blockId, null));
+ options.AccessCondition.ApplyCondition(request);
+ var length = source.Length - source.Position;
+ CommonUtils.ApplyRequestOptimizations(request, length);
+
+ if (!string.IsNullOrEmpty(contentMD5))
+ {
+ request.Headers.Set(HttpRequestHeader.ContentMd5, contentMD5);
+ }
+
+ this.ServiceClient.Credentials.SignRequest(request);
+
+ var uploadTask = new InvokeTaskSequenceTask((result) =>
+ {
+ return UploadData(request, source, result);
+ });
+
+ yield return uploadTask;
+
+ uploadTask.Result.Close();
+
+ TraceHelper.WriteLine("Ending upload for id {0}", id);
+ }
+
+ ///
+ /// Uploads the data into the web request.
+ ///
+ /// The request that is setup for a put.
+ /// The source of data.
+ /// The response from the server.
+ /// The sequence used for uploading data.
+ internal TaskSequence UploadData(HttpWebRequest request, Stream source, Action result)
+ {
+ // Retrieve the stream
+ var requestStreamTask = request.GetRequestStreamAsync();
+ yield return requestStreamTask;
+
+ // Copy the data
+ using (var outputStream = requestStreamTask.Result)
+ {
+ var copyTask = new InvokeTaskSequenceTask(() => { return source.WriteTo(outputStream); });
+ yield return copyTask;
+
+ // Materialize any exceptions
+ var scratch = copyTask.Result;
+ }
+
+ // Get the response
+ var responseTask = request.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return responseTask;
+
+ // Return the response object
+ var response = responseTask.Result;
+
+ result(response);
+ }
+
+ ///
+ /// Implements the FetchAttributes method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that fetches the attributes.
+ internal TaskSequence FetchAttributesImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.GetProperties(this.TransformedAddress, timeout, this.SnapshotTime, null));
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ BlobAttributes attributes = BlobResponse.GetAttributes(webResponse);
+
+ // If BlobType is not unspecified and the value returned from cloud is different,
+ // then it's a client error and we need to throw.
+ if (this.attributes.Properties.BlobType != BlobType.Unspecified && this.attributes.Properties.BlobType != attributes.Properties.BlobType)
+ {
+ throw new InvalidOperationException(SR.BlobTypeMismatchExceptionMessage);
+ }
+
+ this.attributes.Properties = attributes.Properties;
+ this.attributes.Metadata = attributes.Metadata;
+ }
+ }
+
+ ///
+ /// Implements the DownloadToStream method.
+ ///
+ /// The target stream.
+ /// An object that specifies any additional options for the request.
+ /// A that downloads the blob to the stream.
+ internal TaskSequence DownloadToStreamImpl(Stream target, BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webResponseTask = new InvokeTaskSequenceTask((result) => { return GetStreamImpl(options, result); });
+ yield return webResponseTask;
+
+ using (var responseStream = webResponseTask.Result)
+ {
+ var copyTask = new InvokeTaskSequenceTask(() => { return responseStream.WriteTo(target); });
+ yield return copyTask;
+
+ // Materialize any exceptions
+ var scratch = copyTask.Result;
+ }
+ }
+
+ ///
+ /// Implements the DownloadToStream method.
+ ///
+ /// The target stream.
+ /// An object that specifies any additional options for the request.
+ /// A that downloads the blob to the stream.
+ internal SynchronousTask DownloadToStreamSyncImpl(Stream target, BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ return new SynchronousTask(() =>
+ {
+ using (var responseStream = GetStreamSyncImpl(options))
+ {
+ responseStream.WriteToSync(target);
+ }
+ });
+ }
+
+ ///
+ /// Implements getting the stream.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The offset.
+ /// The count.
+ /// The set result.
+ /// A that gets the stream.
+ internal TaskSequence GetStreamImpl(BlobRequestOptions options, long offset, long count, Action setResult)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.Get(this.TransformedAddress, timeout, this.SnapshotTime, offset, count, null));
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ // Retrieve the stream
+ var responseTask = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return responseTask;
+
+ // Retrieve the response
+ var response = responseTask.Result as HttpWebResponse;
+
+ BlobAttributes attributes = BlobResponse.GetAttributes(response);
+ this.attributes.Metadata = attributes.Metadata;
+ this.attributes.Properties = attributes.Properties;
+
+ setResult(response.GetResponseStream());
+ }
+
+ ///
+ /// Implements getting the stream without specifying a range.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that gets the stream.
+ internal Stream GetStreamSyncImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.Get(this.TransformedAddress, timeout, this.SnapshotTime, null));
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ // Retrieve the response
+ webRequest.Timeout = (int)options.Timeout.Value.TotalMilliseconds;
+
+ var response = this.ServiceClient.GetResponse(webRequest) as HttpWebResponse;
+
+ BlobAttributes attributes = BlobResponse.GetAttributes(response);
+ this.attributes.Metadata = attributes.Metadata;
+ this.attributes.Properties = attributes.Properties;
+
+ return response.GetResponseStream();
+ }
+
+ ///
+ /// Implements getting the stream without specifying a range.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The set result.
+ /// A that gets the stream.
+ internal TaskSequence GetStreamImpl(BlobRequestOptions options, Action setResult)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.Get(this.TransformedAddress, timeout, this.SnapshotTime, null));
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ // Retrieve the stream
+ var responseTask = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return responseTask;
+
+ // Retrieve the response
+ var response = responseTask.Result as HttpWebResponse;
+
+ BlobAttributes attributes = BlobResponse.GetAttributes(response);
+ this.attributes.Metadata = attributes.Metadata;
+ this.attributes.Properties = attributes.Properties;
+
+ setResult(response.GetResponseStream());
+ }
+
+ ///
+ /// Retreive ETag and LastModified date time from response.
+ ///
+ /// The response to parse.
+ protected void ParseSizeAndLastModified(HttpWebResponse response)
+ {
+ var newProperties = BlobResponse.GetAttributes(response);
+ this.Properties.ETag = newProperties.Properties.ETag ?? this.Properties.ETag;
+ this.Properties.LastModifiedUtc = newProperties.Properties.LastModifiedUtc;
+ if (newProperties.Properties.Length != 0)
+ {
+ this.Properties.Length = newProperties.Properties.Length;
+ }
+ }
+
+ ///
+ /// Parses the blob address query and returns snapshot and SAS.
+ ///
+ /// The query to parse.
+ /// The snapshot value, if any.
+ /// The SAS credentials.
+ private static void ParseBlobAddressQuery(string query, out string snapshot, out StorageCredentialsSharedAccessSignature sasCreds)
+ {
+ snapshot = null;
+ sasCreds = null;
+
+ var queryParameters = HttpUtility.ParseQueryString(query);
+
+ if (queryParameters.AllKeys.Contains(Constants.QueryConstants.Snapshot))
+ {
+ snapshot = queryParameters[Constants.QueryConstants.Snapshot];
+ }
+
+ SharedAccessSignatureHelper.ParseQuery(queryParameters, out sasCreds);
+ }
+
+ ///
+ /// Gets the default encoding for the blob, which is UTF-8.
+ ///
+ /// The default object.
+ private static Encoding GetDefaultEncoding()
+ {
+ Encoding encoding = Encoding.UTF8;
+
+ return encoding;
+ }
+
+ ///
+ /// Gets the canonical name of the blob, formatted as /<account-name>/<container-name>/<blob-name>.
+ /// If ignoreSnapshotTime is false and this blob is a snapshot, the canonical name is augmented with a
+ /// query of the form ?snapshot=<snapshot-time>.
+ /// This is used by both Shared Access and Copy blob operations.
+ /// Used by both Shared Access and Copy blob operation.
+ ///
+ /// Indicates if the snapshot time is ignored.
+ /// The canonical name of the blob.
+ private string GetCanonicalName(bool ignoreSnapshotTime)
+ {
+ string accountName = this.ServiceClient.Credentials.AccountName;
+ string containerName = this.Container.Name;
+ string blobName = this.Name;
+
+ string canonicalName = string.Format("/{0}/{1}/{2}", accountName, containerName, blobName);
+
+ if (!ignoreSnapshotTime && this.SnapshotTime != null)
+ {
+ canonicalName += "?snapshot=" + Request.ConvertDateTimeToSnapshotString(this.SnapshotTime.Value.ToUniversalTime());
+ }
+
+ return canonicalName;
+ }
+
+ ///
+ /// Uploads a stream that doesn't have a known length. In this case we can't do any optimizations like creating a request before we read all of the data.
+ ///
+ /// The stream that is the source of data.
+ /// An object that specifies any additional options for the request.
+ /// The block size to be used (null - no blocks).
+ /// A sequence that represents uploading the blob.
+ /// This is implemented by creating a BlobStream and using that to buffer data until we have sufficient amount to be sent.
+ private TaskSequence UploadUnknownSizeStream(Stream source, BlobRequestOptions options, long blockSize)
+ {
+ // Potential Improvement: is there a better uploading scheme in absense of a known length
+ // This function is more a defensive measure; it should be rarely called, if ever. (Can seekable stream not have length?
+ // Cannot close as this is deferred execution
+ var target = this.OpenWrite(options);
+
+ target.BlockSize = blockSize;
+ return source.WriteToAndCloseOutput(target);
+ }
+
+ ///
+ /// Uploads the blob with blocks.
+ ///
+ /// The source stream.
+ /// An object that specifies any additional options for the request.
+ /// The size of the block.
+ /// A that uploads the blob.
+ private TaskSequence UploadBlobWithBlocks(Stream source, BlobRequestOptions options, long blockSize)
+ {
+ CommonUtils.AssertNotNull("modifers", options);
+
+ var target = this.OpenWrite(options);
+
+ target.BlockSize = blockSize;
+ return source.WriteToAndCloseOutput(target);
+ }
+
+ ///
+ /// Uploads the full blob with a retry sequence.
+ ///
+ /// The source stream.
+ /// An object that specifies any additional options for the request.
+ /// A that uploads the blob.
+ private TaskSequence UploadFullBlobWithRetrySequenceImpl(Stream source, BlobRequestOptions options)
+ {
+ var task = this.UploadFullBlobWithRetryImpl(source, options);
+
+ yield return task;
+
+ var scratch = task.Result;
+ }
+
+ ///
+ /// Implementation of the CopyFromBlob method.
+ ///
+ /// The source blob.
+ /// An object that specifies any additional options for the request.
+ /// A that copies the blob.
+ private TaskSequence CopyFromBlobImpl(CloudBlob source, BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ string sourceBlobUri = source.GetCanonicalName(false);
+
+ ConditionHeaderKind header;
+ string value;
+ AccessCondition.GetSourceConditions(options.CopySourceAccessCondition, out header, out value);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.CopyFrom(this.TransformedAddress, timeout, sourceBlobUri, this.SnapshotTime, header, value, null));
+
+ BlobRequest.AddMetadata(webRequest, this.Metadata);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var copyTask = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return copyTask;
+
+ // Parse the response
+ using (HttpWebResponse webResponse = copyTask.Result as HttpWebResponse)
+ {
+ this.ParseSizeAndLastModified(webResponse);
+ }
+ }
+
+ ///
+ /// Implements the DeleteBlob method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that deletes the blob.
+ private TaskSequence DeleteBlobImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.Delete(this.TransformedAddress, timeout, this.SnapshotTime, options.DeleteSnapshotsOption, null));
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Implementation for the DeleteIfExists method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The set result.
+ /// A that deletes the blob if it exists.
+ private TaskSequence DeleteBlobIfExistsImpl(BlobRequestOptions options, Action setResult)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var task = new InvokeTaskSequenceTask(() => this.DeleteBlobImpl(options));
+
+ yield return task;
+
+ try
+ {
+ // Materialize exceptions
+ var scratch = task.Result;
+
+ setResult(true);
+ }
+ catch (StorageClientException e)
+ {
+ if (e.StatusCode == HttpStatusCode.NotFound)
+ {
+ setResult(false);
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ ///
+ /// Implementation for the CreateSnapshot method.
+ ///
+ /// A collection of name-value pairs defining the metadata of the snapshot, or null.
+ /// An object that specifies any additional options for the request.
+ /// The result report delegate.
+ /// A that creates the snapshot.
+ /// If the metadata parameter is null then no metadata is associated with the request.
+ private TaskSequence CreateSnapshotImpl(NameValueCollection metadata, BlobRequestOptions options, Action setResult)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.Snapshot(this.TransformedAddress, timeout));
+
+ BlobAttributes snapshotAttributes = new BlobAttributes(this.attributes);
+
+ // If metadata was supplied it should be passed to the request.
+ // Otherwise, no metadata should be sent.
+ if (metadata != null)
+ {
+ BlobRequest.AddMetadata(webRequest, metadata);
+
+ // Update the snapshot's attributes to reflect the new metadata.
+ snapshotAttributes.Metadata.Clear();
+ snapshotAttributes.Metadata.Add(metadata);
+ }
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ string snapshotTime = BlobResponse.GetSnapshotTime(webResponse);
+
+ CloudBlob snapshot = new CloudBlob(snapshotAttributes, this.ServiceClient, snapshotTime);
+
+ snapshot.ParseSizeAndLastModified(webResponse);
+
+ setResult(snapshot);
+ }
+ }
+
+ ///
+ /// Implementation for the SetMetadata method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that sets the metadata.
+ private TaskSequence SetMetadataImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.SetMetadata(this.TransformedAddress, timeout, null));
+
+ BlobRequest.AddMetadata(webRequest, this.Metadata);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+
+ yield return task;
+
+ // Parse the response
+ using (HttpWebResponse webResponse = task.Result as HttpWebResponse)
+ {
+ this.ParseSizeAndLastModified(webResponse);
+ }
+ }
+
+ ///
+ /// Implementation for the SetProperties method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that sets the properties.
+ private TaskSequence SetPropertiesImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ if (this.Properties == null)
+ {
+ throw new InvalidOperationException("BlobProperties is null");
+ }
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.SetProperties(this.TransformedAddress, timeout, this.Properties, null, null));
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+
+ yield return task;
+
+ // Parse the response
+ using (HttpWebResponse webResponse = task.Result as HttpWebResponse)
+ {
+ this.ParseSizeAndLastModified(webResponse);
+ }
+ }
+
+ ///
+ /// Verifies that write operation is not done for snapshot.
+ ///
+ private void VerifyNoWriteOperationForSnapshot()
+ {
+ if (this.SnapshotTime != null)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.CannotModifySnapshot);
+ throw new ArgumentException(errorMessage);
+ }
+ }
+
+ ///
+ /// Parses the snapshot time.
+ ///
+ /// The snapshot time.
+ /// The parsed snapshot time.
+ private DateTime ParseSnapshotTime(string snapshotTime)
+ {
+ DateTime snapshotDateTime;
+
+ if (!DateTime.TryParse(
+ snapshotTime,
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.AdjustToUniversal,
+ out snapshotDateTime))
+ {
+ CommonUtils.ArgumentOutOfRange("snapshotTime", snapshotTime);
+ }
+
+ if (this.SnapshotTime != null && this.SnapshotTime != snapshotDateTime)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.MultipleSnapshotTimesProvided, snapshotDateTime, this.SnapshotTime);
+ throw new ArgumentException(errorMessage);
+ }
+
+ return snapshotDateTime;
+ }
+
+ ///
+ /// Parse Uri for any snapshot and SAS (Shared access signature) information. Validate that no other query parameters are passed in.
+ ///
+ /// The complete Uri.
+ /// The existing blob client.
+ /// True to use path style Uris.
+ ///
+ /// Any snapshot information will be saved.
+ /// Any SAS information will be recorded as corresponding credentials instance.
+ /// If existingClient is passed in, any SAS information found will not be supported.
+ /// Otherwise a new client is created based on SAS information or as anonymous credentials.
+ ///
+ private void ParseQueryAndVerify(Uri completeUri, CloudBlobClient existingClient, bool? usePathStyleUris)
+ {
+ CommonUtils.AssertNotNull("completeUri", completeUri);
+
+ if (!completeUri.IsAbsoluteUri)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.RelativeAddressNotPermitted, completeUri.ToString());
+ throw new ArgumentException(errorMessage, "completeUri");
+ }
+
+ this.attributes.Uri = new Uri(completeUri.GetLeftPart(UriPartial.Path));
+
+ string snapshot;
+ StorageCredentialsSharedAccessSignature sasCreds;
+ ParseBlobAddressQuery(completeUri.Query, out snapshot, out sasCreds);
+
+ if (!string.IsNullOrEmpty(snapshot))
+ {
+ this.SnapshotTime = this.ParseSnapshotTime(snapshot);
+ }
+
+ if (existingClient != null)
+ {
+ if (sasCreds != null && existingClient.Credentials != null && !sasCreds.Equals(existingClient.Credentials))
+ {
+ string error = string.Format(CultureInfo.CurrentCulture, SR.MultipleCredentialsProvided);
+ throw new ArgumentException(error);
+ }
+ }
+ else
+ {
+ StorageCredentials credentials = (StorageCredentials)sasCreds ?? StorageCredentialsAnonymous.Anonymous;
+ this.ServiceClient = new CloudBlobClient(usePathStyleUris, new Uri(NavigationHelper.GetServiceClientBaseAddress(this.Uri.AbsoluteUri, usePathStyleUris)), credentials);
+ }
+ }
+ }
+}
+
diff --git a/microsoft-azure-api/StorageClient/CloudBlobClient.cs b/microsoft-azure-api/StorageClient/CloudBlobClient.cs
new file mode 100644
index 0000000000000..afcd5c50475bc
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudBlobClient.cs
@@ -0,0 +1,1057 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudBlobClient class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Threading;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// Provides a client for accessing the Windows Azure Blob service.
+ ///
+ public class CloudBlobClient
+ {
+ ///
+ /// Constant for the max value of ParallelOperationThreadCount.
+ ///
+ private const int MaxParallelOperationThreadCount = 64;
+
+ ///
+ /// Stores the default delimiter.
+ ///
+ private string defaultDelimiter;
+
+ ///
+ /// Stores the parallelism factor.
+ ///
+ private int parallelismFactor = -1;
+
+ ///
+ /// Default is 32 MB.
+ ///
+ private long singleBlobUploadThresholdInBytes = Constants.MaxSingleUploadBlobSize / 2;
+
+ ///
+ /// Default is 4 MB.
+ ///
+ private long writeBlockSizeInBytes = Constants.DefaultWriteBlockSizeBytes;
+
+ ///
+ /// Initializes a new instance of the class to be used for anonymous access.
+ ///
+ /// The Blob service endpoint to use to create the client.
+ public CloudBlobClient(string baseAddress)
+ : this(baseAddress, StorageCredentialsAnonymous.Anonymous)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified Blob service endpoint
+ /// and account credentials.
+ ///
+ /// The Blob service endpoint to use to create the client.
+ /// The account credentials.
+ public CloudBlobClient(string baseAddress, StorageCredentials credentials)
+ : this(new Uri(baseAddress), credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified Blob service endpoint
+ /// and account credentials.
+ ///
+ /// The Blob service endpoint to use to create the client.
+ /// The account credentials.
+ public CloudBlobClient(Uri baseUri, StorageCredentials credentials)
+ : this(null, baseUri, credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// True to use path style Uris.
+ /// The base Uri.
+ /// The credentials.
+ internal CloudBlobClient(bool? usePathStyleUris, Uri baseUri, StorageCredentials credentials)
+ {
+ CommonUtils.AssertNotNull("baseUri", baseUri);
+ if (credentials == null)
+ {
+ credentials = StorageCredentialsAnonymous.Anonymous;
+ }
+
+ if (!baseUri.IsAbsoluteUri)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.RelativeAddressNotPermitted, baseUri.ToString());
+ throw new ArgumentException(errorMessage, "baseUri");
+ }
+
+ this.BaseUri = baseUri;
+ this.Credentials = credentials;
+ this.RetryPolicy = RetryPolicies.RetryExponential(RetryPolicies.DefaultClientRetryCount, RetryPolicies.DefaultClientBackoff);
+ this.Timeout = Constants.DefaultClientSideTimeout;
+ this.DefaultDelimiter = "/";
+ this.ReadAheadInBytes = Constants.DefaultReadAheadSizeBytes;
+ this.UseIntegrityControlForStreamReading = true;
+
+ if (usePathStyleUris.HasValue)
+ {
+ this.UsePathStyleUris = usePathStyleUris.Value;
+ }
+ else
+ {
+ // Automatically decide whether to use host style uri or path style uri
+ this.UsePathStyleUris = CommonUtils.UsePathStyleAddressing(this.BaseUri);
+ }
+ }
+
+ ///
+ /// Occurs when a response is received from the server.
+ ///
+ public event EventHandler ResponseReceived;
+
+ ///
+ /// Gets the account credentials used to create the Blob service client.
+ ///
+ /// The account credentials.
+ public StorageCredentials Credentials { get; private set; }
+
+ ///
+ /// Gets the base URI for the Blob service client.
+ ///
+ /// The base URI used to construct the Blob service client.
+ public Uri BaseUri { get; private set; }
+
+ ///
+ /// Gets or sets the default retry policy for requests made via the Blob service client.
+ ///
+ /// The retry policy.
+ public RetryPolicy RetryPolicy { get; set; }
+
+ ///
+ /// Gets or sets the default server and client timeout for requests made by the Blob service client.
+ ///
+ /// The server and client timeout interval.
+ public TimeSpan Timeout { get; set; }
+
+ ///
+ /// Gets or sets the default delimiter that may be used to create a virtual directory structure of blobs.
+ ///
+ /// The default delimiter.
+ public string DefaultDelimiter
+ {
+ get
+ {
+ return this.defaultDelimiter;
+ }
+
+ set
+ {
+ CommonUtils.AssertNotNullOrEmpty("DefaultDelimiter", value);
+ this.defaultDelimiter = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum size of a blob in bytes that may be uploaded as a single blob.
+ ///
+ /// The maximum size of a blob, in bytes, that may be uploaded as a single blob,
+ /// ranging from between 1 and 64 MB inclusive.
+ public long SingleBlobUploadThresholdInBytes
+ {
+ get
+ {
+ return this.singleBlobUploadThresholdInBytes;
+ }
+
+ set
+ {
+ if (value > Constants.MaxSingleUploadBlobSize || value < 1 * Constants.MB)
+ {
+ throw new ArgumentOutOfRangeException("SingleBlobUploadThresholdInBytes");
+ }
+
+ this.singleBlobUploadThresholdInBytes = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the maximum block size for writing to a block blob.
+ ///
+ /// The maximum size of a block, in bytes, ranging from between 1 and 4 MB inclusive.
+ public long WriteBlockSizeInBytes
+ {
+ get
+ {
+ return this.writeBlockSizeInBytes;
+ }
+
+ set
+ {
+ if (value > Constants.DefaultWriteBlockSizeBytes || value < 1 * Constants.MB)
+ {
+ throw new ArgumentOutOfRangeException("WriteBlockSizeInBytes");
+ }
+
+ this.writeBlockSizeInBytes = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the number of bytes to pre-fetch when reading from a stream.
+ ///
+ /// The number of bytes to read ahead from a stream.
+ public long ReadAheadInBytes { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the integrity of each block should be verified when reading from a stream.
+ ///
+ /// True if using integrity control for stream reading; otherwise, false. The default value is true.
+ public bool UseIntegrityControlForStreamReading { get; set; }
+
+ ///
+ /// Gets or sets the number of blocks that may be simultaneously uploaded when uploading a blob that is greater than
+ /// the value specified by the property.
+ /// in size.
+ ///
+ /// The number of parallel operations that may proceed.
+ public int ParallelOperationThreadCount
+ {
+ get
+ {
+ if (this.parallelismFactor == -1)
+ {
+ int workerThreads;
+ int ioThreads;
+ ThreadPool.GetMinThreads(out workerThreads, out ioThreads);
+ this.parallelismFactor = ioThreads;
+ }
+
+ return this.parallelismFactor;
+ }
+
+ set
+ {
+ CommonUtils.AssertInBounds("UploadParallelActiveTasks", value, 1, MaxParallelOperationThreadCount);
+
+ this.parallelismFactor = value;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the service client is used with Path style or Host style.
+ ///
+ /// Is true if use path style uris; otherwise, false.
+ internal bool UsePathStyleUris { get; private set; }
+
+ ///
+ /// Gets the properties of the blob service.
+ ///
+ /// The blob service properties.
+ public ServiceProperties GetServiceProperties()
+ {
+ return TaskImplHelper.ExecuteImplWithRetry((setResult) => this.GetServicePropertiesImpl(setResult), this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to get the properties of the blob service.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user defined object to be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetServiceProperties(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry((setResult) => this.GetServicePropertiesImpl(setResult), this.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to get the properties of the blob service.
+ ///
+ /// The result returned from a prior call to .
+ /// The blob service properties.
+ public ServiceProperties EndGetServiceProperties(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Sets the properties of the blob service.
+ ///
+ /// The blob service properties.
+ public void SetServiceProperties(ServiceProperties properties)
+ {
+ TaskImplHelper.ExecuteImplWithRetry(() => this.SetServicePropertiesImpl(properties), this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to set the properties of the blob service.
+ ///
+ /// The blob service properties.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user defined object to be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetServiceProperties(ServiceProperties properties, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(() => this.SetServicePropertiesImpl(properties), this.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to set the properties of the blob service.
+ ///
+ /// The result returned from a prior call to .
+ public void EndSetServiceProperties(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Returns a reference to a object with the specified address.
+ ///
+ /// The absolute URI to the blob, or a relative URI beginning with the container name.
+ /// A reference to a page blob.
+ [Obsolete]
+ [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
+ public CloudPageBlob GetPageBlob(string blobAddress)
+ {
+ return this.GetPageBlobReference(blobAddress);
+ }
+
+ ///
+ /// Returns a reference to a object with the specified address.
+ ///
+ /// The absolute URI to the blob, or a relative URI beginning with the container name.
+ /// A reference to a page blob.
+ public CloudPageBlob GetPageBlobReference(string blobAddress)
+ {
+ return this.GetPageBlobReference(blobAddress, null);
+ }
+
+ ///
+ /// Returns a reference to a object with the specified address.
+ ///
+ /// The absolute URI to the blob, or a relative URI beginning with the container name.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A reference to a page blob.
+ public CloudPageBlob GetPageBlobReference(string blobAddress, DateTime? snapshotTime)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobAbsoluteUriString", blobAddress);
+ return new CloudPageBlob(blobAddress, snapshotTime, this);
+ }
+
+ ///
+ /// Returns a reference to a object with the specified address.
+ ///
+ /// The absolute URI to the blob, or a relative URI beginning with the container name.
+ /// A reference to a block blob.
+ [Obsolete]
+ [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
+ public CloudBlockBlob GetBlockBlob(string blobAddress)
+ {
+ return this.GetBlockBlobReference(blobAddress);
+ }
+
+ ///
+ /// Returns a reference to a with the specified address.
+ ///
+ /// The absolute URI to the blob, or a relative URI beginning with the container name.
+ /// A reference to a block blob.
+ public CloudBlockBlob GetBlockBlobReference(string blobAddress)
+ {
+ return this.GetBlockBlobReference(blobAddress, null);
+ }
+
+ ///
+ /// Returns a reference to a with the specified address.
+ ///
+ /// The absolute URI to the blob, or a relative URI beginning with the container name.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A reference to a block blob.
+ public CloudBlockBlob GetBlockBlobReference(string blobAddress, DateTime? snapshotTime)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobAbsoluteUriString", blobAddress);
+ return new CloudBlockBlob(blobAddress, snapshotTime, this);
+ }
+
+ ///
+ /// Returns a reference to a with the specified address.
+ ///
+ /// The absolute URI to the blob, or a relative URI beginning with the container name.
+ /// A reference to a blob.
+ public CloudBlob GetBlobReference(string blobAddress)
+ {
+ return this.GetBlobReference(blobAddress, null);
+ }
+
+ ///
+ /// Returns a reference to a blob with the specified address.
+ ///
+ /// The absolute URI to the blob, or a relative URI beginning with the container name.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A reference to a blob.
+ public CloudBlob GetBlobReference(string blobAddress, DateTime? snapshotTime)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobAbsoluteUriString", blobAddress);
+ return new CloudBlob(blobAddress, snapshotTime, this);
+ }
+
+ ///
+ /// Returns a reference to a object with the specified address.
+ ///
+ /// The name of the container, or an absolute URI to the container.
+ /// A reference to a container.
+ public CloudBlobContainer GetContainerReference(string containerAddress)
+ {
+ CommonUtils.AssertNotNullOrEmpty("containerAddress", containerAddress);
+ return new CloudBlobContainer(containerAddress, this);
+ }
+
+ ///
+ /// Returns a reference to a object with the specified address.
+ ///
+ /// The absolute URI to the virtual directory,
+ /// or a relative URI beginning with the container name.
+ /// A reference to a virtual directory.
+ public CloudBlobDirectory GetBlobDirectoryReference(string blobDirectoryAddress)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobDirectoryAddress", blobDirectoryAddress);
+ return new CloudBlobDirectory(blobDirectoryAddress, this);
+ }
+
+ ///
+ /// Returns an enumerable collection of containers.
+ ///
+ /// An enumerable collection of containers.
+ public IEnumerable ListContainers()
+ {
+ return this.ListContainers(null, ContainerListingDetails.None);
+ }
+
+ ///
+ /// Returns an enumerable collection of containers whose names begin with the specified prefix.
+ ///
+ /// The container name prefix.
+ /// An enumerable collection of containers.
+ public IEnumerable ListContainers(string prefix)
+ {
+ return this.ListContainers(prefix, ContainerListingDetails.None);
+ }
+
+ ///
+ /// Returns an enumerable collection of containers whose names
+ /// begin with the specified prefix and that are retrieved lazily.
+ ///
+ /// The container name prefix.
+ /// A value that indicates whether to return container metadata with the listing.
+ /// An enumerable collection of containers that are retrieved lazily.
+ public IEnumerable ListContainers(string prefix, ContainerListingDetails detailsIncluded)
+ {
+ return CommonUtils.LazyEnumerateSegmented(
+ (setResult) => this.ListContainersImpl(prefix, detailsIncluded, null, null, setResult),
+ this.RetryPolicy);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of containers.
+ ///
+ /// A result segment of containers.
+ public ResultSegment ListContainersSegmented()
+ {
+ return this.ListContainersSegmented(null);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of containers
+ /// whose names begin with the specified prefix.
+ ///
+ /// The container name prefix.
+ /// A result segment of containers.
+ public ResultSegment ListContainersSegmented(string prefix)
+ {
+ return this.ListContainersSegmented(prefix, ContainerListingDetails.None, 0, null);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of containers
+ /// whose names begin with the specified prefix.
+ ///
+ /// The container name prefix.
+ /// A value that indicates whether to return container metadata with the listing.
+ /// A non-negative integer value that indicates the maximum number of results to be returned
+ /// in the result segment, up to the per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// A result segment of containers.
+ public ResultSegment ListContainersSegmented(
+ string prefix,
+ ContainerListingDetails detailsIncluded,
+ int maxResults,
+ ResultContinuation continuationToken)
+ {
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.ListContainersImpl(prefix, detailsIncluded, continuationToken, maxResults, setResult),
+ this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous request to return a result segment containing a collection of containers.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListContainersSegmented(AsyncCallback callback, object state)
+ {
+ return this.BeginListContainersSegmented(null, ContainerListingDetails.None, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous request to return a result segment containing a collection of containers
+ /// whose names begin with the specified prefix.
+ ///
+ /// The container name prefix.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListContainersSegmented(string prefix, AsyncCallback callback, object state)
+ {
+ return this.BeginListContainersSegmented(prefix, ContainerListingDetails.None, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous request to return a result segment containing a collection of containers
+ /// whose names begin with the specified prefix.
+ ///
+ /// The container name prefix.
+ /// A value that indicates whether to return container metadata with the listing.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListContainersSegmented(string prefix, ContainerListingDetails detailsIncluded, AsyncCallback callback, object state)
+ {
+ return this.BeginListContainersSegmented(prefix, detailsIncluded, 0, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous request to return a result segment containing a collection of containers
+ /// whose names begin with the specified prefix.
+ ///
+ /// The container name prefix.
+ /// A value that indicates whether to return container metadata with the listing.
+ /// A non-negative integer value that indicates the maximum number of results to be returned
+ /// in the result segment, up to the per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListContainersSegmented(
+ string prefix,
+ ContainerListingDetails detailsIncluded,
+ int maxResults,
+ ResultContinuation continuationToken,
+ AsyncCallback callback,
+ object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.ListContainersImpl(prefix, detailsIncluded, continuationToken, maxResults, setResult),
+ this.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to return a result segment containing a collection of containers.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A result segment of containers.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public ResultSegment EndListContainersSegmented(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Returns an enumerable collection of blob items whose names begin with the specified prefix.
+ ///
+ /// The blob name prefix. This value must be preceded either by the name of the container or by the absolute URI to the container.
+ /// An enumerable collection of objects that implement .
+ public IEnumerable ListBlobsWithPrefix(string prefix)
+ {
+ return this.ListBlobsWithPrefix(prefix, null);
+ }
+
+ ///
+ /// Returns an enumerable collection of blob items whose names begin with the specified prefix and that are retrieved lazily.
+ ///
+ /// The blob name prefix. This value must be preceded either by the name of the container or by the absolute URI to the container.
+ /// An object that specifies any additional options for the request.
+ /// An enumerable collection of objects that implement and are retrieved lazily.
+ public IEnumerable ListBlobsWithPrefix(string prefix, BlobRequestOptions options)
+ {
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this, options);
+
+ return CommonUtils.LazyEnumerateSegmented(
+ (setResult) => this.ListBlobImpl(prefix, null, null, fullModifier, setResult),
+ this.RetryPolicy);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of blob items whose names
+ /// begin with the specified prefix.
+ ///
+ /// The blob name prefix. This value must be preceded either by the name of the container or by the absolute path to the container.
+ /// A result segment containing objects that implement .
+ public ResultSegment ListBlobsWithPrefixSegmented(string prefix)
+ {
+ return this.ListBlobsWithPrefixSegmented(prefix, 0, null, null);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of blob items whose names
+ /// begin with the specified prefix.
+ ///
+ /// The blob name prefix. This value must be preceded either by the name of the container or by the absolute path to the container.
+ /// An object that specifies any additional options for the request.
+ /// A result segment containing objects that implement .
+ public ResultSegment ListBlobsWithPrefixSegmented(string prefix, BlobRequestOptions options)
+ {
+ return this.ListBlobsWithPrefixSegmented(prefix, 0, null, options);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of blob items whose names
+ /// begin with the specified prefix.
+ ///
+ /// The blob name prefix. This value must be preceded either by the name of the container or by the absolute path to the container.
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the per-operation limit of 5000.
+ /// If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// A result segment containing objects that implement .
+ public ResultSegment ListBlobsWithPrefixSegmented(
+ string prefix,
+ int maxResults,
+ ResultContinuation continuationToken)
+ {
+ return this.ListBlobsWithPrefixSegmented(prefix, maxResults, continuationToken, null);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of blob items whose names
+ /// begin with the specified prefix.
+ ///
+ /// The blob name prefix. This value must be preceded either by the name of the container or by the absolute path to the container.
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the per-operation limit of 5000.
+ /// If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// An object that specifies any additional options for the request.
+ /// A result segment containing objects that implement .
+ public ResultSegment ListBlobsWithPrefixSegmented(
+ string prefix,
+ int maxResults,
+ ResultContinuation continuationToken,
+ BlobRequestOptions options)
+ {
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.ListBlobImpl(prefix, continuationToken, maxResults, fullModifier, setResult),
+ this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection
+ /// of blob items whose names begin with the specified prefix.
+ ///
+ /// The blob name prefix. This value must be preceded either by the name of the container or by the absolute path to the container.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListBlobsWithPrefixSegmented(string prefix, AsyncCallback callback, object state)
+ {
+ return this.BeginListBlobsWithPrefixSegmented(prefix, 0, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection
+ /// of blob items whose names begin with the specified prefix.
+ ///
+ /// The blob name prefix. This value must be preceded either by the name of the container or by the absolute path to the container.
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListBlobsWithPrefixSegmented(
+ string prefix,
+ int maxResults,
+ ResultContinuation continuationToken,
+ AsyncCallback callback,
+ object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.ListBlobImpl(prefix, continuationToken, maxResults, null, setResult),
+ this.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to return a result segment containing a collection
+ /// of blob items whose names begin with the specified prefix.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A result segment containing objects that implement .
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public ResultSegment EndListBlobsWithPrefixSegmented(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Selects the protocol response.
+ ///
+ /// The protocol item.
+ /// The service.
+ /// The container.
+ /// The parsed .
+ internal static IListBlobItem SelectProtocolResponse(IListBlobEntry protocolItem, CloudBlobClient service, CloudBlobContainer container)
+ {
+ BlobEntry blob = protocolItem as BlobEntry;
+ if (blob != null)
+ {
+ var attributes = blob.Attributes;
+ CloudBlob cloudBlob;
+ if (attributes.Properties.BlobType == BlobType.BlockBlob)
+ {
+ cloudBlob = new CloudBlockBlob(attributes, service, ConvertDateTimeToSnapshotString(attributes.Snapshot));
+ }
+ else if (attributes.Properties.BlobType == BlobType.PageBlob)
+ {
+ cloudBlob = new CloudPageBlob(attributes, service, ConvertDateTimeToSnapshotString(attributes.Snapshot));
+ }
+ else
+ {
+ cloudBlob = new CloudBlob(attributes, service, ConvertDateTimeToSnapshotString(attributes.Snapshot));
+ }
+
+ return cloudBlob;
+ }
+
+ BlobPrefixEntry blobPrefix = protocolItem as BlobPrefixEntry;
+
+ if (blobPrefix != null)
+ {
+ if (container != null)
+ {
+ return container.GetDirectoryReference(blobPrefix.Name);
+ }
+ else
+ {
+ return new CloudBlobDirectory(blobPrefix.Name, service);
+ }
+ }
+
+ throw new InvalidOperationException("Invalid blob list item returned");
+ }
+
+ ///
+ /// Ends the asynchronous GetResponse operation.
+ ///
+ /// An that references the asynchronous operation.
+ /// The request to end the operation on.
+ /// The from the asynchronous request.
+ internal WebResponse EndGetResponse(IAsyncResult asyncresult, WebRequest req)
+ {
+ return EventHelper.ProcessWebResponse(req, asyncresult, this.ResponseReceived, this);
+ }
+
+ ///
+ /// Gets the response for the operation.
+ ///
+ /// The request to end the operation on.
+ /// The from the request.
+ internal WebResponse GetResponse(WebRequest req)
+ {
+ return EventHelper.ProcessWebResponseSync(req, this.ResponseReceived, this);
+ }
+
+ ///
+ /// Parses the user prefix.
+ ///
+ /// The prefix.
+ /// Name of the container.
+ /// The listing prefix.
+ private static void ParseUserPrefix(string prefix, out string containerName, out string listingPrefix)
+ {
+ containerName = null;
+ listingPrefix = null;
+
+ string[] prefixParts = prefix.Split(NavigationHelper.SlashAsSplitOptions, 2, StringSplitOptions.None);
+ if (prefixParts.Length == 1)
+ {
+ // No slash in prefix
+ // Case abc => container = $root, prefix=abc; Listing with prefix at root
+ listingPrefix = prefixParts[0];
+ }
+ else
+ {
+ // Case "/abc" => container=$root, prefix=abc; Listing with prefix at root
+ // Case "abc/" => container=abc, no prefix; Listing all under a container
+ // Case "abc/def" => container = abc, prefix = def; Listing with prefix under a container
+ // Case "/" => container=$root, no prefix; Listing all under root
+ containerName = prefixParts[0];
+ listingPrefix = prefixParts[1];
+ }
+
+ if (string.IsNullOrEmpty(containerName))
+ {
+ containerName = "$root";
+ }
+
+ if (string.IsNullOrEmpty(listingPrefix))
+ {
+ listingPrefix = null;
+ }
+ }
+
+ ///
+ /// Converts the date time to snapshot string.
+ ///
+ /// The snapshot time to convert.
+ /// A string representing the snapshot time.
+ private static string ConvertDateTimeToSnapshotString(DateTime? snapshot)
+ {
+ if (snapshot.HasValue)
+ {
+ return Protocol.Request.ConvertDateTimeToSnapshotString(snapshot.Value);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Implementation for the ListContainers method.
+ ///
+ /// The container prefix.
+ /// The details included.
+ /// The continuation token.
+ /// The maximum results to return.
+ /// The result report delegate.
+ /// A that lists the containers.
+ private TaskSequence ListContainersImpl(
+ string prefix,
+ ContainerListingDetails detailsIncluded,
+ ResultContinuation continuationToken,
+ int? maxResults,
+ Action> setResult)
+ {
+ ResultPagination pagination = new ResultPagination(maxResults.GetValueOrDefault());
+
+ return this.ListContainersImplCore(prefix, detailsIncluded, continuationToken, pagination, setResult);
+ }
+
+ ///
+ /// Core implementation for the ListContainers method.
+ ///
+ /// The container prefix.
+ /// The details included.
+ /// The continuation token.
+ /// The pagination.
+ /// The result report delegate.
+ /// A that lists the containers.
+ private TaskSequence ListContainersImplCore(
+ string prefix,
+ ContainerListingDetails detailsIncluded,
+ ResultContinuation continuationToken,
+ ResultPagination pagination,
+ Action> setResult)
+ {
+ CommonUtils.AssertContinuationType(continuationToken, ResultContinuation.ContinuationType.Container);
+
+ ListingContext listingContext = new ListingContext(prefix, pagination.GetNextRequestPageSize())
+ {
+ Marker = continuationToken != null ? continuationToken.NextMarker : null
+ };
+
+ var containersList = new List();
+
+ var request = ContainerRequest.List(this.BaseUri, this.Timeout.RoundUpToSeconds(), listingContext, detailsIncluded);
+
+ this.Credentials.SignRequest(request);
+
+ var listTask = request.GetResponseAsyncWithTimeout(this, this.Timeout);
+
+ yield return listTask;
+
+ string nextMarker;
+
+ using (var response = listTask.Result as HttpWebResponse)
+ {
+ ListContainersResponse listContainersResponse = ContainerResponse.List(response);
+
+ containersList.AddRange(listContainersResponse.Containers.Select((item) => new CloudBlobContainer(item.Attributes, this)));
+
+ nextMarker = listContainersResponse.NextMarker;
+ }
+
+ ResultContinuation newContinuationToken = new ResultContinuation() { NextMarker = nextMarker, Type = ResultContinuation.ContinuationType.Container };
+
+ ResultSegment.CreateResultSegment(
+ setResult,
+ containersList,
+ newContinuationToken,
+ pagination,
+ this.RetryPolicy,
+ (paginationArg, continuationArg, resultSegmentArg) =>
+ this.ListContainersImplCore(
+ prefix,
+ detailsIncluded,
+ continuationArg,
+ paginationArg,
+ resultSegmentArg));
+ }
+
+ ///
+ /// Implementation for the ListBlobs method.
+ ///
+ /// The blob prefix.
+ /// The continuation token.
+ /// The max results.
+ /// An object that specifies any additional options for the request.
+ /// The result report delegate.
+ ///
+ /// A that lists the blobs.
+ ///
+ private TaskSequence ListBlobImpl(
+ string prefix,
+ ResultContinuation continuationToken,
+ int? maxResults,
+ BlobRequestOptions options,
+ Action> setResult)
+ {
+ CommonUtils.AssertContinuationType(continuationToken, ResultContinuation.ContinuationType.Blob);
+
+ string containerName = null;
+ string listingPrefix = null;
+
+ ParseUserPrefix(prefix, out containerName, out listingPrefix);
+ var containerInfo = new CloudBlobContainer(containerName, this);
+
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this, options);
+
+ return containerInfo.ListBlobsImpl(listingPrefix, fullModifier, continuationToken, maxResults, setResult);
+ }
+
+ ///
+ /// Generates a task sequence for getting the properties of the blob service.
+ ///
+ /// A delegate to receive the service properties.
+ /// A task sequence that gets the properties of the blob service.
+ private TaskSequence GetServicePropertiesImpl(Action setResult)
+ {
+ HttpWebRequest request = BlobRequest.GetServiceProperties(this.BaseUri, this.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(request, -1);
+ this.Credentials.SignRequest(request);
+
+ // Get the web response.
+ Task responseTask = request.GetResponseAsyncWithTimeout(this, this.Timeout);
+ yield return responseTask;
+
+ using (HttpWebResponse response = responseTask.Result as HttpWebResponse)
+ using (Stream responseStream = response.GetResponseStream())
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ // Download the service properties.
+ Task downloadTask = new InvokeTaskSequenceTask(() => { return responseStream.WriteTo(memoryStream); });
+ yield return downloadTask;
+
+ // Materialize any exceptions.
+ NullTaskReturn scratch = downloadTask.Result;
+
+ // Get the result from the memory stream.
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ setResult(BlobResponse.ReadServiceProperties(memoryStream));
+ }
+ }
+
+ ///
+ /// Generates a task sequence for setting the properties of the blob service.
+ ///
+ /// The blob service properties to set.
+ /// A task sequence that sets the properties of the blob service.
+ private TaskSequence SetServicePropertiesImpl(ServiceProperties properties)
+ {
+ CommonUtils.AssertNotNull("properties", properties);
+
+ HttpWebRequest request = BlobRequest.SetServiceProperties(this.BaseUri, this.Timeout.RoundUpToSeconds());
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ try
+ {
+ BlobRequest.WriteServiceProperties(properties, memoryStream);
+ }
+ catch (InvalidOperationException invalidOpException)
+ {
+ throw new ArgumentException(invalidOpException.Message, "properties");
+ }
+
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ CommonUtils.ApplyRequestOptimizations(request, memoryStream.Length);
+ this.Credentials.SignRequest(request);
+
+ // Get the request stream
+ Task getStreamTask = request.GetRequestStreamAsync();
+ yield return getStreamTask;
+
+ using (Stream requestStream = getStreamTask.Result)
+ {
+ // Upload the service properties.
+ Task uploadTask = new InvokeTaskSequenceTask(() => { return (memoryStream as Stream).WriteTo(requestStream); });
+ yield return uploadTask;
+
+ // Materialize any exceptions.
+ NullTaskReturn scratch = uploadTask.Result;
+ }
+ }
+
+ // Get the web response.
+ Task responseTask = request.GetResponseAsyncWithTimeout(this, this.Timeout);
+ yield return responseTask;
+
+ // Materialize any exceptions.
+ using (HttpWebResponse response = responseTask.Result as HttpWebResponse)
+ {
+ }
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudBlobContainer.cs b/microsoft-azure-api/StorageClient/CloudBlobContainer.cs
new file mode 100644
index 0000000000000..da852a5c1ccb8
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudBlobContainer.cs
@@ -0,0 +1,1225 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudBlobContainer class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Globalization;
+ using System.Linq;
+ using System.Net;
+ using System.Web;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// Represents a container in the Windows Azure Blob service.
+ ///
+ public class CloudBlobContainer
+ {
+ ///
+ /// Stores the transformed address.
+ ///
+ private Uri transformedAddress;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The absolute URI to the container.
+ public CloudBlobContainer(string containerAddress)
+ : this(null, containerAddress)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The absolute URI to the container.
+ /// The account credentials.
+ public CloudBlobContainer(string containerAddress, StorageCredentials credentials)
+ : this(containerAddress, new CloudBlobClient(NavigationHelper.GetServiceClientBaseAddress(containerAddress, null), credentials))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Either the absolute URI to the container, or the container name.
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudBlobContainer(string containerAddress, CloudBlobClient service)
+ {
+ this.Attributes = new BlobContainerAttributes();
+ this.ServiceClient = service;
+
+ Uri completeUri = NavigationHelper.AppendPathToUri(this.ServiceClient.BaseUri, containerAddress);
+
+ this.ParseQueryAndVerify(completeUri, this.ServiceClient, this.ServiceClient.UsePathStyleUris);
+
+ this.Name = NavigationHelper.GetContainerNameFromContainerAddress(this.Uri, this.ServiceClient.UsePathStyleUris);
+ }
+
+ ///
+ /// Initializes a new instance of the class with the given address and path style Uri preference.
+ ///
+ /// The container's address.
+ /// True to use path style Uris.
+ internal CloudBlobContainer(string containerAddress, bool usePathStyleUris)
+ : this(usePathStyleUris, containerAddress)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The container address.
+ /// The credentials.
+ /// If set to true path style Uris are used.
+ internal CloudBlobContainer(string containerAddress, StorageCredentials credentials, bool usePathStyleUris)
+ : this(containerAddress, new CloudBlobClient(NavigationHelper.GetServiceClientBaseAddress(containerAddress, usePathStyleUris), credentials))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The attributes for the container (NOTE: Stored by reference).
+ /// The client to be used.
+ internal CloudBlobContainer(BlobContainerAttributes attrib, CloudBlobClient client)
+ {
+ this.ServiceClient = client;
+ this.Attributes = attrib;
+
+ Uri completeUri = NavigationHelper.AppendPathToUri(this.ServiceClient.BaseUri, attrib.Uri.AbsoluteUri);
+
+ this.ParseQueryAndVerify(completeUri, this.ServiceClient, this.ServiceClient.UsePathStyleUris);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// True to use path style Uris.
+ /// The container's address.
+ internal CloudBlobContainer(bool? usePathStyleUris, string containerAddress)
+ {
+ this.Attributes = new BlobContainerAttributes();
+
+ Uri completeUri = new Uri(containerAddress);
+
+ this.ParseQueryAndVerify(completeUri, null, usePathStyleUris);
+ }
+
+ ///
+ /// Gets the service client for the container.
+ ///
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudBlobClient ServiceClient { get; private set; }
+
+ ///
+ /// Gets the container's URI.
+ ///
+ /// The absolute URI to the container.
+ public Uri Uri
+ {
+ get
+ {
+ return this.Attributes.Uri;
+ }
+
+ private set
+ {
+ this.Attributes.Uri = value;
+ }
+ }
+
+ ///
+ /// Gets the name of the container.
+ ///
+ /// The container's name.
+ public string Name
+ {
+ get
+ {
+ return this.Attributes.Name;
+ }
+
+ private set
+ {
+ this.Attributes.Name = value;
+ }
+ }
+
+ ///
+ /// Gets the container's metadata.
+ ///
+ /// The container's metadata.
+ public NameValueCollection Metadata
+ {
+ get
+ {
+ return this.Attributes.Metadata;
+ }
+ }
+
+ ///
+ /// Gets the container's attributes.
+ ///
+ /// The container's attributes.
+ public BlobContainerAttributes Attributes { get; internal set; }
+
+ ///
+ /// Gets the container's system properties.
+ ///
+ /// The container's properties.
+ public BlobContainerProperties Properties
+ {
+ get
+ {
+ return this.Attributes.Properties;
+ }
+ }
+
+ ///
+ /// Gets the Uri after applying authentication transformation.
+ ///
+ internal Uri TransformedAddress
+ {
+ get
+ {
+ if (this.ServiceClient.Credentials.NeedsTransformUri)
+ {
+ // This is required to support key rotation
+ // Potential improvement: cache the value of credential and derived Uri to avoid recomputation
+ this.transformedAddress = new Uri(this.ServiceClient.Credentials.TransformUri(this.Uri.AbsoluteUri));
+
+ return this.transformedAddress;
+ }
+ else
+ {
+ return this.Uri;
+ }
+ }
+ }
+
+ ///
+ /// Gets a reference to a blob in this container.
+ ///
+ /// The name of the blob, or the absolute URI to the blob.
+ /// A reference to a blob.
+ public CloudBlob GetBlobReference(string blobAddressUri)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobAddressUri", blobAddressUri);
+
+ return new CloudBlob(this.GetBlobAbsoluteUri(blobAddressUri), this.ServiceClient, this);
+ }
+
+ ///
+ /// Gets a reference to a page blob in this container.
+ ///
+ /// The name of the blob, or the absolute URI to the blob.
+ /// A reference to a page blob.
+ public CloudPageBlob GetPageBlobReference(string blobAddressUri)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobAbsoluteUri", blobAddressUri);
+
+ return new CloudPageBlob(this.GetBlobAbsoluteUri(blobAddressUri), this.ServiceClient, this);
+ }
+
+ ///
+ /// Gets a reference to a block blob in this container.
+ ///
+ /// The name of the blob, or the absolute URI to the blob.
+ /// A reference to a block blob.
+ public CloudBlockBlob GetBlockBlobReference(string blobAddressUri)
+ {
+ CommonUtils.AssertNotNullOrEmpty("blobAbsoluteUri", blobAddressUri);
+
+ return new CloudBlockBlob(this.GetBlobAbsoluteUri(blobAddressUri), this.ServiceClient, this);
+ }
+
+ ///
+ /// Gets a reference to a virtual blob directory beneath this container.
+ ///
+ /// The name of the virtual blob directory, or the absolute URI to the virtual blob directory.
+ /// A reference to a virtual blob directory.
+ public CloudBlobDirectory GetDirectoryReference(string relativeAddress)
+ {
+ CommonUtils.AssertNotNullOrEmpty("relativeAddress", relativeAddress);
+
+ var blobDirectoryUri = NavigationHelper.AppendPathToUri(this.Uri, relativeAddress);
+ return new CloudBlobDirectory(blobDirectoryUri.AbsoluteUri, this.ServiceClient);
+ }
+
+ ///
+ /// Returns an enumerable collection of the blobs in the container.
+ ///
+ /// An enumerable collection of objects that implement .
+ public IEnumerable ListBlobs()
+ {
+ return this.ListBlobs(null);
+ }
+
+ ///
+ /// Returns an enumerable collection of the blobs in the container that are retrieved lazily.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// An enumerable collection of objects that implement and are retrieved lazily.
+ public IEnumerable ListBlobs(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return CommonUtils.LazyEnumerateSegmented(
+ (setResult) => this.ListBlobsImpl(null, fullModifiers, null, null, setResult),
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of blob items
+ /// in the container.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A result segment containing objects that implement .
+ public ResultSegment ListBlobsSegmented(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.ListBlobsImpl(null, fullModifiers, null, null, setResult),
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of blob items
+ /// in the container.
+ ///
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// An object that specifies any additional options for the request.
+ /// A result segment containing objects that implement .
+ public ResultSegment ListBlobsSegmented(int maxResults, ResultContinuation continuationToken, BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.ListBlobsImpl(null, fullModifiers, continuationToken, maxResults, setResult),
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection of blob items
+ /// in the container.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListBlobsSegmented(AsyncCallback callback, object state)
+ {
+ return this.BeginListBlobsSegmented(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection of blob items
+ /// in the container.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListBlobsSegmented(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ return this.BeginListBlobsSegmented(0, null, options, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection of blob items
+ /// in the container.
+ ///
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListBlobsSegmented(
+ int maxResults,
+ ResultContinuation continuationToken,
+ BlobRequestOptions options,
+ AsyncCallback callback,
+ object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.ListBlobsImpl(null, fullModifiers, continuationToken, maxResults, setResult),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to return a result segment containing a collection of blob items
+ /// in the container.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A result segment containing objects that implement .
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public ResultSegment EndListBlobsSegmented(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Creates the container.
+ ///
+ public void Create()
+ {
+ this.Create(null);
+ }
+
+ ///
+ /// Creates the container.
+ ///
+ /// An object that specifies any additional options for the request.
+ public void Create(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(
+ () => this.CreateContainerImpl(fullModifiers),
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a container.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreate(AsyncCallback callback, object state)
+ {
+ return this.BeginCreate(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a container.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreate(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(
+ () => this.CreateContainerImpl(fullModifiers),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to create a container.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndCreate(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Creates the container if it does not already exist.
+ ///
+ /// true if the container did not already exist and was created; otherwise, false.
+ public bool CreateIfNotExist()
+ {
+ return this.CreateIfNotExist(null);
+ }
+
+ ///
+ /// Creates the container if it does not already exist.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// true if the container did not already exist and was created; otherwise false.
+ public bool CreateIfNotExist(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry(
+ (setResult) => this.CreateContainerIfNotExistImpl(fullModifiers, setResult),
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous request to create the container if it does not already exist.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreateIfNotExist(AsyncCallback callback, object state)
+ {
+ return this.BeginCreateIfNotExist(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous request to create the container if it does not already exist.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreateIfNotExist(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(
+ (setResult) => this.CreateContainerIfNotExistImpl(fullModifiers, setResult),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Returns the result of an asynchronous request to create the container if it does not already exist.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// true if the container did not already exist and was created; otherwise, false.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public bool EndCreateIfNotExist(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Deletes the container.
+ ///
+ public void Delete()
+ {
+ this.Delete(null);
+ }
+
+ ///
+ /// Deletes the container.
+ ///
+ /// An object that specifies any additional options for the request.
+ public void Delete(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.DeleteContainerImpl(fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete a container.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDelete(AsyncCallback callback, object state)
+ {
+ return this.BeginDelete(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete a container.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDelete(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.DeleteContainerImpl(fullModifiers), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to delete a container.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndDelete(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Sets permissions for the container.
+ ///
+ /// The permissions to apply to the container.
+ public void SetPermissions(BlobContainerPermissions permissions)
+ {
+ this.SetPermissions(permissions, null);
+ }
+
+ ///
+ /// Sets permissions for the container.
+ ///
+ /// The permissions to apply to the container.
+ /// An object that specifies any additional options for the request.
+ public void SetPermissions(BlobContainerPermissions permissions, BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.SetPermissionsImpl(permissions, fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous request to set permissions for the container.
+ ///
+ /// The permissions to apply to the container.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetPermissions(BlobContainerPermissions permissions, AsyncCallback callback, object state)
+ {
+ return this.BeginSetPermissions(permissions, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous request to set permissions for the container.
+ ///
+ /// The permissions to apply to the container.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetPermissions(BlobContainerPermissions permissions, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(
+ () => this.SetPermissionsImpl(permissions, fullModifiers),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Returns the result of an asynchronous request to set permissions for the container.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndSetPermissions(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Gets the permissions settings for the container.
+ ///
+ /// The container's permissions.
+ public BlobContainerPermissions GetPermissions()
+ {
+ return this.GetPermissions(null);
+ }
+
+ ///
+ /// Gets the permissions settings for the container.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The container's permissions.
+ public BlobContainerPermissions GetPermissions(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry(
+ (setResult) => this.GetPermissionsImpl(fullModifiers, setResult),
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous request to get the permissions settings for the container.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetPermissions(AsyncCallback callback, object state)
+ {
+ return this.BeginGetPermissions(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous request to get the permissions settings for the container.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetPermissions(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(
+ (setResult) => this.GetPermissionsImpl(fullModifiers, setResult),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Returns the asynchronous result of the request to get the permissions settings for the container.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// The container's permissions.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public BlobContainerPermissions EndGetPermissions(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Retrieves the container's attributes.
+ ///
+ public void FetchAttributes()
+ {
+ this.FetchAttributes(null);
+ }
+
+ ///
+ /// Retrieves the container's attributes.
+ ///
+ /// An object that specifies any additional options for the request.
+ public void FetchAttributes(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.FetchAttributesImpl(fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to retrieve the container's attributes.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginFetchAttributes(AsyncCallback callback, object state)
+ {
+ return this.BeginFetchAttributes(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to retrieve the container's attributes.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginFetchAttributes(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.FetchAttributesImpl(fullModifiers), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to retrieve the container's attributes.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndFetchAttributes(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Sets the container's user-defined metadata.
+ ///
+ public void SetMetadata()
+ {
+ this.SetMetadata(null);
+ }
+
+ ///
+ /// Sets the container's user-defined metadata.
+ ///
+ /// An object that specifies any additional options for the request.
+ public void SetMetadata(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+ TaskImplHelper.ExecuteImplWithRetry(() => this.SetMetadataImpl(fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to set user-defined metadata on the container.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetMetadata(AsyncCallback callback, object state)
+ {
+ return this.BeginSetMetadata(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to set user-defined metadata on the container.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetMetadata(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.SetMetadataImpl(fullModifiers), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous request operation to set user-defined metadata on the container.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public void EndSetMetadata(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Returns a shared access signature for the container.
+ ///
+ /// The access policy for the shared access signature.
+ /// A shared access signature.
+ public string GetSharedAccessSignature(SharedAccessPolicy policy)
+ {
+ return this.GetSharedAccessSignature(policy, null);
+ }
+
+ ///
+ /// Returns a shared access signature for the container.
+ ///
+ /// The access policy for the shared access signature.
+ /// A container-level access policy.
+ /// A shared access signature.
+ public string GetSharedAccessSignature(SharedAccessPolicy policy, string groupPolicyIdentifier)
+ {
+ if (!this.ServiceClient.Credentials.CanSignRequest)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.CannotCreateSASWithoutAccountKey);
+ throw new InvalidOperationException(errorMessage);
+ }
+
+ string resourceName = this.GetSharedAccessCanonicalName();
+
+ string signature = SharedAccessSignatureHelper.GetSharedAccessSignatureHashImpl(policy, groupPolicyIdentifier, resourceName, this.ServiceClient);
+
+ // Future resource type changes from "c" => "container"
+ var builder = SharedAccessSignatureHelper.GetShareAccessSignatureImpl(policy, groupPolicyIdentifier, "c", signature);
+
+ return builder.ToString();
+ }
+
+ ///
+ /// Implementation for the ListBlobs method.
+ ///
+ /// The blob prefix.
+ /// An object that specifies any additional options for the request.
+ /// The continuation token.
+ /// The maximum result size.
+ /// The result report delegate.
+ /// A that lists the blobs.
+ internal TaskSequence ListBlobsImpl(
+ string prefix,
+ BlobRequestOptions options,
+ ResultContinuation continuationToken,
+ int? maxResults,
+ Action> setResult)
+ {
+ ResultPagination pagination = new ResultPagination(maxResults.GetValueOrDefault());
+
+ return this.ListBlobsImplCore(prefix, options, continuationToken, pagination, setResult);
+ }
+
+ ///
+ /// Converts the ACL string to a object.
+ ///
+ /// The string to convert.
+ /// The resulting object.
+ private static BlobContainerPermissions GetContainerAcl(string aclstring)
+ {
+ BlobContainerPublicAccessType accessType = BlobContainerPublicAccessType.Off;
+
+ if (!string.IsNullOrEmpty(aclstring))
+ {
+ switch (aclstring.ToLower())
+ {
+ case "container":
+ accessType = BlobContainerPublicAccessType.Container;
+ break;
+ case "blob":
+ accessType = BlobContainerPublicAccessType.Blob;
+ break;
+ default:
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.InvalidAclType, aclstring);
+ throw new InvalidOperationException(errorMessage);
+ }
+ }
+
+ return new BlobContainerPermissions()
+ {
+ PublicAccess = accessType
+ };
+ }
+
+ ///
+ /// Parse Uri for SAS (Shared access signature) information.
+ ///
+ /// The complete Uri.
+ /// The client to use.
+ /// If true, path style Uris are used.
+ ///
+ /// Validate that no other query parameters are passed in.
+ /// Any SAS information will be recorded as corresponding credentials instance.
+ /// If existingClient is passed in, any SAS information found will not be supported.
+ /// Otherwise a new client is created based on SAS information or as anonymous credentials.
+ ///
+ private void ParseQueryAndVerify(Uri completeUri, CloudBlobClient existingClient, bool? usePathStyleUris)
+ {
+ CommonUtils.AssertNotNull("completeUri", completeUri);
+
+ if (!completeUri.IsAbsoluteUri)
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.RelativeAddressNotPermitted, completeUri.ToString());
+ throw new ArgumentException(errorMessage, "address");
+ }
+
+ this.Uri = new Uri(completeUri.GetLeftPart(UriPartial.Path));
+
+ StorageCredentialsSharedAccessSignature sasCreds;
+
+ var queryParameters = HttpUtility.ParseQueryString(completeUri.Query);
+ SharedAccessSignatureHelper.ParseQuery(queryParameters, out sasCreds);
+
+ if (existingClient != null)
+ {
+ if (sasCreds != null && existingClient.Credentials != null && sasCreds != existingClient.Credentials)
+ {
+ string error = string.Format(CultureInfo.CurrentCulture, SR.MultipleCredentialsProvided);
+ throw new ArgumentException(error);
+ }
+ }
+ else
+ {
+ this.ServiceClient = new CloudBlobClient(NavigationHelper.GetServiceClientBaseAddress(this.Uri.AbsoluteUri, usePathStyleUris), sasCreds);
+ }
+ }
+
+ ///
+ /// Returns the canonical name for shared access.
+ ///
+ /// The canonical name.
+ private string GetSharedAccessCanonicalName()
+ {
+ if (this.ServiceClient.UsePathStyleUris)
+ {
+ return this.Uri.AbsolutePath;
+ }
+ else
+ {
+ return NavigationHelper.GetCanonicalPathFromCreds(this.ServiceClient.Credentials, this.Uri.AbsolutePath);
+ }
+ }
+
+ ///
+ /// Gets the absolute Uri of the blob.
+ ///
+ /// Name of the blob.
+ /// The blob's absolute Uri.
+ private string GetBlobAbsoluteUri(string blobName)
+ {
+ var blobUri = NavigationHelper.AppendPathToUri(this.Uri, blobName);
+ return blobUri.AbsoluteUri;
+ }
+
+ ///
+ /// Core implementation of the ListBlobs method.
+ ///
+ /// The blob prefix.
+ /// An object that specifies any additional options for the request.
+ /// The continuation token.
+ /// The pagination.
+ /// The result report delegate.
+ /// A that lists the blobs.
+ private TaskSequence ListBlobsImplCore(
+ string prefix,
+ BlobRequestOptions options,
+ ResultContinuation continuationToken,
+ ResultPagination pagination,
+ Action> setResult)
+ {
+ CommonUtils.AssertContinuationType(continuationToken, ResultContinuation.ContinuationType.Blob);
+ CommonUtils.AssertNotNull("options", options);
+
+ if (!options.UseFlatBlobListing
+ && (options.BlobListingDetails & BlobListingDetails.Snapshots) == BlobListingDetails.Snapshots)
+ {
+ throw new ArgumentException(SR.ListSnapshotsWithDelimiterError, "options");
+ }
+
+ var delimiter = options.UseFlatBlobListing ? null : this.ServiceClient.DefaultDelimiter;
+
+ BlobListingContext listingContext = new BlobListingContext(prefix, pagination.GetNextRequestPageSize(), delimiter, options.BlobListingDetails)
+ {
+ Marker = continuationToken != null ? continuationToken.NextMarker : null
+ };
+
+ var blobList = new List();
+
+ var request = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => BlobRequest.List(this.TransformedAddress, timeout, listingContext));
+ this.ServiceClient.Credentials.SignRequest(request);
+ var listTask = request.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return listTask;
+
+ string nextMarker;
+ using (var response = listTask.Result as HttpWebResponse)
+ {
+ ListBlobsResponse listBlobResponse = BlobResponse.List(response);
+ blobList.AddRange(listBlobResponse.Blobs.Select(
+ (item) => CloudBlobClient.SelectProtocolResponse(item, this.ServiceClient, this)));
+
+ nextMarker = listBlobResponse.NextMarker;
+ }
+
+ ResultContinuation newContinuationToken = new ResultContinuation() { NextMarker = nextMarker, Type = ResultContinuation.ContinuationType.Blob };
+
+ ResultSegment.CreateResultSegment(
+ setResult,
+ blobList,
+ newContinuationToken,
+ pagination,
+ options.RetryPolicy,
+ (paginationArg, continuationArg, resultSegmentArg) =>
+ this.ListBlobsImplCore(prefix, options, continuationArg, paginationArg, resultSegmentArg));
+ }
+
+ ///
+ /// Implementation for the Create method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that creates the container.
+ private TaskSequence CreateContainerImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => ContainerRequest.Create(this.Uri, timeout));
+ ContainerRequest.AddMetadata(webRequest, this.Metadata);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ this.Attributes = ContainerResponse.GetAttributes(webResponse);
+ }
+ }
+
+ ///
+ /// Implementation for the CreateIfNotExist method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The set result.
+ /// A that creates the container if it does not exist.
+ private TaskSequence CreateContainerIfNotExistImpl(BlobRequestOptions options, Action setResult)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var task = new InvokeTaskSequenceTask(() => this.CreateContainerImpl(options));
+ yield return task;
+
+ try
+ {
+ // Materialize exceptions
+ var scratch = task.Result;
+ setResult(true);
+ }
+ catch (StorageClientException e)
+ {
+ if (e.StatusCode == HttpStatusCode.Conflict && e.ExtendedErrorInformation.ErrorCode == StorageErrorCodeStrings.ContainerAlreadyExists)
+ {
+ setResult(false);
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ ///
+ /// Implementation for the Delete method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that deletes the container.
+ private TaskSequence DeleteContainerImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => ContainerRequest.Delete(this.TransformedAddress, timeout));
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Implementation for the FetchAttributes method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that fetches the attributes.
+ private TaskSequence FetchAttributesImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => ContainerRequest.GetProperties(this.TransformedAddress, timeout));
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ this.Attributes = ContainerResponse.GetAttributes(webResponse);
+ }
+ }
+
+ ///
+ /// Implementation for the SetMetadata method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A that sets the metadata.
+ private TaskSequence SetMetadataImpl(BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => ContainerRequest.SetMetadata(this.TransformedAddress, timeout));
+ ContainerRequest.AddMetadata(webRequest, this.Metadata);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Implementation for the SetPermissions method.
+ ///
+ /// The permissions to set.
+ /// An object that specifies any additional options for the request.
+ /// A that sets the permissions.
+ private TaskSequence SetPermissionsImpl(BlobContainerPermissions acl, BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => ContainerRequest.SetAcl(this.TransformedAddress, timeout, acl.PublicAccess));
+
+ using (var memoryStream = new SmallBlockMemoryStream(Constants.DefaultBufferSize))
+ {
+ ContainerRequest.WriteSharedAccessIdentifiers(acl.SharedAccessPolicies, memoryStream);
+
+ memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
+
+ CommonUtils.ApplyRequestOptimizations(webRequest, memoryStream.Length);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ var requestStreamTask = webRequest.GetRequestStreamAsync();
+ yield return requestStreamTask;
+
+ using (var requestStream = requestStreamTask.Result)
+ {
+ // Copy the data
+ var copyTask = new InvokeTaskSequenceTask(() => { return memoryStream.WriteTo(requestStream); });
+ yield return copyTask;
+
+ // Materialize any exceptions
+ var scratch = copyTask.Result;
+ }
+ }
+
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Implementation for the GetPermissions method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The result report delegate.
+ /// A that gets the permissions.
+ private TaskSequence GetPermissionsImpl(BlobRequestOptions options, Action setResult)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => ContainerRequest.GetAcl(this.TransformedAddress, timeout));
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ string publicAccess = ContainerResponse.GetAcl(webResponse);
+ var containerAcl = GetContainerAcl(publicAccess);
+
+ // Materialize results so that we can close the response
+ AccessPolicyResponse policyResponse = new AccessPolicyResponse(webResponse.GetResponseStream());
+ foreach (var item in policyResponse.AccessIdentifiers)
+ {
+ containerAcl.SharedAccessPolicies.Add(item.Key, item.Value);
+ }
+
+ setResult(containerAcl);
+ }
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudBlobDirectory.cs b/microsoft-azure-api/StorageClient/CloudBlobDirectory.cs
new file mode 100644
index 0000000000000..b00a7bd81ba5d
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudBlobDirectory.cs
@@ -0,0 +1,332 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudBlobDirectory class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Represents a virtual directory of blobs, designated by a delimiter character.
+ ///
+ public class CloudBlobDirectory : IListBlobItem
+ {
+ ///
+ /// Stores the parent directory.
+ ///
+ private CloudBlobDirectory parent;
+
+ ///
+ /// Stores the parent container.
+ ///
+ private CloudBlobContainer container;
+
+ ///
+ /// Stores the prefix this directory represents.
+ ///
+ private string prefix;
+
+ ///
+ /// Initializes a new instance of the class given an address and a client.
+ ///
+ /// The blob directory's address.
+ /// The client to use.
+ internal CloudBlobDirectory(string address, CloudBlobClient service)
+ {
+ CommonUtils.AssertNotNullOrEmpty("address", address);
+ CommonUtils.AssertNotNull("service", service);
+
+ this.ServiceClient = service;
+
+ if (!address.EndsWith(this.ServiceClient.DefaultDelimiter))
+ {
+ address = address + this.ServiceClient.DefaultDelimiter;
+ }
+
+ this.Uri = NavigationHelper.AppendPathToUri(service.BaseUri, address);
+ }
+
+ ///
+ /// Gets the service client for the virtual directory.
+ ///
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudBlobClient ServiceClient { get; private set; }
+
+ ///
+ /// Gets the URI that identifies the virtual directory.
+ ///
+ /// The URI to the virtual directory.
+ public Uri Uri { get; private set; }
+
+ ///
+ /// Gets the container for the virtual directory.
+ ///
+ /// The container for the virtual directory.
+ public CloudBlobContainer Container
+ {
+ get
+ {
+ if (this.container == null)
+ {
+ this.container = new CloudBlobContainer(
+ NavigationHelper.GetContainerAddress(this.Uri, this.ServiceClient.UsePathStyleUris),
+ this.ServiceClient.Credentials);
+ }
+
+ return this.container;
+ }
+ }
+
+ ///
+ /// Gets the parent directory for the virtual directory.
+ ///
+ /// The virtual directory's parent directory.
+ public CloudBlobDirectory Parent
+ {
+ get
+ {
+ if (this.parent == null)
+ {
+ this.parent = new CloudBlobDirectory(
+ NavigationHelper.GetParentAddress(
+ this.Uri,
+ this.ServiceClient.DefaultDelimiter,
+ this.ServiceClient.UsePathStyleUris),
+ this.ServiceClient);
+ }
+
+ return this.parent;
+ }
+ }
+
+ ///
+ /// Gets the prefix.
+ ///
+ /// The prefix.
+ internal string Prefix
+ {
+ get
+ {
+ if (this.prefix == null)
+ {
+ this.InitializePrefix();
+ }
+
+ return this.prefix;
+ }
+ }
+
+ ///
+ /// Returns a reference to a blob in this virtual directory.
+ ///
+ /// The name of the blob.
+ /// A reference to a blob.
+ public CloudBlob GetBlobReference(string itemName)
+ {
+ return this.GetBlobReference(itemName, null);
+ }
+
+ ///
+ /// Returns a reference to a blob in this virtual directory.
+ ///
+ /// The name of the blob.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A reference to a blob.
+ public CloudBlob GetBlobReference(string itemName, DateTime? snapshotTime)
+ {
+ CommonUtils.AssertNotNull("itemName", itemName);
+
+ Uri blobUri = NavigationHelper.AppendPathToUri(this.Uri, itemName, this.ServiceClient.DefaultDelimiter);
+ return new CloudBlob(blobUri.AbsoluteUri, snapshotTime, this.ServiceClient);
+ }
+
+ ///
+ /// Returns a reference to a page blob in this virtual directory.
+ ///
+ /// The name of the page blob.
+ /// A reference to a page blob.
+ public CloudPageBlob GetPageBlobReference(string itemName)
+ {
+ return this.GetPageBlobReference(itemName, null);
+ }
+
+ ///
+ /// Returns a reference to a page blob in this virtual directory.
+ ///
+ /// The name of the page blob.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A reference to a page blob.
+ public CloudPageBlob GetPageBlobReference(string itemName, DateTime? snapshotTime)
+ {
+ CommonUtils.AssertNotNull("itemName", itemName);
+
+ Uri blobUri = NavigationHelper.AppendPathToUri(this.Uri, itemName, this.ServiceClient.DefaultDelimiter);
+ return new CloudPageBlob(blobUri.AbsoluteUri, snapshotTime, this.ServiceClient);
+ }
+
+ ///
+ /// Returns a reference to a block blob in this virtual directory.
+ ///
+ /// The name of the block blob.
+ /// A reference to a block blob.
+ public CloudBlockBlob GetBlockBlobReference(string itemName)
+ {
+ return this.GetBlockBlobReference(itemName, null);
+ }
+
+ ///
+ /// Returns a reference to a block blob in this virtual directory.
+ ///
+ /// The name of the block blob.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A reference to a block blob.
+ public CloudBlockBlob GetBlockBlobReference(string itemName, DateTime? snapshotTime)
+ {
+ CommonUtils.AssertNotNull("itemName", itemName);
+
+ Uri blobUri = NavigationHelper.AppendPathToUri(this.Uri, itemName, this.ServiceClient.DefaultDelimiter);
+ return new CloudBlockBlob(blobUri.AbsoluteUri, snapshotTime, this.ServiceClient);
+ }
+
+ ///
+ /// Returns a virtual subdirectory within this virtual directory.
+ ///
+ /// The name of the virtual subdirectory.
+ /// A object representing the virtual subdirectory.
+ public CloudBlobDirectory GetSubdirectory(string itemName)
+ {
+ CommonUtils.AssertNotNull("itemName", itemName);
+
+ Uri subdirectoryUri = NavigationHelper.AppendPathToUri(this.Uri, itemName, this.ServiceClient.DefaultDelimiter);
+ return new CloudBlobDirectory(subdirectoryUri.AbsoluteUri, this.ServiceClient);
+ }
+
+ ///
+ /// Returns an enumerable collection of blob items in this virtual directory that is lazily retrieved, either as a flat listing or by virtual subdirectory.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// An enumerable collection of objects that implement and are retrieved lazily.
+ public IEnumerable ListBlobs(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return CommonUtils.LazyEnumerateSegmented((setResult) => this.Container.ListBlobsImpl(this.Prefix, fullModifiers, null, null, setResult), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Returns an enumerable collection of blob items in this virtual directory, either as a flat listing or by virtual subdirectory.
+ ///
+ /// An enumerable collection of objects that implement .
+ public IEnumerable ListBlobs()
+ {
+ return this.ListBlobs(null);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of blob items.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A result segment containing objects that implement .
+ public ResultSegment ListBlobsSegmented(BlobRequestOptions options)
+ {
+ return this.ListBlobsSegmented(0, null, options);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of blob items.
+ ///
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// An object that specifies any additional options for the request.
+ /// A result segment containing objects that implement .
+ public ResultSegment ListBlobsSegmented(int maxResults, ResultContinuation continuationToken, BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry>((setResult) => this.Container.ListBlobsImpl(this.Prefix, fullModifiers, continuationToken, maxResults, setResult), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous request to return a result segment containing a collection of blob items.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListBlobsSegmented(AsyncCallback callback, object state)
+ {
+ return this.BeginListBlobsSegmented(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous request to return a result segment containing a collection of blob items.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListBlobsSegmented(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ return this.BeginListBlobsSegmented(0, null, options, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous request to return a result segment containing a collection of blob items.
+ ///
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListBlobsSegmented(int maxResults, ResultContinuation continuationToken, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry>((setResult) => this.Container.ListBlobsImpl(this.Prefix, fullModifiers, continuationToken, maxResults, setResult), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous request to return a result segment containing a collection of blob items.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A result segment containing objects that implement .
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public ResultSegment EndListBlobsSegmented(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Initializes the prefix.
+ ///
+ private void InitializePrefix()
+ {
+ // Need to add the trailing slash or MakeRelativeUri will return the containerName again
+ var parentUri = new Uri(this.Container.Uri + NavigationHelper.Slash);
+
+ this.prefix = Uri.UnescapeDataString(parentUri.MakeRelativeUri(this.Uri).OriginalString);
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudBlockBlob.cs b/microsoft-azure-api/StorageClient/CloudBlockBlob.cs
new file mode 100644
index 0000000000000..564e8b44345bc
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudBlockBlob.cs
@@ -0,0 +1,517 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudBlockBlob class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// Represents a blob that is uploaded as a set of blocks.
+ ///
+ public class CloudBlockBlob : CloudBlob
+ {
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob.
+ ///
+ /// The absolute URI to the blob.
+ /// The account credentials.
+ public CloudBlockBlob(string blobAbsoluteUri, StorageCredentials credentials)
+ : base(blobAbsoluteUri, credentials)
+ {
+ this.Properties.BlobType = BlobType.BlockBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class using a relative URI to the blob.
+ ///
+ /// The relative URI to the blob, beginning with the container name.
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudBlockBlob(string blobUri, CloudBlobClient client)
+ : this(blobUri, null, client)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using a relative URI to the blob.
+ ///
+ /// The relative URI to the blob, beginning with the container name.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudBlockBlob(string blobUri, DateTime? snapshotTime, CloudBlobClient client)
+ : base(blobUri, snapshotTime, client)
+ {
+ this.Properties.BlobType = BlobType.BlockBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob.
+ ///
+ /// The absolute URI to the blob.
+ public CloudBlockBlob(string blobAbsoluteUri)
+ : base(blobAbsoluteUri)
+ {
+ this.Properties.BlobType = BlobType.BlockBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob.
+ ///
+ /// The absolute URI to the blob.
+ /// The account credentials.
+ /// True to use path-style URIs; otherwise, false.
+ public CloudBlockBlob(string blobAbsoluteUri, StorageCredentials credentials, bool usePathStyleUris)
+ : base(blobAbsoluteUri, credentials, usePathStyleUris)
+ {
+ this.Properties.BlobType = BlobType.BlockBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob.
+ ///
+ /// The absolute URI to the blob.
+ /// True to use path-style URIs; otherwise, false.
+ public CloudBlockBlob(string blobAbsoluteUri, bool usePathStyleUris)
+ : base(blobAbsoluteUri, usePathStyleUris)
+ {
+ this.Properties.BlobType = BlobType.BlockBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class based on an existing blob.
+ ///
+ /// The blob to clone.
+ internal CloudBlockBlob(CloudBlob cloudBlob) : base(cloudBlob)
+ {
+ this.Properties.BlobType = BlobType.BlockBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The attributes.
+ /// The service client.
+ /// The snapshot time.
+ internal CloudBlockBlob(BlobAttributes attributes, CloudBlobClient serviceClient, string snapshotTime)
+ : base(attributes, serviceClient, snapshotTime)
+ {
+ this.Properties.BlobType = BlobType.BlockBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified blob Uri.
+ /// Note that this is just a reference to a blob instance and no requests are issued to the service
+ /// yet to update the blob properties, attribute or metaddata. FetchAttributes is the API that
+ /// issues such request to the service.
+ ///
+ /// Relative Uri to the blob.
+ /// Existing Blob service client which provides the base address.
+ /// The reference to the parent container.
+ internal CloudBlockBlob(string blobUri, CloudBlobClient client, CloudBlobContainer containerReference)
+ : this(blobUri, null, client, containerReference)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified blob Uri.
+ /// If snapshotTime is not null, the blob instance represents a Snapshot.
+ /// Note that this is just a reference to a blob instance and no requests are issued to the service
+ /// yet to update the blob properties, attribute or metaddata. FetchAttributes is the API that
+ /// issues such request to the service.
+ ///
+ /// Relative Uri to the blob.
+ /// Snapshot time in case the blob is a snapshot.
+ /// Existing Blob service client which provides the base address.
+ /// The reference to the parent container.
+ internal CloudBlockBlob(string blobUri, DateTime? snapshotTime, CloudBlobClient client, CloudBlobContainer containerReference)
+ : base(blobUri, snapshotTime, client, containerReference)
+ {
+ this.Properties.BlobType = BlobType.BlockBlob;
+ }
+
+ ///
+ /// Returns an enumerable collection of the committed blocks comprising the blob.
+ ///
+ /// An enumerable collection of objects implementing .
+ public IEnumerable DownloadBlockList()
+ {
+ return this.DownloadBlockList(null);
+ }
+
+ ///
+ /// Returns an enumerable collection of the committed blocks comprising the blob.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// An enumerable collection of objects implementing .
+ public IEnumerable DownloadBlockList(BlobRequestOptions options)
+ {
+ return this.DownloadBlockList(BlockListingFilter.Committed, options);
+ }
+
+ ///
+ /// Returns an enumerable collection of the blob's blocks, using the specified block list filter.
+ ///
+ /// One of the enumeration values that indicates whether to return
+ /// committed blocks, uncommitted blocks, or both.
+ /// An enumerable collection of objects implementing .
+ public IEnumerable DownloadBlockList(BlockListingFilter blockListingFilter)
+ {
+ return this.DownloadBlockList(blockListingFilter, null);
+ }
+
+ ///
+ /// Returns an enumerable collection of the blob's blocks, using the specified block list filter.
+ ///
+ /// One of the enumeration values that indicates whether to return
+ /// committed blocks, uncommitted blocks, or both.
+ /// An object that specifies any additional options for the request.
+ /// An enumerable collection of objects implementing .
+ public IEnumerable DownloadBlockList(BlockListingFilter blockListingFilter, BlobRequestOptions options)
+ {
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (result) =>
+ {
+ return this.GetDownloadBlockList(blockListingFilter, fullModifier, result);
+ },
+ fullModifier.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return an enumerable collection of the blob's blocks,
+ /// using the specified block list filter.
+ ///
+ /// One of the enumeration values that indicates whether to return
+ /// committed blocks, uncommitted blocks, or both.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDownloadBlockList(BlockListingFilter blockListingFilter, AsyncCallback callback, object state)
+ {
+ return this.BeginDownloadBlockList(blockListingFilter, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return an enumerable collection of the blob's blocks,
+ /// using the specified block list filter.
+ ///
+ /// One of the enumeration values that indicates whether to return
+ /// committed blocks, uncommitted blocks, or both.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDownloadBlockList(BlockListingFilter blockListingFilter, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry>(
+ (result) =>
+ {
+ return this.GetDownloadBlockList(blockListingFilter, fullModifier, result);
+ },
+ fullModifier.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to return an enumerable collection of the blob's blocks,
+ /// using the specified block list filter.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// An enumerable collection of objects implementing .
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public IEnumerable EndDownloadBlockList(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Uploads a single block.
+ ///
+ /// A base64-encoded block ID that identifies the block.
+ /// A stream that provides the data for the block.
+ /// An optional hash value that will be used to set the property
+ /// on the blob. May be null or an empty string.
+ public void PutBlock(string blockId, Stream blockData, string contentMD5)
+ {
+ this.PutBlock(blockId, blockData, contentMD5, null);
+ }
+
+ ///
+ /// Uploads a single block.
+ ///
+ /// A base64-encoded block ID that identifies the block.
+ /// A stream that provides the data for the block.
+ /// An optional hash value that will be used to set the property
+ /// on the blob. May be null or an empty string.
+ /// An object that specifies any additional options for the request.
+ public void PutBlock(string blockId, Stream blockData, string contentMD5, BlobRequestOptions options)
+ {
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+ var position = blockData.Position;
+ var retryPolicy = blockData.CanSeek ? fullModifier.RetryPolicy : RetryPolicies.NoRetry();
+
+ TaskImplHelper.ExecuteImplWithRetry(
+ () =>
+ {
+ if (blockData.CanSeek)
+ {
+ blockData.Position = position;
+ }
+
+ return this.UploadBlock(blockData, blockId, contentMD5, fullModifier);
+ },
+ retryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to upload a single block.
+ ///
+ /// A base64-encoded block ID that identifies the block.
+ /// A stream that provides the data for the block.
+ /// An optional hash value that will be used to set the property
+ /// on the blob. May be null or an empty string.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginPutBlock(string blockId, Stream blockData, string contentMD5, AsyncCallback callback, object state)
+ {
+ return this.BeginPutBlock(blockId, blockData, contentMD5, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to upload a single block.
+ ///
+ /// A base64-encoded block ID that identifies the block.
+ /// A stream that provides the data for the block.
+ /// An optional hash value that will be used to set the property
+ /// on the blob. May be null or an empty string.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginPutBlock(string blockId, Stream blockData, string contentMD5, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+ var position = blockData.Position;
+ var retryPolicy = blockData.CanSeek ? fullModifier.RetryPolicy : RetryPolicies.NoRetry();
+
+ return TaskImplHelper.BeginImplWithRetry(
+ () =>
+ {
+ if (blockData.CanSeek)
+ {
+ blockData.Position = position;
+ }
+
+ return this.UploadBlock(blockData, blockId, contentMD5, fullModifier);
+ },
+ retryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to upload a single block.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public void EndPutBlock(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Uploads a list of blocks to a new or existing blob.
+ ///
+ /// An enumerable collection of block IDs, as base64-encoded strings.
+ public void PutBlockList(IEnumerable blockList)
+ {
+ this.PutBlockList(blockList, null);
+ }
+
+ ///
+ /// Uploads a list of blocks to a new or existing blob.
+ ///
+ /// An enumerable collection of block IDs, as base64-encoded strings.
+ /// An object that specifies any additional options for the request.
+ public void PutBlockList(IEnumerable blockList, BlobRequestOptions options)
+ {
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ List items = blockList.Select(i => { return new PutBlockListItem(i, BlockSearchMode.Latest); }).ToList();
+
+ TaskImplHelper.ExecuteImplWithRetry(() => { return this.UploadBlockList(items, fullModifier); }, fullModifier.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to upload a list of blocks to a new or existing blob.
+ ///
+ /// An enumerable collection of block IDs, as base64-encoded strings.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginPutBlockList(IEnumerable blockList, AsyncCallback callback, object state)
+ {
+ return this.BeginPutBlockList(blockList, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to upload a list of blocks to a new or existing blob.
+ ///
+ /// An enumerable collection of block IDs, as base64-encoded strings.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginPutBlockList(IEnumerable blockList, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifier = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ List items = blockList.Select(i => { return new PutBlockListItem(i, BlockSearchMode.Latest); }).ToList();
+
+ return TaskImplHelper.BeginImplWithRetry(() => { return this.UploadBlockList(items, fullModifier); }, fullModifier.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to upload a list of blocks to a new or existing blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public void EndPutBlockList(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Uploads the block list.
+ ///
+ /// The blocks to upload.
+ /// An object that specifies any additional options for the request.
+ /// A that uploads the block list.
+ internal TaskSequence UploadBlockList(List blocks, BlobRequestOptions options)
+ {
+ if (options == null)
+ {
+ throw new ArgumentNullException("modifers");
+ }
+
+ var request = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => BlobRequest.PutBlockList(this.TransformedAddress, timeout, this.Properties, null));
+
+ options.AccessCondition.ApplyCondition(request);
+ BlobRequest.AddMetadata(request, this.Metadata);
+
+ using (var memoryStream = new SmallBlockMemoryStream(Constants.DefaultBufferSize))
+ {
+ BlobRequest.WriteBlockListBody(blocks, memoryStream);
+
+ CommonUtils.ApplyRequestOptimizations(request, memoryStream.Length);
+
+ memoryStream.Seek(0, SeekOrigin.Begin);
+
+ // Compute the MD5
+ var md5 = System.Security.Cryptography.MD5.Create();
+
+ request.Headers[HttpRequestHeader.ContentMd5] = Convert.ToBase64String(md5.ComputeHash(memoryStream));
+
+ this.ServiceClient.Credentials.SignRequest(request);
+
+ memoryStream.Seek(0, SeekOrigin.Begin);
+
+ // Retrieve the stream
+ var requestStreamTask = request.GetRequestStreamAsync();
+ yield return requestStreamTask;
+
+ using (Stream requestStream = requestStreamTask.Result)
+ {
+ // Copy the data
+ var copyTask = new InvokeTaskSequenceTask(() => { return memoryStream.WriteTo(requestStream); });
+ yield return copyTask;
+
+ // Materialize any exceptions
+ var scratch = copyTask.Result;
+ }
+ }
+
+ // Get the response
+ var responseTask = request.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return responseTask;
+
+ using (var response = responseTask.Result as HttpWebResponse)
+ {
+ ParseSizeAndLastModified(response);
+ this.Properties.Length = 0;
+ }
+ }
+
+ ///
+ /// Gets the download block list.
+ ///
+ /// The types of blocks.
+ /// An object that specifies any additional options for the request.
+ /// The result report delegate.
+ /// A that gets the download block list.
+ internal TaskSequence GetDownloadBlockList(BlockListingFilter typesOfBlocks, BlobRequestOptions options, Action> setResult)
+ {
+ var request = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => BlobRequest.GetBlockList(this.TransformedAddress, timeout, this.SnapshotTime, typesOfBlocks, null));
+ this.ServiceClient.Credentials.SignRequest(request);
+
+ // Retrieve the stream
+ var requestStreamTask = request.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return requestStreamTask;
+
+ // Copy the data
+ using (var response = requestStreamTask.Result as HttpWebResponse)
+ {
+ using (var responseStream = response.GetResponseStream())
+ {
+ var blockListResponse = new GetBlockListResponse(responseStream);
+
+ setResult(ParseResponse(blockListResponse));
+ }
+
+ this.ParseSizeAndLastModified(response);
+ }
+ }
+
+ ///
+ /// Parses the response.
+ ///
+ /// The block list response.
+ /// An enumerable list of objects.
+ private static IEnumerable ParseResponse(GetBlockListResponse blockListResponse)
+ {
+ List result = new List();
+
+ result.AddRange(blockListResponse.Blocks);
+
+ return result;
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudPageBlob.cs b/microsoft-azure-api/StorageClient/CloudPageBlob.cs
new file mode 100644
index 0000000000000..3763d070a1100
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudPageBlob.cs
@@ -0,0 +1,788 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudPageBlob class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// Represents a blob made up of a collection of pages.
+ ///
+ public class CloudPageBlob : CloudBlob
+ {
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob.
+ ///
+ /// The absolute URI to the blob.
+ /// The account credentials.
+ public CloudPageBlob(string blobAddress, StorageCredentials credentials)
+ : base(blobAddress, credentials)
+ {
+ this.Properties.BlobType = BlobType.PageBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class using an absolute URI to the blob.
+ ///
+ /// The absolute URI to the blob.
+ public CloudPageBlob(string blobAddress)
+ : base(blobAddress)
+ {
+ this.Properties.BlobType = BlobType.PageBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class using a relative URI to the blob.
+ ///
+ /// The relative URI to the blob, beginning with the container name.
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudPageBlob(string blobAddress, CloudBlobClient serviceClient)
+ : this(blobAddress, null, serviceClient)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using a relative URI to the blob.
+ ///
+ /// The relative URI to the blob, beginning with the container name.
+ /// The snapshot timestamp, if the blob is a snapshot.
+ /// A client object that specifies the endpoint for the Blob service.
+ public CloudPageBlob(string blobAddress, DateTime? snapshotTime, CloudBlobClient serviceClient)
+ : base(blobAddress, snapshotTime, serviceClient)
+ {
+ this.Properties.BlobType = BlobType.PageBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class based on an existing object.
+ ///
+ /// An object of type .
+ public CloudPageBlob(CloudBlob cloudBlob)
+ : base(cloudBlob)
+ {
+ this.Properties.BlobType = BlobType.PageBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The attributes.
+ /// The service client.
+ /// The snapshot time.
+ internal CloudPageBlob(BlobAttributes attributes, CloudBlobClient serviceClient, string snapshotTime)
+ : base(attributes, serviceClient, snapshotTime)
+ {
+ this.Properties.BlobType = BlobType.PageBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The blob address.
+ /// The credentials.
+ /// If set to true, use path style Uris.
+ internal CloudPageBlob(string blobAddress, StorageCredentials credentials, bool usePathStyleUris)
+ : base(blobAddress, credentials, usePathStyleUris)
+ {
+ this.Properties.BlobType = BlobType.PageBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The blob address.
+ /// If set to true, use path style Uris.
+ internal CloudPageBlob(string blobAddress, bool usePathStyleUris)
+ : base(blobAddress, usePathStyleUris)
+ {
+ this.Properties.BlobType = BlobType.PageBlob;
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified blob Uri.
+ /// Note that this is just a reference to a blob instance and no requests are issued to the service
+ /// yet to update the blob properties, attribute or metaddata. FetchAttributes is the API that
+ /// issues such request to the service.
+ ///
+ /// The blob address.
+ /// The service client.
+ /// The reference to the parent container.
+ internal CloudPageBlob(string blobAddress, CloudBlobClient serviceClient, CloudBlobContainer containerReference)
+ : this(blobAddress, null, serviceClient, containerReference)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified blob Uri.
+ /// If snapshotTime is not null, the blob instance represents a Snapshot.
+ /// Note that this is just a reference to a blob instance and no requests are issued to the service
+ /// yet to update the blob properties, attribute or metaddata. FetchAttributes is the API that
+ /// issues such request to the service.
+ ///
+ /// The blob address.
+ /// Snapshot time in case the blob is a snapshot.
+ /// The service client.
+ /// The reference to the parent container.
+ internal CloudPageBlob(string blobAddress, DateTime? snapshotTime, CloudBlobClient serviceClient, CloudBlobContainer containerReference)
+ : base(blobAddress, snapshotTime, serviceClient, containerReference)
+ {
+ this.Properties.BlobType = BlobType.PageBlob;
+ }
+
+ ///
+ /// Creates a page blob.
+ ///
+ /// The maximum size of the page blob, in bytes.
+ public void Create(long size)
+ {
+ this.Create(size, null);
+ }
+
+ ///
+ /// Creates a page blob.
+ ///
+ /// The maximum size of the page blob, in bytes.
+ /// An object that specifies any additional options for the request.
+ public void Create(long size, BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.CreateImpl(fullModifiers, size), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a page blob.
+ ///
+ /// The maximum size of the page blob, in bytes.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreate(long size, AsyncCallback callback, object state)
+ {
+ return this.BeginCreate(size, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a page blob.
+ ///
+ /// The maximum size of the blob, in bytes.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreate(long size, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.CreateImpl(fullModifiers, size), fullModifiers.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to create a page blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndCreate(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Writes pages to a page blob.
+ ///
+ /// A stream providing the page data.
+ /// The offset at which to begin writing, in bytes. The offset must be a multiple of 512.
+ public void WritePages(Stream pageData, long startOffset)
+ {
+ this.WritePages(pageData, startOffset, null);
+ }
+
+ ///
+ /// Writes pages to a page blob.
+ ///
+ /// A stream providing the page data.
+ /// The offset at which to begin writing, in bytes. The offset must be a multiple of 512.
+ /// An object that specifies any additional options for the request.
+ public void WritePages(Stream pageData, long startOffset, BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ long sourcePosition = pageData.CanSeek ? pageData.Position : 0;
+
+ TaskImplHelper.ExecuteImplWithRetry(
+ () =>
+ {
+ if (pageData.CanSeek == false)
+ {
+ sourcePosition--;
+ }
+
+ return this.WritePageImpl(pageData, startOffset, sourcePosition, fullModifiers);
+ },
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to write pages to a page blob.
+ ///
+ /// A stream providing the page data.
+ /// The offset at which to begin writing, in bytes. The offset must be a multiple of 512.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginWritePages(Stream pageData, long startOffset, AsyncCallback callback, object state)
+ {
+ return this.BeginWritePages(pageData, startOffset, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to write pages to a page blob.
+ ///
+ /// A stream providing the page data.
+ /// The offset at which to begin writing, in bytes. The offset must be a multiple of 512.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginWritePages(Stream pageData, long startOffset, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ long sourcePosition = pageData.CanSeek ? pageData.Position : 0;
+
+ return TaskImplHelper.BeginImplWithRetry(
+ () =>
+ {
+ if (pageData.CanSeek == false)
+ {
+ sourcePosition--;
+ }
+
+ return this.WritePageImpl(pageData, startOffset, sourcePosition, fullModifiers);
+ },
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to write pages to a page blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public void EndWritePages(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Clears pages from a page blob.
+ ///
+ /// The offset at which to begin clearing pages, in bytes. The offset must be a multiple of 512.
+ /// The length of the data range to be cleared, in bytes. The length must be a multiple of 512.
+ public void ClearPages(long startOffset, long length)
+ {
+ this.ClearPages(startOffset, length, null);
+ }
+
+ ///
+ /// Clears pages from a page blob.
+ ///
+ /// The offset at which to begin clearing pages, in bytes. The offset must be a multiple of 512.
+ /// The length of the data range to be cleared, in bytes. The length must be a multiple of 512.
+ /// An object that specifies any additional options for the request.
+ public void ClearPages(long startOffset, long length, BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ TaskImplHelper.ExecuteImplWithRetry(() => this.ClearPageImpl(startOffset, length, fullModifiers), fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to clear pages from a page blob.
+ ///
+ /// The offset at which to begin clearing pages, in bytes. The offset must be a multiple of 512.
+ /// The length of the data range to be cleared, in bytes. The length must be a multiple of 512.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginClearPages(long startOffset, long length, AsyncCallback callback, object state)
+ {
+ return this.BeginClearPages(startOffset, length, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to clear pages from a page blob.
+ ///
+ /// The offset at which to begin clearing pages, in bytes. The offset must be a multiple of 512.
+ /// The length of the data range to be cleared, in bytes. The length must be a multiple of 512.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginClearPages(long startOffset, long length, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry(
+ () => this.ClearPageImpl(startOffset, length, fullModifiers),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to clear pages from a page blob.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public void EndClearPages(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Gets a collection of page ranges and their starting and ending bytes.
+ ///
+ /// An enumerable collection of page ranges.
+ public IEnumerable GetPageRanges()
+ {
+ return this.GetPageRanges(null);
+ }
+
+ ///
+ /// Gets a collection of page ranges and their starting and ending bytes.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// An enumerable collection of page ranges.
+ public IEnumerable GetPageRanges(BlobRequestOptions options)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.GetPageRangesImpl(fullModifiers, setResult),
+ fullModifiers.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a collection of page ranges and their starting and ending bytes.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetPageRanges(AsyncCallback callback, object state)
+ {
+ return this.BeginGetPageRanges(null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a collection of page ranges and their starting and ending bytes.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetPageRanges(BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ var fullModifiers = BlobRequestOptions.CreateFullModifier(this.ServiceClient, options);
+
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.GetPageRangesImpl(fullModifiers, setResult),
+ fullModifiers.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to return a collection of page ranges and their starting and ending bytes.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// An enumerable collection of page ranges.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public IEnumerable EndGetPageRanges(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl>(asyncResult);
+ }
+
+ ///
+ /// Opens a stream for writing to the blob.
+ ///
+ /// A stream for writing to the blob.
+ /// This operation is not supported on objects of type .
+ public override BlobStream OpenWrite()
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Opens a stream for writing to the blob.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// A stream for writing to the blob.
+ /// This operation is not supported on objects of type .
+ public override BlobStream OpenWrite(BlobRequestOptions options)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Uploads a blob from a stream.
+ ///
+ /// A stream that provides the blob content.
+ /// This operation is not supported on objects of type .
+ public override void UploadFromStream(Stream source)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Uploads a blob from a stream.
+ ///
+ /// A stream that provides the blob content.
+ /// An object that specifies any additional options for the request.
+ /// This operation is not supported on objects of type .
+ public override void UploadFromStream(Stream source, BlobRequestOptions options)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Begins an asynchronous operation to upload a blob from a stream.
+ ///
+ /// The data stream to upload.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ /// This operation is not supported on objects of type .
+ public override IAsyncResult BeginUploadFromStream(Stream source, AsyncCallback callback, object state)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Begins an asynchronous operation to upload a blob from a stream.
+ ///
+ /// The data stream to upload.
+ /// An object that specifies any additional options for the request.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ /// This operation is not supported on objects of type .
+ public override IAsyncResult BeginUploadFromStream(Stream source, BlobRequestOptions options, AsyncCallback callback, object state)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Ends an asynchronous operation to upload a blob from a stream.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// This operation is not supported on objects of type .
+ public override void EndUploadFromStream(IAsyncResult asyncResult)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Uploads a string of text to a blob.
+ ///
+ /// The text to upload, encoded as a UTF-8 string.
+ /// This operation is not supported on objects of type .
+ public override void UploadText(string content)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Uploads a string of text to a blob.
+ ///
+ /// The text to upload.
+ /// An object indicating the text encoding to use.
+ /// An object that specifies any additional options for the request.
+ /// This operation is not supported on objects of type .
+ public override void UploadText(string content, Encoding encoding, BlobRequestOptions options)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Uploads a file from the file system to a blob.
+ ///
+ /// The path and file name of the file to upload.
+ /// This operation is not supported on objects of type .
+ public override void UploadFile(string fileName)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Uploads a file from the file system to a blob.
+ ///
+ /// The path and file name of the file to upload.
+ /// An object that specifies any additional options for the request.
+ /// This operation is not supported on objects of type .
+ public override void UploadFile(string fileName, BlobRequestOptions options)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Uploads an array of bytes to a blob.
+ ///
+ /// The array of bytes to upload.
+ /// This operation is not supported on objects of type .
+ public override void UploadByteArray(byte[] content)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Uploads an array of bytes to a blob.
+ ///
+ /// The array of bytes to upload.
+ /// An object that specifies any additional options for the request.
+ /// This operation is not supported on objects of type .
+ public override void UploadByteArray(byte[] content, BlobRequestOptions options)
+ {
+ throw ThisCreationMethodNotSupportedException();
+ }
+
+ ///
+ /// Creates an exception reporting that the creation method is not supported.
+ ///
+ /// The created exception.
+ private static NotSupportedException ThisCreationMethodNotSupportedException()
+ {
+ string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.NotSupportedForPageBlob);
+ return new NotSupportedException(errorMessage);
+ }
+
+ ///
+ /// Implements the Create method.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The size in bytes.
+ /// A that creates the blob.
+ private TaskSequence CreateImpl(BlobRequestOptions options, long sizeInBytes)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ this.Properties.BlobType = BlobType.PageBlob;
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.Put(this.TransformedAddress, timeout, this.Properties, BlobType.PageBlob, null, sizeInBytes));
+
+ BlobRequest.AddMetadata(webRequest, this.Metadata);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+
+ yield return task;
+
+ // Parse the response
+ using (HttpWebResponse webResponse = task.Result as HttpWebResponse)
+ {
+ this.ParseSizeAndLastModified(webResponse);
+ }
+ }
+
+ ///
+ /// Gets the page ranges impl.
+ ///
+ /// An object that specifies any additional options for the request.
+ /// The set result.
+ /// A for getting the page ranges.
+ private TaskSequence GetPageRangesImpl(BlobRequestOptions options, Action> setResult)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ var webRequest = ProtocolHelper.GetWebRequest(this.ServiceClient, options, (timeout) => BlobRequest.GetPageRanges(this.TransformedAddress, timeout, this.SnapshotTime, null));
+ BlobRequest.AddMetadata(webRequest, this.Metadata);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ var getPageRangesResponse = BlobResponse.GetPageRanges(webResponse);
+ List pageRanges = new List();
+
+ // materialize response as we need to close the webResponse
+ pageRanges.AddRange(getPageRangesResponse.PageRanges.ToList());
+
+ setResult(pageRanges);
+
+ this.ParseSizeAndLastModified(webResponse);
+ }
+ }
+
+ ///
+ /// Implementation method for the WritePage methods.
+ ///
+ /// The page data.
+ /// The start offset.
+ /// The beginning position of the source stream prior to execution, negative if stream is unseekable.
+ /// An object that specifies any additional options for the request.
+ /// A that writes the pages.
+ private TaskSequence WritePageImpl(Stream pageData, long startOffset, long sourceStreamPosition, BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ long length = pageData.Length;
+
+ // Logic to rewind stream on a retry
+ // For non seekable streams we need a way to detect the retry iteration so as to only attempt to execute once.
+ // The first attempt will have SourceStreamPosition = -1, which means the first iteration on a non seekable stream.
+ // The second attempt will have SourceStreamPosition = -2, anything below -1 is considered an abort. Since the Impl method
+ // does not have an execution context to be aware of what iteration is used the SourceStreamPosition is utilized as counter to
+ // differentiate between the first attempt and a retry.
+ if (sourceStreamPosition >= 0 && pageData.CanSeek)
+ {
+ if (sourceStreamPosition != pageData.Position)
+ {
+ pageData.Seek(sourceStreamPosition, 0);
+ }
+ }
+ else if (sourceStreamPosition < -1)
+ {
+ throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, SR.CannotRetryNonSeekableStreamError));
+ }
+
+ if (startOffset % Protocol.Constants.PageSize != 0)
+ {
+ CommonUtils.ArgumentOutOfRange("startOffset", startOffset);
+ }
+
+ long rangeStreamOffset = pageData.CanSeek ? pageData.Position : 0;
+
+ PutPageProperties properties = new PutPageProperties()
+ {
+ Range = new PageRange(startOffset, startOffset + length - rangeStreamOffset - 1),
+ PageWrite = PageWrite.Update,
+ };
+
+ if ((1 + properties.Range.EndOffset - properties.Range.StartOffset) % Protocol.Constants.PageSize != 0 ||
+ (1 + properties.Range.EndOffset - properties.Range.StartOffset) == 0)
+ {
+ CommonUtils.ArgumentOutOfRange("pageData", pageData);
+ }
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.PutPage(this.TransformedAddress, timeout, properties, null));
+
+ ////BlobRequest.AddMetadata(webRequest, this.Metadata);
+
+ // Retrieve the stream
+ var requestStreamTask = webRequest.GetRequestStreamAsync();
+ yield return requestStreamTask;
+
+ // Copy the data
+ using (var outputStream = requestStreamTask.Result)
+ {
+ var copyTask = new InvokeTaskSequenceTask(() => { return pageData.WriteTo(outputStream); });
+ yield return copyTask;
+
+ // Materialize any exceptions
+ var scratch = copyTask.Result;
+ }
+
+ // signing request needs Size to be set
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ // Get the response
+ var responseTask = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return responseTask;
+
+ // Parse the response
+ using (HttpWebResponse webResponse = responseTask.Result as HttpWebResponse)
+ {
+ this.ParseSizeAndLastModified(webResponse);
+ }
+ }
+
+ ///
+ /// Implementation method for the ClearPage methods.
+ ///
+ /// The start offset. Must be multiples of 512.
+ /// Length of the data range to be cleared. Must be multiples of 512.
+ /// An object that specifies any additional options for the request.
+ /// A that writes the pages.
+ private TaskSequence ClearPageImpl(long startOffset, long length, BlobRequestOptions options)
+ {
+ CommonUtils.AssertNotNull("options", options);
+
+ if (startOffset < 0 || startOffset % Protocol.Constants.PageSize != 0)
+ {
+ CommonUtils.ArgumentOutOfRange("startOffset", startOffset);
+ }
+
+ if (length <= 0 || length % Protocol.Constants.PageSize != 0)
+ {
+ CommonUtils.ArgumentOutOfRange("length", length);
+ }
+
+ PutPageProperties properties = new PutPageProperties()
+ {
+ Range = new PageRange(startOffset, startOffset + length - 1),
+ PageWrite = PageWrite.Clear,
+ };
+
+ var webRequest = ProtocolHelper.GetWebRequest(
+ this.ServiceClient,
+ options,
+ (timeout) => BlobRequest.PutPage(this.TransformedAddress, timeout, properties, null));
+
+ webRequest.ContentLength = 0;
+
+ // signing request needs Size to be set
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ // Get the response
+ var responseTask = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, options.Timeout);
+ yield return responseTask;
+
+ // Parse the response
+ using (HttpWebResponse webResponse = responseTask.Result as HttpWebResponse)
+ {
+ this.ParseSizeAndLastModified(webResponse);
+ }
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudQueue.cs b/microsoft-azure-api/StorageClient/CloudQueue.cs
new file mode 100644
index 0000000000000..70a55e846699e
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudQueue.cs
@@ -0,0 +1,1411 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudQueue class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// Represents a Windows Azure queue.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Naming",
+ "CA1711:IdentifiersShouldNotHaveIncorrectSuffix",
+ Justification = "This is a queue")]
+ public class CloudQueue
+ {
+ ///
+ /// Stores the queue attributes.
+ ///
+ private readonly QueueAttributes attributes;
+
+ ///
+ /// Uri for the messages.
+ ///
+ private Uri messageRequestAddress;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The absolute URI to the queue.
+ /// The account credentials.
+ public CloudQueue(string address, StorageCredentials credentials)
+ : this(null, address, credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The relative address.
+ /// The storage account credentials.
+ /// If set to true, use path style Uris.
+ internal CloudQueue(string address, StorageCredentials credentials, bool usePathStyleUris)
+ : this(usePathStyleUris, address, credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// True to use path style Uris.
+ /// The address.
+ /// The credentials.
+ internal CloudQueue(bool? usePathStyleUris, string address, StorageCredentials credentials)
+ {
+ CommonUtils.AssertNotNullOrEmpty("address", address);
+ CommonUtils.AssertNotNull("credentials", credentials);
+
+ if (!credentials.CanSignRequest)
+ {
+ throw new ArgumentException(SR.CredentialsCantSignRequest, "credentials");
+ }
+
+ this.EncodeMessage = true;
+ this.attributes = new QueueAttributes() { Uri = new Uri(address) };
+
+ string baseAddress = NavigationHelper.GetServiceClientBaseAddress(this.Uri, usePathStyleUris);
+
+ this.ServiceClient = new CloudQueueClient(baseAddress, credentials);
+ this.Name = NavigationHelper.GetQueueNameFromUri(this.Uri, this.ServiceClient.UsePathStyleUris);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The queue address.
+ /// The client.
+ internal CloudQueue(Uri queueAddress, CloudQueueClient client)
+ {
+ CommonUtils.AssertNotNull("queueAddress", queueAddress);
+ CommonUtils.AssertNotNull("client", client);
+
+ this.EncodeMessage = true;
+ this.attributes = new QueueAttributes() { Uri = queueAddress };
+ this.ServiceClient = client;
+
+ this.Name = NavigationHelper.GetQueueNameFromUri(this.Uri, this.ServiceClient.UsePathStyleUris);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The attributes of the queue.
+ /// The client.
+ internal CloudQueue(QueueAttributes attributes, CloudQueueClient client)
+ {
+ CommonUtils.AssertNotNull("attributes", attributes);
+ CommonUtils.AssertNotNull("client", client);
+
+ this.EncodeMessage = true;
+ this.attributes = attributes;
+ this.ServiceClient = client;
+
+ this.Name = NavigationHelper.GetQueueNameFromUri(this.Uri, this.ServiceClient.UsePathStyleUris);
+ }
+
+ ///
+ /// Gets the object that represents the Queue service.
+ ///
+ /// A client object that specifies the Queue service endpoint.
+ public CloudQueueClient ServiceClient { get; private set; }
+
+ ///
+ /// Gets the queue name.
+ ///
+ /// The queue name.
+ public string Name { get; private set; }
+
+ ///
+ /// Gets the URI that identifies the queue.
+ ///
+ /// The address of the queue.
+ public Uri Uri
+ {
+ get
+ {
+ return this.attributes.Uri;
+ }
+ }
+
+ ///
+ /// Gets the queue's attributes.
+ ///
+ /// The queue's attributes.
+ public QueueAttributes Attributes
+ {
+ get
+ {
+ return this.attributes;
+ }
+ }
+
+ ///
+ /// Gets the queue's user-defined metadata.
+ ///
+ /// The queue's user-defined metadata.
+ public NameValueCollection Metadata
+ {
+ get
+ {
+ return this.attributes.Metadata;
+ }
+
+ internal set
+ {
+ this.attributes.Metadata = value;
+ }
+ }
+
+ ///
+ /// Gets the approximate message count for the queue.
+ ///
+ /// The approximate message count.
+ public int? ApproximateMessageCount { get; private set; }
+
+ ///
+ /// Gets or sets a value indicating whether to apply base64 encoding when adding or retrieving messages.
+ ///
+ /// True to encode messages; otherwise, false. The default value is true.
+ public bool EncodeMessage { get; set; }
+
+ ///
+ /// Gets the Uri for general message operations.
+ ///
+ internal Uri MessageRequestAddress
+ {
+ get
+ {
+ if (this.messageRequestAddress == null)
+ {
+ this.messageRequestAddress = NavigationHelper.AppendPathToUri(this.Uri, Constants.Messages);
+ }
+
+ return this.messageRequestAddress;
+ }
+ }
+
+ ///
+ /// Creates a queue.
+ ///
+ public void Create()
+ {
+ TaskImplHelper.ExecuteImplWithRetry(this.CreateImpl, this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a queue.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreate(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(this.CreateImpl, this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to create a queue.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndCreate(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Creates the queue if it does not exist.
+ ///
+ /// true if the queue did not exist and was created; otherwise false.
+ public bool CreateIfNotExist()
+ {
+ return TaskImplHelper.ExecuteImplWithRetry(
+ (setResult) => this.CreateIfNotExistImpl(setResult),
+ this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create the queue if it does not exist.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreateIfNotExist(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(
+ (setResult) => this.CreateIfNotExistImpl(setResult),
+ this.ServiceClient.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to create the queue if it does not exist.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// Returns true if the creation succeeded; otherwise, false.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public bool EndCreateIfNotExist(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Deletes the queue.
+ ///
+ public void Delete()
+ {
+ TaskImplHelper.ExecuteImplWithRetry(this.DeleteImpl, this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete the queue.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDelete(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(this.DeleteImpl, this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to delete the queue.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation")]
+ public void EndDelete(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Determines if the queue exists.
+ ///
+ /// True if the queue exists; otherwise false.
+ public bool Exists()
+ {
+ return TaskImplHelper.ExecuteImplWithRetry(this.ExistsImpl, this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to determine whether the queue exists.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginExists(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(this.ExistsImpl, this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to determine whether the queue exists.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// Returns true if the queue exists; otherwise, false.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public bool EndExists(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Fetches the queue's attributes.
+ ///
+ public void FetchAttributes()
+ {
+ TaskImplHelper.ExecuteImplWithRetry(this.FetchAttributesImpl, this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to fetch the queue's attributes.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginFetchAttributes(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(this.FetchAttributesImpl, this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to fetch the queue's attributes.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndFetchAttributes(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Sets the queue's metadata.
+ ///
+ public void SetMetadata()
+ {
+ TaskImplHelper.ExecuteImplWithRetry(this.SetMetadataImpl, this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to set the queue's metadata.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetMetadata(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(this.SetMetadataImpl, this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to set the queue's metadata.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndSetMetadata(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Retrieves the approximate message count for the queue. This method fetches
+ /// the value from the server and updates the
+ /// property as well.
+ ///
+ /// The approximate message count.
+ public int RetrieveApproximateMessageCount()
+ {
+ this.FetchAttributes();
+
+ return this.ApproximateMessageCount.Value;
+ }
+
+ ///
+ /// Adds a message to the queue.
+ ///
+ /// The message to add.
+ public void AddMessage(CloudQueueMessage message)
+ {
+ CommonUtils.AssertNotNull("message", message);
+ TaskImplHelper.ExecuteImplWithRetry(() => this.AddMessageImpl(message, null, null), this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Adds a message to the queue.
+ ///
+ /// The message to add.
+ /// The maximum time to allow the message to be in the queue.
+ public void AddMessage(CloudQueueMessage message, TimeSpan timeToLive)
+ {
+ CommonUtils.AssertNotNull("message", message);
+ TaskImplHelper.ExecuteImplWithRetry(() => this.AddMessageImpl(message, timeToLive, null), this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Adds a message to the queue.
+ ///
+ /// The message to add.
+ /// The maximum time to allow the message to be in the queue, or null.
+ /// The length of time from now during which the message will be invisible.
+ /// If null then the message will be visible immediately.
+ public void AddMessage(CloudQueueMessage message, TimeSpan? timeToLive, TimeSpan? initialVisibilityDelay)
+ {
+ CommonUtils.AssertNotNull("message", message);
+ TaskImplHelper.ExecuteImplWithRetry(() => this.AddMessageImpl(message, timeToLive, initialVisibilityDelay), this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to add a message to the queue.
+ ///
+ /// The message to add.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginAddMessage(CloudQueueMessage message, AsyncCallback callback, object state)
+ {
+ CommonUtils.AssertNotNull("message", message);
+ return TaskImplHelper.BeginImplWithRetry(() => this.AddMessageImpl(message, null, null), this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to add a message to the queue.
+ ///
+ /// The message to add.
+ /// The maximum time to allow the message to be in the queue.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginAddMessage(CloudQueueMessage message, TimeSpan timeToLive, AsyncCallback callback, object state)
+ {
+ CommonUtils.AssertNotNull("message", message);
+ return TaskImplHelper.BeginImplWithRetry(() => this.AddMessageImpl(message, timeToLive, null), this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to add a message to the queue.
+ ///
+ /// The message to add.
+ /// The maximum time to allow the message to be in the queue, or null.
+ /// The length of time from now during which the message will be invisible.
+ /// If null then the message will be visible immediately.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginAddMessage(CloudQueueMessage message, TimeSpan? timeToLive, TimeSpan? initialVisibilityDelay, AsyncCallback callback, object state)
+ {
+ CommonUtils.AssertNotNull("message", message);
+ return TaskImplHelper.BeginImplWithRetry(() => this.AddMessageImpl(message, timeToLive, initialVisibilityDelay), this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to add a message to the queue.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public void EndAddMessage(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Gets a list of messages from the queue.
+ ///
+ /// The number of messages to retrieve.
+ /// An enumerable collection of messages.
+ public IEnumerable GetMessages(int messageCount)
+ {
+ return this.GetMessagesInternal(messageCount, null);
+ }
+
+ ///
+ /// Gets a list of messages from the queue.
+ ///
+ /// The number of messages to retrieve.
+ /// The visibility timeout interval.
+ /// An enumerable collection of messages.
+ public IEnumerable GetMessages(int messageCount, TimeSpan visibilityTimeout)
+ {
+ return this.GetMessagesInternal(messageCount, visibilityTimeout);
+ }
+
+ ///
+ /// Begins an asynchronous operation to get messages from the queue.
+ ///
+ /// The number of messages to retrieve.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetMessages(int messageCount, AsyncCallback callback, object state)
+ {
+ return this.BeginGetMessagesInternal(messageCount, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to get messages from the queue.
+ ///
+ /// The number of messages to retrieve.
+ /// The visibility timeout interval.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetMessages(int messageCount, TimeSpan visibilityTimeout, AsyncCallback callback, object state)
+ {
+ return this.BeginGetMessagesInternal(messageCount, visibilityTimeout, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to get messages from the queue.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// An enumerable collection of messages.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public IEnumerable EndGetMessages(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Gets a single message from the queue.
+ ///
+ /// A message.
+ public CloudQueueMessage GetMessage()
+ {
+ return this.GetMessageInternal(null);
+ }
+
+ ///
+ /// Gets a single message from the queue.
+ ///
+ /// The visibility timeout interval.
+ /// A message.
+ public CloudQueueMessage GetMessage(TimeSpan visibilityTimeout)
+ {
+ return this.GetMessageInternal(visibilityTimeout);
+ }
+
+ ///
+ /// Begins an asynchronous operation to get a single message from the queue.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetMessage(AsyncCallback callback, object state)
+ {
+ return this.BeginGetMessagesInternal(1, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to get a single message from the queue.
+ ///
+ /// The visibility timeout interval.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetMessage(TimeSpan visibilityTimeout, AsyncCallback callback, object state)
+ {
+ return this.BeginGetMessagesInternal(1, visibilityTimeout, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to get a single message from the queue.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A message.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public CloudQueueMessage EndGetMessage(IAsyncResult asyncResult)
+ {
+ var resultList = TaskImplHelper.EndImplWithRetry>(asyncResult);
+
+ return resultList.FirstOrDefault();
+ }
+
+ ///
+ /// Peeks a message from the queue.
+ ///
+ /// A message.
+ public CloudQueueMessage PeekMessage()
+ {
+ IEnumerable peekedMessages = TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.PeekMessagesImpl(1, setResult),
+ this.ServiceClient.RetryPolicy);
+
+ return peekedMessages.FirstOrDefault();
+ }
+
+ ///
+ /// Begins an asynchronous operation to peek a message from the queue.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginPeekMessage(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.PeekMessagesImpl(1, setResult),
+ this.ServiceClient.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to peek a message from the queue.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A message.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public CloudQueueMessage EndPeekMessage(IAsyncResult asyncResult)
+ {
+ var resultList = TaskImplHelper.EndImplWithRetry>(asyncResult);
+ return resultList.FirstOrDefault();
+ }
+
+ ///
+ /// Peeks a set of messages from the queue.
+ ///
+ /// The number of messages to retrieve.
+ /// A enumerable collection of messages.
+ public IEnumerable PeekMessages(int messageCount)
+ {
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.PeekMessagesImpl(messageCount, setResult),
+ this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to peek a set of messages from the queue.
+ ///
+ /// The number of messages to retrieve.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginPeekMessages(int messageCount, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.PeekMessagesImpl(messageCount, setResult),
+ this.ServiceClient.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to peek a set of messages from the queue.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// An enumerable collection of messages.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public IEnumerable EndPeekMessages(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Deletes a message.
+ ///
+ /// A message.
+ public void DeleteMessage(CloudQueueMessage message)
+ {
+ CommonUtils.AssertNotNull("message", message);
+
+ TaskImplHelper.ExecuteImplWithRetry(
+ () => this.DeleteMessageImpl(message.Id, message.PopReceipt),
+ this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Deletes a message.
+ ///
+ /// The message ID.
+ /// The pop receipt value.
+ public void DeleteMessage(string messageId, string popReceipt)
+ {
+ TaskImplHelper.ExecuteImplWithRetry(() => this.DeleteMessageImpl(messageId, popReceipt), this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete a message.
+ ///
+ /// A message.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDeleteMessage(CloudQueueMessage message, AsyncCallback callback, object state)
+ {
+ CommonUtils.AssertNotNull("message", message);
+
+ return TaskImplHelper.BeginImplWithRetry(
+ () => this.DeleteMessageImpl(message.Id, message.PopReceipt),
+ this.ServiceClient.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete a message.
+ ///
+ /// The message ID.
+ /// The pop receipt value.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDeleteMessage(string messageId, string popReceipt, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(
+ () => this.DeleteMessageImpl(messageId, popReceipt),
+ this.ServiceClient.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to delete a message.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndDeleteMessage(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Updates the visibility timeout and optionally the content of a message.
+ ///
+ /// The message to update.
+ /// The length of time from now during which the message will be invisible.
+ /// Flags indicating which parts of the message are to be updated. This must include the Visibility flag.
+ public void UpdateMessage(CloudQueueMessage message, TimeSpan visibilityTimeout, MessageUpdateFields updateFields)
+ {
+ TaskImplHelper.ExecuteImplWithRetry(
+ () => this.UpdateMessageImpl(message, visibilityTimeout, updateFields),
+ this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to update the visibility timeout and optionally the content of a message.
+ ///
+ /// The message to update.
+ /// The length of time from now during which the message will be invisible.
+ /// Flags indicating which parts of the message are to be updated. This must include the Visibility flag.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginUpdateMessage(CloudQueueMessage message, TimeSpan visibilityTimeout, MessageUpdateFields updateFields, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(
+ () => this.UpdateMessageImpl(message, visibilityTimeout, updateFields),
+ this.ServiceClient.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to update the visibility timeout and possibly the contents of a message.
+ ///
+ /// The IAsyncResult returned from a prior call to .
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndUpdateMessage(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Clears all messages from the queue.
+ ///
+ public void Clear()
+ {
+ TaskImplHelper.ExecuteImplWithRetry(this.ClearMessagesImpl, this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to clear all messages from the queue.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginClear(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(this.ClearMessagesImpl, this.ServiceClient.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to clear all messages from the queue.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public void EndClear(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Gets the individual message address.
+ ///
+ /// The message id.
+ /// The Uri of the message.
+ internal Uri GetIndividualMessageAddress(string messageId)
+ {
+ Uri individualMessageUri = NavigationHelper.AppendPathToUri(this.Uri, Constants.Messages + NavigationHelper.Slash + messageId);
+ return individualMessageUri;
+ }
+
+ ///
+ /// Materialize results so that we can close the response object.
+ ///
+ /// List of response objects from the Protocol layer.
+ /// Projection function.
+ /// A materialized list of messages.
+ private static IEnumerable MaterializeAndParseResponse(IEnumerable protocolList, Func responseProjector)
+ {
+ List messages = new List();
+ messages.AddRange(protocolList.Select(responseProjector));
+ return messages;
+ }
+
+ ///
+ /// Selects the get message response.
+ ///
+ /// The protocol message.
+ /// The parsed message.
+ private CloudQueueMessage SelectGetMessageResponse(QueueMessage protocolMessage)
+ {
+ var message = this.SelectPeekMessageResponse(protocolMessage);
+ message.PopReceipt = protocolMessage.PopReceipt;
+
+ if (protocolMessage.TimeNextVisible.HasValue)
+ {
+ message.NextVisibleTime = protocolMessage.TimeNextVisible.Value;
+ }
+
+ return message;
+ }
+
+ ///
+ /// Selects the peek message response.
+ ///
+ /// The protocol message.
+ /// The parsed message.
+ private CloudQueueMessage SelectPeekMessageResponse(QueueMessage protocolMessage)
+ {
+ CloudQueueMessage message = null;
+ if (this.EncodeMessage)
+ {
+ // if EncodeMessage is true, we assume the string returned from server is Base64 encoding of original message;
+ // if this is not true, exception will likely be thrown.
+ // it is user's responsibility to make sure EncodeMessage setting matches the queue that is being read.
+ message = new CloudQueueMessage(protocolMessage.Text, true);
+ }
+ else
+ {
+ message = new CloudQueueMessage(protocolMessage.Text);
+ }
+
+ message.Id = protocolMessage.Id;
+ message.InsertionTime = protocolMessage.InsertionTime;
+ message.ExpirationTime = protocolMessage.ExpirationTime;
+ message.DequeueCount = protocolMessage.DequeueCount;
+
+ // PopReceipt and TimeNextVisible are not returned during peek
+ return message;
+ }
+
+ ///
+ /// Gets the message internal.
+ ///
+ /// The visibility timeout.
+ /// The retrieved message.
+ private CloudQueueMessage GetMessageInternal(TimeSpan? visibilityTimeout)
+ {
+ IEnumerable messages = this.GetMessagesInternal(1, visibilityTimeout);
+
+ return messages.FirstOrDefault();
+ }
+
+ ///
+ /// Gets the messages internal.
+ ///
+ /// The number of messages.
+ /// The visibility timeout.
+ /// A list of retrieved messages.
+ private IEnumerable GetMessagesInternal(int? numberOfMessages, TimeSpan? visibilityTimeout)
+ {
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.GetMessagesImpl(numberOfMessages, visibilityTimeout, setResult),
+ this.ServiceClient.RetryPolicy);
+ }
+
+ ///
+ /// Begins the get messages internal.
+ ///
+ /// The number of mesages.
+ /// The visibility timeout.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An asynchronous result that represents the operation.
+ private IAsyncResult BeginGetMessagesInternal(int? numberOfMesages, TimeSpan? visibilityTimeout, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.GetMessagesImpl(numberOfMesages, visibilityTimeout, setResult),
+ this.ServiceClient.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Existses the impl.
+ ///
+ /// The set result.
+ /// A that detects whether the queue exists.
+ private TaskSequence ExistsImpl(Action setResult)
+ {
+ var webRequest = QueueRequest.GetMetadata(this.Uri, this.ServiceClient.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ try
+ {
+ // Materialize exceptions
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ setResult(true);
+ }
+ }
+ catch (StorageClientException e)
+ {
+ if (e.StatusCode == HttpStatusCode.NotFound)
+ {
+ setResult(false);
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ ///
+ /// Sets the metadata impl.
+ ///
+ /// A that sets the metadata.
+ private TaskSequence SetMetadataImpl()
+ {
+ var webRequest = QueueRequest.SetMetadata(this.Uri, this.ServiceClient.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+
+ QueueRequest.AddMetadata(webRequest, this.Metadata);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Fetches the metadata and properties impl.
+ ///
+ /// A that fetches the attributes.
+ private TaskSequence FetchAttributesImpl()
+ {
+ var webRequest = QueueRequest.GetMetadata(this.Uri, this.ServiceClient.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ this.GetPropertiesAndMetadataFromResponse(webResponse);
+ }
+ }
+
+ ///
+ /// Creates the impl.
+ ///
+ /// A that creates the queue.
+ private TaskSequence CreateImpl()
+ {
+ var webRequest = QueueRequest.Create(this.Uri, this.ServiceClient.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+
+ QueueRequest.AddMetadata(webRequest, this.Metadata);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Creates if not exist impl.
+ ///
+ /// The set result.
+ /// A that creates the queue if it doesn't exist.
+ private TaskSequence CreateIfNotExistImpl(Action setResult)
+ {
+ var webRequest = QueueRequest.Create(this.Uri, this.ServiceClient.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+
+ QueueRequest.AddMetadata(webRequest, this.Metadata);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ try
+ {
+ // Materialize exceptions
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ if (webResponse.StatusCode != HttpStatusCode.NoContent)
+ {
+ setResult(true);
+
+ this.GetPropertiesAndMetadataFromResponse(webResponse);
+ }
+ else
+ {
+ setResult(false);
+ }
+ }
+ }
+ catch (StorageClientException e)
+ {
+ if (e.StatusCode == HttpStatusCode.Conflict && e.ExtendedErrorInformation.ErrorCode == QueueErrorCodeStrings.QueueAlreadyExists)
+ {
+ setResult(false);
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ ///
+ /// Deletes the impl.
+ ///
+ /// A that deletes the queue.
+ private TaskSequence DeleteImpl()
+ {
+ var webRequest = QueueRequest.Delete(this.Uri, this.ServiceClient.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Clears the messages impl.
+ ///
+ /// A that clears the messages in the queue.
+ private TaskSequence ClearMessagesImpl()
+ {
+ var webRequest = QueueRequest.ClearMessages(this.MessageRequestAddress, this.ServiceClient.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Generates a task sequence for adding a message to the queue.
+ ///
+ /// The message.
+ /// The time to live.
+ /// The initial visibility delay.
+ /// A that adds the message.
+ private TaskSequence AddMessageImpl(CloudQueueMessage message, TimeSpan? timeToLive, TimeSpan? initialVisibilityDelay)
+ {
+ int? timeToLiveInSec = null;
+ int? initialVisibilityDelayInSec = null;
+
+ if (timeToLive != null)
+ {
+ CommonUtils.AssertInBounds("timeToLive", timeToLive.Value, TimeSpan.Zero, CloudQueueMessage.MaxTimeToLive);
+ timeToLiveInSec = (int)timeToLive.Value.TotalSeconds;
+ }
+
+ if (initialVisibilityDelay != null)
+ {
+ CommonUtils.AssertInBounds("initialVisibilityDelay", initialVisibilityDelay.Value, TimeSpan.Zero, timeToLive ?? CloudQueueMessage.MaxTimeToLive);
+ initialVisibilityDelayInSec = (int)initialVisibilityDelay.Value.TotalSeconds;
+ }
+
+ CommonUtils.AssertNotNull("message", message);
+ CommonUtils.AssertNotNull("MessageContent", message.AsBytes);
+
+ HttpWebRequest webRequest = QueueRequest.PutMessage(this.MessageRequestAddress, this.ServiceClient.Timeout.RoundUpToSeconds(), timeToLiveInSec, initialVisibilityDelayInSec);
+
+ byte[] requestBody = QueueRequest.GenerateMessageRequestBody(this.GenerateMessageContentsForRequest(message));
+ CommonUtils.ApplyRequestOptimizations(webRequest, requestBody.Length);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ var requestTask = webRequest.GetRequestStreamAsync();
+ yield return requestTask;
+
+ using (var requestStream = requestTask.Result)
+ {
+ var writeTask = requestStream.WriteAsync(requestBody, 0, requestBody.Length);
+ yield return writeTask;
+ var scratch = writeTask.Result;
+ }
+
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Generates a suitable string for representing the content of a message in the body of a web request.
+ ///
+ /// The message.
+ /// A string appropriately encoded depending on the format of the message and the EncodeMessage property.
+ private string GenerateMessageContentsForRequest(CloudQueueMessage message)
+ {
+ // when EncodeMessage is false, it is not allowed to upload a CloudQueueMessage that has been created with a byte array
+ // even if the byte array is the UTF8 encoding of a text string.
+ if (!this.EncodeMessage && message.MessageType == QueueMessageType.Base64Encoded)
+ {
+ throw new ArgumentException(SR.BinaryMessageShouldUseBase64Encoding);
+ }
+
+ string outgoingMessageString = null;
+ if (message.MessageType == QueueMessageType.RawString)
+ {
+ if (this.EncodeMessage)
+ {
+ outgoingMessageString = Convert.ToBase64String(message.AsBytes);
+
+ // the size of Base64 encoded string is the number of bytes this message will take up on server.
+ if (outgoingMessageString.Length > CloudQueueMessage.MaxMessageSize)
+ {
+ throw new ArgumentException(string.Format(
+ CultureInfo.InvariantCulture,
+ SR.MessageTooLarge,
+ CloudQueueMessage.MaxMessageSize));
+ }
+ }
+ else
+ {
+ outgoingMessageString = message.RawString;
+
+ // we need to calculate the size of its UTF8 byte array, as that will be the storage usage on server.
+ if (Encoding.UTF8.GetBytes(outgoingMessageString).Length > CloudQueueMessage.MaxMessageSize)
+ {
+ throw new ArgumentException(string.Format(
+ CultureInfo.InvariantCulture,
+ SR.MessageTooLarge,
+ CloudQueueMessage.MaxMessageSize));
+ }
+ }
+ }
+ else
+ {
+ // at this point, this.EncodeMessage must be true
+ outgoingMessageString = message.RawString;
+
+ // the size of Base64 encoded string is the number of bytes this message will take up on server.
+ if (outgoingMessageString.Length > CloudQueueMessage.MaxMessageSize)
+ {
+ throw new ArgumentException(string.Format(
+ CultureInfo.InvariantCulture,
+ SR.MessageTooLarge,
+ CloudQueueMessage.MaxMessageSize));
+ }
+ }
+
+ return outgoingMessageString;
+ }
+
+ ///
+ /// Peeks the messages impl.
+ ///
+ /// The number of messages.
+ /// The set result.
+ /// A that returns the peeked messages.
+ private TaskSequence PeekMessagesImpl(int? numberOfMessages, Action> setResult)
+ {
+ if (numberOfMessages.HasValue)
+ {
+ CommonUtils.AssertInBounds("numberOfMessages", numberOfMessages.Value, 0, CloudQueueMessage.MaxNumberOfMessagesToPeek);
+ }
+
+ var webRequest = QueueRequest.PeekMessages(this.MessageRequestAddress, this.ServiceClient.Timeout.RoundUpToSeconds(), numberOfMessages);
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ var parsedResponse = QueueResponse.PeekMessages(webResponse);
+ setResult(MaterializeAndParseResponse(parsedResponse.Messages, this.SelectGetMessageResponse));
+ }
+ }
+
+ ///
+ /// Deletes the message impl.
+ ///
+ /// The message id.
+ /// The pop receipt value.
+ /// A that deletes the message.
+ private TaskSequence DeleteMessageImpl(string messageId, string popReceipt)
+ {
+ CommonUtils.AssertNotNullOrEmpty("messageId", messageId);
+ CommonUtils.AssertNotNullOrEmpty("popReceipt", popReceipt);
+
+ Uri messageUri = this.GetIndividualMessageAddress(messageId);
+
+ var webRequest = QueueRequest.DeleteMessage(messageUri, this.ServiceClient.Timeout.RoundUpToSeconds(), popReceipt);
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ }
+ }
+
+ ///
+ /// Generates a task sequence for updating a message in the queue.
+ ///
+ /// The message.
+ /// The visibility timeout.
+ /// The flags controlling which parts of the message to update. Must include Visibility.
+ /// A that updates the message.
+ private TaskSequence UpdateMessageImpl(CloudQueueMessage message, TimeSpan visibilityTimeout, MessageUpdateFields updateFlags)
+ {
+ CommonUtils.AssertNotNull("message", message);
+ CommonUtils.AssertNotNullOrEmpty("messageId", message.Id);
+ CommonUtils.AssertNotNullOrEmpty("popReceipt", message.PopReceipt);
+ CommonUtils.AssertInBounds("visibilityTimeout", visibilityTimeout, TimeSpan.Zero, CloudQueueMessage.MaxTimeToLive);
+
+ if ((updateFlags & MessageUpdateFields.Visibility) == 0)
+ {
+ throw new ArgumentException("Calls to UpdateMessage must include the Visibility flag.", "updateFlags");
+ }
+
+ Uri messageUri = this.GetIndividualMessageAddress(message.Id);
+
+ HttpWebRequest webRequest = QueueRequest.UpdateMessage(messageUri, this.ServiceClient.Timeout.RoundUpToSeconds(), message.PopReceipt, (int)visibilityTimeout.TotalSeconds);
+
+ if ((updateFlags & MessageUpdateFields.Content) != 0)
+ {
+ byte[] requestBody = QueueRequest.GenerateMessageRequestBody(this.GenerateMessageContentsForRequest(message));
+ CommonUtils.ApplyRequestOptimizations(webRequest, requestBody.Length);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+
+ Task requestTask = webRequest.GetRequestStreamAsync();
+ yield return requestTask;
+
+ using (Stream requestStream = requestTask.Result)
+ {
+ Task writeTask = requestStream.WriteAsync(requestBody, 0, requestBody.Length);
+ yield return writeTask;
+ NullTaskReturn scratch = writeTask.Result;
+ }
+ }
+ else
+ {
+ CommonUtils.ApplyRequestOptimizations(webRequest, 0);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ }
+
+ Task task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (HttpWebResponse webResponse = task.Result as HttpWebResponse)
+ {
+ // Update the message pop receipt and next visible time
+ message.PopReceipt = QueueResponse.GetPopReceipt(webResponse);
+ message.NextVisibleTime = QueueResponse.GetNextVisibleTime(webResponse);
+ }
+ }
+
+ ///
+ /// Gets the messages impl.
+ ///
+ /// The number of messages.
+ /// The visibility timeout.
+ /// The set result.
+ /// A that gets the message.
+ private TaskSequence GetMessagesImpl(int? numberOfMessages, TimeSpan? visibilityTimeout, Action> setResult)
+ {
+ if (visibilityTimeout.HasValue)
+ {
+ CommonUtils.AssertInBounds("visibiliyTimeout", visibilityTimeout.Value, TimeSpan.Zero, TimeSpan.MaxValue);
+ }
+
+ if (numberOfMessages.HasValue)
+ {
+ CommonUtils.AssertInBounds("numberOfMessage", numberOfMessages.Value, 0, int.MaxValue);
+ }
+
+ int? visibilityTimeoutInSec = visibilityTimeout != null ? visibilityTimeout.RoundUpToSeconds() : (int?)null;
+
+ var webRequest = QueueRequest.GetMessages(this.MessageRequestAddress, this.ServiceClient.Timeout.RoundUpToSeconds(), numberOfMessages, visibilityTimeoutInSec);
+ CommonUtils.ApplyRequestOptimizations(webRequest, -1);
+ this.ServiceClient.Credentials.SignRequest(webRequest);
+ var task = webRequest.GetResponseAsyncWithTimeout(this.ServiceClient, this.ServiceClient.Timeout);
+ yield return task;
+
+ using (var webResponse = task.Result as HttpWebResponse)
+ {
+ var parsedResponse = QueueResponse.GetMessages(webResponse);
+ setResult(MaterializeAndParseResponse(parsedResponse.Messages, this.SelectGetMessageResponse));
+ }
+ }
+
+ ///
+ /// Gets the properties and metadata from response.
+ ///
+ /// The web response.
+ private void GetPropertiesAndMetadataFromResponse(HttpWebResponse webResponse)
+ {
+ this.Metadata = QueueResponse.GetMetadata(webResponse);
+
+ string count = QueueResponse.GetApproximateMessageCount(webResponse);
+ this.ApproximateMessageCount = string.IsNullOrEmpty(count) ? (int?)null : int.Parse(count);
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudQueueClient.cs b/microsoft-azure-api/StorageClient/CloudQueueClient.cs
new file mode 100644
index 0000000000000..e3f7d64fd882e
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudQueueClient.cs
@@ -0,0 +1,561 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudQueueClient class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// Provides a client for accessing the Windows Azure Queue service.
+ ///
+ public class CloudQueueClient
+ {
+ ///
+ /// Initializes a new instance of the class using the specified
+ /// Queue service endpoint and account credentials.
+ ///
+ /// The Queue service endpoint to use to create the client.
+ /// The account credentials.
+ public CloudQueueClient(string baseAddress, StorageCredentials credentials)
+ : this(new Uri(baseAddress), credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified
+ /// Queue service endpoint and account credentials.
+ ///
+ /// The Queue service endpoint to use to create the client.
+ /// The account credentials.
+ public CloudQueueClient(Uri baseAddressUri, StorageCredentials credentials)
+ : this(null, baseAddressUri, credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The base address.
+ /// The credentials.
+ /// If set to true [use path style Uris].
+ internal CloudQueueClient(string baseAddress, StorageCredentials credentials, bool usePathStyleUris)
+ : this(usePathStyleUris, new Uri(baseAddress), credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The base address Uri.
+ /// The credentials.
+ /// If set to true [use path style Uris].
+ internal CloudQueueClient(Uri baseAddressUri, StorageCredentials credentials, bool usePathStyleUris)
+ : this(usePathStyleUris, baseAddressUri, credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// True to use path style Uris.
+ /// The base address Uri.
+ /// The credentials.
+ internal CloudQueueClient(bool? usePathStyleUris, Uri baseAddressUri, StorageCredentials credentials)
+ {
+ CommonUtils.AssertNotNull("baseAddress", baseAddressUri);
+ CommonUtils.AssertNotNull("credentials", credentials);
+
+ if (!credentials.CanSignRequest)
+ {
+ throw new ArgumentException(SR.CredentialsCantSignRequest, "credentials");
+ }
+
+ this.BaseUri = baseAddressUri;
+
+ if (!this.BaseUri.IsAbsoluteUri)
+ {
+ CommonUtils.ArgumentOutOfRange("baseAddress", baseAddressUri);
+ }
+
+ this.Timeout = Constants.DefaultClientSideTimeout;
+ this.RetryPolicy = RetryPolicies.RetryExponential(RetryPolicies.DefaultClientRetryCount, RetryPolicies.DefaultClientBackoff);
+ this.Credentials = credentials;
+
+ if (usePathStyleUris.HasValue)
+ {
+ this.UsePathStyleUris = usePathStyleUris.Value;
+ }
+ else
+ {
+ // Automatically decide whether to use host style uri or path style uri
+ this.UsePathStyleUris = CommonUtils.UsePathStyleAddressing(this.BaseUri);
+ }
+ }
+
+ ///
+ /// Occurs when a response is received from the server.
+ ///
+ public event EventHandler ResponseReceived;
+
+ ///
+ /// Gets or sets the default retry policy for requests made via the Queue service client.
+ ///
+ /// The retry policy.
+ public RetryPolicy RetryPolicy { get; set; }
+
+ ///
+ /// Gets or sets the server timeout for requests made via the Queue service client.
+ ///
+ /// The server timeout interval.
+ public TimeSpan Timeout { get; set; }
+
+ ///
+ /// Gets or sets the lifetime of the approximate message count cache. Obsolete.
+ ///
+ /// The lifetime of the approximate message count cache.
+ /// The approximate message count is not cached. This is an obsolete property that has no effect.
+ [System.Obsolete("The approximate message count cache is obsolete. Use FetchAttributes to refresh the approximate message count as needed.")]
+ public TimeSpan ApproximateMessageCountCacheLength { get; set; }
+
+ ///
+ /// Gets a value indicating whether requests made via the Queue service client will employ path-style URIs.
+ ///
+ /// True to use path-style URIs; otherwise, false.
+ public bool UsePathStyleUris { get; private set; }
+
+ ///
+ /// Gets the account credentials used to create the Queue service client.
+ ///
+ /// An object of type .
+ public StorageCredentials Credentials { get; private set; }
+
+ ///
+ /// Gets the base URI for the Queue service client.
+ ///
+ /// The base URI used to construct the Queue service client.
+ public Uri BaseUri { get; private set; }
+
+ ///
+ /// Gets a reference to the queue at the specified address.
+ ///
+ /// Either the name of the queue, or the absolute URI to the queue.
+ /// A reference to the queue.
+ public CloudQueue GetQueueReference(string queueAddress)
+ {
+ Uri queueUri = NavigationHelper.AppendPathToUri(this.BaseUri, queueAddress);
+
+ return new CloudQueue(queueUri, this);
+ }
+
+ ///
+ /// Returns an enumerable collection of the queues in the storage account.
+ ///
+ /// An enumerable collection of queues.
+ public IEnumerable ListQueues()
+ {
+ return this.ListQueues(String.Empty);
+ }
+
+ ///
+ /// Returns an enumerable collection of the queues in the storage account whose names begin with the specified prefix.
+ ///
+ /// The queue name prefix.
+ /// An enumerable collection of queues.
+ public IEnumerable ListQueues(string prefix)
+ {
+ return this.ListQueues(prefix, QueueListingDetails.None);
+ }
+
+ ///
+ /// Returns an enumerable collection of the queues in the storage account whose names begin with the specified prefix and that are retrieved lazily.
+ ///
+ /// The queue name prefix.
+ /// One of the enumeration values that indicates which details to include in the listing.
+ /// An enumerable collection of queues that are retrieved lazily.
+ public IEnumerable ListQueues(string prefix, QueueListingDetails detailsIncluded)
+ {
+ return CommonUtils.LazyEnumerateSegmented(
+ (setResult) => this.ListQueuesImpl(prefix, detailsIncluded, null, null, setResult),
+ this.RetryPolicy);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of queues
+ /// in the storage account.
+ ///
+ /// A result segment containing a collection of queues.
+ public ResultSegment ListQueuesSegmented()
+ {
+ return this.ListQueuesSegmented(String.Empty, QueueListingDetails.None);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of queues
+ /// in the storage account.
+ ///
+ /// The queue name prefix.
+ /// One of the enumeration values that indicates which details to include in the listing.
+ /// A result segment containing a collection of queues.
+ public ResultSegment ListQueuesSegmented(string prefix, QueueListingDetails detailsIncluded)
+ {
+ return this.ListQueuesSegmented(prefix, detailsIncluded, 0, null);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of queues
+ /// whose names begin with the specified prefix.
+ ///
+ /// The queue name prefix.
+ /// One of the enumeration values that indicates which details to include in the listing.
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// A result segment containing a collection of queues.
+ public ResultSegment ListQueuesSegmented(string prefix, QueueListingDetails detailsIncluded, int maxResults, ResultContinuation continuationToken)
+ {
+ return TaskImplHelper.ExecuteImplWithRetry>(
+ (setResult) => this.ListQueuesImpl(prefix, detailsIncluded, continuationToken, maxResults, setResult),
+ this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection of queues
+ /// in the storage account.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListQueuesSegmented(AsyncCallback callback, object state)
+ {
+ return this.BeginListQueuesSegmented(String.Empty, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection of queues
+ /// whose names begin with the specified prefix.
+ ///
+ /// The queue name prefix.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListQueuesSegmented(string prefix, AsyncCallback callback, object state)
+ {
+ return this.BeginListQueuesSegmented(prefix, QueueListingDetails.None, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection of queues
+ /// whose names begin with the specified prefix.
+ ///
+ /// The queue name prefix.
+ /// One of the enumeration values that indicates which details to include in the listing.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListQueuesSegmented(string prefix, QueueListingDetails detailsIncluded, AsyncCallback callback, object state)
+ {
+ return this.BeginListQueuesSegmented(prefix, detailsIncluded, 0, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection of queues
+ /// whose names begin with the specified prefix.
+ ///
+ /// The queue name prefix.
+ /// One of the enumeration values that indicates which details to include in the listing.
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListQueuesSegmented(string prefix, QueueListingDetails detailsIncluded, int maxResults, ResultContinuation continuationToken, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.ListQueuesImpl(prefix, detailsIncluded, continuationToken, maxResults, setResult),
+ this.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to return a result segment containing a collection of queues.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A result segment containing the results of the first request.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public ResultSegment EndListQueuesSegmented(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Gets the properties of the queue service.
+ ///
+ /// The queue service properties.
+ public ServiceProperties GetServiceProperties()
+ {
+ return TaskImplHelper.ExecuteImplWithRetry((setResult) => this.GetServicePropertiesImpl(setResult), this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to get the properties of the queue service.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user defined object to be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetServiceProperties(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry((setResult) => this.GetServicePropertiesImpl(setResult), this.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to get the properties of the queue service.
+ ///
+ /// The result returned from a prior call to .
+ /// The queue service properties.
+ public ServiceProperties EndGetServiceProperties(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Sets the properties of the queue service.
+ ///
+ /// The queue service properties.
+ public void SetServiceProperties(ServiceProperties properties)
+ {
+ TaskImplHelper.ExecuteImplWithRetry(() => this.SetServicePropertiesImpl(properties), this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to set the properties of the queue service.
+ ///
+ /// The queue service properties.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user defined object to be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetServiceProperties(ServiceProperties properties, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(() => this.SetServicePropertiesImpl(properties), this.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to set the properties of the queue service.
+ ///
+ /// The result returned from a prior call to .
+ public void EndSetServiceProperties(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Ends the get response.
+ ///
+ /// The asynchronous result.
+ /// The request.
+ /// The web response.
+ internal WebResponse EndGetResponse(IAsyncResult asyncresult, WebRequest req)
+ {
+ return EventHelper.ProcessWebResponse(req, asyncresult, this.ResponseReceived, this);
+ }
+
+ ///
+ /// Lists the queues impl.
+ ///
+ /// The prefix.
+ /// The details included.
+ /// The continuation token.
+ /// The max results.
+ /// The set result.
+ /// A for listing the queues.
+ private TaskSequence ListQueuesImpl(
+ string prefix,
+ QueueListingDetails detailsIncluded,
+ ResultContinuation continuationToken,
+ int? maxResults,
+ Action> setResult)
+ {
+ ResultPagination pagination = new ResultPagination(maxResults.GetValueOrDefault());
+
+ return this.ListQueuesImplCore(
+ prefix,
+ detailsIncluded,
+ continuationToken,
+ pagination,
+ setResult);
+ }
+
+ ///
+ /// Lists the queues impl core.
+ ///
+ /// The prefix.
+ /// The details included.
+ /// The continuation token.
+ /// The pagination.
+ /// The set result.
+ /// A for listing the queues.
+ private TaskSequence ListQueuesImplCore(
+ string prefix,
+ QueueListingDetails detailsIncluded,
+ ResultContinuation continuationToken,
+ ResultPagination pagination,
+ Action> setResult)
+ {
+ CommonUtils.AssertContinuationType(continuationToken, ResultContinuation.ContinuationType.Queue);
+
+ ListingContext listingContext = new ListingContext(prefix, pagination.GetNextRequestPageSize())
+ {
+ Marker = continuationToken != null ? continuationToken.NextMarker : null
+ };
+
+ var queueList = new List();
+
+ var webRequest = QueueRequest.List(this.BaseUri, this.Timeout.RoundUpToSeconds(), listingContext, detailsIncluded);
+ this.Credentials.SignRequest(webRequest);
+
+ var listTask = webRequest.GetResponseAsyncWithTimeout(this, this.Timeout);
+ yield return listTask;
+
+ string nextMarker;
+
+ using (var response = listTask.Result as HttpWebResponse)
+ {
+ var parsedResponse = QueueResponse.List(response);
+
+ // Materialize the results so that we can close the response
+ queueList.AddRange(parsedResponse.Queues.Select((Func)this.SelectResponse));
+
+ nextMarker = parsedResponse.NextMarker;
+ }
+
+ ResultContinuation newContinuationToken = new ResultContinuation() { NextMarker = nextMarker, Type = ResultContinuation.ContinuationType.Queue };
+
+ ResultSegment.CreateResultSegment(
+ setResult,
+ queueList,
+ newContinuationToken,
+ pagination,
+ this.RetryPolicy,
+ (paginationArg, continuationArg, resultSegment) =>
+ this.ListQueuesImplCore(prefix, detailsIncluded, continuationArg, paginationArg, resultSegment));
+ }
+
+ ///
+ /// Selects the response.
+ ///
+ /// The item to parse.
+ /// A object representing the item.
+ private CloudQueue SelectResponse(QueueEntry item)
+ {
+ CloudQueue info = new CloudQueue(item.Attributes, this);
+ return info;
+ }
+
+ ///
+ /// Generates a task sequence for getting the properties of the queue service.
+ ///
+ /// A delegate to receive the service properties.
+ /// A task sequence that gets the properties of the queue service.
+ private TaskSequence GetServicePropertiesImpl(Action setResult)
+ {
+ HttpWebRequest request = QueueRequest.GetServiceProperties(this.BaseUri, this.Timeout.RoundUpToSeconds());
+ CommonUtils.ApplyRequestOptimizations(request, -1);
+ this.Credentials.SignRequest(request);
+
+ // Get the web response.
+ Task responseTask = request.GetResponseAsyncWithTimeout(this, this.Timeout);
+ yield return responseTask;
+
+ using (HttpWebResponse response = responseTask.Result as HttpWebResponse)
+ using (Stream responseStream = response.GetResponseStream())
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ // Download the service properties.
+ Task downloadTask = new InvokeTaskSequenceTask(() => { return responseStream.WriteTo(memoryStream); });
+ yield return downloadTask;
+
+ // Materialize any exceptions.
+ NullTaskReturn scratch = downloadTask.Result;
+
+ // Get the result from the memory stream.
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ setResult(QueueResponse.ReadServiceProperties(memoryStream));
+ }
+ }
+
+ ///
+ /// Generates a task sequence for setting the properties of the queue service.
+ ///
+ /// The queue service properties to set.
+ /// A task sequence that sets the properties of the queue service.
+ private TaskSequence SetServicePropertiesImpl(ServiceProperties properties)
+ {
+ CommonUtils.AssertNotNull("properties", properties);
+
+ HttpWebRequest request = QueueRequest.SetServiceProperties(this.BaseUri, this.Timeout.RoundUpToSeconds());
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ try
+ {
+ QueueRequest.WriteServiceProperties(properties, memoryStream);
+ }
+ catch (InvalidOperationException invalidOpException)
+ {
+ throw new ArgumentException(invalidOpException.Message, "properties");
+ }
+
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ CommonUtils.ApplyRequestOptimizations(request, memoryStream.Length);
+ this.Credentials.SignRequest(request);
+
+ // Get the request stream
+ Task getStreamTask = request.GetRequestStreamAsync();
+ yield return getStreamTask;
+
+ using (Stream requestStream = getStreamTask.Result)
+ {
+ // Upload the service properties.
+ Task uploadTask = new InvokeTaskSequenceTask(() => { return (memoryStream as Stream).WriteTo(requestStream); });
+ yield return uploadTask;
+
+ // Materialize any exceptions.
+ NullTaskReturn scratch = uploadTask.Result;
+ }
+ }
+
+ // Get the web response.
+ Task responseTask = request.GetResponseAsyncWithTimeout(this, this.Timeout);
+ yield return responseTask;
+
+ // Materialize any exceptions.
+ using (HttpWebResponse response = responseTask.Result as HttpWebResponse)
+ {
+ }
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudQueueMessage.cs b/microsoft-azure-api/StorageClient/CloudQueueMessage.cs
new file mode 100644
index 0000000000000..06db928ff1e02
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudQueueMessage.cs
@@ -0,0 +1,223 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudQueueMessage class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Globalization;
+ using System.Text;
+ using Protocol;
+
+ ///
+ /// Enum for Queue message type.
+ ///
+ internal enum QueueMessageType
+ {
+ ///
+ /// Indicates the message object stores the raw text string.
+ ///
+ RawString,
+
+ ///
+ /// Indicates the message object stores the Base64-Encoded representation of the raw data.
+ ///
+ Base64Encoded
+ }
+
+ ///
+ /// Represents a message in a queue.
+ ///
+ public class CloudQueueMessage
+ {
+ ///
+ /// The maximum message size in bytes.
+ ///
+ public static readonly long MaxMessageSize = 64 * Constants.KB;
+
+ ///
+ /// The maximum amount of time a message is kept in the queue.
+ ///
+ public static readonly TimeSpan MaxTimeToLive = TimeSpan.FromDays(7);
+
+ ///
+ /// The maximum number of messages that can be peeked at a time.
+ ///
+ public static readonly int MaxNumberOfMessagesToPeek = 32;
+
+ ///
+ /// Custom UTF8Encoder to throw exception in case of invalid bytes.
+ ///
+ private static UTF8Encoding utf8Encoder = new UTF8Encoding(false, true);
+
+ ///
+ /// Initializes a new instance of the class with the given byte array.
+ ///
+ /// The content of the message as a byte array.
+ public CloudQueueMessage(byte[] content)
+ {
+ this.SetMessageContent(content);
+
+ // While binary messages will be Base64-encoded and we could validate the message size here,
+ // for consistency, we leave it to CloudQueue so that we have a central place for this logic.
+ }
+
+ ///
+ /// Initializes a new instance of the class with the given string.
+ ///
+ /// The content of the message as a string of text.
+ public CloudQueueMessage(string content)
+ {
+ this.SetMessageContent(content);
+
+ // At this point, without knowing whether or not the message will be Base64 encoded, we can't fully validate the message size.
+ // So we leave it to CloudQueue so that we have a central place for this logic.
+ }
+
+ ///
+ /// Initializes a new instance of the class with the given Base64 encoded string.
+ /// This method is only used internally.
+ ///
+ /// The text string.
+ /// Whether the string is Base64 encoded.
+ internal CloudQueueMessage(string content, bool isBase64Encoded)
+ {
+ if (content == null)
+ {
+ content = string.Empty;
+ }
+
+ this.RawString = content;
+ this.MessageType = isBase64Encoded ? QueueMessageType.Base64Encoded : QueueMessageType.RawString;
+ }
+
+ ///
+ /// Gets the content of the message as a byte array.
+ ///
+ /// The content of the message as a byte array.
+ public byte[] AsBytes
+ {
+ get
+ {
+ if (this.MessageType == QueueMessageType.RawString)
+ {
+ return Encoding.UTF8.GetBytes(this.RawString);
+ }
+ else
+ {
+ return Convert.FromBase64String(this.RawString);
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the message ID.
+ ///
+ /// The message ID.
+ public string Id { get; protected internal set; }
+
+ ///
+ /// Gets or sets the message's pop receipt.
+ ///
+ /// The pop receipt value.
+ public string PopReceipt { get; protected internal set; }
+
+ ///
+ /// Gets or sets the time that the message was added to the queue.
+ ///
+ /// The time that that message was added to the queue.
+ public DateTime? InsertionTime { get; protected internal set; }
+
+ ///
+ /// Gets or sets the time that the message expires.
+ ///
+ /// The time that the message expires.
+ public DateTime? ExpirationTime { get; protected internal set; }
+
+ ///
+ /// Gets or sets the time that the message will next be visible.
+ ///
+ /// The time that the message will next be visible.
+ public DateTime? NextVisibleTime { get; protected internal set; }
+
+ ///
+ /// Gets the content of the message, as a string.
+ ///
+ /// The message content.
+ public string AsString
+ {
+ get
+ {
+ if (this.MessageType == QueueMessageType.RawString)
+ {
+ return this.RawString;
+ }
+ else
+ {
+ return utf8Encoder.GetString(Convert.FromBase64String(this.RawString));
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the number of times this message has been dequeued.
+ ///
+ /// The number of times this message has been dequeued.
+ public int DequeueCount { get; protected internal set; }
+
+ ///
+ /// Gets message type that indicates if the RawString is the original message string or Base64 encoding of the original binary data.
+ ///
+ internal QueueMessageType MessageType { get; private set; }
+
+ ///
+ /// Gets or sets the original message string or Base64 encoding of the original binary data.
+ ///
+ /// The original message string.
+ protected internal string RawString { get; set; }
+
+ ///
+ /// Sets the content of this message.
+ ///
+ /// The new message content.
+ public void SetMessageContent(string content)
+ {
+ if (content == null)
+ {
+ // Protocol will return null for empty content
+ content = string.Empty;
+ }
+
+ this.RawString = content;
+ this.MessageType = QueueMessageType.RawString;
+ }
+
+ ///
+ /// Sets the content of this message.
+ ///
+ /// The new message content.
+ public void SetMessageContent(byte[] content)
+ {
+ CommonUtils.AssertNotNull("content", content);
+
+ this.RawString = Convert.ToBase64String(content);
+ this.MessageType = QueueMessageType.Base64Encoded;
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudStorageAccountStorageClientExtensions.cs b/microsoft-azure-api/StorageClient/CloudStorageAccountStorageClientExtensions.cs
new file mode 100644
index 0000000000000..c9dcaf9a8dd36
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudStorageAccountStorageClientExtensions.cs
@@ -0,0 +1,101 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudStorageAccountStorageClientExtensions class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+
+ ///
+ /// Provides a set of extensions to the class that may be used to generate client objects for
+ /// the Windows Azure storage services.
+ ///
+ public static class CloudStorageAccountStorageClientExtensions
+ {
+ ///
+ /// Creates a new Blob service client.
+ ///
+ /// The storage account.
+ /// A client object that specifies the Blob service endpoint.
+ public static CloudBlobClient CreateCloudBlobClient(this CloudStorageAccount account)
+ {
+ if (account.BlobEndpoint == null)
+ {
+ throw new InvalidOperationException("No blob endpoint configured.");
+ }
+
+ if (account.Credentials == null)
+ {
+ throw new InvalidOperationException("No credentials provided configured.");
+ }
+
+ return new CloudBlobClient(account.BlobEndpoint, account.Credentials);
+ }
+
+ ///
+ /// Creates a new Queue service client.
+ ///
+ /// The storage account.
+ /// A client object that specifies the Queue service endpoint.
+ public static CloudQueueClient CreateCloudQueueClient(this CloudStorageAccount account)
+ {
+ if (account.QueueEndpoint == null)
+ {
+ throw new InvalidOperationException("No queue endpoint configured.");
+ }
+
+ if (account.Credentials == null)
+ {
+ throw new InvalidOperationException("No credentials provided.");
+ }
+
+ if (!account.Credentials.CanSignRequest)
+ {
+ throw new InvalidOperationException("CloudQueueClient requires a credential that can sign request");
+ }
+
+ return new CloudQueueClient(account.QueueEndpoint, account.Credentials);
+ }
+
+ ///
+ /// Creates the Table service client.
+ ///
+ /// The storage account.
+ /// A client object that specifies the Table service endpoint.
+ public static CloudTableClient CreateCloudTableClient(this CloudStorageAccount account)
+ {
+ if (account.TableEndpoint == null)
+ {
+ throw new InvalidOperationException("No table endpoint configured.");
+ }
+
+ if (account.Credentials == null)
+ {
+ throw new InvalidOperationException("No credentials provided.");
+ }
+
+ if (!account.Credentials.CanSignRequest || !account.Credentials.CanSignRequestLite)
+ {
+ throw new InvalidOperationException("CloudTableClient requires a credential that can sign request");
+ }
+
+ return new CloudTableClient(account.TableEndpoint, account.Credentials);
+ }
+ }
+}
diff --git a/microsoft-azure-api/StorageClient/CloudTableClient.cs b/microsoft-azure-api/StorageClient/CloudTableClient.cs
new file mode 100644
index 0000000000000..c1c868702e91f
--- /dev/null
+++ b/microsoft-azure-api/StorageClient/CloudTableClient.cs
@@ -0,0 +1,969 @@
+//-----------------------------------------------------------------------
+//
+// Copyright 2011 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Contains code for the CloudTableClient class.
+//
+//-----------------------------------------------------------------------
+
+namespace Microsoft.WindowsAzure.StorageClient
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Data.Services.Client;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using Microsoft.WindowsAzure.StorageClient.Protocol;
+ using Tasks;
+ using TaskSequence = System.Collections.Generic.IEnumerable;
+
+ ///
+ /// Provides a client for accessing the Windows Azure Table service.
+ ///
+ public class CloudTableClient
+ {
+ ///
+ /// Initializes a new instance of the class using the specified Table service endpoint
+ /// and account credentials.
+ ///
+ /// The Table service endpoint to use to create the client.
+ /// The account credentials.
+ public CloudTableClient(string baseAddress, StorageCredentials credentials)
+ : this(new Uri(baseAddress), credentials)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified Table service endpoint
+ /// and account credentials.
+ ///
+ /// The Table service endpoint to use to create the client.
+ /// The account credentials.
+ public CloudTableClient(Uri baseAddressUri, StorageCredentials credentials)
+ {
+ if (baseAddressUri == null)
+ {
+ throw new ArgumentNullException("baseAddressUri");
+ }
+
+ if (credentials == null)
+ {
+ throw new ArgumentNullException("credentials");
+ }
+
+ if ((!credentials.CanSignRequest) || (!credentials.CanSignRequestLite))
+ {
+ throw new ArgumentException(SR.CredentialsCantSignRequest, "credentials");
+ }
+
+ this.BaseUri = baseAddressUri;
+ this.Credentials = credentials;
+ this.RetryPolicy = RetryPolicies.RetryExponential(RetryPolicies.DefaultClientRetryCount, RetryPolicies.DefaultClientBackoff);
+ this.Timeout = TimeSpan.FromSeconds(90);
+ }
+
+ ///
+ /// Occurs when a response is received from the server.
+ ///
+ public event EventHandler ResponseReceived;
+
+ ///
+ /// Gets the minimum supported timestamp value for a table entity.
+ ///
+ /// The minimum supported timestamp value for a table entity.
+ public DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return DateTime.FromFileTimeUtc(0);
+ }
+ }
+
+ ///
+ /// Gets the base URI for the Table service client.
+ ///
+ /// The base URI used to construct the Table service client.
+ public Uri BaseUri { get; private set; }
+
+ ///
+ /// Gets or sets the default retry policy for requests made via the Table service client.
+ ///
+ /// The retry policy.
+ public RetryPolicy RetryPolicy { get; set; }
+
+ ///
+ /// Gets or sets the default server timeout for requests made by the Table service client.
+ ///
+ /// The server timeout interval.
+ public TimeSpan Timeout { get; set; }
+
+ ///
+ /// Gets the account credentials used to create the Table service client.
+ ///
+ /// The account credentials.
+ public StorageCredentials Credentials { get; private set; }
+
+ ///
+ /// Creates the tables needed for the specified service context.
+ ///
+ /// The type of service context.
+ /// The Table service endpoint to use to create the client.
+ /// The account credentials.
+ public static void CreateTablesFromModel(Type serviceContextType, string baseAddress, StorageCredentials credentials)
+ {
+ var client = new CloudTableClient(baseAddress, credentials);
+
+ foreach (var table in TableServiceUtilities.EnumerateEntitySetNames(serviceContextType))
+ {
+ client.CreateTableIfNotExist(table);
+ }
+ }
+
+ ///
+ /// Creates a new object for performing operations against the Table service.
+ ///
+ /// A service context to use for performing operations against the Table service.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Design",
+ "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "This method creates a new object each time.")]
+ public TableServiceContext GetDataServiceContext()
+ {
+ return new TableServiceContext(this.BaseUri.ToString(), this.Credentials)
+ {
+ RetryPolicy = this.RetryPolicy,
+ Timeout = (int)this.Timeout.TotalSeconds
+ };
+ }
+
+ ///
+ /// Attaches to the specified service context.
+ ///
+ /// The service context to attach to.
+ public void Attach(DataServiceContext serviceContext)
+ {
+ if (serviceContext is TableServiceContext)
+ {
+ throw new ArgumentException(SR.AttachToTableServiceContext);
+ }
+
+ // because this is an anonymous method the closure object acts as GC isolation
+ serviceContext.SendingRequest += (sender, e) =>
+ {
+ HttpWebRequest request = e.Request as HttpWebRequest;
+
+ // do the authentication
+ Credentials.SignRequestLite(request);
+ };
+ }
+
+ ///
+ /// Begins an asychronous operation to create a table.
+ ///
+ /// The table name.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreateTable(string tableName, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(() => this.CreateTableImpl(tableName, null), RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asychronous operation to create a table.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public void EndCreateTable(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Creates a table with specified name.
+ ///
+ /// The table name.
+ public void CreateTable(string tableName)
+ {
+ TaskImplHelper.ExecuteImplWithRetry(() => this.CreateTableImpl(tableName, null), RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to create a table with the specified name if it does not already exist.
+ ///
+ /// The table name.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginCreateTableIfNotExist(string tableName, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImpl(
+ (setResult) => this.CreateTableIfNotExistImpl(tableName, setResult),
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to create a table with the specified name if it does not already exist.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// true if table was created; otherwise, false.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public bool EndCreateTableIfNotExist(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Creates the table if it does not already exist.
+ ///
+ /// The table name.
+ /// true if table was created; otherwise, false.
+ public bool CreateTableIfNotExist(string tableName)
+ {
+ return TaskImplHelper.ExecuteImpl((setResult) => this.CreateTableIfNotExistImpl(tableName, setResult));
+ }
+
+ ///
+ /// Begins an asynchronous operation to determine whether a table exists.
+ ///
+ /// The table name.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDoesTableExist(string tableName, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImpl((setResult) => this.DoesTableExistImpl(tableName, setResult), callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to determine whether a table exists.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// true if table exists; otherwise, false.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public bool EndDoesTableExist(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Checks whether the table exists.
+ ///
+ /// The table name.
+ /// true if table exists; otherwise, false.
+ public bool DoesTableExist(string tableName)
+ {
+ return TaskImplHelper.ExecuteImpl((setResult) => this.DoesTableExistImpl(tableName, setResult));
+ }
+
+ ///
+ /// Returns an enumerable collection of table names in the storage account.
+ ///
+ /// An enumerable collection of table names.
+ public IEnumerable ListTables()
+ {
+ return this.ListTables(String.Empty);
+ }
+
+ ///
+ /// Returns an enumerable collection of table names that begin with the specified prefix and that are retrieved lazily.
+ ///
+ /// The table name prefix.
+ /// An enumerable collection of table names that are retrieved lazily.
+ public IEnumerable ListTables(string prefix)
+ {
+ return CommonUtils.LazyEnumerateSegmented(
+ (setResult) => this.ListTablesSegmentedImpl(prefix, null, null, setResult),
+ this.RetryPolicy);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of table names in the storage account.
+ ///
+ /// A result segment containing table names.
+ public ResultSegment ListTablesSegmented()
+ {
+ return this.ListTablesSegmented(0, null);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of table names in the storage account.
+ ///
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// A result segment containing table names.
+ public ResultSegment ListTablesSegmented(int maxResults, ResultContinuation continuationToken)
+ {
+ return this.ListTablesSegmented(String.Empty, maxResults, continuationToken);
+ }
+
+ ///
+ /// Returns a result segment containing a collection of table names beginning with the specified prefix.
+ ///
+ /// The table name prefix.
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// A result segment containing table names.
+ public ResultSegment ListTablesSegmented(string prefix, int maxResults, ResultContinuation continuationToken)
+ {
+ return this.EndListTablesSegmented(
+ this.BeginListTablesSegmented(prefix, maxResults, continuationToken, null, null));
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection of table names
+ /// in the storage account.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListTablesSegmented(AsyncCallback callback, object state)
+ {
+ return this.BeginListTablesSegmented(String.Empty, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection
+ /// of table names beginning with the specified prefix.
+ ///
+ /// The table name prefix.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListTablesSegmented(string prefix, AsyncCallback callback, object state)
+ {
+ return this.BeginListTablesSegmented(prefix, 0, null, callback, state);
+ }
+
+ ///
+ /// Begins an asynchronous operation to return a result segment containing a collection
+ /// of table names beginning with the specified prefix.
+ ///
+ /// The table name prefix.
+ /// A non-negative integer value that indicates the maximum number of results to be returned at a time, up to the
+ /// per-operation limit of 5000. If this value is zero, the maximum possible number of results will be returned, up to 5000.
+ /// A continuation token returned by a previous listing operation.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginListTablesSegmented(string prefix, int maxResults, ResultContinuation continuationToken, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry>(
+ (setResult) => this.ListTablesSegmentedImpl(
+ prefix,
+ maxResults,
+ continuationToken,
+ setResult),
+ this.RetryPolicy,
+ callback,
+ state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to return a result segment containing a collection
+ /// of table names.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// A result segment containing table names.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Microsoft.Performance",
+ "CA1822:MarkMembersAsStatic",
+ Justification = "This is a member-operation.")]
+ public ResultSegment EndListTablesSegmented(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry>(asyncResult);
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete a table.
+ ///
+ /// The table name.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ ///
+ /// An that references the asynchronous request.
+ ///
+ public IAsyncResult BeginDeleteTable(string tableName, AsyncCallback callback, object state)
+ {
+ CommonUtils.CheckStringParameter(tableName, false, "tableName", Protocol.Constants.TableServiceMaxStringPropertySizeInChars);
+ TableServiceUtilities.CheckTableName(tableName, "tableName");
+
+ return TaskImplHelper.BeginImplWithRetry(() => this.DeleteTableImpl(tableName), RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to delete a table.
+ ///
+ /// An that references the pending asynchronous operation.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public void EndDeleteTable(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Deletes the table.
+ ///
+ /// The table name.
+ public void DeleteTable(string tableName)
+ {
+ this.EndDeleteTable(this.BeginDeleteTable(tableName, null, null));
+ }
+
+ ///
+ /// Begins an asynchronous operation to delete the tables if it exists.
+ ///
+ /// The table name.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user-defined object that will be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginDeleteTableIfExist(string tableName, AsyncCallback callback, object state)
+ {
+ CommonUtils.CheckStringParameter(tableName, false, "tableName", Protocol.Constants.TableServiceMaxStringPropertySizeInChars);
+
+ TableServiceUtilities.CheckTableName(tableName, "tableName");
+
+ return TaskImplHelper.BeginImpl((setResult) => this.DeleteTableIfExistImpl(tableName, setResult), callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to delete the tables if it exists.
+ ///
+ /// An that references the pending asynchronous operation.
+ /// true if the table was deleted; otherwise, false.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This is a member-operation")]
+ public bool EndDeleteTableIfExist(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImpl(asyncResult);
+ }
+
+ ///
+ /// Deletes the table if it exists.
+ ///
+ /// The table name.
+ /// true if the table was deleted; otherwise, false.
+ public bool DeleteTableIfExist(string tableName)
+ {
+ return this.EndDeleteTableIfExist(this.BeginDeleteTableIfExist(tableName, null, null));
+ }
+
+ ///
+ /// Gets the properties of the table service.
+ ///
+ /// The table service properties.
+ public ServiceProperties GetServiceProperties()
+ {
+ return TaskImplHelper.ExecuteImplWithRetry((setResult) => this.GetServicePropertiesImpl(setResult), this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to get the properties of the table service.
+ ///
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user defined object to be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginGetServiceProperties(AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry((setResult) => this.GetServicePropertiesImpl(setResult), this.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to get the properties of the table service.
+ ///
+ /// The result returned from a prior call to .
+ /// The table service properties.
+ public ServiceProperties EndGetServiceProperties(IAsyncResult asyncResult)
+ {
+ return TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Sets the properties of the table service.
+ ///
+ /// The table service properties.
+ public void SetServiceProperties(ServiceProperties properties)
+ {
+ TaskImplHelper.ExecuteImplWithRetry(() => this.SetServicePropertiesImpl(properties), this.RetryPolicy);
+ }
+
+ ///
+ /// Begins an asynchronous operation to set the properties of the table service.
+ ///
+ /// The table service properties.
+ /// The callback delegate that will receive notification when the asynchronous operation completes.
+ /// A user defined object to be passed to the callback delegate.
+ /// An that references the asynchronous operation.
+ public IAsyncResult BeginSetServiceProperties(ServiceProperties properties, AsyncCallback callback, object state)
+ {
+ return TaskImplHelper.BeginImplWithRetry(() => this.SetServicePropertiesImpl(properties), this.RetryPolicy, callback, state);
+ }
+
+ ///
+ /// Ends an asynchronous operation to set the properties of the table service.
+ ///
+ /// The result returned from a prior call to .
+ public void EndSetServiceProperties(IAsyncResult asyncResult)
+ {
+ TaskImplHelper.EndImplWithRetry(asyncResult);
+ }
+
+ ///
+ /// Ends the asynchronous GetResponse operation.
+ ///
+ /// An that references the asynchronous operation.
+ /// The request to end the operation on.
+ /// The from the asynchronous request.
+ internal WebResponse EndGetResponse(IAsyncResult asyncresult, WebRequest req)
+ {
+ return EventHelper.ProcessWebResponse(req, asyncresult, this.ResponseReceived, this);
+ }
+
+ ///
+ /// Gets the result or default.
+ ///
+ /// The type of the result.
+ /// The task to retrieve the result from.
+ /// Receives result of the task.
+ /// true if the result was returned; otherwise, false.
+ private static bool GetResultOrDefault(Task task, out T result)
+ {
+ try
+ {
+ result = task.Result;
+
+ return true;
+ }
+ catch (DataServiceQueryException ex)
+ {
+ if ((HttpStatusCode)ex.Response.StatusCode == HttpStatusCode.NotFound)
+ {
+ result = default(T);
+
+ return false;
+ }
+
+ throw Utilities.TranslateDataServiceClientException(ex);
+ }
+ catch (InvalidOperationException ex)
+ {
+ DataServiceClientException dsce = CommonUtils.FindInnerDataServiceClientException(ex);
+
+ if (dsce != null)
+ {
+ if ((HttpStatusCode)dsce.StatusCode == HttpStatusCode.NotFound)
+ {
+ result = default(T);
+
+ return false;
+ }
+ }
+
+ throw Utilities.TranslateDataServiceClientException(ex);
+ }
+ }
+
+ ///
+ /// Creates the table implementation.
+ ///
+ /// The table name.
+ /// The set result.
+ /// A sequence of tasks to do the operation.
+ private TaskSequence CreateTableImpl(string tableName, Action setResult)
+ {
+ CommonUtils.CheckStringParameter(tableName, false, "tableName", Protocol.Constants.TableServiceMaxStringPropertySizeInChars);
+
+ var svc = this.GetDataServiceContext();
+
+ svc.AddObject(Protocol.Constants.TableServiceTablesName, new TableServiceTable() { TableName = tableName });
+
+ var saveChangesTask = svc.SaveChangesAsync();
+
+ yield return saveChangesTask;
+
+ // wrap any exceptions
+ try
+ {
+ var result = saveChangesTask.Result;
+ }
+ catch (InvalidOperationException ex)
+ {
+ if (setResult == null)
+ {
+ throw Utilities.TranslateDataServiceClientException(ex);
+ }
+ else
+ {
+ setResult(ex);
+ }
+ }
+ }
+
+ ///
+ /// Creates the table if not exist implementation.
+ ///
+ /// The table name.
+ /// The set result.
+ /// A sequence of tasks to do the operation.
+ private TaskSequence CreateTableIfNotExistImpl(string tableName, Action setResult)
+ {
+ CommonUtils.CheckStringParameter(tableName, false, "tableName", Protocol.Constants.TableServiceMaxStringPropertySizeInChars);
+ TableServiceUtilities.CheckTableName(tableName, "tableName");
+
+ var doesTableExistTask = new InvokeTaskSequenceTask((set) => this.DoesTableExistImpl(tableName, set));
+
+ yield return doesTableExistTask;
+
+ if (doesTableExistTask.Result)
+ {
+ setResult(false);
+ }
+ else
+ {
+ var createTableTask = TaskImplHelper.GetRetryableAsyncTask((resultSetter) => this.CreateTableImpl(tableName, resultSetter), RetryPolicy);
+
+ yield return createTableTask;
+
+ // wrap any exceptions
+ try
+ {
+ if (createTableTask.Result == null)
+ {
+ setResult(true);
+ }
+ else
+ {
+ StorageClientException exception = Utilities.TranslateDataServiceClientException(createTableTask.Result) as StorageClientException;
+ if (exception != null
+ && exception.ErrorCode == StorageErrorCode.ResourceAlreadyExists
+ && exception.ExtendedErrorInformation != null
+ && exception.ExtendedErrorInformation.ErrorCode == TableErrorCodeStrings.TableAlreadyExists)
+ {
+ setResult(false);
+ }
+ else
+ {
+ throw createTableTask.Result;
+ }
+ }
+ }
+ catch (InvalidOperationException ex)
+ {
+ throw Utilities.TranslateDataServiceClientException(ex);
+ }
+ }
+ }
+
+ ///
+ /// Verifies whether the table exist implementation.
+ ///
+ /// The table name.
+ /// The set result.
+ /// A sequence of tasks to do the operation.
+ private TaskSequence DoesTableExistImpl(string tableName, Action setResult)
+ {
+ CommonUtils.CheckStringParameter(tableName, false, "tableName", Protocol.Constants.TableServiceMaxStringPropertySizeInChars);
+ TableServiceUtilities.CheckTableName(tableName, "tableName");
+
+ var svc = this.GetDataServiceContext();
+
+ var tableExistsQuery = (from table in svc.CreateQuery(Protocol.Constants.TableServiceTablesName)
+ where table.TableName == tableName
+ select table).AsTableServiceQuery();
+
+ ResultSegment segment = null;
+
+ while (true)
+ {
+ Task> tableExistsSegmentTask;
+
+ if (segment == null)
+ {
+ tableExistsSegmentTask = TaskImplHelper.GetRetryableAsyncTask>(
+ (setResultInner) => tableExistsQuery.ExecuteSegmentedImpl(null, setResultInner), RetryPolicy);
+ }
+ else
+ {
+ tableExistsSegmentTask = TaskImplHelper.GetRetryableAsyncTask>(segment.GetNextImpl, RetryPolicy);
+ }
+
+ yield return tableExistsSegmentTask;
+
+ if (GetResultOrDefault(tableExistsSegmentTask, out segment))
+ {
+ if (segment.Results.Any())
+ {
+ setResult(true);
+
+ break;
+ }
+ else
+ {
+ setResult(false);
+
+ break;
+ }
+ }
+ else
+ {
+ setResult(false);
+
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Returns an enumerable collection of tables segmented impl.
+ ///
+ /// The prefix.
+ /// The max results.
+ /// The continuation token.
+ /// The set result.
+ /// A that lists the tables.
+ private TaskSequence ListTablesSegmentedImpl(
+ string prefix,
+ int? maxResults,
+ ResultContinuation continuationToken,
+ Action> setResult)
+ {
+ ResultPagination pagination = new ResultPagination(maxResults.GetValueOrDefault());
+
+ return this.ListTablesSegmentedImplCore(prefix, continuationToken, pagination, null, setResult);
+ }
+
+ ///
+ /// Returns an enumerable collection of tables segmented implementation.
+ ///
+ /// The prefix.
+ /// The continuation token.
+ /// The pagination.
+ /// The last result.
+ /// The set result.
+ /// A sequence of tasks to do the operation.
+ private TaskSequence ListTablesSegmentedImplCore(
+ string prefix,
+ ResultContinuation continuationToken,
+ ResultPagination pagination,
+ ResultSegment lastResult,
+ Action> setResult)
+ {
+ CommonUtils.AssertContinuationType(continuationToken, ResultContinuation.ContinuationType.Table);
+
+ InvokeTaskSequenceTask> listTablesSegmentedTask;
+
+ if (lastResult == null)
+ {
+ var svc = this.GetDataServiceContext();
+ var query = from table in svc.CreateQuery(Protocol.Constants.TableServiceTablesName)
+ select table;
+
+ if (prefix != string.Empty)
+ {
+ // Append Max char to end '{' is 1 + 'z' in AsciiTable
+ string uppperBound = prefix + '{';
+
+ query = query.Where((table) => table.TableName.CompareTo(prefix) >= 0 && table.TableName.CompareTo(uppperBound) < 0);
+ }
+
+ if (pagination.IsPagingEnabled)
+ {
+ query = query.Take(pagination.GetNextRequestPageSize().Value);
+ }
+
+ var listTablesQuery = query.AsTableServiceQuery();
+
+ listTablesSegmentedTask = new InvokeTaskSequenceTask>(
+ (setResultInner) =>
+ listTablesQuery.ExecuteSegmentedImpl(continuationToken, setResultInner));
+ }
+ else
+ {
+ listTablesSegmentedTask = new InvokeTaskSequenceTask>(lastResult.GetNextImpl);
+ }
+
+ yield return listTablesSegmentedTask;
+
+ if (GetResultOrDefault>(listTablesSegmentedTask, out lastResult))
+ {
+ setResult(new ResultSegment