/Lib/StorageAccountInfo.cs
C# | 612 lines | 425 code | 52 blank | 135 comment | 109 complexity | 857d3af088d6462db5886a5086d1f2c9 MD5 | raw file
- //
- // <copyright file="StorageAccountInfo.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- //
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Globalization;
- using System.Configuration;
- using System.Collections.Specialized;
- using System.Diagnostics;
- using Microsoft.ServiceHosting.ServiceRuntime;
-
- namespace Microsoft.Samples.ServiceHosting.StorageClient
- {
- /// <summary>
- /// Objects of this class encapsulate information about a storage account and endpoint configuration.
- /// Associated with a storage account is the account name, the base URI of the account and a shared key.
- /// </summary>
- public class StorageAccountInfo
- {
-
- /// <summary>
- /// The default configuration string in configuration files for setting the queue storage endpoint.
- /// </summary>
- public static readonly string DefaultQueueStorageEndpointConfigurationString = "QueueStorageEndpoint";
-
- /// <summary>
- /// The default configuration string in configuration files for setting the blob storage endpoint.
- /// </summary>
- public static readonly string DefaultBlobStorageEndpointConfigurationString = "BlobStorageEndpoint";
-
- /// <summary>
- /// The default configuration string in configuration files for setting the table storage endpoint.
- /// </summary>
- public static readonly string DefaultTableStorageEndpointConfigurationString = "TableStorageEndpoint";
-
- /// <summary>
- /// The default configuration string in configuration files for setting the storage account name.
- /// </summary>
- public static readonly string DefaultAccountNameConfigurationString = "AccountName";
-
- /// <summary>
- /// The default configuration string in configuration files for setting the shared key associated with a storage account.
- /// </summary>
- public static readonly string DefaultAccountSharedKeyConfigurationString = "AccountSharedKey";
-
- /// <summary>
- /// The default configuration string in configuration files for setting the UsePathStyleUris option.
- /// </summary>
- public static readonly string DefaultUsePathStyleUrisConfigurationString = "UsePathStyleUris";
-
- /// <summary>
- /// The default prefix string in application config and Web.config files to indicate that this setting should be looked up
- /// in the fabric's configuration system.
- /// </summary>
- public static readonly string CSConfigStringPrefix = "CSConfigName";
-
- private bool? _usePathStyleUris;
-
- /// <summary>
- /// Constructor for creating account info objects.
- /// </summary>
- /// <param name="baseUri">The account's base URI.</param>
- /// <param name="usePathStyleUris">If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used.
- /// If false host-style URIs (http://accountname.baseuri/containername/objectname) are used,
- /// where baseuri is the URI of the service..
- /// If null, the choice is made automatically: path-style URIs if host name part of base URI is an
- /// IP addres, host-style otherwise.</param>
- /// <param name="accountName">The account name.</param>
- /// <param name="base64Key">The account's shared key.</param>
- public StorageAccountInfo(Uri baseUri, bool? usePathStyleUris, string accountName, string base64Key)
- : this(baseUri, usePathStyleUris, accountName, base64Key, false)
- {
- }
-
- /// <summary>
- /// Constructor for creating account info objects.
- /// </summary>
- /// <param name="baseUri">The account's base URI.</param>
- /// <param name="usePathStyleUris">If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used.
- /// If false host-style URIs (http://accountname.baseuri/containername/objectname) are used,
- /// where baseuri is the URI of the service.
- /// If null, the choice is made automatically: path-style URIs if host name part of base URI is an
- /// IP addres, host-style otherwise.</param>
- /// <param name="accountName">The account name.</param>
- /// <param name="base64Key">The account's shared key.</param>
- /// <param name="allowIncompleteSettings">true if it shall be allowed to only set parts of the StorageAccountInfo properties.</param>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
- public StorageAccountInfo(Uri baseUri, bool? usePathStyleUris, string accountName, string base64Key, bool allowIncompleteSettings)
- {
- if (baseUri == null && !allowIncompleteSettings)
- {
- throw new ArgumentNullException("baseUri");
- }
- if (string.IsNullOrEmpty(base64Key) && !allowIncompleteSettings)
- {
- throw new ArgumentNullException("base64Key");
- }
- if (baseUri != null)
- {
- string newAccountName = null;
- Uri newBaseUri = null;
- if (IsStandardStorageEndpoint(baseUri, out newAccountName, out newBaseUri)) {
- if (!string.IsNullOrEmpty(newAccountName) &&
- !string.IsNullOrEmpty(accountName) &&
- string.Compare(accountName, newAccountName, StringComparison.Ordinal) != 0)
- {
- throw new ArgumentException("The configured base URI " + baseUri.AbsoluteUri + " for the storage service is incorrect. " +
- "The configured account name " + accountName + " must match the account name " + newAccountName +
- " as specified in the storage service base URI." +
- GeneralAccountConfigurationExceptionString);
- }
- Debug.Assert((newBaseUri == null && newAccountName == null) || (newBaseUri != null && newAccountName != null));
- if (newAccountName != null && newBaseUri != null) {
- accountName = newAccountName;
- baseUri = newBaseUri;
- }
- }
- }
- if (string.IsNullOrEmpty(accountName) && !allowIncompleteSettings)
- {
- throw new ArgumentNullException("accountName");
- }
- if (!string.IsNullOrEmpty(accountName) && accountName.ToLowerInvariant() != accountName) {
- throw new ArgumentException("The account name must not contain upper-case letters. " +
- "The account name is the first part of the URL for accessing the storage services as presented to you by the portal or " +
- "the predefined storage account name when using the development storage tool. " +
- GeneralAccountConfigurationExceptionString);
- }
-
- BaseUri = baseUri;
- AccountName = accountName;
- Base64Key = base64Key;
- if (usePathStyleUris == null && baseUri == null && !allowIncompleteSettings) {
- throw new ArgumentException("Cannot determine setting from empty URI.");
- }
- else if (usePathStyleUris == null)
- {
- if (baseUri != null)
- {
- _usePathStyleUris = Utilities.StringIsIPAddress(baseUri.Host);
- }
- else
- {
- _usePathStyleUris = null;
- }
- }
- else
- {
- UsePathStyleUris = usePathStyleUris.Value;
- }
- }
-
- /// <summary>
- /// The base URI of the account.
- /// </summary>
- public Uri BaseUri
- {
- get;
- set;
- }
-
- /// <summary>
- /// The account name.
- /// </summary>
- public string AccountName
- {
- get;
- set;
- }
-
- /// <summary>
- /// The account's key.
- /// </summary>
- public string Base64Key
- {
- get;
- set;
- }
-
- /// <summary>
- /// If set, returns the UsePathStyleUris properties. If the property has not been explicitly set,
- /// the implementation tries to derive the correct value from the base URI.
- /// </summary>
- public bool UsePathStyleUris
- {
- get
- {
- if (_usePathStyleUris == null)
- {
- if (BaseUri == null)
- {
- return false;
- }
- else
- {
- return Utilities.StringIsIPAddress(BaseUri.Host);
- }
- }
- else
- {
- return _usePathStyleUris.Value;
- }
- }
- set {
- _usePathStyleUris = value;
- }
- }
-
- /// <summary>
- /// Retrieves account settings for the queue service from the default settings.
- /// </summary>
- public static StorageAccountInfo GetDefaultQueueStorageAccountFromConfiguration(bool allowIncompleteSettings)
- {
- return GetAccountInfoFromConfiguration(DefaultQueueStorageEndpointConfigurationString, allowIncompleteSettings);
- }
-
- /// <summary>
- /// Retrieves account settings for the queue service from the default settings.
- /// Throws an exception in case of incomplete settings.
- /// </summary>
- public static StorageAccountInfo GetDefaultQueueStorageAccountFromConfiguration()
- {
- return GetAccountInfoFromConfiguration(DefaultQueueStorageEndpointConfigurationString, false);
- }
-
- /// <summary>
- /// Retrieves account settings for the table service from the default settings.
- /// </summary>
- public static StorageAccountInfo GetDefaultTableStorageAccountFromConfiguration(bool allowIncompleteSettings)
- {
- return GetAccountInfoFromConfiguration(DefaultTableStorageEndpointConfigurationString, allowIncompleteSettings);
- }
-
- /// <summary>
- /// Retrieves account settings for the table service from the default settings.
- /// Throws an exception in case of incomplete settings.
- /// </summary>
- public static StorageAccountInfo GetDefaultTableStorageAccountFromConfiguration()
- {
- return GetAccountInfoFromConfiguration(DefaultTableStorageEndpointConfigurationString, false);
- }
-
- /// <summary>
- /// Retrieves account settings for the blob service from the default settings.
- /// </summary>
- public static StorageAccountInfo GetDefaultBlobStorageAccountFromConfiguration(bool allowIncompleteSettings)
- {
- return GetAccountInfoFromConfiguration(DefaultBlobStorageEndpointConfigurationString, allowIncompleteSettings);
- }
-
- /// <summary>
- /// Retrieves account settings for the blob service from the default settings.
- /// Throws an exception in case of incomplete settings.
- /// </summary>
- public static StorageAccountInfo GetDefaultBlobStorageAccountFromConfiguration()
- {
- return GetAccountInfoFromConfiguration(DefaultBlobStorageEndpointConfigurationString, false);
- }
-
- /// <summary>
- /// Gets settings from default configuration names except for the endpoint configuration string.
- /// </summary>
- public static StorageAccountInfo GetAccountInfoFromConfiguration(string endpointConfiguration, bool allowIncompleteSettings)
- {
- return GetAccountInfoFromConfiguration(DefaultAccountNameConfigurationString,
- DefaultAccountSharedKeyConfigurationString,
- endpointConfiguration,
- DefaultUsePathStyleUrisConfigurationString,
- allowIncompleteSettings);
- }
-
- /// <summary>
- /// Gets settings from default configuration names except for the endpoint configuration string. Throws an exception
- /// in the case of incomplete settings.
- /// </summary>
- public static StorageAccountInfo GetAccountInfoFromConfiguration(string endpointConfiguration)
- {
- return GetAccountInfoFromConfiguration(DefaultAccountNameConfigurationString,
- DefaultAccountSharedKeyConfigurationString,
- endpointConfiguration,
- DefaultUsePathStyleUrisConfigurationString,
- false);
- }
-
- /// <summary>
- /// Gets a configuration setting from application settings in the Web.config or App.config file.
- /// When running in a hosted environment, configuration settings are read from .cscfg
- /// files.
- /// </summary>
- public static string GetConfigurationSetting(string configurationSetting, string defaultValue, bool throwIfNull)
- {
- if (string.IsNullOrEmpty(configurationSetting))
- {
- throw new ArgumentException("configurationSetting cannot be empty or null", "configurationSetting");
- }
-
- string ret = null;
-
- // first, try to read from appsettings
- ret = TryGetAppSetting(configurationSetting);
-
- // settings in the csc file overload settings in Web.config
- if (RoleManager.IsRoleManagerRunning)
- {
- string cscRet = TryGetConfigurationSetting(configurationSetting);
- if (!string.IsNullOrEmpty(cscRet))
- {
- ret = cscRet;
- }
-
- // if there is a csc config name in the app settings, this config name even overloads the
- // setting we have right now
- string refWebRet = TryGetAppSetting(StorageAccountInfo.CSConfigStringPrefix + configurationSetting);
- if (!string.IsNullOrEmpty(refWebRet))
- {
- cscRet = TryGetConfigurationSetting(refWebRet);
- if (!string.IsNullOrEmpty(cscRet))
- {
- ret = cscRet;
- }
- }
- }
-
- // if we could not retrieve any configuration string set return value to the default value
- if (string.IsNullOrEmpty(ret) && defaultValue != null)
- {
- ret = defaultValue;
- }
-
- if (string.IsNullOrEmpty(ret) && throwIfNull)
- {
- throw new ConfigurationErrorsException(
- string.Format(CultureInfo.InvariantCulture, "Cannot find configuration string {0}.", configurationSetting));
- }
- return ret;
- }
-
- /// <summary>
- /// Retrieves account information settings from configuration settings. First, the implementation checks for
- /// settings in an application config section of an app.config or Web.config file. These values are overwritten
- /// if the same settings appear in a .csdef file.
- /// The implementation also supports indirect settings. In this case, indirect settings overwrite all other settings.
- /// </summary>
- /// <param name="accountNameConfiguration">Configuration string for the account name.</param>
- /// <param name="accountSharedKeyConfiguration">Configuration string for the key.</param>
- /// <param name="endpointConfiguration">Configuration string for the endpoint.</param>
- /// <param name="usePathStyleUrisConfiguration">Configuration string for the path style.</param>
- /// <param name="allowIncompleteSettings">If false, an exception is thrown if not all settings are available.</param>
- /// <returns>StorageAccountInfo object containing the retrieved settings.</returns>
- public static StorageAccountInfo GetAccountInfoFromConfiguration(string accountNameConfiguration,
- string accountSharedKeyConfiguration,
- string endpointConfiguration,
- string usePathStyleUrisConfiguration,
- bool allowIncompleteSettings)
- {
- if (string.IsNullOrEmpty(endpointConfiguration))
- {
- throw new ArgumentException("Endpoint configuration is missing", "endpointConfiguration");
- }
- string endpoint = null;
- string name = null;
- string key = null;
- string pathStyle = null;
-
- name = TryGetAppSetting(accountNameConfiguration);
- key = TryGetAppSetting(accountSharedKeyConfiguration);
- endpoint = TryGetAppSetting(endpointConfiguration);
- pathStyle = TryGetAppSetting(usePathStyleUrisConfiguration);
-
-
- // settings in the csc file overload settings in Web.config
- if (RoleManager.IsRoleManagerRunning)
- {
- // get config settings from the csc file
- string cscName = TryGetConfigurationSetting(accountNameConfiguration);
- if (!string.IsNullOrEmpty(cscName))
- {
- name = cscName;
- }
- string cscKey = TryGetConfigurationSetting(accountSharedKeyConfiguration);
- if (!string.IsNullOrEmpty(cscKey))
- {
- key = cscKey;
- }
- string cscEndpoint = TryGetConfigurationSetting(endpointConfiguration);
- if (!string.IsNullOrEmpty(cscEndpoint))
- {
- endpoint = cscEndpoint;
- }
- string cscPathStyle = TryGetConfigurationSetting(usePathStyleUrisConfiguration);
- if (!string.IsNullOrEmpty(cscPathStyle))
- {
- pathStyle = cscPathStyle;
- }
-
- // the Web.config can have references to csc setting strings
- // these count event stronger than the direct settings in the csc file
- string refWebName = TryGetAppSetting(CSConfigStringPrefix + accountNameConfiguration);
- if (!string.IsNullOrEmpty(refWebName))
- {
- cscName = TryGetConfigurationSetting(refWebName);
- if (!string.IsNullOrEmpty(cscName))
- {
- name = cscName;
- }
- }
- string refWebKey = TryGetAppSetting(CSConfigStringPrefix + accountSharedKeyConfiguration);
- if (!string.IsNullOrEmpty(refWebKey))
- {
- cscKey = TryGetConfigurationSetting(refWebKey);
- if (!string.IsNullOrEmpty(cscKey))
- {
- key = cscKey;
- }
- }
- string refWebEndpoint = TryGetAppSetting(CSConfigStringPrefix + endpointConfiguration);
- if (!string.IsNullOrEmpty(refWebEndpoint))
- {
- cscEndpoint = TryGetConfigurationSetting(refWebEndpoint);
- if (!string.IsNullOrEmpty(cscEndpoint))
- {
- endpoint = cscEndpoint;
- }
- }
- string refWebPathStyle = TryGetAppSetting(CSConfigStringPrefix + usePathStyleUrisConfiguration);
- if (!string.IsNullOrEmpty(refWebPathStyle))
- {
- cscPathStyle = TryGetConfigurationSetting(refWebPathStyle);
- if (!string.IsNullOrEmpty(cscPathStyle))
- {
- pathStyle = cscPathStyle;
- }
- }
- }
-
- if (string.IsNullOrEmpty(key) && !allowIncompleteSettings)
- {
- throw new ArgumentException("No account key specified!");
- }
- if (string.IsNullOrEmpty(endpoint) && !allowIncompleteSettings)
- {
- throw new ArgumentException("No endpoint specified!");
- }
- if (string.IsNullOrEmpty(name))
- {
- // in this case let's try to derive the account name from the Uri
- string newAccountName = null;
- Uri newBaseUri = null;
- if (IsStandardStorageEndpoint(new Uri(endpoint), out newAccountName, out newBaseUri))
- {
- Debug.Assert((newAccountName != null && newBaseUri != null) || (newAccountName == null && newBaseUri == null));
- if (newAccountName != null && newBaseUri != null)
- {
- endpoint = newBaseUri.AbsoluteUri;
- name = newAccountName;
- }
- }
- if (string.IsNullOrEmpty(name) && !allowIncompleteSettings)
- {
- throw new ArgumentException("No account name specified.");
- }
- }
-
- bool? usePathStyleUris = null;
- if (!string.IsNullOrEmpty(pathStyle))
- {
- bool b;
- if (!bool.TryParse(pathStyle, out b))
- {
- throw new ConfigurationErrorsException("Cannot parse value of setting UsePathStyleUris as a boolean");
- }
- usePathStyleUris = b;
- }
- Uri tmpBaseUri = null;
- if (!string.IsNullOrEmpty(endpoint))
- {
- tmpBaseUri = new Uri(endpoint);
- }
- return new StorageAccountInfo(tmpBaseUri, usePathStyleUris, name, key, allowIncompleteSettings);
- }
-
-
- /// <summary>
- /// Checks whether all essential properties of this object are set. Only then, the account info object
- /// should be used in ohter APIs of this library.
- /// </summary>
- /// <returns></returns>
- public bool IsCompleteSetting()
- {
- return BaseUri != null && Base64Key != null && AccountName != null;
- }
-
- /// <summary>
- /// Checks whether this StorageAccountInfo object is complete in the sense that all properties are set.
- /// </summary>
- public void CheckComplete()
- {
- if (!IsCompleteSetting())
- {
- throw new ConfigurationErrorsException("Account information incomplete!");
- }
- }
-
- #region Private methods
-
- private static string TryGetConfigurationSetting(string configName)
- {
- string ret = null;
- try
- {
- ret = RoleManager.GetConfigurationSetting(configName);
- }
- catch (RoleException)
- {
- return null;
- }
- return ret;
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
- Justification = "Make sure that nothing prevents us to read from the fabric's configuration envrionment.")]
- private static string TryGetAppSetting(string configName)
- {
- string ret = null;
- try
- {
- ret = ConfigurationSettings.AppSettings[configName];
- }
- // some exception happened when accessing the app settings section
- // most likely this is because there is no app setting file
- // we assume that this is because there is no app settings file; this is not an error
- // and explicitly all exceptions are captured here
- catch (Exception)
- {
- return null;
- }
- return ret;
- }
-
- private static string GeneralAccountConfigurationExceptionString {
- get {
- return "If the portal defines http://test.blob.core.windows.net as your blob storage endpoint, the string \"test\" " +
- "is your account name, and you can specify http://blob.core.windows.net as the BlobStorageEndpoint in your " +
- "service's configuration file(s).";
- }
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
- private static bool IsStandardStorageEndpoint(Uri baseUri, out string newAccountName, out Uri newBaseUri) {
- if (baseUri == null) {
- throw new ArgumentNullException("baseUri");
- }
-
- newAccountName = null;
- newBaseUri = null;
-
- string host = baseUri.Host;
- if (string.IsNullOrEmpty(host)) {
- throw new ArgumentException("The host part of the Uri " + baseUri.AbsoluteUri + " must not be null or empty.");
- }
- if (host != host.ToLowerInvariant()) {
- throw new ArgumentException("The specified host string " + host + " must not contain upper-case letters.");
- }
-
- string suffix = null;
- if (host.EndsWith(StorageHttpConstants.StandardPortalEndpoints.TableStorageEndpoint, StringComparison.Ordinal)) {
- suffix = StorageHttpConstants.StandardPortalEndpoints.TableStorageEndpoint;
- }
- if (host.EndsWith(StorageHttpConstants.StandardPortalEndpoints.BlobStorageEndpoint, StringComparison.Ordinal))
- {
- suffix = StorageHttpConstants.StandardPortalEndpoints.BlobStorageEndpoint;
- }
- if (host.EndsWith(StorageHttpConstants.StandardPortalEndpoints.QueueStorageEndpoint, StringComparison.Ordinal))
- {
- suffix = StorageHttpConstants.StandardPortalEndpoints.QueueStorageEndpoint;
- }
- // a URL as presented on the portal was specified, lets find out whether it is in the correct format
- if (suffix != null) {
- int index = host.IndexOf(suffix, StringComparison.Ordinal);
- Debug.Assert(index != -1);
- if (index > 0) {
- string first = host.Substring(0, index);
- Debug.Assert(!string.IsNullOrEmpty(first));
- if (first[first.Length-1] != StorageHttpConstants.ConstChars.Dot[0]) {
- return false;
- }
- first = first.Substring(0, first.Length - 1);
- if (string.IsNullOrEmpty(first)) {
- throw new ArgumentException("The configured base URI " + baseUri.AbsoluteUri + " for the storage service is incorrect. " +
- GeneralAccountConfigurationExceptionString);
- }
- if (first.Contains(StorageHttpConstants.ConstChars.Dot)) {
- throw new ArgumentException("The configured base URI " + baseUri.AbsoluteUri + " for the storage service is incorrect. " +
- GeneralAccountConfigurationExceptionString);
- }
- newAccountName = first;
- newBaseUri = new Uri(baseUri.Scheme + Uri.SchemeDelimiter + suffix + baseUri.PathAndQuery);
- }
- return true;
- }
- return false;
- }
-
-
- #endregion
-
- }
- }