5.3.32871 AzureStoreAsp sample initial release

--HG--
extra : convert_revision : svn%3Aa83551a4-30f6-4d81-a974-c6ced450ddbf%4030945
This commit is contained in:
dotneteer 2009-10-25 23:23:01 +00:00
parent 5254aa9ed8
commit 0d53978379
377 changed files with 283925 additions and 0 deletions

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{306D2F9E-D6D0-4D96-94F1-173C60A13875}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.Samples.ServiceHosting.AspProviders</RootNamespace>
<AssemblyName>AspProviders</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.Services">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.Services.Client">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.ServiceHosting.ServiceRuntime">
<HintPath>$(UtilityComputingSDKRoot)\ref\Microsoft.ServiceHosting.ServiceRuntime.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="BlobProvider.cs" />
<Compile Include="Configuration.cs" />
<Compile Include="SecUtil.cs" />
<Compile Include="TableStorageMembershipProvider.cs" />
<Compile Include="TableStorageProfileProvider.cs" />
<Compile Include="TableStorageRoleProvider.cs" />
<Compile Include="TableStorageSessionStateProvider.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StorageClient\StorageClient.csproj">
<Project>{C6F30C10-E1C2-4327-BB6B-3160B479CCA1}</Project>
<Name>StorageClient</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,277 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="BlobProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Configuration;
using Microsoft.ServiceHosting.ServiceRuntime;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Globalization;
using Microsoft.Samples.ServiceHosting.StorageClient;
namespace Microsoft.Samples.ServiceHosting.AspProviders
{
public enum EventKind
{
Critical,
Error,
Warning,
Information,
Verbose
};
static class Log
{
internal static void WriteToLog(string logName, string fmt, params object[] args)
{
RoleManager.WriteToLog(logName, String.Format(CultureInfo.InvariantCulture, fmt, args));
}
internal static void Write(EventKind eventKind, string message, params object[] args)
{
if (RoleManager.IsRoleManagerRunning)
{
switch(eventKind)
{
case EventKind.Error:
WriteToLog("Error", message, args);
break;
case EventKind.Critical:
WriteToLog("Critical", message, args);
break;
case EventKind.Warning:
WriteToLog("Warning", message, args);
break;
case EventKind.Information:
WriteToLog("Information", message, args);
break;
case EventKind.Verbose:
WriteToLog("Verbose", message, args);
break;
}
}
else
{
switch (eventKind)
{
case EventKind.Error:
case EventKind.Critical:
Trace.TraceError(message, args);
break;
case EventKind.Warning:
Trace.TraceWarning(message, args);
break;
case EventKind.Information:
case EventKind.Verbose:
Trace.TraceInformation(message, args);
break;
}
}
}
}
internal class BlobProvider
{
private StorageAccountInfo _info;
private BlobContainer _container;
private string _containerName;
private object _lock = new object();
private static readonly TimeSpan _Timeout = TimeSpan.FromSeconds(30);
private static readonly RetryPolicy _RetryPolicy = RetryPolicies.RetryN(3, TimeSpan.FromSeconds(1));
private const string _PathSeparator = "/";
internal BlobProvider(StorageAccountInfo info, string containerName)
{
this._info = info;
this._containerName = containerName;
}
internal string ContainerUrl
{
get
{
return string.Join(_PathSeparator, new string[] { _info.BaseUri.AbsolutePath, _containerName });
}
}
internal bool GetBlobContentsWithoutInitialization(string blobName, Stream outputStream, out BlobProperties properties)
{
Debug.Assert(outputStream != null);
BlobContainer container = GetContainer();
try
{
properties = container.GetBlob(blobName, new BlobContents(outputStream), false);
Log.Write(EventKind.Information, "Getting contents of blob {0}", _info.BaseUri + _PathSeparator + _containerName + _PathSeparator + blobName);
return true;
}
catch (StorageClientException sc)
{
if (sc.ErrorCode == StorageErrorCode.ResourceNotFound || sc.ErrorCode == StorageErrorCode.BlobNotFound)
{
properties = null;
return false;
}
else
throw;
}
}
internal MemoryStream GetBlobContent(string blobName, out BlobProperties properties)
{
MemoryStream blobContent = new MemoryStream();
properties = GetBlobContent(blobName, blobContent);
blobContent.Seek(0, SeekOrigin.Begin);
return blobContent;
}
internal BlobProperties GetBlobContent(string blobName, Stream outputStream)
{
BlobProperties properties;
BlobContainer container = GetContainer();
try
{
properties = container.GetBlob(blobName, new BlobContents(outputStream), false);
Log.Write(EventKind.Information, "Getting contents of blob {0}", ContainerUrl + _PathSeparator + blobName);
return properties;
}
catch (StorageClientException sc)
{
Log.Write(EventKind.Error, "Error getting contents of blob {0}: {1}", ContainerUrl + _PathSeparator + blobName, sc.Message);
throw;
}
}
internal void UploadStream(string blobName, Stream output)
{
UploadStream(blobName, output, true);
}
internal bool UploadStream(string blobName, Stream output, bool overwrite)
{
BlobContainer container = GetContainer();
try
{
output.Position = 0; //Rewind to start
Log.Write(EventKind.Information, "Uploading contents of blob {0}", ContainerUrl + _PathSeparator + blobName);
BlobProperties properties = new BlobProperties(blobName);
return container.CreateBlob(properties, new BlobContents(output), overwrite);
}
catch (StorageException se)
{
Log.Write(EventKind.Error, "Error uploading blob {0}: {1}", ContainerUrl + _PathSeparator + blobName, se.Message);
throw;
}
}
internal bool DeleteBlob(string blobName)
{
BlobContainer container = GetContainer();
try
{
return container.DeleteBlob(blobName);
}
catch (StorageException se)
{
Log.Write(EventKind.Error, "Error deleting blob {0}: {1}", ContainerUrl + _PathSeparator + blobName, se.Message);
throw;
}
}
internal bool DeleteBlobsWithPrefix(string prefix)
{
bool ret = true;
IEnumerable<BlobProperties> e = ListBlobs(prefix);
if (e == null)
{
return true;
}
IEnumerator<BlobProperties> props = e.GetEnumerator();
if (props == null)
{
return true;
}
while (props.MoveNext())
{
if (props.Current != null)
{
if (!DeleteBlob(props.Current.Name))
{
// ignore this; it is possible that another thread could try to delete the blob
// at the same time
ret = false;
}
}
}
return ret;
}
public IEnumerable<BlobProperties> ListBlobs(string folder)
{
BlobContainer container = GetContainer();
try
{
return container.ListBlobs(folder, false).OfType<BlobProperties>();
}
catch (StorageException se)
{
Log.Write(EventKind.Error, "Error enumerating contents of folder {0} exists: {1}", ContainerUrl + _PathSeparator + folder, se.Message);
throw;
}
}
private BlobContainer GetContainer()
{
// we have to make sure that only one thread tries to create the container
lock (_lock)
{
if (_container != null)
{
return _container;
}
try
{
BlobContainer container = BlobStorage.Create(_info).GetBlobContainer(_containerName);
container.Timeout = _Timeout;
container.RetryPolicy = _RetryPolicy;
container.CreateContainer();
_container = container;
return _container;
}
catch (StorageException se)
{
Log.Write(EventKind.Error, "Error creating container {0}: {1}", ContainerUrl, se.Message);
throw;
}
}
}
}
}

View file

@ -0,0 +1,309 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="Configuration.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.Runtime.InteropServices;
using Microsoft.Samples.ServiceHosting.StorageClient;
using Microsoft.ServiceHosting.ServiceRuntime;
[assembly: CLSCompliant(true)]
namespace Microsoft.Samples.ServiceHosting.AspProviders
{
internal static class Configuration
{
internal const string DefaultMembershipTableNameConfigurationString = "DefaultMembershipTableName";
internal const string DefaultRoleTableNameConfigurationString = "DefaultRoleTableName";
internal const string DefaultSessionTableNameConfigurationString = "DefaultSessionTableName";
internal const string DefaultSessionContainerNameConfigurationString = "DefaultSessionContainerName";
internal const string DefaultProfileContainerNameConfigurationString = "DefaultProfileContainerName";
internal const string DefaultProviderApplicationNameConfigurationString = "DefaultProviderApplicationName";
internal const string DefaultMembershipTableName = "Membership";
internal const string DefaultRoleTableName = "Roles";
internal const string DefaultSessionTableName = "Sessions";
internal const string DefaultSessionContainerName = "sessionprovidercontainer";
internal const string DefaultProfileContainerName = "profileprovidercontainer";
internal const string DefaultProviderApplicationName = "appname";
internal static string GetConfigurationSetting(string configurationString, string defaultValue)
{
return GetConfigurationSetting(configurationString, defaultValue, 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 the settings specified in
/// .cscfg files (i.e., the settings are read from the fabrics configuration system).
/// </summary>
internal static string GetConfigurationSetting(string configurationString, string defaultValue, bool throwIfNull)
{
if (string.IsNullOrEmpty(configurationString)) {
throw new ArgumentException("The parameter configurationString cannot be null or empty.");
}
string ret = null;
// first, try to read from appsettings
ret = TryGetAppSetting(configurationString);
// settings in the csc file overload settings in Web.config
if (RoleManager.IsRoleManagerRunning)
{
string cscRet = TryGetConfigurationSetting(configurationString);
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 + configurationString);
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.InstalledUICulture, "Cannot find configuration string {0}.", configurationString));
}
return ret;
}
internal static string GetConfigurationSettingFromNameValueCollection(NameValueCollection config, string valueName)
{
if (config == null)
{
throw new ArgumentNullException("config");
}
if (valueName == null) {
throw new ArgumentNullException("valueName");
}
string sValue = config[valueName];
if (RoleManager.IsRoleManagerRunning)
{
// settings in the hosting configuration are stronger than settings in app config
string cscRet = TryGetConfigurationSetting(valueName);
if (!string.IsNullOrEmpty(cscRet))
{
sValue = 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 = config[StorageAccountInfo.CSConfigStringPrefix + valueName];
if (!string.IsNullOrEmpty(refWebRet))
{
cscRet = TryGetConfigurationSetting(refWebRet);
if (!string.IsNullOrEmpty(cscRet))
{
sValue = cscRet;
}
}
}
return sValue;
}
internal static bool GetBooleanValue(NameValueCollection config, string valueName, bool defaultValue)
{
string sValue = GetConfigurationSettingFromNameValueCollection(config, valueName);
if (string.IsNullOrEmpty(sValue))
{
return defaultValue;
}
bool result;
if (bool.TryParse(sValue, out result))
{
return result;
}
else
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be boolean (true or false) for property '{0}'.", valueName));
}
}
internal static int GetIntValue(NameValueCollection config, string valueName, int defaultValue, bool zeroAllowed, int maxValueAllowed)
{
string sValue = GetConfigurationSettingFromNameValueCollection(config, valueName);
if (string.IsNullOrEmpty(sValue))
{
return defaultValue;
}
int iValue;
if (!Int32.TryParse(sValue, out iValue))
{
if (zeroAllowed)
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be a non-negative 32-bit integer for property '{0}'.", valueName));
}
throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be a positive 32-bit integer for property '{0}'.", valueName));
}
if (zeroAllowed && iValue < 0)
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be a non-negative 32-bit integer for property '{0}'.", valueName));
}
if (!zeroAllowed && iValue <= 0)
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value must be a positive 32-bit integer for property '{0}'.", valueName));
}
if (maxValueAllowed > 0 && iValue > maxValueAllowed)
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The value '{0}' can not be greater than '{1}'.", valueName, maxValueAllowed.ToString(CultureInfo.InstalledUICulture)));
}
return iValue;
}
internal static string GetStringValue(NameValueCollection config, string valueName, string defaultValue, bool nullAllowed)
{
string sValue = GetConfigurationSettingFromNameValueCollection(config, valueName);
if (string.IsNullOrEmpty(sValue) && nullAllowed)
{
return null;
}
else if (string.IsNullOrEmpty(sValue) && defaultValue != null)
{
return defaultValue;
}
else if (string.IsNullOrEmpty(sValue))
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' must not be empty.", valueName));
}
return sValue;
}
internal static string GetStringValueWithGlobalDefault(NameValueCollection config, string valueName, string defaultConfigString, string defaultValue, bool nullAllowed)
{
string sValue = GetConfigurationSettingFromNameValueCollection(config, valueName);
if (string.IsNullOrEmpty(sValue))
{
sValue = GetConfigurationSetting(defaultConfigString, null);
}
if (string.IsNullOrEmpty(sValue) && nullAllowed)
{
return null;
}
else if (string.IsNullOrEmpty(sValue) && defaultValue != null)
{
return defaultValue;
}
else if (string.IsNullOrEmpty(sValue))
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' must not be empty.", valueName));
}
return sValue;
}
internal static string GetInitExceptionDescription(StorageAccountInfo table, StorageAccountInfo blob) {
StringBuilder builder = new StringBuilder();
builder.Append(GetInitExceptionDescription(table, "table storage configuration"));
builder.Append(GetInitExceptionDescription(blob, "blob storage configuration"));
return builder.ToString();
}
internal static string GetInitExceptionDescription(StorageAccountInfo info, string desc) {
StringBuilder builder = new StringBuilder();
builder.Append("The reason for this exception is typically that the endpoints are not correctly configured. " + Environment.NewLine);
if (info == null)
{
builder.Append("The current " + desc + " is null. Please specify a table endpoint!" + Environment.NewLine);
}
else
{
string baseUri = (info.BaseUri == null) ? "null" : info.BaseUri.ToString();
builder.Append("The current " + desc + " is: " + baseUri + ", usePathStyleUris is: " + info.UsePathStyleUris + Environment.NewLine);
builder.Append("Please also make sure that the account name and the shared key are specified correctly. This information cannot be shown here because of security reasons.");
}
return builder.ToString();
}
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 no error condition prevents environment from reading service configuration.")]
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
// this is not an error because configuration settings can also be located in the cscfg file, and explicitly
// all exceptions are captured here
catch (Exception)
{
return null;
}
return ret;
}
}
}

View file

@ -0,0 +1,502 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="SecUtil.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
using System;
using System.Globalization;
using System.Web.Hosting;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Configuration.Provider;
using System.Configuration;
using System.Text.RegularExpressions;
using System.Xml;
using System.Net;
using System.Diagnostics;
using System.Security;
using System.Text;
using Microsoft.Samples.ServiceHosting.StorageClient;
using System.Threading;
namespace Microsoft.Samples.ServiceHosting.AspProviders
{
internal static class SecUtility
{
internal const int Infinite = Int32.MaxValue;
internal static bool ValidateParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize)
{
if (param == null)
{
return !checkForNull;
}
param = param.Trim();
if ((checkIfEmpty && param.Length < 1) ||
(maxSize > 0 && param.Length > maxSize) ||
(checkForCommas && param.Contains(",")))
{
return false;
}
return true;
}
internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
{
if (param == null)
{
if (checkForNull)
{
throw new ArgumentNullException(paramName);
}
return;
}
param = param.Trim();
if (checkIfEmpty && param.Length < 1)
{
throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' must not be empty.", paramName), paramName);
}
if (maxSize > 0 && param.Length > maxSize)
{
throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' is too long: it must not exceed {1} chars in length.", paramName, maxSize.ToString(CultureInfo.InvariantCulture)), paramName);
}
if (checkForCommas && param.Contains(","))
{
throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The parameter '{0}' must not contain commas.", paramName), paramName);
}
}
internal static void CheckArrayParameter(ref string[] param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
{
if (param == null)
{
throw new ArgumentNullException(paramName);
}
if (param.Length < 1)
{
throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The array parameter '{0}' should not be empty.", paramName), paramName);
}
Hashtable values = new Hashtable(param.Length);
for (int i = param.Length - 1; i >= 0; i--)
{
SecUtility.CheckParameter(ref param[i], checkForNull, checkIfEmpty, checkForCommas, maxSize,
paramName + "[ " + i.ToString(CultureInfo.InvariantCulture) + " ]");
if (values.Contains(param[i]))
{
throw new ArgumentException(string.Format(CultureInfo.InstalledUICulture, "The array '{0}' should not contain duplicate values.", paramName), paramName);
}
else
{
values.Add(param[i], param[i]);
}
}
}
internal static void SetUtcTime(DateTime value, out DateTime res)
{
res = TableStorageConstants.MinSupportedDateTime;
if ((value.Kind == DateTimeKind.Local && value.ToUniversalTime() < TableStorageConstants.MinSupportedDateTime) ||
value < TableStorageConstants.MinSupportedDateTime)
{
throw new ArgumentException("Invalid time value!");
}
if (value.Kind == DateTimeKind.Local)
{
res = value.ToUniversalTime();
}
else
{
res = value;
}
}
internal static bool IsValidTableName(string name)
{
if (string.IsNullOrEmpty(name))
{
return false;
}
Regex reg = new Regex(StorageClient.StorageHttpConstants.RegularExpressionStrings.ValidTableNameRegex);
if (reg.IsMatch(name))
{
return true;
}
else
{
return false;
}
}
internal static bool IsValidContainerName(string name)
{
if (string.IsNullOrEmpty(name))
{
return false;
}
Regex reg = new Regex(StorageClient.StorageHttpConstants.RegularExpressionStrings.ValidContainerNameRegex);
if (reg.IsMatch(name))
{
return true;
}
else
{
return false;
}
}
// the table storage system currently does not support the StartsWith() operation in
// queries. As a result we transform s.StartsWith(substring) into s.CompareTo(substring) > 0 &&
// s.CompareTo(NextComparisonString(substring)) < 0
// we assume that comparison on the service side is as ordinal comparison
internal static string NextComparisonString(string s)
{
if (string.IsNullOrEmpty(s))
{
throw new ArgumentException("The string argument must not be null or empty!");
}
string ret;
char last = s[s.Length - 1];
if ((int)last + 1 > (int)char.MaxValue)
{
throw new ArgumentException("Cannot convert the string.");
}
// don't use "as" because we want to have an explicit exception here if something goes wrong
last = (char)((int)last + 1);
ret = s.Substring(0, s.Length - 1) + last;
return ret;
}
// we use a normal character as the separator because of string comparison operations
// these have to be valid characters
internal const char KeySeparator = 'a';
internal static readonly string KeySeparatorString = new string(KeySeparator, 1);
internal const char EscapeCharacter = 'b';
internal static readonly string EscapeCharacterString = new string(EscapeCharacter, 1);
// Some characters can cause problems when they are contained in columns
// that are included in queries. We are very defensive here and escape a wide range
// of characters for key columns (as the key columns are present in most queries)
internal static bool IsInvalidKeyCharacter(char c)
{
return ((c < 32)
|| (c >= 127 && c < 160)
|| (c == '#')
|| (c == '&')
|| (c == '+')
|| (c == '/')
|| (c == '?')
|| (c == ':')
|| (c == '%')
|| (c == '\\')
);
}
internal static string CharToEscapeSequence(char c) {
string ret;
ret = EscapeCharacterString + string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c);
return ret;
}
internal static string Escape(string s)
{
if (string.IsNullOrEmpty(s)) {
return s;
}
StringBuilder ret = new StringBuilder();
foreach (char c in s)
{
if (c == EscapeCharacter || c == KeySeparator || IsInvalidKeyCharacter(c))
{
ret.Append(CharToEscapeSequence(c));
}
else
{
ret.Append(c);
}
}
return ret.ToString();
}
internal static string UnEscape(string s)
{
if (string.IsNullOrEmpty(s))
{
return s;
}
StringBuilder ret = new StringBuilder();
char c;
for (int i = 0; i < s.Length; i++)
{
c = s[i];
if (c == EscapeCharacter)
{
if (i + 2 >= s.Length)
{
throw new FormatException("The string " + s + " is not correctly escaped!");
}
int ascii = Convert.ToInt32(s.Substring(i + 1, 2), 16);
ret.Append((char)ascii);
i += 2;
}
else
{
ret.Append(c);
}
}
return ret.ToString();
}
internal static string CombineToKey(string s1, string s2)
{
return Escape(s1) + KeySeparator + Escape(s2);
}
internal static string EscapedFirst(string s)
{
return Escape(s) + KeySeparator;
}
internal static string GetFirstFromKey(string key) {
Debug.Assert(key.IndexOf(KeySeparator) != -1);
string first = key.Substring(0, key.IndexOf(KeySeparator));
return UnEscape(first);
}
internal static string GetSecondFromKey(string key)
{
Debug.Assert(key.IndexOf(KeySeparator) != -1);
string second = key.Substring(key.IndexOf(KeySeparator) + 1);
return UnEscape(second);
}
internal static void CheckAllowInsecureEndpoints(bool allowInsecureRemoteEndpoints, StorageAccountInfo info)
{
if (info == null)
{
throw new ArgumentNullException("info");
}
if (allowInsecureRemoteEndpoints)
{
return;
}
if (info.BaseUri == null || string.IsNullOrEmpty(info.BaseUri.Scheme))
{
throw new SecurityException("allowInsecureRemoteEndpoints is set to false (default setting) but the endpoint URL seems to be empty or there is no URL scheme." +
"Please configure the provider to use an https enpoint for the storage endpoint or " +
"explicitly set the configuration option allowInsecureRemoteEndpoints to true.");
}
if (info.BaseUri.Scheme.ToUpper(CultureInfo.InvariantCulture) == Uri.UriSchemeHttps.ToUpper(CultureInfo.InvariantCulture))
{
return;
}
if (info.BaseUri.IsLoopback)
{
return;
}
throw new SecurityException("The provider is configured with allowInsecureRemoteEndpoints set to false (default setting) but the endpoint for " +
"the storage system does not seem to be an https or local endpoint. " +
"Please configure the provider to use an https enpoint for the storage endpoint or " +
"explicitly set the configuration option allowInsecureRemoteEndpoints to true.");
}
}
internal static class Constants
{
internal const int MaxTableUsernameLength = 256;
internal const int MaxTableApplicationNameLength = 256;
}
/// <summary>
/// This delegate defines the shape of a provider retry policy.
/// Provider retry policies are only used to retry when a row retrieved from a table
/// was changed by another entity before it could be saved to the data store.A retry policy will invoke the given
/// <paramref name="action"/> as many times as it wants to in the face of
/// retriable InvalidOperationExceptions.
/// </summary>
/// <param name="action">The action to retry</param>
/// <returns></returns>
public delegate void ProviderRetryPolicy(Action action);
/// <summary>
/// We are using this retry policies for only one purpose: the ASP providers often read data from the server, process it
/// locally and then write the result back to the server. The problem is that the row that has been read might have changed
/// between the read and write operation. This retry policy is used to retry the whole process in this case.
/// </summary>
/// <summary>
/// Provides definitions for some standard retry policies.
/// </summary>
public static class ProviderRetryPolicies
{
public static readonly TimeSpan StandardMinBackoff = TimeSpan.FromMilliseconds(100);
public static readonly TimeSpan StandardMaxBackoff = TimeSpan.FromSeconds(30);
private static readonly Random Random = new Random();
/// <summary>
/// Policy that does no retries i.e., it just invokes <paramref name="action"/> exactly once
/// </summary>
/// <param name="action">The action to retry</param>
/// <returns>The return value of <paramref name="action"/></returns>
internal static void NoRetry(Action action)
{
action();
}
/// <summary>
/// Policy that retries a specified number of times with an exponential backoff scheme
/// </summary>
/// <param name="numberOfRetries">The number of times to retry. Should be a non-negative number.</param>
/// <param name="deltaBackoff">The multiplier in the exponential backoff scheme</param>
/// <returns></returns>
/// <remarks>For this retry policy, the minimum amount of milliseconds between retries is given by the
/// StandardMinBackoff constant, and the maximum backoff is predefined by the StandardMaxBackoff constant.
/// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff.</remarks>
public static ProviderRetryPolicy RetryN(int numberOfRetries, TimeSpan deltaBackoff)
{
return new ProviderRetryPolicy((Action action) =>
{
RetryNImpl(action, numberOfRetries, StandardMinBackoff, StandardMaxBackoff, deltaBackoff);
}
);
}
/// <summary>
/// Policy that retries a specified number of times with an exponential backoff scheme
/// </summary>
/// <param name="numberOfRetries">The number of times to retry. Should be a non-negative number</param>
/// <param name="deltaBackoff">The multiplier in the exponential backoff scheme</param>
/// <param name="minBackoff">The minimum backoff interval</param>
/// <param name="minBackoff">The minimum backoff interval</param>
/// <returns></returns>
/// <remarks>For this retry policy, the minimum amount of milliseconds between retries is given by the
/// minBackoff parameter, and the maximum backoff is predefined by the maxBackoff parameter.
/// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff.</remarks>
public static ProviderRetryPolicy RetryN(int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff)
{
return new ProviderRetryPolicy((Action action) =>
{
RetryNImpl(action, numberOfRetries, minBackoff, maxBackoff, deltaBackoff);
}
);
}
private static void RetryNImpl(Action action, int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff)
{
int totalNumberOfRetries = numberOfRetries;
int backoff;
if (minBackoff > maxBackoff)
{
throw new ArgumentException("The minimum backoff must not be larger than the maximum backoff period.");
}
if (minBackoff.TotalMilliseconds < 0)
{
throw new ArgumentException("The minimum backoff period must not be negative.");
}
do
{
try
{
action();
break;
}
catch (InvalidOperationException e)
{
HttpStatusCode status;
// precondition failed is the status code returned by the server to indicate that the etag is wrong
if (TableStorageHelpers.EvaluateException(e, out status))
{
if (status == HttpStatusCode.PreconditionFailed)
{
if (numberOfRetries == 0)
{
throw;
}
backoff = CalculateCurrentBackoff(minBackoff, maxBackoff, deltaBackoff, totalNumberOfRetries - numberOfRetries);
Debug.Assert(backoff >= minBackoff.TotalMilliseconds);
Debug.Assert(backoff <= maxBackoff.TotalMilliseconds);
if (backoff > 0)
{
Thread.Sleep(backoff);
}
}
else
{
throw;
}
}
else
{
throw;
}
}
}
while (numberOfRetries-- > 0);
}
private static int CalculateCurrentBackoff(TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff, int curRetry)
{
int backoff;
if (curRetry > 31)
{
backoff = (int)maxBackoff.TotalMilliseconds;
}
try
{
backoff = Random.Next((1 << curRetry) + 1);
// Console.WriteLine("backoff:" + backoff);
// Console.WriteLine("index:" + ((1 << curRetry) + 1));
backoff *= (int)deltaBackoff.TotalMilliseconds;
backoff += (int)minBackoff.TotalMilliseconds;
}
catch (OverflowException)
{
backoff = (int)maxBackoff.TotalMilliseconds;
}
if (backoff > (int)maxBackoff.TotalMilliseconds)
{
backoff = (int)maxBackoff.TotalMilliseconds;
}
Debug.Assert(backoff >= (int)minBackoff.TotalMilliseconds);
// Console.WriteLine("real backoff:" + backoff);
return backoff;
}
}
}

View file

@ -0,0 +1,881 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="TableStorageRoleProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Data.Services.Client;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Security;
using System.Web.Security;
using Microsoft.Samples.ServiceHosting.StorageClient;
namespace Microsoft.Samples.ServiceHosting.AspProviders
{
/// <summary>
/// This class allows DevtableGen to generate the correct table (named 'Roles')
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses",
Justification="Class is used by the devtablegen tool to generate a database for the development storage tool.")]
internal class RoleDataServiceContext : TableStorageDataServiceContext
{
public IQueryable<RoleRow> Roles
{
get
{
return this.CreateQuery<RoleRow>("Roles");
}
}
}
[CLSCompliant(false)]
public class RoleRow : TableStorageEntity
{
private string _applicationName;
private string _roleName;
private string _userName;
// applicationName + userName is partitionKey
// roleName is rowKey
public RoleRow(string applicationName, string roleName, string userName)
: base()
{
SecUtility.CheckParameter(ref applicationName, true, true, true, Constants.MaxTableApplicationNameLength, "applicationName");
SecUtility.CheckParameter(ref roleName, true, true, true, TableStorageRoleProvider.MaxTableRoleNameLength, "roleName");
SecUtility.CheckParameter(ref userName, true, false, true, Constants.MaxTableUsernameLength, "userName");
PartitionKey = SecUtility.CombineToKey(applicationName, userName);
RowKey = SecUtility.Escape(roleName);
ApplicationName = applicationName;
RoleName = roleName;
UserName = userName;
}
public RoleRow()
: base()
{
}
public string ApplicationName
{
set
{
if (value == null)
{
throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
}
_applicationName = value;
PartitionKey = SecUtility.CombineToKey(ApplicationName, UserName);
}
get
{
return _applicationName;
}
}
public string RoleName
{
set
{
if (value == null)
{
throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
}
_roleName = value;
RowKey = SecUtility.Escape(RoleName);
}
get
{
return _roleName;
}
}
public string UserName
{
set
{
if (value == null)
{
throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
}
_userName = value;
PartitionKey = SecUtility.CombineToKey(ApplicationName, UserName);
}
get
{
return _userName;
}
}
}
public class TableStorageRoleProvider : RoleProvider
{
#region Member variables and constants
internal const int MaxTableRoleNameLength = 512;
internal const int NumRetries = 3;
// member variables shared between most providers
private string _applicationName;
private string _accountName;
private string _sharedKey;
private string _tableName;
private string _membershipTableName;
private string _tableServiceBaseUri;
private TableStorage _tableStorage;
private object _lock = new object();
private RetryPolicy _tableRetry = RetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1));
// private ProviderRetryPolicy _providerRetry = ProviderRetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1));
#endregion
#region Properties
public override string ApplicationName
{
get { return _applicationName; }
set
{
lock (_lock)
{
SecUtility.CheckParameter(ref value, true, true, true, Constants.MaxTableApplicationNameLength, "ApplicationName");
_applicationName = value;
}
}
}
#endregion
#region Public methods
// RoleProvider methods
public override void Initialize(string name, NameValueCollection config)
{
// Verify that config isn't null
if (config == null)
{
throw new ArgumentNullException("config");
}
// Assign the provider a default name if it doesn't have one
if (String.IsNullOrEmpty(name))
{
name = "TableStorageRoleProvider";
}
// Add a default "description" attribute to config if the
// attribute doesn't exist or is empty
if (string.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", "Table storage-based role provider");
}
// Call the base class's Initialize method
base.Initialize(name, config);
bool allowInsecureRemoteEndpoints = Configuration.GetBooleanValue(config, "allowInsecureRemoteEndpoints", false);
// structure storage-related properties
ApplicationName = Configuration.GetStringValueWithGlobalDefault(config, "applicationName",
Configuration.DefaultProviderApplicationNameConfigurationString,
Configuration.DefaultProviderApplicationName, false);
_accountName = Configuration.GetStringValue(config, "accountName", null, true);
_sharedKey = Configuration.GetStringValue(config, "sharedKey", null, true);
_tableName = Configuration.GetStringValueWithGlobalDefault(config, "roleTableName",
Configuration.DefaultRoleTableNameConfigurationString,
Configuration.DefaultRoleTableName, false);
_membershipTableName = Configuration.GetStringValueWithGlobalDefault(config, "membershipTableName",
Configuration.DefaultMembershipTableNameConfigurationString,
Configuration.DefaultMembershipTableName, false);
_tableServiceBaseUri = Configuration.GetStringValue(config, "tableServiceBaseUri", null, true);
// remove required attributes
config.Remove("allowInsecureRemoteEndpoints");
config.Remove("applicationName");
config.Remove("accountName");
config.Remove("sharedKey");
config.Remove("roleTableName");
config.Remove("membershipTableName");
config.Remove("tableServiceBaseUri");
// Throw an exception if unrecognized attributes remain
if (config.Count > 0)
{
string attr = config.GetKey(0);
if (!String.IsNullOrEmpty(attr))
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Unrecognized attribute: {0}", attr));
}
}
StorageAccountInfo info = null;
try
{
info = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true);
if (_tableServiceBaseUri != null)
{
info.BaseUri = new Uri(_tableServiceBaseUri);
}
if (_accountName != null)
{
info.AccountName = _accountName;
}
if (_sharedKey != null)
{
info.Base64Key = _sharedKey;
}
info.CheckComplete();
SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, info);
_tableStorage = TableStorage.Create(info);
_tableStorage.RetryPolicy = _tableRetry;
_tableStorage.TryCreateTable(_tableName);
}
catch (SecurityException)
{
throw;
}
// catch InvalidOperationException as well as StorageException
catch (Exception e)
{
string exceptionDescription = Configuration.GetInitExceptionDescription(info, "table storage configuration");
string tableName = (_tableName == null) ? "no role table name specified" : _tableName;
Log.Write(EventKind.Error, "Could not create or find role table: " + tableName + "!" + Environment.NewLine +
exceptionDescription + Environment.NewLine +
e.Message + Environment.NewLine + e.StackTrace);
throw new ProviderException("Could not create or find role table. The most probable reason for this is that " +
"the storage endpoints are not configured correctly. Please look at the configuration settings " +
"in your .cscfg and Web.config files. More information about this error " +
"can be found in the logs when running inside the hosting environment or in the output " +
"window of Visual Studio.", e);
}
}
public override bool IsUserInRole(string username, string roleName)
{
SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename");
SecUtility.CheckParameter(ref username, true, false, true, Constants.MaxTableUsernameLength, "username");
if (username.Length < 1)
{
return false;
}
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
DataServiceQuery<RoleRow> queryObj = svc.CreateQuery<RoleRow>(_tableName);
IEnumerable<RoleRow> query = from user in queryObj
where (user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) ||
user.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty)) &&
user.RowKey == SecUtility.Escape(roleName)
select user;
TableStorageDataServiceQuery<RoleRow> q = new TableStorageDataServiceQuery<RoleRow>(query as DataServiceQuery<RoleRow>, _tableRetry);
IEnumerable<RoleRow> userRows = q.ExecuteWithRetries();
if (userRows == null)
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist.", roleName));
}
List<RoleRow> l = new List<RoleRow>(userRows);
if (l.Count == 0)
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist.", roleName));
}
RoleRow row;
if (IsStaleRole(l, out row)) {
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist.", roleName));
}
if (l.Count > 2)
{
throw new ProviderException("User name appears twice in the same role!");
}
if (l.Count == 1)
{
Debug.Assert(string.IsNullOrEmpty(l.ElementAt(0).UserName));
return false;
}
return true;
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
public override string[] GetRolesForUser(string username)
{
SecUtility.CheckParameter(ref username, true, false, true, Constants.MaxTableUsernameLength, "username");
if (username.Length < 1)
{
return new string[0];
}
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
DataServiceQuery<RoleRow> queryObj = svc.CreateQuery<RoleRow>(_tableName);
IEnumerable<RoleRow> query = from user in queryObj
where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) ||
user.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty)
select user;
TableStorageDataServiceQuery<RoleRow> q = new TableStorageDataServiceQuery<RoleRow>(query as DataServiceQuery<RoleRow>, _tableRetry);
IEnumerable<RoleRow> userRows = q.ExecuteAllWithRetries();
if (userRows == null)
{
return new string[0];
}
List<RoleRow> l = new List<RoleRow>(userRows);
if (l.Count == 0)
{
return new string[0];
}
List<string> ret = new List<string>();
foreach (RoleRow user in l) {
if (!string.IsNullOrEmpty(user.UserName) && !IsStaleRole(l, user.RoleName))
{
ret.Add(user.RoleName);
}
}
return ret.ToArray();
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
public override string[] GetUsersInRole(string roleName)
{
SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename");
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
DataServiceQuery<RoleRow> queryObj = svc.CreateQuery<RoleRow>(_tableName);
IEnumerable<RoleRow> query = from user in queryObj
where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 &&
user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 &&
user.RowKey == SecUtility.Escape(roleName)
select user;
TableStorageDataServiceQuery<RoleRow> q = new TableStorageDataServiceQuery<RoleRow>(query as DataServiceQuery<RoleRow>, _tableRetry);
IEnumerable<RoleRow> userRows = q.ExecuteAllWithRetries();
if (userRows == null)
{
// role does not exist; we are supposed to throw an exception here
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", roleName));
}
List<RoleRow> l = new List<RoleRow>(userRows);
if (l.Count == 0 || IsStaleRole(l, roleName))
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", roleName));
}
List<string> ret = new List<string>();
foreach (RoleRow user in l)
{
if (!string.IsNullOrEmpty(user.UserName))
{
ret.Add(user.UserName);
}
}
return ret.ToArray();
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
public override string[] GetAllRoles()
{
try
{
DataServiceContext svc = CreateDataServiceContext();
DataServiceQuery<RoleRow> queryObj = svc.CreateQuery<RoleRow>(_tableName);
IEnumerable<RoleRow> query = from role in queryObj
where role.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty)
select role;
TableStorageDataServiceQuery<RoleRow> q = new TableStorageDataServiceQuery<RoleRow>(query as DataServiceQuery<RoleRow>, _tableRetry);
IEnumerable<RoleRow> userRows = q.ExecuteAllWithRetries();
if (userRows == null)
{
return new string[0];
}
List<RoleRow> l = new List<RoleRow>(userRows);
if (l.Count == 0)
{
return new string[0];
}
List<string> ret = new List<string>();
foreach (RoleRow role in l)
{
Debug.Assert(role.UserName != null);
if (string.IsNullOrEmpty(role.UserName))
{
ret.Add(role.RoleName);
}
}
return ret.ToArray();
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
public override bool RoleExists(string roleName)
{
SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename");
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
DataServiceQuery<RoleRow> queryObj = svc.CreateQuery<RoleRow>(_tableName);
IEnumerable<RoleRow> query = from role in queryObj
where role.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty) &&
role.RowKey == SecUtility.Escape(roleName)
select role;
TableStorageDataServiceQuery<RoleRow> q = new TableStorageDataServiceQuery<RoleRow>(query as DataServiceQuery<RoleRow>, _tableRetry);
try
{
// this query addresses exactly one result
// we thus should get an exception if there is no element
q.ExecuteWithRetries();
return true;
}
catch (DataServiceQueryException e)
{
HttpStatusCode s;
if (TableStorageHelpers.EvaluateException(e, out s) && s == HttpStatusCode.NotFound)
{
return false;
}
else
{
throw;
}
}
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
public override void CreateRole(string roleName)
{
SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename");
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
RoleRow newRole = new RoleRow(_applicationName, roleName, string.Empty);
svc.AddObject(_tableName, newRole);
svc.SaveChangesWithRetries();
}
catch (InvalidOperationException e)
{
HttpStatusCode status;
// when retry policies are used we cannot distinguish between a conflict and success
// so, in the case of a conflict, we just retrun success here
if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.Conflict)
{
return;
// the role already exists
}
throw new ProviderException("Error accessing role table.", e);
}
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename");
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
DataServiceQuery<RoleRow> queryObj = svc.CreateQuery<RoleRow>(_tableName);
IEnumerable<RoleRow> query = from userRole in queryObj
where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 &&
userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 &&
userRole.RowKey == SecUtility.Escape(roleName)
select userRole;
TableStorageDataServiceQuery<RoleRow> q = new TableStorageDataServiceQuery<RoleRow>(query as DataServiceQuery<RoleRow>, _tableRetry);
IEnumerable<RoleRow> userRows = q.ExecuteAllWithRetries();
if (userRows == null)
{
return false;
}
List<RoleRow> l = new List<RoleRow>(userRows);
if (l.Count == 0)
{
// the role does not exist
return false;
}
RoleRow role;
if (IsStaleRole(l, out role)) {
return false;
}
if (l.Count > 1 && throwOnPopulatedRole) {
throw new ProviderException("Cannot delete populated role.");
}
svc.DeleteObject(role);
svc.SaveChangesWithRetries();
// lets try to remove all remaining elements in the role
foreach(RoleRow row in l) {
if (row != role) {
try
{
svc.DeleteObject(row);
svc.SaveChangesWithRetries();
}
catch (InvalidOperationException ex)
{
HttpStatusCode status;
if (TableStorageHelpers.EvaluateException(ex, out status) && (status == HttpStatusCode.NoContent || status == HttpStatusCode.NotFound))
{
// this element already was already deleted by another process or during a failed retry
// this is not a fatal error; continue deleting elements
Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", row.UserName, row.RoleName));
}
else
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error deleting user {0} from role {1}.", row.UserName, row.RoleName));
}
}
}
}
return true;
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
// Because of limited transactional support in the table storage offering, this function gives limited guarantees
// for inserting all users into all roles.
// We do not recommend using this function because of missing transactional support.
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
SecUtility.CheckArrayParameter(ref roleNames, true, true, true, MaxTableRoleNameLength, "roleNames");
SecUtility.CheckArrayParameter(ref usernames, true, true, true, Constants.MaxTableUsernameLength, "usernames");
RoleRow row;
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
foreach (string role in roleNames)
{
if (!RoleExists(role))
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", role));
}
foreach (string user in usernames)
{
row = new RoleRow(_applicationName, role, user);
try
{
svc.AddObject(_tableName, row);
svc.SaveChangesWithRetries();
}
catch (InvalidOperationException e)
{
HttpStatusCode status;
if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.Conflict)
{
// this element already exists or was created in a failed retry
// this is not a fatal error; continue adding elements
Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} already exists in the role {1}.", user, role));
svc.Detach(row);
}
else
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error adding user {0} to role {1}", user, role));
}
}
}
}
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
// the username to match can be in a format that varies between providers
// for this implementation, a syntax similar to the one used in the SQL provider is applied
// "user%" will return all users in a role that start with the string "user"
// the % sign can only appear at the end of the usernameToMatch parameter
// because the current version of the table storage service does not support StartsWith in LINQ queries,
// calling this function can cause significant network trafic when '%' is used in the usernameToMach
// parameter
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename");
SecUtility.CheckParameter(ref usernameToMatch, true, true, false, Constants.MaxTableUsernameLength, "usernameToMatch");
bool startswith = false;
if (usernameToMatch.Contains('%'))
{
if (usernameToMatch.IndexOf('%') != usernameToMatch.Length - 1)
{
throw new ArgumentException("The TableStorageRoleProvider only supports search strings that contain '%' as the last character!");
}
usernameToMatch = usernameToMatch.Substring(0, usernameToMatch.Length - 1);
startswith = true;
}
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
DataServiceQuery<RoleRow> queryObj = svc.CreateQuery<RoleRow>(_tableName);
IEnumerable<RoleRow> query;
if (startswith && string.IsNullOrEmpty(usernameToMatch)) {
// get all users in the role
query = from userRole in queryObj
where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 &&
userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 &&
userRole.RowKey == SecUtility.Escape(roleName)
select userRole;
} else if (startswith) {
// get all users in the role that start with the specified string (we cannot restrict the query more because StartsWith is not supported)
// we cannot include the username to search for in the key, because the key might e escaped
query = from userRole in queryObj
where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 &&
userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 &&
userRole.RowKey == SecUtility.Escape(roleName) &&
(userRole.UserName.CompareTo(usernameToMatch) >= 0 || userRole.UserName == string.Empty)
select userRole;
} else {
// get a specific user
query = from userRole in queryObj
where (userRole.PartitionKey == SecUtility.CombineToKey(_applicationName, usernameToMatch) ||
userRole.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty)) &&
userRole.RowKey == SecUtility.Escape(roleName)
select userRole;
}
TableStorageDataServiceQuery<RoleRow> q = new TableStorageDataServiceQuery<RoleRow>(query as DataServiceQuery<RoleRow>, _tableRetry);
IEnumerable<RoleRow> userRows = q.ExecuteAllWithRetries();
if (userRows == null)
{
throw new ProviderException("The role does not exist!");
}
List<RoleRow> l = new List<RoleRow>(userRows);
if (l.Count == 0)
{
// the role does not exist
throw new ProviderException("The role does not exist!");
}
RoleRow role;
if (IsStaleRole(l, out role))
{
throw new ProviderException("The role does not exist!");
}
List<string> ret = new List<string>();
foreach (RoleRow row in l)
{
if (row != role)
{
if (startswith && !string.IsNullOrEmpty(usernameToMatch) && !row.UserName.StartsWith(usernameToMatch, StringComparison.Ordinal))
{
continue;
}
ret.Add(row.UserName);
}
}
return ret.ToArray();
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
// remember that there is no is no rollback functionality for the table storage service right now
// be cautious when using this function
// if a role does not exist, we stop deleting roles, if a user in a role does not exist, we continue deleting
// in case of error conditions, the behavior of this function is different than the SQL role provider
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
SecUtility.CheckArrayParameter(ref roleNames, true, true, true, MaxTableRoleNameLength, "roleNames");
SecUtility.CheckArrayParameter(ref usernames, true, true, true, Constants.MaxTableUsernameLength, "usernames");
RoleRow row;
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
foreach (string role in roleNames)
{
if (!RoleExists(role))
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", role));
}
foreach (string user in usernames)
{
row = GetUserInRole(svc, role, user);
if (row == null)
{
Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", user, role));
continue;
}
try
{
svc.DeleteObject(row);
svc.SaveChangesWithRetries();
}
catch (Exception e)
{
HttpStatusCode status;
if (TableStorageHelpers.EvaluateException(e, out status) && (status == HttpStatusCode.NoContent || status == HttpStatusCode.NotFound))
{
Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", user, role));
svc.Detach(row);
}
else
{
throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error deleting user {0} from role {1}.", user, role));
}
}
}
}
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
#endregion
#region Helper methods
private TableStorageDataServiceContext CreateDataServiceContext()
{
return _tableStorage.GetDataServiceContext();
}
private static bool IsStaleRole(List<RoleRow> l, string rolename) {
if (l == null || l.Count == 0)
{
return false;
}
foreach (RoleRow row in l)
{
// if (row.RoleName == rolename && row.UserName == string.Empty)
if (string.Compare(row.RoleName, rolename, StringComparison.Ordinal) == 0 && string.IsNullOrEmpty(row.UserName))
{
return false;
}
}
return true;
}
private static bool IsStaleRole(List<RoleRow> l, out RoleRow role)
{
role = null;
if (l == null || l.Count == 0)
{
return false;
}
string rolename = l.ElementAt(0).RoleName;
foreach (RoleRow row in l)
{
Debug.Assert(row.RoleName == rolename);
if (string.IsNullOrEmpty(row.UserName))
{
role = row;
return false;
}
}
return true;
}
private RoleRow GetUserInRole(DataServiceContext svc, string rolename, string username)
{
SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username");
SecUtility.CheckParameter(ref rolename, true, true, true, MaxTableRoleNameLength, "rolename");
try
{
DataServiceQuery<RoleRow> queryObj = svc.CreateQuery<RoleRow>(_tableName);
IEnumerable<RoleRow> query = from user in queryObj
where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) &&
user.RowKey == SecUtility.Escape(rolename)
select user;
TableStorageDataServiceQuery<RoleRow> q = new TableStorageDataServiceQuery<RoleRow>(query as DataServiceQuery<RoleRow>, _tableRetry);
try
{
IEnumerable<RoleRow> userRows = q.ExecuteAllWithRetries();
return userRows.First();
}
catch (DataServiceQueryException e)
{
HttpStatusCode s;
if (TableStorageHelpers.EvaluateException(e, out s) && s == HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error while accessing the data store.", e);
}
}
#endregion
}
}

View file

@ -0,0 +1,882 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="TableStorageSessionStateProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// This file contains an implementation of a session state provider that uses both blob and table storage.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Data.Services.Client;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Security;
using System.Web;
using System.Web.SessionState;
using Microsoft.Samples.ServiceHosting.StorageClient;
namespace Microsoft.Samples.ServiceHosting.AspProviders
{
/// <summary>
/// This class allows DevtableGen to generate the correct table (named 'Sessions')
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses",
Justification="Class is used by the devtablegen tool to generate a database for the development storage tool")]
internal class SessionDataServiceContext : TableStorageDataServiceContext
{
public IQueryable<SessionRow> Sessions
{
get
{
return this.CreateQuery<SessionRow>("Sessions");
}
}
}
[CLSCompliant(false)]
public class SessionRow : TableStorageEntity
{
private string _id;
private string _applicationName;
private string _blobName;
private DateTime _expires;
private DateTime _created;
private DateTime _lockDate;
// application name + session id is partitionKey
public SessionRow(string sessionId, string applicationName)
: base()
{
SecUtility.CheckParameter(ref sessionId, true, true, true, TableStorageConstants.MaxStringPropertySizeInChars, "sessionId");
SecUtility.CheckParameter(ref applicationName, true, true, true, Constants.MaxTableApplicationNameLength, "applicationName");
PartitionKey = SecUtility.CombineToKey(applicationName, sessionId);
RowKey = string.Empty;
Id = sessionId;
ApplicationName = applicationName;
ExpiresUtc = TableStorageConstants.MinSupportedDateTime;
LockDateUtc = TableStorageConstants.MinSupportedDateTime;
CreatedUtc = TableStorageConstants.MinSupportedDateTime;
Timeout = 0;
BlobName = string.Empty;
}
public SessionRow()
: base()
{
}
public string Id
{
set {
if (value == null)
{
throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
}
_id = value;
PartitionKey = SecUtility.CombineToKey(ApplicationName, Id);
}
get
{
return _id;
}
}
public string ApplicationName
{
set
{
if (value == null)
{
throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
}
_applicationName = value;
PartitionKey = SecUtility.CombineToKey(ApplicationName, Id);
}
get
{
return _applicationName;
}
}
public int Timeout
{
set;
get;
}
public DateTime ExpiresUtc
{
set
{
SecUtility.SetUtcTime(value, out _expires);
}
get
{
return _expires;
}
}
public DateTime CreatedUtc
{
set
{
SecUtility.SetUtcTime(value, out _created);
}
get
{
return _created;
}
}
public DateTime LockDateUtc
{
set
{
SecUtility.SetUtcTime(value, out _lockDate);
}
get
{
return _lockDate;
}
}
public bool Locked
{
set;
get;
}
public int Lock
{
set;
get;
}
public string BlobName
{
set
{
if (value == null)
{
throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
}
_blobName = value;
}
get
{
return _blobName;
}
}
public bool Initialized
{
set;
get;
}
}
public class TableStorageSessionStateProvider : SessionStateStoreProviderBase
{
#region Member variables and constants
private string _applicationName;
private string _accountName;
private string _sharedKey;
private string _tableName;
private string _tableServiceBaseUri;
private string _blobServiceBaseUri;
private string _containerName;
private TableStorage _tableStorage;
private BlobProvider _blobProvider;
private const int NumRetries = 3;
private RetryPolicy _tableRetry = RetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1));
private ProviderRetryPolicy _providerRetry = ProviderRetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1));
#endregion
#region public methods
public override void Initialize(string name, NameValueCollection config)
{
// Verify that config isn't null
if (config == null)
{
throw new ArgumentNullException("config");
}
// Assign the provider a default name if it doesn't have one
if (String.IsNullOrEmpty(name))
{
name = "TableServiceSessionStateProvider";
}
// Add a default "description" attribute to config if the
// attribute doesn't exist or is empty
if (string.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", "Session state provider using table storage");
}
// Call the base class's Initialize method
base.Initialize(name, config);
bool allowInsecureRemoteEndpoints = Configuration.GetBooleanValue(config, "allowInsecureRemoteEndpoints", false);
// structure storage-related properties
_applicationName = Configuration.GetStringValueWithGlobalDefault(config, "applicationName",
Configuration.DefaultProviderApplicationNameConfigurationString,
Configuration.DefaultProviderApplicationName, false);
_accountName = Configuration.GetStringValue(config, "accountName", null, true);
_sharedKey = Configuration.GetStringValue(config, "sharedKey", null, true);
_tableName = Configuration.GetStringValueWithGlobalDefault(config, "sessionTableName",
Configuration.DefaultSessionTableNameConfigurationString,
Configuration.DefaultSessionTableName, false);
_tableServiceBaseUri = Configuration.GetStringValue(config, "tableServiceBaseUri", null, true);
_containerName = Configuration.GetStringValueWithGlobalDefault(config, "containerName",
Configuration.DefaultSessionContainerNameConfigurationString,
Configuration.DefaultSessionContainerName, false);
if (!SecUtility.IsValidContainerName(_containerName))
{
throw new ProviderException("The provider configuration for the TableStorageSessionStateProvider does not contain a valid container name. " +
"Please refer to the documentation for the concrete rules for valid container names." +
"The current container name is: " + _containerName);
}
_blobServiceBaseUri = Configuration.GetStringValue(config, "blobServiceBaseUri", null, true);
config.Remove("allowInsecureRemoteEndpoints");
config.Remove("accountName");
config.Remove("sharedKey");
config.Remove("containerName");
config.Remove("applicationName");
config.Remove("blobServiceBaseUri");
config.Remove("tableServiceBaseUri");
config.Remove("sessionTableName");
// Throw an exception if unrecognized attributes remain
if (config.Count > 0)
{
string attr = config.GetKey(0);
if (!String.IsNullOrEmpty(attr))
throw new ProviderException
("Unrecognized attribute: " + attr);
}
StorageAccountInfo tableInfo = null;
StorageAccountInfo blobInfo = null;
try
{
tableInfo = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true);
blobInfo = StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration(true);
if (_tableServiceBaseUri != null)
{
tableInfo.BaseUri = new Uri(_tableServiceBaseUri);
}
if (_blobServiceBaseUri != null)
{
blobInfo.BaseUri = new Uri(_blobServiceBaseUri);
}
if (_accountName != null)
{
tableInfo.AccountName = _accountName;
blobInfo.AccountName = _accountName;
}
if (_sharedKey != null)
{
tableInfo.Base64Key = _sharedKey;
blobInfo.Base64Key = _sharedKey;
}
tableInfo.CheckComplete();
SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, tableInfo);
blobInfo.CheckComplete();
SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, blobInfo);
_tableStorage = TableStorage.Create(tableInfo);
_tableStorage.RetryPolicy = _tableRetry;
_tableStorage.TryCreateTable(_tableName);
_blobProvider = new BlobProvider(blobInfo, _containerName);
}
catch (SecurityException)
{
throw;
}
// catch InvalidOperationException as well as StorageException
catch (Exception e)
{
string exceptionDescription = Configuration.GetInitExceptionDescription(tableInfo, blobInfo);
string tableName = (_tableName == null) ? "no session table name specified" : _tableName;
string containerName = (_containerName == null) ? "no container name specified" : _containerName;
Log.Write(EventKind.Error, "Initialization of data service structures (tables and/or blobs) failed!" +
exceptionDescription + Environment.NewLine +
"Configured blob container: " + containerName + Environment.NewLine +
"Configured table name: " + tableName + Environment.NewLine +
e.Message + Environment.NewLine + e.StackTrace);
throw new ProviderException("Initialization of data service structures (tables and/or blobs) failed!" +
"The most probable reason for this is that " +
"the storage endpoints are not configured correctly. Please look at the configuration settings " +
"in your .cscfg and Web.config files. More information about this error " +
"can be found in the logs when running inside the hosting environment or in the output " +
"window of Visual Studio.", e);
}
Debug.Assert(_blobProvider != null);
}
public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
{
Debug.Assert(context != null);
return new SessionStateStoreData(new SessionStateItemCollection(),
SessionStateUtility.GetSessionStaticObjects(context),
timeout);
}
public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
{
Debug.Assert(context != null);
SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id");
if (timeout < 0)
{
throw new ArgumentException("Parameter timeout must be a non-negative integer!");
}
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
SessionRow session = new SessionRow(id, _applicationName);
session.Lock = 0; // no lock
session.Initialized = false;
session.Id = id;
session.Timeout = timeout;
session.ExpiresUtc = DateTime.UtcNow.AddMinutes(timeout);
svc.AddObject(_tableName, session);
svc.SaveChangesWithRetries();
} catch (InvalidOperationException e) {
throw new ProviderException("Error accessing the data store.", e);
}
}
public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge,
out object lockId, out SessionStateActions actions)
{
Debug.Assert(context != null);
SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id");
return GetSession(context, id, out locked, out lockAge, out lockId, out actions, false);
}
public override SessionStateStoreData GetItemExclusive(HttpContext context, string id,
out bool locked, out TimeSpan lockAge, out object lockId,
out SessionStateActions actions)
{
Debug.Assert(context != null);
SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id");
return GetSession(context, id, out locked, out lockAge, out lockId, out actions, true);
}
public override void SetAndReleaseItemExclusive(HttpContext context, string id,
SessionStateStoreData item, object lockId, bool newItem)
{
Debug.Assert(context != null);
SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id");
_providerRetry(() =>
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
SessionRow session;
if (!newItem)
{
session = GetSession(id, svc);
if (session == null || session.Lock != (int)lockId)
{
Debug.Assert(false);
return;
}
}
else
{
session = new SessionRow(id, _applicationName);
session.Lock = 1;
session.LockDateUtc = DateTime.UtcNow;
}
session.Initialized = true;
Debug.Assert(session.Timeout >= 0);
session.Timeout = item.Timeout;
session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout);
session.Locked = false;
// yes, we always create a new blob here
session.BlobName = GetBlobNamePrefix(id) + Guid.NewGuid().ToString("N");
// Serialize the session and write the blob
byte[] items, statics;
SerializeSession(item, out items, out statics);
string serializedItems = Convert.ToBase64String(items);
string serializedStatics = Convert.ToBase64String(statics);
MemoryStream output = new MemoryStream();
StreamWriter writer = new StreamWriter(output);
try
{
writer.WriteLine(serializedItems);
writer.WriteLine(serializedStatics);
writer.Flush();
// for us, it shouldn't matter whether newItem is set to true or false
// because we always create the entire blob and cannot append to an
// existing one
_blobProvider.UploadStream(session.BlobName, output);
writer.Close();
output.Close();
}
catch (Exception e)
{
if (!newItem)
{
ReleaseItemExclusive(svc, session, lockId);
}
throw new ProviderException("Error accessing the data store.", e);
}
finally
{
if (writer != null)
{
writer.Close();
}
if (output != null)
{
output.Close();
}
}
if (newItem)
{
svc.AddObject(_tableName, session);
svc.SaveChangesWithRetries();
}
else
{
// Unlock the session and save changes
ReleaseItemExclusive(svc, session, lockId);
}
});
}
public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
{
Debug.Assert(context != null);
Debug.Assert(lockId != null);
SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id");
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
SessionRow session = GetSession(id, svc);
ReleaseItemExclusive(svc, session, lockId);
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error accessing the data store!", e);
}
}
public override void ResetItemTimeout(HttpContext context, string id)
{
Debug.Assert(context != null);
SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id");
_providerRetry(() => {
TableStorageDataServiceContext svc = CreateDataServiceContext();
SessionRow session = GetSession(id, svc);
session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout);
svc.UpdateObject(session);
svc.SaveChangesWithRetries();
});
}
public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
{
Debug.Assert(context != null);
Debug.Assert(lockId != null);
Debug.Assert(_blobProvider != null);
SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id");
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
SessionRow session = GetSession(id, svc);
if (session == null)
{
Debug.Assert(false);
return;
}
if (session.Lock != (int)lockId)
{
Debug.Assert(false);
return;
}
svc.DeleteObject(session);
svc.SaveChangesWithRetries();
}
catch (InvalidOperationException e)
{
throw new ProviderException("Error accessing the data store!", e);
}
// delete associated blobs
try
{
IEnumerable<BlobProperties> e = _blobProvider.ListBlobs(GetBlobNamePrefix(id));
if (e == null)
{
return;
}
IEnumerator<BlobProperties> props = e.GetEnumerator();
if (props == null)
{
return;
}
while (props.MoveNext())
{
if (props.Current != null)
{
if (!_blobProvider.DeleteBlob(props.Current.Name)) {
// ignore this; it is possible that another thread could try to delete the blob
// at the same time
}
}
}
} catch(StorageException e) {
throw new ProviderException("Error accessing blob storage.", e);
}
}
public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
{
// This provider doesn't support expiration callbacks
// so simply return false here
return false;
}
public override void InitializeRequest(HttpContext context)
{
// no specific logic for initializing requests in this provider
}
public override void EndRequest(HttpContext context)
{
// no specific logic for ending requests in this provider
}
// nothing can be done here because there might be session managers at different machines involved in
// handling sessions
public override void Dispose()
{
}
#endregion
#region Helper methods
private TableStorageDataServiceContext CreateDataServiceContext()
{
return _tableStorage.GetDataServiceContext();
}
private static void ReleaseItemExclusive(TableStorageDataServiceContext svc, SessionRow session, object lockId)
{
if ((int)lockId != session.Lock)
{
// obviously that can happen, but let's see when at least in Debug mode
Debug.Assert(false);
return;
}
session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout);
session.Locked = false;
svc.UpdateObject(session);
svc.SaveChangesWithRetries();
}
private SessionRow GetSession(string id)
{
DataServiceContext svc = CreateDataServiceContext();
return GetSession(id, svc);
}
private SessionRow GetSession(string id, DataServiceContext context)
{
Debug.Assert(context != null);
Debug.Assert(id != null && id.Length <= TableStorageConstants.MaxStringPropertySizeInChars);
try
{
DataServiceQuery<SessionRow> queryObj = context.CreateQuery<SessionRow>(_tableName);
IEnumerable<SessionRow> query = from session in queryObj
where session.PartitionKey == SecUtility.CombineToKey(_applicationName, id)
select session;
TableStorageDataServiceQuery<SessionRow> q = new TableStorageDataServiceQuery<SessionRow>(query as DataServiceQuery<SessionRow>, _tableRetry);
IEnumerable<SessionRow> sessions = q.ExecuteWithRetries();
// enumerate the result and store it in a list
List<SessionRow> sessionList = new List<SessionRow>(sessions);
if (sessionList != null && sessionList.Count() == 1)
{
return sessionList.First();
} else if (sessionList != null && sessionList.Count() > 1) {
throw new ProviderException("Multiple sessions with the same name!");
}
else
{
return null;
}
}
catch (Exception e)
{
throw new ProviderException("Error accessing storage.", e);
}
}
// we don't use the retry policy itself in this function because out parameters are not well handled by
// retry policies
private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge,
out object lockId, out SessionStateActions actions,
bool exclusive)
{
Debug.Assert(context != null);
SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id");
SessionRow session = null;
int curRetry = 0;
bool retry = false;
// Assign default values to out parameters
locked = false;
lockId = null;
lockAge = TimeSpan.Zero;
actions = SessionStateActions.None;
do
{
retry = false;
try
{
TableStorageDataServiceContext svc = CreateDataServiceContext();
session = GetSession(id, svc);
// Assign default values to out parameters
locked = false;
lockId = null;
lockAge = TimeSpan.Zero;
actions = SessionStateActions.None;
// if the blob does not exist, we return null
// ASP.NET will call the corresponding method for creating the session
if (session == null)
{
return null;
}
if (session.Initialized == false)
{
Debug.Assert(session.Locked == false);
actions = SessionStateActions.InitializeItem;
session.Initialized = true;
}
session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout);
if (exclusive)
{
if (!session.Locked)
{
if (session.Lock == Int32.MaxValue)
{
session.Lock = 0;
}
else
{
session.Lock++;
}
session.LockDateUtc = DateTime.UtcNow;
}
lockId = session.Lock;
locked = session.Locked;
session.Locked = true;
}
lockAge = DateTime.UtcNow.Subtract(session.LockDateUtc);
lockId = session.Lock;
if (locked == true)
{
return null;
}
// let's try to write this back to the data store
// in between, someone else could have written something to the store for the same session
// we retry a number of times; if all fails, we throw an exception
svc.UpdateObject(session);
svc.SaveChangesWithRetries();
}
catch (InvalidOperationException e)
{
HttpStatusCode status;
// precondition fails indicates problems with the status code
if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.PreconditionFailed)
{
retry = true;
}
else
{
throw new ProviderException("Error accessing the data store.", e);
}
}
} while (retry && curRetry++ < NumRetries);
// ok, now we have successfully written back our state
// we can now read the blob
// note that we do not need to care about read/write locking when accessing the
// blob because each time we write a new session we create a new blob with a different name
SessionStateStoreData result = null;
MemoryStream stream = null;
StreamReader reader = null;
BlobProperties properties;
try
{
try
{
stream = _blobProvider.GetBlobContent(session.BlobName, out properties);
}
catch (StorageException e)
{
throw new ProviderException("Couldn't read session blob!", e);
}
reader = new StreamReader(stream);
if (actions == SessionStateActions.InitializeItem)
{
// Return an empty SessionStateStoreData
result = new SessionStateStoreData(new SessionStateItemCollection(),
SessionStateUtility.GetSessionStaticObjects(context), session.Timeout);
}
else
{
// Read Items, StaticObjects, and Timeout from the file
byte[] items = Convert.FromBase64String(reader.ReadLine());
byte[] statics = Convert.FromBase64String(reader.ReadLine());
int timeout = session.Timeout;
// Deserialize the session
result = DeserializeSession(items, statics, timeout);
}
}
finally
{
if (stream != null)
{
stream.Close();
}
if (reader != null)
{
reader.Close();
}
}
return result;
}
private string GetBlobNamePrefix(string id)
{
return string.Format(CultureInfo.InstalledUICulture, "{0}{1}", id, _applicationName);
}
private static void SerializeSession(SessionStateStoreData store, out byte[] items, out byte[] statics)
{
bool hasItems = (store.Items != null && store.Items.Count > 0);
bool hasStaticObjects = (store.StaticObjects != null && store.StaticObjects.Count > 0 && !store.StaticObjects.NeverAccessed);
items = null;
statics = new byte [0];
using (MemoryStream stream1 = new MemoryStream())
{
using (BinaryWriter writer1 = new BinaryWriter(stream1))
{
writer1.Write(hasItems);
if (hasItems)
{
((SessionStateItemCollection)store.Items).Serialize(writer1);
}
items = stream1.ToArray();
}
}
if (hasStaticObjects)
{
throw new ProviderException("Static objects are not supported in this provider because of security-related hosting constraints.");
}
}
private static SessionStateStoreData DeserializeSession(byte[] items, byte[] statics, int timeout)
{
SessionStateItemCollection itemCol = null;
HttpStaticObjectsCollection staticCol = null;
using (MemoryStream stream1 = new MemoryStream(items))
{
using (BinaryReader reader1 = new BinaryReader(stream1))
{
bool hasItems = reader1.ReadBoolean();
if (hasItems)
{
itemCol = SessionStateItemCollection.Deserialize(reader1);
}
else
{
itemCol = new SessionStateItemCollection();
}
}
}
if (HttpContext.Current != null && HttpContext.Current.Application != null &&
HttpContext.Current.Application.StaticObjects != null && HttpContext.Current.Application.StaticObjects.Count > 0) {
throw new ProviderException("This provider does not support static session objects because of security-related hosting constraints.");
}
if (statics != null && statics.Count() > 0) {
throw new ProviderException("This provider does not support static session objects because of security-related hosting constraints.");
}
return new SessionStateStoreData(itemCol, staticCol, timeout);
}
#endregion
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\AspProviders.dll
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\AspProviders.pdb
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\StorageClient.dll
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\StorageClient.pdb
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\bin\Debug\StorageClient.xml
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\obj\Debug\ResolveAssemblyReference.cache
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\obj\Debug\AspProviders.dll
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\AspProviders\obj\Debug\AspProviders.pdb
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\AspProviders.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\AspProviders.pdb
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\StorageClient.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\StorageClient.pdb
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Debug\StorageClient.xml
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Debug\ResolveAssemblyReference.cache
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Debug\AspProviders.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Debug\AspProviders.pdb

View file

@ -0,0 +1,7 @@
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Release\AspProviders.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Release\AspProviders.pdb
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Release\StorageClient.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\bin\Release\StorageClient.pdb
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Release\ResolveAssemblyReference.cache
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Release\AspProviders.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\AspProviders\obj\Release\AspProviders.pdb

View file

@ -0,0 +1,525 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="Authentication.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.Collections;
using System.Collections.Specialized;
using System.Net;
using System.Web;
using System.Security.Cryptography;
using System.Globalization;
using System.Diagnostics;
namespace Microsoft.Samples.ServiceHosting.StorageClient
{
/// <summary>
/// This type represents the different constituent parts that make up a resource Uri in the context of cloud services.
/// </summary>
public class ResourceUriComponents
{
/// <summary>
/// The account name in the URI.
/// </summary>
public string AccountName { get; set; }
/// <summary>
/// This is really the first component (delimited by '/') after the account name. Since it happens to
/// be a container name in the context of all our storage services (containers in blob storage,
/// queues in the queue service and table names in table storage), it's named as ContainerName to make it more
/// readable at the cost of slightly being incorrectly named.
/// </summary>
public string ContainerName { get; set; }
/// <summary>
/// The remaining string in the URI.
/// </summary>
public string RemainingPart { get; set; }
/// <summary>
/// Construct a ResourceUriComponents object.
/// </summary>
/// <param name="accountName">The account name that should become part of the URI.</param>
/// <param name="containerName">The container name (container, queue or table name) that should become part of the URI.</param>
/// <param name="remainingPart">Remaining part of the URI.</param>
public ResourceUriComponents(string accountName, string containerName, string remainingPart)
{
this.AccountName = accountName;
this.ContainerName = containerName;
this.RemainingPart = remainingPart;
}
/// <summary>
/// Construct a ResourceUriComponents object.
/// </summary>
/// <param name="accountName">The account name that should become part of the URI.</param>
/// <param name="containerName">The container name (container, queue or table name) that should become part of the URI.</param>
public ResourceUriComponents(string accountName, string containerName)
: this(accountName, containerName, null)
{
}
/// <summary>
/// Construct a ResourceUriComponents object.
/// </summary>
/// <param name="accountName">The account name that should become part of the URI.</param>
public ResourceUriComponents(string accountName)
: this(accountName, null, null)
{
}
/// <summary>
/// Construct a ResourceUriComponents object.
/// </summary>
public ResourceUriComponents()
{
}
}
internal static class MessageCanonicalizer
{
/// <summary>
/// An internal class that stores the canonicalized string version of an HTTP request.
/// </summary>
private class CanonicalizedString
{
private StringBuilder canonicalizedString = new StringBuilder();
/// <summary>
/// Property for the canonicalized string.
/// </summary>
internal string Value
{
get
{
return canonicalizedString.ToString();
}
}
/// <summary>
/// Constructor for the class.
/// </summary>
/// <param name="initialElement">The first canonicalized element to start the string with.</param>
internal CanonicalizedString(string initialElement)
{
canonicalizedString.Append(initialElement);
}
/// <summary>
/// Append additional canonicalized element to the string.
/// </summary>
/// <param name="element">An additional canonicalized element to append to the string.</param>
internal void AppendCanonicalizedElement(string element)
{
canonicalizedString.Append(StorageHttpConstants.ConstChars.Linefeed);
canonicalizedString.Append(element);
}
}
/// <summary>
/// Create a canonicalized string out of HTTP request header contents for signing
/// blob/queue requests with the Shared Authentication scheme.
/// </summary>
/// <param name="address">The uri address of the HTTP request.</param>
/// <param name="uriComponents">Components of the Uri extracted out of the request.</param>
/// <param name="method">The method of the HTTP request (GET/PUT, etc.).</param>
/// <param name="contentType">The content type of the HTTP request.</param>
/// <param name="date">The date of the HTTP request.</param>
/// <param name="headers">Should contain other headers of the HTTP request.</param>
/// <returns>A canonicalized string of the HTTP request.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase",
Justification = "Authentication algorithm requires canonicalization by converting to lower case")]
internal static string CanonicalizeHttpRequest(
Uri address,
ResourceUriComponents uriComponents,
string method,
string contentType,
string date,
NameValueCollection headers)
{
// The first element should be the Method of the request.
// I.e. GET, POST, PUT, or HEAD.
CanonicalizedString canonicalizedString = new CanonicalizedString(method);
// The second element should be the MD5 value.
// This is optional and may be empty.
string httpContentMD5Value = string.Empty;
// First extract all the content MD5 values from the header.
ArrayList httpContentMD5Values = HttpRequestAccessor.GetHeaderValues(headers, StorageHttpConstants.HeaderNames.ContentMD5);
// If we only have one, then set it to the value we want to append to the canonicalized string.
if (httpContentMD5Values.Count == 1)
{
httpContentMD5Value = (string)httpContentMD5Values[0];
}
canonicalizedString.AppendCanonicalizedElement(httpContentMD5Value);
// The third element should be the content type.
canonicalizedString.AppendCanonicalizedElement(contentType);
// The fourth element should be the request date.
// See if there's an storage date header.
// If there's one, then don't use the date header.
ArrayList httpStorageDateValues = HttpRequestAccessor.GetHeaderValues(headers, StorageHttpConstants.HeaderNames.StorageDateTime);
if (httpStorageDateValues.Count > 0)
{
date = null;
}
canonicalizedString.AppendCanonicalizedElement(date);
// Look for header names that start with StorageHttpConstants.HeaderNames.PrefixForStorageHeader
// Then sort them in case-insensitive manner.
ArrayList httpStorageHeaderNameArray = new ArrayList();
foreach (string key in headers.Keys)
{
if (key.ToLowerInvariant().StartsWith(StorageHttpConstants.HeaderNames.PrefixForStorageHeader, StringComparison.Ordinal))
{
httpStorageHeaderNameArray.Add(key.ToLowerInvariant());
}
}
httpStorageHeaderNameArray.Sort();
// Now go through each header's values in the sorted order and append them to the canonicalized string.
foreach (string key in httpStorageHeaderNameArray)
{
StringBuilder canonicalizedElement = new StringBuilder(key);
string delimiter = ":";
ArrayList values = HttpRequestAccessor.GetHeaderValues(headers, key);
// Go through values, unfold them, and then append them to the canonicalized element string.
foreach (string value in values)
{
// Unfolding is simply removal of CRLF.
string unfoldedValue = value.Replace(StorageHttpConstants.ConstChars.CarriageReturnLinefeed, string.Empty);
// Append it to the canonicalized element string.
canonicalizedElement.Append(delimiter);
canonicalizedElement.Append(unfoldedValue);
delimiter = ",";
}
// Now, add this canonicalized element to the canonicalized header string.
canonicalizedString.AppendCanonicalizedElement(canonicalizedElement.ToString());
}
// Now we append the canonicalized resource element.
string canonicalizedResource = GetCanonicalizedResource(address, uriComponents);
canonicalizedString.AppendCanonicalizedElement(canonicalizedResource);
return canonicalizedString.Value;
}
internal static string GetCanonicalizedResource(Uri address, ResourceUriComponents uriComponents)
{
// Algorithem is as follows
// 1. Start with the empty string ("")
// 2. Append the account name owning the resource preceded by a /. This is not
// the name of the account making the request but the account that owns the
// resource being accessed.
// 3. Append the path part of the un-decoded HTTP Request-URI, up-to but not
// including the query string.
// 4. If the request addresses a particular component of a resource, like?comp=
// metadata then append the sub-resource including question mark (like ?comp=
// metadata)
StringBuilder canonicalizedResource = new StringBuilder(StorageHttpConstants.ConstChars.Slash);
canonicalizedResource.Append(uriComponents.AccountName);
// Note that AbsolutePath starts with a '/'.
canonicalizedResource.Append(address.AbsolutePath);
NameValueCollection queryVariables = HttpUtility.ParseQueryString(address.Query);
string compQueryParameterValue = queryVariables[StorageHttpConstants.QueryParams.QueryParamComp];
if (compQueryParameterValue != null)
{
canonicalizedResource.Append(StorageHttpConstants.ConstChars.QuestionMark);
canonicalizedResource.Append(StorageHttpConstants.QueryParams.QueryParamComp);
canonicalizedResource.Append(StorageHttpConstants.QueryParams.SeparatorForParameterAndValue);
canonicalizedResource.Append(compQueryParameterValue);
}
return canonicalizedResource.ToString();
}
/// <summary>
/// Canonicalize HTTP header contents.
/// </summary>
/// <param name="request">An HttpWebRequest object.</param>
/// <param name="uriComponents">Components of the Uri extracted out of the request.</param>
/// <returns>The canonicalized string of the given HTTP request's header.</returns>
internal static string CanonicalizeHttpRequest(HttpWebRequest request, ResourceUriComponents uriComponents)
{
return CanonicalizeHttpRequest(
request.Address, uriComponents, request.Method, request.ContentType, string.Empty, request.Headers);
}
/// <summary>
/// Creates a standard datetime string for the shared key lite authentication scheme.
/// </summary>
/// <param name="dateTime">DateTime value to convert to a string in the expected format.</param>
/// <returns></returns>
internal static string ConvertDateTimeToHttpString(DateTime dateTime)
{
// On the wire everything should be represented in UTC. This assert will catch invalid callers who
// are violating this rule.
Debug.Assert(dateTime == DateTime.MaxValue || dateTime == DateTime.MinValue || dateTime.Kind == DateTimeKind.Utc);
// 'R' means rfc1123 date which is what the storage services use for all dates...
// It will be in the following format:
// Sun, 28 Jan 2008 12:11:37 GMT
return dateTime.ToString("R", CultureInfo.InvariantCulture);
}
private static string AppendStringToCanonicalizedString(StringBuilder canonicalizedString, string stringToAppend)
{
canonicalizedString.Append(StorageHttpConstants.ConstChars.Linefeed);
canonicalizedString.Append(stringToAppend);
return canonicalizedString.ToString();
}
internal static string CanonicalizeHttpRequestForSharedKeyLite(HttpWebRequest request, ResourceUriComponents uriComponents, string date)
{
StringBuilder canonicalizedString = new StringBuilder(date);
AppendStringToCanonicalizedString(canonicalizedString, MessageCanonicalizer.GetCanonicalizedResource(request.Address, uriComponents));
return canonicalizedString.ToString();
}
}
/// <summary>
/// Use this class to extract various header values from Http requests.
/// </summary>
public static class HttpRequestAccessor
{
/// <summary>
/// A helper function for extracting HTTP header values from a NameValueCollection object.
/// </summary>
/// <param name="headers">A NameValueCollection object that should contain HTTP header name-values pairs.</param>
/// <param name="headerName">Name of the header that we want to get values of.</param>
/// <returns>A array list of values for the header. The values are in the same order as they are stored in the NameValueCollection object.</returns>
internal static ArrayList GetHeaderValues(NameValueCollection headers, string headerName)
{
ArrayList arrayOfValues = new ArrayList();
string[] values = headers.GetValues(headerName);
if (values != null)
{
foreach (string value in values)
{
// canonization formula requires the string to be left trimmed.
arrayOfValues.Add(value.TrimStart());
}
}
return arrayOfValues;
}
/// <summary>
/// Constructs an URI given all its constituents
/// </summary>
/// <param name="endpoint">
/// This is the service endpoint in case of path-style URIs and a host suffix in case of host-style URIs
/// IMPORTANT: This does NOT include the service name or account name
/// </param>
/// <param name="uriComponents">Uri constituents</param>
/// <param name="pathStyleUri">Indicates whether to construct a path-style Uri (true) or host-style URI (false)</param>
/// <returns>Full uri</returns>
public static Uri ConstructResourceUri(Uri endpoint, ResourceUriComponents uriComponents, bool pathStyleUri)
{
return pathStyleUri ?
ConstructPathStyleResourceUri(endpoint, uriComponents) :
ConstructHostStyleResourceUri(endpoint, uriComponents);
}
/// <summary>
/// Constructs a path-style resource URI given all its constituents
/// </summary>
private static Uri ConstructPathStyleResourceUri(Uri endpoint, ResourceUriComponents uriComponents)
{
StringBuilder path = new StringBuilder(string.Empty);
if (uriComponents.AccountName != null)
{
path.Append(uriComponents.AccountName);
if (uriComponents.ContainerName != null)
{
path.Append(StorageHttpConstants.ConstChars.Slash);
path.Append(uriComponents.ContainerName);
if (uriComponents.RemainingPart != null)
{
path.Append(StorageHttpConstants.ConstChars.Slash);
path.Append(uriComponents.RemainingPart);
}
}
}
return ConstructUriFromUriAndString(endpoint, path.ToString());
}
/// <summary>
/// Constructs a host-style resource URI given all its constituents
/// </summary>
private static Uri ConstructHostStyleResourceUri(Uri hostSuffix, ResourceUriComponents uriComponents)
{
if (uriComponents.AccountName == null)
{
// When there is no account name, full URI is same as hostSuffix
return hostSuffix;
}
else
{
// accountUri will be something like "http://accountname.hostSuffix/" and then we append
// container name and remaining part if they are present.
Uri accountUri = ConstructHostStyleAccountUri(hostSuffix, uriComponents.AccountName);
StringBuilder path = new StringBuilder(string.Empty);
if (uriComponents.ContainerName != null)
{
path.Append(uriComponents.ContainerName);
if (uriComponents.RemainingPart != null)
{
path.Append(StorageHttpConstants.ConstChars.Slash);
path.Append(uriComponents.RemainingPart);
}
}
return ConstructUriFromUriAndString(accountUri, path.ToString());
}
}
/// <summary>
/// Given the host suffix part, service name and account name, this method constructs the account Uri
/// </summary>
private static Uri ConstructHostStyleAccountUri(Uri hostSuffix, string accountName)
{
// Example:
// Input: serviceEndpoint="http://blob.windows.net/", accountName="youraccount"
// Output: accountUri="http://youraccount.blob.windows.net/"
Uri serviceUri = hostSuffix;
// serviceUri in our example would be "http://blob.windows.net/"
string accountUriString = string.Format(CultureInfo.InvariantCulture,
"{0}{1}{2}.{3}:{4}/",
serviceUri.Scheme,
Uri.SchemeDelimiter,
accountName,
serviceUri.Host,
serviceUri.Port);
return new Uri(accountUriString);
}
private static Uri ConstructUriFromUriAndString(
Uri endpoint,
string path)
{
// This is where we encode the url path to be valid
string encodedPath = HttpUtility.UrlPathEncode(path);
return new Uri(endpoint, encodedPath);
}
}
/// <summary>
/// Objects of this class contain the credentials (name and key) of a storage account.
/// </summary>
public class SharedKeyCredentials
{
/// <summary>
/// Create a SharedKeyCredentials object given an account name and a shared key.
/// </summary>
public SharedKeyCredentials(string accountName, byte[] key)
{
this.accountName = accountName;
this.key = key;
}
/// <summary>
/// Signs the request appropriately to make it an authenticated request.
/// Note that this method takes the URI components as decoding the URI components requires the knowledge
/// of whether the URI is in path-style or host-style and a host-suffix if it's host-style.
/// </summary>
public void SignRequest(HttpWebRequest request, ResourceUriComponents uriComponents)
{
if (request == null)
throw new ArgumentNullException("request");
string message = MessageCanonicalizer.CanonicalizeHttpRequest(request, uriComponents);
string computedBase64Signature = ComputeMacSha(message);
request.Headers.Add(StorageHttpConstants.HeaderNames.Authorization,
string.Format(CultureInfo.InvariantCulture,
"{0} {1}:{2}",
StorageHttpConstants.AuthenticationSchemeNames.SharedKeyAuthSchemeName,
accountName,
computedBase64Signature));
}
/// <summary>
/// Signs requests using the SharedKeyLite authentication scheme with is used for the table storage service.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lite",
Justification = "Name of the authentication scheme in the REST protocol")]
public void SignRequestForSharedKeyLite(HttpWebRequest request, ResourceUriComponents uriComponents)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
// add the date header to the request
string dateString = MessageCanonicalizer.ConvertDateTimeToHttpString(DateTime.UtcNow);
request.Headers.Add(StorageHttpConstants.HeaderNames.StorageDateTime, dateString);
// compute the signature and add the authentication scheme
string message = MessageCanonicalizer.CanonicalizeHttpRequestForSharedKeyLite(request, uriComponents, dateString);
string computedBase64Signature = ComputeMacSha(message);
request.Headers.Add(StorageHttpConstants.HeaderNames.Authorization,
string.Format(CultureInfo.InvariantCulture,
"{0} {1}:{2}",
StorageHttpConstants.AuthenticationSchemeNames.SharedKeyLiteAuthSchemeName,
accountName,
computedBase64Signature));
}
private string ComputeMacSha(string canonicalizedString)
{
byte[] dataToMAC = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);
using (HMACSHA256 hmacsha1 = new HMACSHA256(key))
{
return System.Convert.ToBase64String(hmacsha1.ComputeHash(dataToMAC));
}
}
private string accountName;
private byte[] key;
}
}

View file

@ -0,0 +1,924 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="BlobStorage.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Collections.Specialized;
using System.Threading;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Diagnostics;
[assembly:CLSCompliant(true)]
// disable the generation of warnings for missing documentation elements for
// public classes/members in this file
#pragma warning disable 1591
namespace Microsoft.Samples.ServiceHosting.StorageClient
{
/// <summary>
/// This delegate define the shape of a retry policy. A retry policy will invoke the given
/// <paramref name="action"/> as many times as it wants to in the face of
/// retriable StorageServerExceptions.
/// </summary>
/// <param name="action">The action to retry</param>
/// <returns></returns>
public delegate void RetryPolicy(Action action);
#region Blob Storage API
/// <summary>
/// The entry point of the blob storage API
/// </summary>
public abstract class BlobStorage
{
/// <summary>
/// Factory method for BlobStorage
/// </summary>
/// <param name="baseUri">The base URI of the blob storage service</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 name of the storage account</param>
/// <param name="base64Key">Authentication key used for signing requests</param>
/// <returns>A newly created BlobStorage instance</returns>
public static BlobStorage Create(
Uri baseUri,
bool? usePathStyleUris,
string accountName,
string base64Key
)
{
//We create a StorageAccountInfo and then extract the properties of that object.
//This is because the constructor of StorageAccountInfo does normalization of BaseUri.
StorageAccountInfo accountInfo = new StorageAccountInfo(
baseUri,
usePathStyleUris,
accountName,
base64Key
);
return new BlobStorageRest(
accountInfo.BaseUri,
accountInfo.UsePathStyleUris,
accountInfo.AccountName,
accountInfo.Base64Key
);
}
/// <summary>
/// Factory method for BlobStorage
/// </summary>
/// <param name="accountInfo">Account information</param>
/// <returns>A newly created BlobStorage instance</returns>
public static BlobStorage Create(StorageAccountInfo accountInfo)
{
return new BlobStorageRest(
accountInfo.BaseUri,
accountInfo.UsePathStyleUris,
accountInfo.AccountName,
accountInfo.Base64Key
);
}
/// <summary>
/// Get a reference to a newly created BlobContainer object.
/// This method does not make any calls to the storage service.
/// </summary>
/// <param name="containerName">The name of the container</param>
/// <returns>A reference to a newly created BlobContainer object</returns>
public abstract BlobContainer GetBlobContainer(string containerName);
/// <summary>
/// Lists the containers within the account.
/// </summary>
/// <returns>A list of containers</returns>
public abstract IEnumerable<BlobContainer> ListBlobContainers();
/// <summary>
/// The time out for each request to the storage service.
/// </summary>
public TimeSpan Timeout
{
get;
set;
}
/// <summary>
/// The retry policy used for retrying requests
/// </summary>
public RetryPolicy RetryPolicy
{
get;
set;
}
/// <summary>
/// The base URI of the blob storage service
/// </summary>
public Uri BaseUri
{
get
{
return this.baseUri;
}
}
/// <summary>
/// The name of the storage account
/// </summary>
public string AccountName
{
get
{
return this.accountName;
}
}
/// <summary>
/// Indicates whether to use/generate path-style or host-style URIs
/// </summary>
public bool UsePathStyleUris
{
get
{
return this.usePathStyleUris;
}
}
/// <summary>
/// The default timeout
/// </summary>
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
Justification = "TimeSpan is a non-mutable type")]
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
/// <summary>
/// The default retry policy
/// </summary>
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
Justification = "RetryPolicy is a non-mutable type")]
public static readonly RetryPolicy DefaultRetryPolicy = RetryPolicies.NoRetry;
internal protected BlobStorage(Uri baseUri,
bool? usePathStyleUris,
string accountName,
string base64Key
)
{
this.baseUri = baseUri;
this.accountName = accountName;
this.Base64Key = base64Key;
if (usePathStyleUris == null)
this.usePathStyleUris = Utilities.StringIsIPAddress(baseUri.Host);
else
this.usePathStyleUris = usePathStyleUris.Value;
Timeout = DefaultTimeout;
RetryPolicy = DefaultRetryPolicy;
}
private bool usePathStyleUris;
private Uri baseUri;
private string accountName;
protected internal string Base64Key
{
get;
set;
}
}
/// <summary>
/// Provides definitions for some standard retry policies.
/// </summary>
public static class RetryPolicies
{
public static readonly TimeSpan StandardMinBackoff = TimeSpan.FromMilliseconds(100);
public static readonly TimeSpan StandardMaxBackoff = TimeSpan.FromSeconds(30);
private static readonly Random random = new Random();
/// <summary>
/// Policy that does no retries i.e., it just invokes <paramref name="action"/> exactly once
/// </summary>
/// <param name="action">The action to retry</param>
/// <returns>The return value of <paramref name="action"/></returns>
public static void NoRetry(Action action)
{
try
{
action();
}
catch (TableRetryWrapperException e)
{
throw e.InnerException;
}
}
/// <summary>
/// Policy that retries a specified number of times with a specified fixed time interval between retries
/// </summary>
/// <param name="numberOfRetries">The number of times to retry. Should be a non-negative number</param>
/// <param name="intervalBetweenRetries">The time interval between retries. Use TimeSpan.Zero to specify immediate
/// retries</param>
/// <returns></returns>
/// <remarks>When <paramref name="numberOfRetries"/> is 0 and <paramref name="intervalBetweenRetries"/> is
/// TimeSpan.Zero this policy is equivalent to the NoRetry policy</remarks>
public static RetryPolicy RetryN(int numberOfRetries, TimeSpan intervalBetweenRetries)
{
return new RetryPolicy((Action action) =>
{
RetryNImpl(action, numberOfRetries, intervalBetweenRetries);
}
);
}
/// <summary>
/// Policy that retries a specified number of times with a randomized exponential backoff scheme
/// </summary>
/// <param name="numberOfRetries">The number of times to retry. Should be a non-negative number.</param>
/// <param name="deltaBackoff">The multiplier in the exponential backoff scheme</param>
/// <returns></returns>
/// <remarks>For this retry policy, the minimum amount of milliseconds between retries is given by the
/// StandardMinBackoff constant, and the maximum backoff is predefined by the StandardMaxBackoff constant.
/// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff.</remarks>
public static RetryPolicy RetryExponentialN(int numberOfRetries, TimeSpan deltaBackoff)
{
return new RetryPolicy((Action action) =>
{
RetryExponentialNImpl(action, numberOfRetries, StandardMinBackoff, StandardMaxBackoff, deltaBackoff);
}
);
}
/// <summary>
/// Policy that retries a specified number of times with a randomized exponential backoff scheme
/// </summary>
/// <param name="numberOfRetries">The number of times to retry. Should be a non-negative number</param>
/// <param name="deltaBackoff">The multiplier in the exponential backoff scheme</param>
/// <param name="minBackoff">The minimum backoff interval</param>
/// <param name="maxBackoff">The maximum backoff interval</param>
/// <returns></returns>
/// <remarks>For this retry policy, the minimum amount of milliseconds between retries is given by the
/// minBackoff parameter, and the maximum backoff is predefined by the maxBackoff parameter.
/// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff.</remarks>
public static RetryPolicy RetryExponentialN(int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff)
{
if (minBackoff > maxBackoff)
{
throw new ArgumentException("The minimum backoff must not be larger than the maximum backoff period.");
}
if (minBackoff < TimeSpan.Zero)
{
throw new ArgumentException("The minimum backoff period must not be negative.");
}
return new RetryPolicy((Action action) =>
{
RetryExponentialNImpl(action, numberOfRetries, minBackoff, maxBackoff, deltaBackoff);
}
);
}
#region private helper methods
private static void RetryNImpl(Action action, int numberOfRetries, TimeSpan intervalBetweenRetries)
{
do
{
try
{
action();
break;
}
catch (StorageServerException)
{
if (numberOfRetries == 0)
{
throw;
}
if (intervalBetweenRetries > TimeSpan.Zero)
{
Thread.Sleep(intervalBetweenRetries);
}
}
catch (TableRetryWrapperException e)
{
if (numberOfRetries == 0)
{
throw e.InnerException;
}
if (intervalBetweenRetries > TimeSpan.Zero)
{
Thread.Sleep(intervalBetweenRetries);
}
}
}
while (numberOfRetries-- > 0);
}
private static void RetryExponentialNImpl(Action action, int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff)
{
int totalNumberOfRetries = numberOfRetries;
TimeSpan backoff;
// sanity check
// this is already checked when creating the retry policy in case other than the standard settings are used
// because this library is available in source code, the standard settings can be changed and thus we
// check again at this point
if (minBackoff > maxBackoff)
{
throw new ArgumentException("The minimum backoff must not be larger than the maximum backoff period.");
}
if (minBackoff < TimeSpan.Zero)
{
throw new ArgumentException("The minimum backoff period must not be negative.");
}
do
{
try
{
action();
break;
}
catch (StorageServerException)
{
if (numberOfRetries == 0)
{
throw;
}
backoff = CalculateCurrentBackoff(minBackoff, maxBackoff, deltaBackoff, totalNumberOfRetries - numberOfRetries);
Debug.Assert(backoff >= minBackoff);
Debug.Assert(backoff <= maxBackoff);
if (backoff > TimeSpan.Zero) {
Thread.Sleep(backoff);
}
}
catch (TableRetryWrapperException e)
{
if (numberOfRetries == 0)
{
throw e.InnerException;
}
backoff = CalculateCurrentBackoff(minBackoff, maxBackoff, deltaBackoff, totalNumberOfRetries - numberOfRetries);
Debug.Assert(backoff >= minBackoff);
Debug.Assert(backoff <= maxBackoff);
if (backoff > TimeSpan.Zero)
{
Thread.Sleep(backoff);
}
}
}
while (numberOfRetries-- > 0);
}
private static TimeSpan CalculateCurrentBackoff(TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff, int curRetry)
{
long backoff;
if (curRetry > 30)
{
backoff = maxBackoff.Ticks;
}
else
{
try
{
checked
{
// only randomize the multiplier here
// it would be as correct to randomize the whole backoff result
lock (random)
{
backoff = random.Next((1 << curRetry) + 1);
}
// Console.WriteLine("backoff:" + backoff);
// Console.WriteLine("random range: [0, " + ((1 << curRetry) + 1) + "]");
backoff *= deltaBackoff.Ticks;
backoff += minBackoff.Ticks;
}
}
catch (OverflowException)
{
backoff = maxBackoff.Ticks;
}
if (backoff > maxBackoff.Ticks)
{
backoff = maxBackoff.Ticks;
}
}
Debug.Assert(backoff >= minBackoff.Ticks);
Debug.Assert(backoff <= maxBackoff.Ticks);
return TimeSpan.FromTicks(backoff);
}
#endregion
}
/// <summary>
/// Access control for containers
/// </summary>
public enum ContainerAccessControl
{
Private,
Public
}
/// <summary>
/// The blob container class.
/// Used to access and enumerate blobs in the container.
/// Storage key credentials are needed to access private blobs but not for public blobs.
/// </summary>
public abstract class BlobContainer
{
/// <summary>
/// Use this constructor to access private blobs.
/// </summary>
/// <param name="baseUri">The base Uri for the storage endpoint</param>
/// <param name="accountName">Name of the storage account</param>
/// <param name="containerName">Name of the container</param>
internal protected BlobContainer(Uri baseUri, string accountName, string containerName)
: this(baseUri, true, accountName, containerName, DateTime.MinValue)
{}
/// <summary>
/// Use this constructor to access private blobs.
/// </summary>
/// <param name="baseUri">The base Uri for the storage endpoint</param>
/// <param name="usePathStyleUris">
/// If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used and if false
/// host-style URIs (http://accountname.baseuri/containername/objectname) are used, where baseuri is the
/// URI of the service
/// </param>
/// <param name="accountName">Name of the storage account</param>
/// <param name="containerName">Name of the container</param>
/// <param name="lastModified">Date of last modification</param>
internal protected BlobContainer(Uri baseUri, bool usePathStyleUris, string accountName, string containerName, DateTime lastModified)
{
if (!Utilities.IsValidContainerOrQueueName(containerName))
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The specified container name \"{0}\" is not valid!" +
"Please choose a name that conforms to the naming conventions for containers!", containerName));
}
this.baseUri = baseUri;
this.usePathStyleUris = usePathStyleUris;
this.accountName = accountName;
this.containerName = containerName;
this.Timeout = BlobStorage.DefaultTimeout;
this.RetryPolicy = BlobStorage.DefaultRetryPolicy;
this.LastModifiedTime = lastModified;
}
/// <summary>
/// The time out for each request to the storage service.
/// </summary>
public TimeSpan Timeout
{
get;
set;
}
/// <summary>
/// The retry policy used for retrying requests
/// </summary>
public RetryPolicy RetryPolicy
{
get;
set;
}
/// <summary>
/// The base URI of the blob storage service
/// </summary>
public Uri BaseUri
{
get
{
return this.baseUri;
}
}
/// <summary>
/// The name of the storage account
/// </summary>
public string AccountName
{
get
{
return this.accountName;
}
}
/// <summary>
/// The name of the blob container.
/// </summary>
public string ContainerName
{
get
{
return this.containerName;
}
}
/// <summary>
/// Indicates whether to use/generate path-style or host-style URIs
/// </summary>
public bool UsePathStyleUris
{
get
{
return this.usePathStyleUris;
}
}
/// <summary>
/// The URI of the container
/// </summary>
public abstract Uri ContainerUri
{
get;
}
/// <summary>
/// The timestamp for last modification of container.
/// </summary>
public DateTime LastModifiedTime
{
get;
protected set;
}
/// <summary>
/// Create the container if it does not exist.
/// The container is created with private access control and no metadata.
/// </summary>
/// <returns>true if the container was created. false if the container already exists</returns>
public abstract bool CreateContainer();
/// <summary>
/// Create the container with the specified metadata and access control if it does not exist
/// </summary>
/// <param name="metadata">The metadata for the container. Can be null to indicate no metadata</param>
/// <param name="accessControl">The access control (public or private) with which to create the container</param>
/// <returns>true if the container was created. false if the container already exists</returns>
public abstract bool CreateContainer(NameValueCollection metadata, ContainerAccessControl accessControl);
/// <summary>
/// Check if the blob container exists
/// </summary>
/// <returns>true if the container exists, false otherwise.</returns>
public abstract bool DoesContainerExist();
/// <summary>
/// Get the properties for the container if it exists.
/// </summary>
/// <returns>The properties for the container if it exists, null otherwise</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification="The method makes a call to the blob service")]
public abstract ContainerProperties GetContainerProperties();
/// <summary>
/// Get the access control permissions associated with the container.
/// </summary>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "The method makes a call to the blob service")]
public abstract ContainerAccessControl GetContainerAccessControl();
/// <summary>
/// Set the access control permissions associated with the container.
/// </summary>
/// <param name="acl">The permission to set</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "The method makes a call to the blob service")]
public abstract void SetContainerAccessControl(ContainerAccessControl acl);
/// <summary>
/// Deletes the current container.
/// </summary>
public abstract bool DeleteContainer();
/// <summary>
/// Check if the blob container exists
/// </summary>
/// <param name="blobName">Name of the BLOB.</param>
/// <returns>true if the blob exists, false otherwise.</returns>
public abstract bool DoesBlobExist(string blobName);
/// <summary>
/// Create a new blob or overwrite an existing blob.
/// </summary>
/// <param name="blobProperties">The properties of the blob</param>
/// <param name="blobContents">The contents of the blob</param>
/// <param name="overwrite">Should this request overwrite an existing blob ?</param>
/// <returns>true if the blob was created. false if the blob already exists and <paramref name="overwrite"/>was set to false</returns>
/// <remarks>The LastModifiedTime property of <paramref name="blobProperties"/> is set as a result of this call.
/// This method also has an effect on the ETag values that are managed by the service.</remarks>
public abstract bool CreateBlob(BlobProperties blobProperties, BlobContents blobContents, bool overwrite);
/// <summary>
/// Updates an existing blob if it has not been modified since the specified time which is typically
/// the last modified time of the blob when you retrieved it.
/// Use this method to implement optimistic concurrency by avoiding clobbering changes to the blob
/// made by another writer.
/// </summary>
/// <param name="blob">The properties of the blob. This object should be one previously
/// obtained from a call to GetBlob or GetBlobProperties and have its LastModifiedTime property set.</param>
/// <param name="contents">The contents of the blob. The contents of the blob should be readable</param>
/// <returns>true if the blob was updated. false if the blob has changed since the last time</returns>
/// <remarks>The LastModifiedTime property of <paramref name="blob"/> is set as a result of this call.
/// This method also has an effect on the ETag values that are managed by the service if the update was
/// successful.</remarks>
public abstract bool UpdateBlobIfNotModified(BlobProperties blob, BlobContents contents);
/// <summary>
/// Get the blob contents and properties if the blob exists
/// </summary>
/// <param name="name">The name of the blob</param>
/// <param name="blobContents">Object in which the contents are returned.
/// This object should contain a writable stream or should be a default constructed object.</param>
/// <param name="transferAsChunks">Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure.</param>
/// <returns>The properties of the blob if the blob exists.</returns>
public abstract BlobProperties GetBlob(string name, BlobContents blobContents, bool transferAsChunks);
/// <summary>
/// Gets the blob contents and properties if the blob has not been modified since the time specified.
/// Use this method if you have cached the contents of a blob and want to avoid retrieving the blob
/// if it has not changed since the last time you retrieved it.
/// </summary>
/// <param name="blobProperties">The properties of the blob obtained from an earlier call to GetBlob. This
/// parameter is updated by the call if the blob has been modified</param>
/// <param name="blobContents">Contains the stream to which the contents of the blob are written if it has been
/// modified</param>
/// <param name="transferAsChunks">Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure.</param>
/// <returns>true if the blob has been modified, false otherwise</returns>
public abstract bool GetBlobIfModified(BlobProperties blobProperties, BlobContents blobContents, bool transferAsChunks);
/// <summary>
/// Get the properties of the blob if it exists.
/// This method is also the simplest way to check if a blob exists.
/// </summary>
/// <param name="name">The name of the blob</param>
/// <returns>The properties of the blob if it exists. null otherwise.
/// The properties for the contents of the blob are not set</returns>
public abstract BlobProperties GetBlobProperties(string name);
/// <summary>
/// Set the metadata of an existing blob.
/// </summary>
/// <param name="blobProperties">The blob properties object whose metadata is to be updated</param>
public abstract void UpdateBlobMetadata(BlobProperties blobProperties);
/// <summary>
/// Set the metadata of an existing blob if it has not been modified since it was last retrieved.
/// </summary>
/// <param name="blobProperties">The blob properties object whose metadata is to be updated.
/// Typically obtained by a previous call to GetBlob or GetBlobProperties</param>
/// <returns>true if the blob metadata was updated. false if it was not updated because the blob
/// has been modified</returns>
public abstract bool UpdateBlobMetadataIfNotModified(BlobProperties blobProperties);
/// <summary>
/// Delete a blob with the given name
/// </summary>
/// <param name="name">The name of the blob</param>
/// <returns>true if the blob exists and was successfully deleted, false if the blob does not exist</returns>
public abstract bool DeleteBlob(string name);
/// <summary>
/// Delete a blob with the given name if the blob has not been modified since it was last obtained.
/// Use this method for optimistic concurrency to avoid deleting a blob that has been modified since
/// the last time you retrieved it
/// </summary>
/// <param name="blob">A blob object (typically previously obtained from a GetBlob call)</param>
/// <param name="modified">This out parameter is set to true if the blob was not deleted because
/// it was modified</param>
/// <returns>true if the blob exists and was successfully deleted, false if the blob does not exist or was
/// not deleted because the blob was modified.</returns>
public abstract bool DeleteBlobIfNotModified(BlobProperties blob, out bool modified);
/// <summary>
/// Enumerates all blobs with a given prefix.
/// </summary>
/// <param name="prefix"></param>
/// <param name="combineCommonPrefixes">If true common prefixes with "/" as seperator</param>
/// <returns>The list of blob properties and common prefixes</returns>
public abstract IEnumerable<object> ListBlobs(string prefix, bool combineCommonPrefixes);
private Uri baseUri;
private string accountName;
private string containerName;
private bool usePathStyleUris;
}
/// <summary>
/// The properties of a blob.
/// No member of this class makes a storage service request.
/// </summary>
public class BlobProperties
{
/// <summary>
/// Construct a new BlobProperties object
/// </summary>
/// <param name="name">The name of the blob</param>
public BlobProperties(string name)
{
Name = name;
}
/// <summary>
/// Name of the blob
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// URI of the blob
/// </summary>
public Uri Uri { get; internal set; }
/// <summary>
/// Content encoding of the blob if it set, null otherwise.
/// </summary>
public string ContentEncoding { get; set; }
/// <summary>
/// Content Type of the blob if it is set, null otherwise.
/// </summary>
public string ContentType { get; set; }
/// <summary>
/// Content Language of the blob if it is set, null otherwise.
/// </summary>
public string ContentLanguage { get; set; }
/// <summary>
/// The length of the blob content, null otherwise.
/// </summary>
public long ContentLength { get; internal set; }
/// <summary>
/// Metadata for the blob in the form of name-value pairs.
/// </summary>
public NameValueCollection Metadata { get; set;}
/// <summary>
/// The last modified time for the blob.
/// </summary>
public DateTime LastModifiedTime { get; internal set; }
/// <summary>
/// The ETag of the blob. This is an identifier assigned to the blob by the storage service
/// and is used to distinguish contents of two blobs (or versions of the same blob).
/// </summary>
public string ETag { get; internal set; }
internal void Assign(BlobProperties other)
{
Name = other.Name;
Uri = other.Uri;
ContentEncoding = other.ContentEncoding;
ContentLanguage = other.ContentLanguage;
ContentLength = other.ContentLength;
ContentType = other.ContentType;
ETag = other.ETag;
LastModifiedTime = other.LastModifiedTime;
Metadata = (other.Metadata != null ? new NameValueCollection(other.Metadata) : null) ;
}
}
/// <summary>
/// The properties of a container.
/// No member of this class makes a storage service request.
/// </summary>
public class ContainerProperties
{
public ContainerProperties(string name)
{
Name = name;
}
public string Name
{
get;
internal set;
}
public DateTime LastModifiedTime
{
get;
internal set;
}
public string ETag
{
get;
internal set;
}
public Uri Uri
{
get;
internal set;
}
public NameValueCollection Metadata
{
get;
internal set;
}
}
/// <summary>
/// The contents of the Blob in various forms.
/// </summary>
public class BlobContents
{
/// <summary>
/// Construct a new BlobContents object from a stream.
/// </summary>
/// <param name="stream">The stream to/from which blob contents are written/read. The
/// stream should be seekable in order for requests to be retried.</param>
public BlobContents(Stream stream)
{
this.stream = stream;
}
/// <summary>
/// Construct a new BlobContents object from a byte array.
/// </summary>
/// <param name="value">The byte array to/from which contents are written/read.</param>
public BlobContents(byte[] value)
{
this.bytes = value;
this.stream = new MemoryStream(value, false);
}
/// <summary>
/// Get the contents of a blob as a stream.
/// </summary>
public Stream AsStream
{
get
{
return stream;
}
}
/// <summary>
/// Get the contents of a blob as a byte array.
/// </summary>
public byte[] AsBytes()
{
if (bytes != null)
return bytes;
if (stream != null)
{
stream.Seek(0, SeekOrigin.Begin);
bytes = new byte[stream.Length];
int n = 0;
int offset = 0;
do
{
n = stream.Read(bytes, offset, bytes.Length - offset);
offset += n;
} while (n > 0);
}
return bytes;
}
private Stream stream;
private byte[] bytes;
}
#endregion
}

View file

@ -0,0 +1,767 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="Errors.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
using System;
using System.IO;
using System.Collections.Generic;
using System.Net;
using System.Collections.Specialized;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Diagnostics.CodeAnalysis;
using System.Web;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
// disable the generation of warnings for missing documentation elements for
// public classes/members in this file
// justification is that this file contains many public constants whose names
// sufficiently reflect their intended usage
#pragma warning disable 1591
namespace Microsoft.Samples.ServiceHosting.StorageClient
{
/// <summary>
/// Error codes that can be returned by the storage service or the client library.
/// These are divided into server errors and client errors depending on which side
/// the error can be attributed to.
/// </summary>
public enum StorageErrorCode
{
None = 0,
//Server errors
ServiceInternalError = 1,
ServiceTimeout,
ServiceIntegrityCheckFailed,
TransportError,
ServiceBadResponse,
//Client errors
ResourceNotFound,
AccountNotFound,
ContainerNotFound,
BlobNotFound,
AuthenticationFailure,
AccessDenied,
ResourceAlreadyExists,
ContainerAlreadyExists,
BlobAlreadyExists,
BadRequest,
ConditionFailed,
BadGateway
}
[Serializable]
public class StorageExtendedErrorInformation
{
public string ErrorCode { get; internal set; }
public string ErrorMessage { get; internal set; }
public NameValueCollection AdditionalDetails { get; internal set; }
}
/// <summary>
/// The base class for storage service exceptions
/// </summary>
[Serializable]
public abstract class StorageException : Exception
{
/// <summary>
/// The Http status code returned by the storage service
/// </summary>
public HttpStatusCode StatusCode { get; private set; }
/// <summary>
/// The specific error code returned by the storage service
/// </summary>
public StorageErrorCode ErrorCode { get; private set; }
/// <summary>
///
/// </summary>
public StorageExtendedErrorInformation ExtendedErrorInformation { get; private set; }
protected StorageException()
{
}
protected StorageException(
StorageErrorCode errorCode,
string message,
HttpStatusCode statusCode,
StorageExtendedErrorInformation extendedErrorInfo,
Exception innerException
)
: base(message, innerException)
{
this.ErrorCode = errorCode;
this.StatusCode = statusCode;
this.ExtendedErrorInformation = extendedErrorInfo;
}
/// <summary>
/// Initializes a new instance of the <see cref="StorageException"/> class with
/// serialized data.
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> object that contains serialized object
/// data about the exception being thrown</param>
/// <param name="context">The <see cref="StreamingContext"/> object that contains contextual information
/// about the source or destionation. </param>
protected StorageException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
if (null == info)
{
throw new ArgumentNullException("info");
}
this.StatusCode = (HttpStatusCode)info.GetValue("StatusCode", typeof(HttpStatusCode));
this.ErrorCode = (StorageErrorCode)info.GetValue("ErrorCode", typeof(StorageErrorCode));
this.ExtendedErrorInformation = (StorageExtendedErrorInformation)info.GetValue(
"ExtendedErrorInformation", typeof(StorageExtendedErrorInformation));
}
/// <summary>
/// Sets the <see cref="SerializationInfo"/> object with additional exception information
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> object that holds the
/// serialized object data.</param>
/// <param name="context">The <see cref="StreamingContext"/> object that contains contextual information
/// about the source or destionation. </param>
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (null == info)
{
throw new ArgumentNullException("info");
}
info.AddValue("StatusCode", this.StatusCode);
info.AddValue("ErrorCode", this.ErrorCode);
info.AddValue("ExtendedErrorInformation", this.ExtendedErrorInformation);
base.GetObjectData(info, context);
}
}
/// <summary>
/// Server exceptions are those due to server side problems.
/// These may be transient and requests resulting in such exceptions
/// can be retried with the same parameters.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors",
Justification = "Since this exception comes from the server, there must be an HTTP response code associated with it, hence we exclude the default constructor taking only a string but no status code.")]
[Serializable]
public class StorageServerException : StorageException
{
internal StorageServerException(
StorageErrorCode errorCode,
string message,
HttpStatusCode statusCode,
Exception innerException
)
: base(errorCode, message, statusCode, null, innerException)
{
}
internal StorageServerException(
StorageErrorCode errorCode,
string message,
HttpStatusCode statusCode,
StorageExtendedErrorInformation extendedErrorInfo,
Exception innerException
)
: base(errorCode, message, statusCode, extendedErrorInfo, innerException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="StorageServerException"/> class with
/// serialized data.
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> object that contains serialized object
/// data about the exception being thrown</param>
/// <param name="context">The <see cref="StreamingContext"/> object that contains contextual information
/// about the source or destionation. </param>
protected StorageServerException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
public StorageServerException()
{
}
}
/// <summary>
/// Client side exceptions are due to incorrect parameters to the request.
/// These requests should not be retried with the same parameters
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors",
Justification = "Since this exception comes from the server, there must be an HTTP response code associated with it, hence we exclude the default constructor taking only a string but no status code.")]
[Serializable]
public class StorageClientException : StorageException
{
internal StorageClientException(
StorageErrorCode errorCode,
string message,
HttpStatusCode statusCode,
StorageExtendedErrorInformation extendedErrorInfo,
Exception innerException
)
: base(errorCode, message, statusCode, extendedErrorInfo, innerException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="StorageClientException"/> class with
/// serialized data.
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> object that contains serialized object
/// data about the exception being thrown</param>
/// <param name="context">The <see cref="StreamingContext"/> object that contains contextual information
/// about the source or destionation. </param>
protected StorageClientException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
public StorageClientException()
{
}
}
#region Error code strings that can be returned in the StorageExtendedErrorInformation.ErrorCode
/// <summary>
/// Error code strings that are common to all storage services
/// </summary>
public static class StorageErrorCodeStrings
{
public const string UnsupportedHttpVerb = "UnsupportedHttpVerb";
public const string MissingContentLengthHeader = "MissingContentLengthHeader";
public const string MissingRequiredHeader = "MissingRequiredHeader";
public const string MissingRequiredXmlNode = "MissingRequiredXmlNode";
public const string UnsupportedHeader = "UnsupportedHeader";
public const string UnsupportedXmlNode = "UnsupportedXmlNode";
public const string InvalidHeaderValue = "InvalidHeaderValue";
public const string InvalidXmlNodeValue = "InvalidXmlNodeValue";
public const string MissingRequiredQueryParameter = "MissingRequiredQueryParameter";
public const string UnsupportedQueryParameter = "UnsupportedQueryParameter";
public const string InvalidQueryParameterValue = "InvalidQueryParameterValue";
public const string OutOfRangeQueryParameterValue = "OutOfRangeQueryParameterValue";
public const string InvalidUri = "InvalidUri";
public const string InvalidHttpVerb = "InvalidHttpVerb";
public const string EmptyMetadataKey = "EmptyMetadataKey";
public const string RequestBodyTooLarge = "RequestBodyTooLarge";
public const string InvalidXmlDocument = "InvalidXmlDocument";
public const string InternalError = "InternalError";
public const string AuthenticationFailed = "AuthenticationFailed";
public const string Md5Mismatch = "Md5Mismatch";
public const string InvalidMd5 = "InvalidMd5";
public const string OutOfRangeInput = "OutOfRangeInput";
public const string InvalidInput = "InvalidInput";
public const string OperationTimedOut = "OperationTimedOut";
public const string ResourceNotFound = "ResourceNotFound";
public const string InvalidMetadata = "InvalidMetadata";
public const string MetadataTooLarge = "MetadataTooLarge";
public const string ConditionNotMet = "ConditionNotMet";
public const string InvalidRange = "InvalidRange";
public const string ContainerNotFound = "ContainerNotFound";
public const string ContainerAlreadyExists = "ContainerAlreadyExists";
public const string ContainerDisabled = "ContainerDisabled";
public const string ContainerBeingDeleted = "ContainerBeingDeleted";
public const string ServerBusy = "ServerBusy";
}
/// <summary>
/// Error code strings that are specific to blob service
/// </summary>
public static class BlobErrorCodeStrings
{
public const string InvalidBlockId = "InvalidBlockId";
public const string BlobNotFound = "BlobNotFound";
public const string BlobAlreadyExists = "BlobAlreadyExists";
public const string InvalidBlobOrBlock = "InvalidBlobOrBlock";
public const string InvalidBlockList = "InvalidBlockList";
}
/// <summary>
/// Error code strings that are specific to queue service
/// </summary>
public static class QueueErrorCodeStrings
{
public const string QueueNotFound = "QueueNotFound";
public const string QueueDisabled = "QueueDisabled";
public const string QueueAlreadyExists = "QueueAlreadyExists";
public const string QueueNotEmpty = "QueueNotEmpty";
public const string QueueBeingDeleted = "QueueBeingDeleted";
public const string PopReceiptMismatch = "PopReceiptMismatch";
public const string InvalidParameter = "InvalidParameter";
public const string MessageNotFound = "MessageNotFound";
public const string MessageTooLarge = "MessageTooLarge";
public const string InvalidMarker = "InvalidMarker";
}
/// <summary>
/// Error code strings that are specific to queue service
/// </summary>
/// public static class TableErrorCodeStrings
public static class TableErrorCodeStrings
{
public const string XMethodNotUsingPost = "XMethodNotUsingPost";
public const string XMethodIncorrectValue = "XMethodIncorrectValue";
public const string XMethodIncorrectCount = "XMethodIncorrectCount";
public const string TableHasNoProperties = "TableHasNoProperties";
public const string DuplicatePropertiesSpecified = "DuplicatePropertiesSpecified";
public const string TableHasNoSuchProperty = "TableHasNoSuchProperty";
public const string DuplicateKeyPropertySpecified = "DuplicateKeyPropertySpecified";
public const string TableAlreadyExists = "TableAlreadyExists";
public const string TableNotFound = "TableNotFound";
public const string EntityNotFound = "EntityNotFound";
public const string EntityAlreadyExists = "EntityAlreadyExists";
public const string PartitionKeyNotSpecified = "PartitionKeyNotSpecified";
public const string OperatorInvalid = "OperatorInvalid";
public const string UpdateConditionNotSatisfied = "UpdateConditionNotSatisfied";
public const string PropertiesNeedValue = "PropertiesNeedValue";
public const string PartitionKeyPropertyCannotBeUpdated = "PartitionKeyPropertyCannotBeUpdated";
public const string TooManyProperties = "TooManyProperties";
public const string EntityTooLarge = "EntityTooLarge";
public const string PropertyValueTooLarge = "PropertyValueTooLarge";
public const string InvalidValueType = "InvalidValueType";
public const string TableBeingDeleted = "TableBeingDeleted";
public const string TableServerOutOfMemory = "TableServerOutOfMemory";
public const string PrimaryKeyPropertyIsInvalidType = "PrimaryKeyPropertyIsInvalidType";
public const string PropertyNameTooLong = "PropertyNameTooLong";
public const string PropertyNameInvalid = "PropertyNameInvalid";
public const string BatchOperationNotSupported = "BatchOperationNotSupported";
public const string JsonFormatNotSupported = "JsonFormatNotSupported";
public const string MethodNotAllowed = "MethodNotAllowed";
public const string NotImplemented = "NotImplemented";
}
#endregion
#region Helper functions dealing with errors
internal static partial class Utilities
{
internal static void ProcessUnexpectedStatusCode(HttpWebResponse response)
{
throw new StorageServerException(
StorageErrorCode.ServiceBadResponse,
response.StatusDescription,
response.StatusCode,
null
);
}
internal static Exception TranslateWebException(Exception e)
{
WebException we = e as WebException;
if (null == we)
{
return e;
}
// If the response is not null, let's first see what the status code is.
if (we.Response != null)
{
HttpWebResponse response = ((HttpWebResponse)we.Response);
StorageExtendedErrorInformation extendedError =
GetExtendedErrorDetailsFromResponse(
response.GetResponseStream(),
response.ContentLength
);
Exception translatedException = null;
if (extendedError != null)
{
translatedException = TranslateExtendedError(
extendedError,
response.StatusCode,
response.StatusDescription,
e);
if (translatedException != null)
return translatedException;
}
translatedException = TranslateFromHttpStatus(
response.StatusCode,
response.StatusDescription,
extendedError,
we
);
if (translatedException != null)
return translatedException;
}
switch (we.Status)
{
case WebExceptionStatus.RequestCanceled:
return new StorageServerException(
StorageErrorCode.ServiceTimeout,
"The server request did not complete within the specified timeout",
HttpStatusCode.GatewayTimeout,
we);
case WebExceptionStatus.ConnectFailure:
return we;
default:
return new StorageServerException(
StorageErrorCode.ServiceInternalError,
"The server encountered an unknown failure: " + e.Message,
HttpStatusCode.InternalServerError,
we
);
}
}
internal static Exception TranslateFromHttpStatus(
HttpStatusCode statusCode,
string statusDescription,
StorageExtendedErrorInformation details,
Exception inner
)
{
switch (statusCode)
{
case HttpStatusCode.Forbidden:
return new StorageClientException(
StorageErrorCode.AccessDenied,
statusDescription,
HttpStatusCode.Forbidden,
details,
inner
);
case HttpStatusCode.Gone:
case HttpStatusCode.NotFound:
return new StorageClientException(
StorageErrorCode.ResourceNotFound,
statusDescription,
statusCode,
details,
inner
);
case HttpStatusCode.BadRequest:
return new StorageClientException(
StorageErrorCode.BadRequest,
statusDescription,
statusCode,
details,
inner
);
case HttpStatusCode.PreconditionFailed:
case HttpStatusCode.NotModified:
return new StorageClientException(
StorageErrorCode.BadRequest,
statusDescription,
statusCode,
details,
inner);
case HttpStatusCode.Conflict:
return new StorageClientException(
StorageErrorCode.ResourceAlreadyExists,
statusDescription,
statusCode,
details,
inner
);
case HttpStatusCode.GatewayTimeout:
return new StorageServerException(
StorageErrorCode.ServiceTimeout,
statusDescription,
statusCode,
details,
inner
);
case HttpStatusCode.RequestedRangeNotSatisfiable:
return new StorageClientException(
StorageErrorCode.BadRequest,
statusDescription,
statusCode,
details,
inner
);
case HttpStatusCode.InternalServerError:
return new StorageServerException(
StorageErrorCode.ServiceInternalError,
statusDescription,
statusCode,
details,
inner
);
case HttpStatusCode.BadGateway:
return new StorageServerException(
StorageErrorCode.BadGateway,
statusDescription,
statusCode,
details,
inner
);
}
return null;
}
private static Exception TranslateExtendedError(
StorageExtendedErrorInformation details,
HttpStatusCode statusCode,
string statusDescription,
Exception inner
)
{
StorageErrorCode errorCode = default(StorageErrorCode);
switch (details.ErrorCode)
{
case StorageErrorCodeStrings.UnsupportedHttpVerb:
case StorageErrorCodeStrings.MissingContentLengthHeader:
case StorageErrorCodeStrings.MissingRequiredHeader:
case StorageErrorCodeStrings.UnsupportedHeader:
case StorageErrorCodeStrings.InvalidHeaderValue:
case StorageErrorCodeStrings.MissingRequiredQueryParameter:
case StorageErrorCodeStrings.UnsupportedQueryParameter:
case StorageErrorCodeStrings.InvalidQueryParameterValue:
case StorageErrorCodeStrings.OutOfRangeQueryParameterValue:
case StorageErrorCodeStrings.InvalidUri:
case StorageErrorCodeStrings.InvalidHttpVerb:
case StorageErrorCodeStrings.EmptyMetadataKey:
case StorageErrorCodeStrings.RequestBodyTooLarge:
case StorageErrorCodeStrings.InvalidXmlDocument:
case StorageErrorCodeStrings.InvalidXmlNodeValue:
case StorageErrorCodeStrings.MissingRequiredXmlNode:
case StorageErrorCodeStrings.InvalidMd5:
case StorageErrorCodeStrings.OutOfRangeInput:
case StorageErrorCodeStrings.InvalidInput:
case StorageErrorCodeStrings.InvalidMetadata:
case StorageErrorCodeStrings.MetadataTooLarge:
case StorageErrorCodeStrings.InvalidRange:
errorCode = StorageErrorCode.BadRequest;
break;
case StorageErrorCodeStrings.AuthenticationFailed:
errorCode = StorageErrorCode.AuthenticationFailure;
break;
case StorageErrorCodeStrings.ResourceNotFound:
errorCode = StorageErrorCode.ResourceNotFound;
break;
case StorageErrorCodeStrings.ConditionNotMet:
errorCode = StorageErrorCode.ConditionFailed;
break;
case StorageErrorCodeStrings.ContainerAlreadyExists:
errorCode = StorageErrorCode.ContainerAlreadyExists;
break;
case StorageErrorCodeStrings.ContainerNotFound:
errorCode = StorageErrorCode.ContainerNotFound;
break;
case BlobErrorCodeStrings.BlobNotFound:
errorCode = StorageErrorCode.BlobNotFound;
break;
case BlobErrorCodeStrings.BlobAlreadyExists:
errorCode = StorageErrorCode.BlobAlreadyExists;
break;
}
if (errorCode != default(StorageErrorCode))
return new StorageClientException(
errorCode,
statusDescription,
statusCode,
details,
inner
);
switch (details.ErrorCode)
{
case StorageErrorCodeStrings.InternalError:
case StorageErrorCodeStrings.ServerBusy:
errorCode = StorageErrorCode.ServiceInternalError;
break;
case StorageErrorCodeStrings.Md5Mismatch:
errorCode = StorageErrorCode.ServiceIntegrityCheckFailed;
break;
case StorageErrorCodeStrings.OperationTimedOut:
errorCode = StorageErrorCode.ServiceTimeout;
break;
}
if (errorCode != default(StorageErrorCode))
return new StorageServerException(
errorCode,
statusDescription,
statusCode,
details,
inner
);
return null;
}
// This is the limit where we allow for the error message returned by the server.
// Message longer than that will be truncated.
private const int ErrorTextSizeLimit = 8 * 1024;
private static StorageExtendedErrorInformation GetExtendedErrorDetailsFromResponse(
Stream httpResponseStream,
long contentLength
)
{
try
{
int bytesToRead = (int)Math.Max((long)contentLength, (long)ErrorTextSizeLimit);
byte[] responseBuffer = new byte[bytesToRead];
int bytesRead = CopyStreamToBuffer(httpResponseStream, responseBuffer, (int)bytesToRead);
return GetErrorDetailsFromStream(
new MemoryStream(responseBuffer, 0, bytesRead, false)
);
}
catch (WebException)
{
//Ignore network errors when reading error details.
return null;
}
catch (IOException)
{
return null;
}
catch (TimeoutException)
{
return null;
}
}
private static StorageExtendedErrorInformation GetErrorDetailsFromStream(
Stream inputStream
)
{
StorageExtendedErrorInformation extendedError = new StorageExtendedErrorInformation();
try
{
using (XmlReader reader = XmlReader.Create(inputStream))
{
reader.Read();
reader.ReadStartElement(StorageHttpConstants.XmlElementNames.ErrorRootElement);
extendedError.ErrorCode = reader.ReadElementString(StorageHttpConstants.XmlElementNames.ErrorCode);
extendedError.ErrorMessage = reader.ReadElementString(StorageHttpConstants.XmlElementNames.ErrorMessage);
extendedError.AdditionalDetails = new NameValueCollection();
// After error code and message we can have a number of additional details optionally followed
// by ExceptionDetails element - we'll read all of these into the additionalDetails collection
do
{
if (reader.IsStartElement())
{
if (string.Compare(reader.LocalName, StorageHttpConstants.XmlElementNames.ErrorException, StringComparison.Ordinal) == 0)
{
// Need to read exception details - we have message and stack trace
reader.ReadStartElement(StorageHttpConstants.XmlElementNames.ErrorException);
extendedError.AdditionalDetails.Add(StorageHttpConstants.XmlElementNames.ErrorExceptionMessage,
reader.ReadElementString(StorageHttpConstants.XmlElementNames.ErrorExceptionMessage));
extendedError.AdditionalDetails.Add(StorageHttpConstants.XmlElementNames.ErrorExceptionStackTrace,
reader.ReadElementString(StorageHttpConstants.XmlElementNames.ErrorExceptionStackTrace));
reader.ReadEndElement();
}
else
{
string elementName = reader.LocalName;
extendedError.AdditionalDetails.Add(elementName, reader.ReadString());
}
}
}
while (reader.Read());
}
}
catch (XmlException)
{
//If there is a parsing error we cannot return extended error information
return null;
}
return extendedError;
}
internal static StorageExtendedErrorInformation GetExtendedErrorFromXmlMessage(string xmlErrorMessage)
{
string message = null;
string errorCode = null;
XName xnErrorCode = XName.Get(StorageHttpConstants.XmlElementNames.TableErrorCodeElement,
StorageHttpConstants.XmlElementNames.DataWebMetadataNamespace);
XName xnMessage = XName.Get(StorageHttpConstants.XmlElementNames.TableErrorMessageElement,
StorageHttpConstants.XmlElementNames.DataWebMetadataNamespace);
using (StringReader reader = new StringReader(xmlErrorMessage))
{
XDocument xDocument = null;
try
{
xDocument = XDocument.Load(reader);
}
catch (XmlException)
{
// The XML could not be parsed. This could happen either because the connection
// could not be made to the server, or if the response did not contain the
// error details (for example, if the response status code was neither a failure
// nor a success, but a 3XX code such as NotModified.
return null;
}
XElement errorCodeElement =
xDocument.Descendants(xnErrorCode).FirstOrDefault();
if (errorCodeElement == null)
return null;
errorCode = errorCodeElement.Value;
XElement messageElement =
xDocument.Descendants(xnMessage).FirstOrDefault();
if (messageElement != null)
{
message = messageElement.Value;
}
}
StorageExtendedErrorInformation errorDetails = new StorageExtendedErrorInformation();
errorDetails.ErrorMessage = message;
errorDetails.ErrorCode = errorCode;
return errorDetails;
}
}
#endregion
}

View file

@ -0,0 +1,705 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="Queue.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.Configuration;
using System.Collections.Specialized;
using System.Xml;
using System.IO;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
// disable the generation of warnings for missing documentation elements for
// public classes/members in this file
#pragma warning disable 1591
namespace Microsoft.Samples.ServiceHosting.StorageClient
{
/// <summary>
/// The entry point of the queue storage API
/// </summary>
public abstract class QueueStorage
{
/// <summary>
/// Factory method for QueueStorage
/// </summary>
/// <param name="baseUri">The base URI of the queue service</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 name of the storage account</param>
/// <param name="base64Key">Authentication key used for signing requests</param>
/// <returns>A newly created QueueStorage instance</returns>
public static QueueStorage Create(Uri baseUri, bool? usePathStyleUris, string accountName, string base64Key)
{
return new QueueStorageRest(new StorageAccountInfo(baseUri, usePathStyleUris, accountName, base64Key),null);
}
public static QueueStorage Create(StorageAccountInfo accountInfo)
{
return new QueueStorageRest(accountInfo, null);
}
public static QueueStorage Create(StorageAccountInfo accountInfo,string version)
{
return new QueueStorageRest(accountInfo,version);
}
/// <summary>
/// Get a reference to a Queue object with a specified name. This method does not make a call to
/// the queue service.
/// </summary>
/// <param name="queueName">The name of the queue</param>
/// <returns>A newly created queue object</returns>
public abstract MessageQueue GetQueue(string queueName);
/// <summary>
/// Lists the queues within the account.
/// </summary>
/// <returns>A list of queues</returns>
public virtual IEnumerable<MessageQueue> ListQueues()
{
return ListQueues(null);
}
/// <summary>
/// Lists the queues within the account that start with the given prefix.
/// </summary>
/// <param name="prefix">If prefix is null returns all queues.</param>
/// <returns>A list of queues.</returns>
public abstract IEnumerable<MessageQueue> ListQueues(string prefix);
/// <summary>
/// The time out for each request to the storage service.
/// </summary>
public TimeSpan Timeout
{
get;
set;
}
/// <summary>
/// The retry policy used for retrying requests
/// </summary>
public RetryPolicy RetryPolicy
{
get;
set;
}
/// <summary>
/// The base URI of the blob storage service
/// </summary>
public Uri BaseUri
{
get
{
return AccountInfo.BaseUri;
}
}
/// <summary>
/// The name of the storage account
/// </summary>
public string AccountName
{
get
{
return AccountInfo.AccountName;
}
}
/// <summary>
/// Indicates whether to use/generate path-style or host-style URIs
/// </summary>
public bool UsePathStyleUris
{
get
{
return AccountInfo.UsePathStyleUris;
}
}
/// <summary>
/// The default timeout
/// </summary>
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
Justification = "TimeSpan is a non-mutable type")]
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
/// <summary>
/// The default retry policy
/// </summary>
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
Justification = "RetryPolicy is a non-mutable type")]
public static readonly RetryPolicy DefaultRetryPolicy = RetryPolicies.NoRetry;
internal protected QueueStorage(StorageAccountInfo accountInfo,string version)
{
this.AccountInfo = accountInfo;
Timeout = DefaultTimeout;
RetryPolicy = DefaultRetryPolicy;
Version = version;
}
protected QueueStorage(QueueStorage other)
{
this.AccountInfo = other.AccountInfo;
this.Timeout = other.Timeout;
this.RetryPolicy = other.RetryPolicy;
this.Version = other.Version;
}
internal protected StorageAccountInfo AccountInfo { get; set; }
internal protected SharedKeyCredentials Credentials { get; set; }
internal protected string Version { get; set; }
}
/// <summary>
/// Objects of this class represent a single message in the queue.
/// </summary>
public class Message
{
/// <summary>
/// The maximum message size in bytes.
/// </summary>
public static readonly int MaxMessageSize = 8 * 1024;
/// <summary>
/// The maximum amount of time a message is kept in the queue. Max value is 7 days.
/// Value is given in seconds.
/// </summary>
public static readonly int MaxTimeToLive = 7 * 24 * 60 * 60;
/// <summary>
/// This constructor is not publicly exposed.
/// </summary>
internal Message()
{
}
/// <summary>
/// Creates a message and initializes the content of the message to be the specified string.
/// </summary>
/// <param name="content">A string representing the contents of the message.</param>
public Message(string content)
{
if (content == null)
{
throw new ArgumentNullException("content");
}
this.content = Encoding.UTF8.GetBytes(content);
}
/// <summary>
/// Creates a message and given the specified byte contents.
/// In this implementation, regardless of whether an XML or binary data is passed into this
/// function, message contents are converted to base64 before passing the data to the queue service.
/// When calculating the size of the message, the size of the base64 encoding is thus the important
/// parameter.
/// </summary>
/// <param name="content"></param>
public Message(byte[] content)
{
if (content == null)
{
throw new ArgumentNullException("content");
}
if (Convert.ToBase64String(content).Length > MaxMessageSize)
{
throw new ArgumentException("Message body is too big!");
}
this.content = content;
}
/// <summary>
/// A unique ID of the message as returned from queue operations.
/// </summary>
public string Id
{
get;
internal set;
}
/// <summary>
/// When a message is retrieved from a queue, a PopReceipt is returned. The PopReceipt is used when
/// deleting a message from the queue.
/// </summary>
public string PopReceipt
{
get;
internal set;
}
/// <summary>
/// The point in time when the message was put into the queue.
/// </summary>
public DateTime InsertionTime
{
get;
internal set;
}
/// <summary>
/// A message's expiration time.
/// </summary>
public DateTime ExpirationTime
{
get;
internal set;
}
/// <summary>
/// The point in time when a message becomes visible again after a Get() operation was called
/// that returned the message.
/// </summary>
public DateTime TimeNextVisible
{
get;
internal set;
}
/// <summary>
/// Returns the the contents of the message as a string.
/// </summary>
public string ContentAsString()
{
return Encoding.UTF8.GetString(this.content);
}
/// <summary>
/// Returns the content of the message as a byte array
/// </summary>
public byte[] ContentAsBytes()
{
return content;
}
/// <summary>
/// When calling the Get() operation on a queue, the content of messages
/// returned in the REST protocol are represented as Base64-encoded strings.
/// This internal function transforms the Base64 representation into a byte array.
/// </summary>
/// <param name="str">The Base64-encoded string.</param>
internal void SetContentFromBase64String(string str) {
if (str == null || str == string.Empty)
{
// we got a message with an empty <MessageText> element
this.content = Encoding.UTF8.GetBytes(string.Empty);
}
else
{
this.content = Convert.FromBase64String(str);
}
}
/// <summary>
/// Internal method used for creating the XML that becomes part of a REST request
/// </summary>
internal byte[] GetContentXMLRepresentation(out int length)
{
length = 0;
byte[] ret = null;
StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
System.Text.UTF8Encoding enc = new UTF8Encoding(false);
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement(StorageHttpConstants.XmlElementNames.QueueMessage);
writer.WriteStartElement(StorageHttpConstants.XmlElementNames.MessageText);
writer.WriteRaw(Convert.ToBase64String(content));
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
ret = enc.GetBytes(builder.ToString());
if (ret != null)
{
length = ret.Length;
}
return ret;
}
private byte[] content;
}
/// <summary>
/// Queues in the storage client library expose a functionality for listening for incoming messages.
/// If a message is put into a queue, a corresponding event is issued and this delegate is called. This functionality
/// is implemented internally in this library by periodically polling for incoming messages.
/// </summary>
/// <param name="sender">The queue that has received a new event.</param>
/// <param name="e">The event argument containing the message.</param>
public delegate void MessageReceivedEventHandler(object sender, MessageReceivedEventArgs e);
/// <summary>
/// The argument class for the MessageReceived event.
/// </summary>
public class MessageReceivedEventArgs : EventArgs
{
/// <summary>
/// The message itself.
/// </summary>
private Message _msg;
/// <summary>
/// Constructor for creating a message received argument.
/// </summary>
/// <param name="msg"></param>
public MessageReceivedEventArgs(Message msg)
{
if (msg == null)
{
throw new ArgumentNullException("msg");
}
_msg = msg;
}
/// <summary>
/// The message received by the queue.
/// </summary>
public Message Message
{
get
{
return _msg;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_msg = value;
}
}
}
public class QueueProperties
{
/// <summary>
/// The approximated amount of messages in the queue.
/// </summary>
public int ApproximateMessageCount
{
get;
internal set;
}
/// <summary>
/// Metadata for the queue in the form of name-value pairs.
/// </summary>
public NameValueCollection Metadata
{
get;
set;
}
}
/// <summary>
/// Objects of this class represent a queue in a user's storage account.
/// </summary>
public abstract class MessageQueue
{
/// <summary>
/// The name of the queue.
/// </summary>
private string _name;
/// <summary>
/// The user account this queue lives in.
/// </summary>
private StorageAccountInfo _account;
/// <summary>
/// This constructor is only called by subclasses.
/// </summary>
internal protected MessageQueue()
{
// queues are generated using factory methods
}
internal protected MessageQueue(string name, StorageAccountInfo account)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("Queue name cannot be null or empty!");
}
if (account == null)
{
throw new ArgumentNullException("account");
}
if (!Utilities.IsValidContainerOrQueueName(name))
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The specified queue name \"{0}\" is not valid!" +
"Please choose a name that conforms to the naming conventions for queues!", name));
}
_name = name;
_account = account;
}
/// <summary>
/// The name of the queue exposed as a public property.
/// </summary>
public string Name
{
get
{
return _name;
}
}
/// <summary>
/// The account info object this queue lives in -- exposed as an internal property.
/// </summary>
internal StorageAccountInfo AccountInfo
{
get {
return _account;
}
}
/// <summary>
/// Indicates whether to use/generate path-style or host-style URIs
/// </summary>
public bool UsePathStyleUris
{
get
{
return _account.UsePathStyleUris;
}
}
/// <summary>
/// The URI of the queue
/// </summary>
public abstract Uri QueueUri
{
get;
}
/// <summary>
/// The retry policy used for retrying requests; this is the retry policy of the
/// storage account where this queue was created
/// </summary>
public RetryPolicy RetryPolicy
{
get;
set;
}
/// <summary>
/// The timeout of requests.
/// </summary>
public TimeSpan Timeout
{
get;
set;
}
/// <summary>
/// Creates a queue in the specified storage account.
/// </summary>
/// <param name="queueAlreadyExists">true if a queue with the same name already exists.</param>
/// <returns>true if the queue was successfully created.</returns>
public abstract bool CreateQueue(out bool queueAlreadyExists);
/// <summary>
/// Creates a queue in the specified storage account.
/// </summary>
/// <returns>true if the queue was successfully created.</returns>
public virtual bool CreateQueue()
{
bool ignore;
return CreateQueue(out ignore);
}
/// <summary>
/// Determines whether a queue with the same name already exists in an account.
/// </summary>
/// <returns>true if a queue with the same name already exists.</returns>
public virtual bool DoesQueueExist()
{
try
{
this.GetProperties();
return true;
}
catch (StorageClientException e)
{
if (e.ErrorCode == StorageErrorCode.ResourceNotFound)
return false;
throw;
}
}
/// <summary>
/// Deletes the queue. The queue will be deleted regardless of whether there are messages in the
/// queue or not.
/// </summary>
/// <returns>true if the queue was successfully deleted.</returns>
public abstract bool DeleteQueue();
/// <summary>
/// Sets the properties of a queue.
/// </summary>
/// <param name="properties">The queue's properties to set.</param>
/// <returns>true if the properties were successfully written to the queue.</returns>
public abstract bool SetProperties(QueueProperties properties);
/// <summary>
/// Retrieves the queue's properties.
/// </summary>
/// <returns>The queue's properties.</returns>
public abstract QueueProperties GetProperties();
/// <summary>
/// Retrieves the approximate number of messages in a queue.
/// </summary>
/// <returns>The approximate number of messages in this queue.</returns>
public abstract int ApproximateCount();
/// <summary>
/// Puts a message in the queue.
/// </summary>
/// <param name="msg">The message to store in the queue.</param>
/// <returns>true if the message has been successfully enqueued.</returns>
public abstract bool PutMessage(Message msg);
/// <summary>
/// Puts a message in the queue.
/// </summary>
/// <param name="msg">The message to store in the queue.</param>
/// <param name="timeToLiveInSeconds">The time to live for the message in seconds.</param>
/// <returns>true if the message has been successfully enqueued.</returns>
public abstract bool PutMessage(Message msg, int timeToLiveInSeconds);
/// <summary>
/// Retrieves a message from the queue.
/// </summary>
/// <returns>The message retrieved or null if the queue is empty.</returns>
public abstract Message GetMessage();
/// <summary>
/// Retrieves a message and sets its visibility timeout to the specified number of seconds.
/// </summary>
/// <param name="visibilityTimeoutInSeconds">Visibility timeout of the message retrieved in seconds.</param>
/// <returns></returns>
public abstract Message GetMessage(int visibilityTimeoutInSeconds);
/// <summary>
/// Tries to retrieve the given number of messages.
/// </summary>
/// <param name="numberOfMessages">Maximum number of messages to retrieve.</param>
/// <returns>The list of messages retrieved.</returns>
public abstract IEnumerable<Message> GetMessages(int numberOfMessages);
/// <summary>
/// Tries to retrieve the given number of messages.
/// </summary>
/// <param name="numberOfMessages">Maximum number of messages to retrieve.</param>
/// <param name="visibilityTimeoutInSeconds">The visibility timeout of the retrieved messages in seconds.</param>
/// <returns>The list of messages retrieved.</returns>
public abstract IEnumerable<Message> GetMessages(int numberOfMessages, int visibilityTimeoutInSeconds);
/// <summary>
/// Get a message from the queue but do not actually dequeue it. The message will remain visible
/// for other parties requesting messages.
/// </summary>
/// <returns>The message retrieved or null if there are no messages in the queue.</returns>
public abstract Message PeekMessage();
/// <summary>
/// Tries to get a copy of messages in the queue without actually dequeuing the messages.
/// The messages will remain visible in the queue.
/// </summary>
/// <param name="numberOfMessages">Maximum number of message to retrieve.</param>
/// <returns>The list of messages retrieved.</returns>
public abstract IEnumerable<Message> PeekMessages(int numberOfMessages);
/// <summary>
/// Deletes a message from the queue.
/// </summary>
/// <param name="msg">The message to retrieve with a valid popreceipt.</param>
/// <returns>true if the operation was successful.</returns>
public abstract bool DeleteMessage(Message msg);
/// <summary>
/// Delete all messages in a queue.
/// </summary>
/// <returns>true if all messages were deleted successfully.</returns>
public abstract bool Clear();
/// <summary>
/// The default time interval between polling the queue for messages.
/// Polling is only enabled if the user has called StartReceiving().
/// </summary>
public static readonly int DefaultPollInterval = 5000;
/// <summary>
/// The poll interval in milliseconds. If not explicitly set, this defaults to
/// the DefaultPollInterval.
/// </summary>
public abstract int PollInterval
{
get;
set;
}
/// <summary>
/// Starts the automatic reception of messages.
/// </summary>
/// <returns>true if the operation was successful.</returns>
public abstract bool StartReceiving();
/// <summary>
/// Stop the automatic reception of messages.
/// </summary>
public abstract void StopReceiving();
/// <summary>
/// The event users subscribe to in order to automatically receive messages
/// from a queue.
/// </summary>
public abstract event MessageReceivedEventHandler MessageReceived;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,503 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="RestHelpers.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.IO;
using System.Net;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Specialized;
using System.Web;
using System.Xml;
using System.Text.RegularExpressions;
namespace Microsoft.Samples.ServiceHosting.StorageClient
{
namespace StorageHttpConstants
{
internal static class ConstChars
{
internal const string Linefeed = "\n";
internal const string CarriageReturnLinefeed = "\r\n";
internal const string Colon = ":";
internal const string Comma = ",";
internal const string Slash = "/";
internal const string BackwardSlash = @"\";
internal const string Space = " ";
internal const string Ampersand = "&";
internal const string QuestionMark = "?";
internal const string Equal = "=";
internal const string Bang = "!";
internal const string Star = "*";
internal const string Dot = ".";
}
internal static class RequestParams
{
internal const string NumOfMessages = "numofmessages";
internal const string VisibilityTimeout = "visibilitytimeout";
internal const string PeekOnly = "peekonly";
internal const string MessageTtl = "messagettl";
internal const string Messages = "messages";
internal const string PopReceipt = "popreceipt";
}
internal static class QueryParams
{
internal const string SeparatorForParameterAndValue = "=";
internal const string QueryParamTimeout = "timeout";
internal const string QueryParamComp = "comp";
// Other query string parameter names
internal const string QueryParamBlockId = "blockid";
internal const string QueryParamPrefix = "prefix";
internal const string QueryParamMarker = "marker";
internal const string QueryParamMaxResults = "maxresults";
internal const string QueryParamDelimiter = "delimiter";
internal const string QueryParamModifiedSince = "modifiedsince";
}
internal static class CompConstants
{
internal const string Metadata = "metadata";
internal const string List = "list";
internal const string BlobList = "bloblist";
internal const string BlockList = "blocklist";
internal const string Block = "block";
internal const string Acl = "acl";
}
internal static class XmlElementNames
{
internal const string BlockList = "BlockList";
internal const string Block = "Block";
internal const string EnumerationResults = "EnumerationResults";
internal const string Prefix = "Prefix";
internal const string Marker = "Marker";
internal const string MaxResults = "MaxResults";
internal const string Delimiter = "Delimiter";
internal const string NextMarker = "NextMarker";
internal const string Containers = "Containers";
internal const string Container = "Container";
internal const string ContainerName = "Name";
internal const string ContainerNameAttribute = "ContainerName";
internal const string AccountNameAttribute = "AccountName";
internal const string LastModified = "LastModified";
internal const string Etag = "Etag";
internal const string Url = "Url";
internal const string CommonPrefixes = "CommonPrefixes";
internal const string ContentType = "ContentType";
internal const string ContentEncoding = "ContentEncoding";
internal const string ContentLanguage = "ContentLanguage";
internal const string Size = "Size";
internal const string Blobs = "Blobs";
internal const string Blob = "Blob";
internal const string BlobName = "Name";
internal const string BlobPrefix = "BlobPrefix";
internal const string BlobPrefixName = "Name";
internal const string Name = "Name";
internal const string Queues = "Queues";
internal const string Queue = "Queue";
internal const string QueueName = "QueueName";
internal const string QueueMessagesList = "QueueMessagesList";
internal const string QueueMessage = "QueueMessage";
internal const string MessageId = "MessageId";
internal const string PopReceipt = "PopReceipt";
internal const string InsertionTime = "InsertionTime";
internal const string ExpirationTime = "ExpirationTime";
internal const string TimeNextVisible = "TimeNextVisible";
internal const string MessageText = "MessageText";
// Error specific constants
internal const string ErrorRootElement = "Error";
internal const string ErrorCode = "Code";
internal const string ErrorMessage = "Message";
internal const string ErrorException = "ExceptionDetails";
internal const string ErrorExceptionMessage = "ExceptionMessage";
internal const string ErrorExceptionStackTrace = "StackTrace";
internal const string AuthenticationErrorDetail = "AuthenticationErrorDetail";
//The following are for table error messages
internal const string DataWebMetadataNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
internal const string TableErrorCodeElement = "code";
internal const string TableErrorMessageElement = "message";
}
internal static class HeaderNames
{
internal const string PrefixForStorageProperties = "x-ms-prop-";
internal const string PrefixForMetadata = "x-ms-meta-";
internal const string PrefixForStorageHeader = "x-ms-";
internal const string PrefixForTableContinuation = "x-ms-continuation-";
//
// Standard headers...
//
internal const string ContentLanguage = "Content-Language";
internal const string ContentLength = "Content-Length";
internal const string ContentType = "Content-Type";
internal const string ContentEncoding = "Content-Encoding";
internal const string ContentMD5 = "Content-MD5";
internal const string ContentRange = "Content-Range";
internal const string LastModifiedTime = "Last-Modified";
internal const string Server = "Server";
internal const string Allow = "Allow";
internal const string ETag = "ETag";
internal const string Range = "Range";
internal const string Date = "Date";
internal const string Authorization = "Authorization";
internal const string IfModifiedSince = "If-Modified-Since";
internal const string IfUnmodifiedSince = "If-Unmodified-Since";
internal const string IfMatch = "If-Match";
internal const string IfNoneMatch = "If-None-Match";
internal const string IfRange = "If-Range";
internal const string NextPartitionKey = "NextPartitionKey";
internal const string NextRowKey = "NextRowKey";
internal const string NextTableName = "NextTableName";
//
// Storage specific custom headers...
//
internal const string StorageDateTime = PrefixForStorageHeader + "date";
internal const string PublicAccess = PrefixForStorageProperties + "publicaccess";
internal const string StorageRange = PrefixForStorageHeader + "range";
internal const string CreationTime = PrefixForStorageProperties + "creation-time";
internal const string ForceUpdate = PrefixForStorageHeader + "force-update";
internal const string ApproximateMessagesCount = PrefixForStorageHeader + "approximate-messages-count";
internal const string Version = PrefixForStorageHeader + "version";
}
internal static class HeaderValues
{
internal const string ContentTypeXml = "application/xml";
/// <summary>
/// This is the default content-type xStore uses when no content type is specified
/// </summary>
internal const string DefaultContentType = "application/octet-stream";
// The Range header value is "bytes=start-end", both start and end can be empty
internal const string RangeHeaderFormat = "bytes={0}-{1}";
}
internal static class AuthenticationSchemeNames
{
internal const string SharedKeyAuthSchemeName = "SharedKey";
internal const string SharedKeyLiteAuthSchemeName = "SharedKeyLite";
}
internal static class HttpMethod
{
internal const string Get = "GET";
internal const string Put = "PUT";
internal const string Post = "POST";
internal const string Head = "HEAD";
internal const string Delete = "DELETE";
internal const string Trace = "TRACE";
internal const string Options = "OPTIONS";
internal const string Connect = "CONNECT";
}
internal static class BlobBlockConstants
{
internal const int KB = 1024;
internal const int MB = 1024 * KB;
/// <summary>
/// When transmitting a blob that is larger than this constant, this library automatically
/// transmits the blob as individual blocks. I.e., the blob is (1) partitioned
/// into separate parts (these parts are called blocks) and then (2) each of the blocks is
/// transmitted separately.
/// The maximum size of this constant as supported by the real blob storage service is currently
/// 64 MB; the development storage tool currently restricts this value to 2 MB.
/// Setting this constant can have a significant impact on the performance for uploading or
/// downloading blobs.
/// As a general guideline: If you run in a reliable environment increase this constant to reduce
/// the amount of roundtrips. In an unreliable environment keep this constant low to reduce the
/// amount of data that needs to be retransmitted in case of connection failures.
/// </summary>
internal const long MaximumBlobSizeBeforeTransmittingAsBlocks = 2 * MB;
/// <summary>
/// The size of a single block when transmitting a blob that is larger than the
/// MaximumBlobSizeBeforeTransmittingAsBlocks constant (see above).
/// The maximum size of this constant is currently 4 MB; the development storage
/// tool currently restricts this value to 1 MB.
/// Setting this constant can have a significant impact on the performance for uploading or
/// downloading blobs.
/// As a general guideline: If you run in a reliable environment increase this constant to reduce
/// the amount of roundtrips. In an unreliable environment keep this constant low to reduce the
/// amount of data that needs to be retransmitted in case of connection failures.
/// </summary>
internal const long BlockSize = 1 * MB;
}
internal static class ListingConstants
{
internal const int MaxContainerListResults = 100;
internal const int MaxBlobListResults = 100;
internal const int MaxQueueListResults = 50;
internal const int MaxTableListResults = 50;
}
/// <summary>
/// Contains regular expressions for checking whether container and table names conform
/// to the rules of the storage REST protocols.
/// </summary>
public static class RegularExpressionStrings
{
/// <summary>
/// Container or queue names that match against this regular expression are valid.
/// </summary>
public const string ValidContainerNameRegex = @"^([a-z]|\d){1}([a-z]|-|\d){1,61}([a-z]|\d){1}$";
/// <summary>
/// Table names that match against this regular expression are valid.
/// </summary>
public const string ValidTableNameRegex = @"^([a-z]|[A-Z]){1}([a-z]|[A-Z]|\d){2,62}$";
}
internal static class StandardPortalEndpoints
{
internal const string BlobStorage = "blob";
internal const string QueueStorage = "queue";
internal const string TableStorage = "table";
internal const string StorageHostSuffix = ".core.windows.net";
internal const string BlobStorageEndpoint = BlobStorage + StorageHostSuffix;
internal const string QueueStorageEndpoint = QueueStorage + StorageHostSuffix;
internal const string TableStorageEndpoint = TableStorage + StorageHostSuffix;
}
}
internal static partial class Utilities
{
internal static HttpWebRequest CreateHttpRequest(Uri uri, string httpMethod, TimeSpan timeout)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Timeout = (int)timeout.TotalMilliseconds;
request.ReadWriteTimeout = (int)timeout.TotalMilliseconds;
request.Method = httpMethod;
request.ContentLength = 0;
request.Headers.Add(StorageHttpConstants.HeaderNames.StorageDateTime,
Utilities.ConvertDateTimeToHttpString(DateTime.UtcNow));
return request;
}
/// <summary>
/// Converts the date time to a valid string form as per HTTP standards
/// </summary>
internal static string ConvertDateTimeToHttpString(DateTime dateTime)
{
// On the wire everything should be represented in UTC. This assert will catch invalid callers who
// are violating this rule.
Debug.Assert(dateTime == DateTime.MaxValue || dateTime == DateTime.MinValue || dateTime.Kind == DateTimeKind.Utc);
// 'R' means rfc1123 date which is what our server uses for all dates...
// It will be in the following format:
// Sun, 28 Jan 2008 12:11:37 GMT
return dateTime.ToString("R", CultureInfo.InvariantCulture);
}
/// <summary>
/// Parse a string having the date time information in acceptable formats according to HTTP standards
/// </summary>
internal static bool TryGetDateTimeFromHttpString(string dateString, out DateTime? result)
{
DateTime dateTime;
result = null;
// 'R' means rfc1123 date which is the preferred format used in HTTP
bool parsed = DateTime.TryParseExact(dateString, "R", null, DateTimeStyles.None, out dateTime);
if (parsed)
{
// For some reason, format string "R" makes the DateTime.Kind as Unspecified while it's actually
// Utc. Specifying DateTimeStyles.AssumeUniversal also doesn't make the difference. If we also
// specify AdjustToUniversal it works as expected but we don't really want Parse to adjust
// things automatically.
result = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
return true;
}
return false;
}
/// <summary>
/// Copies from one stream to another
/// </summary>
/// <param name="sourceStream">The stream to copy from</param>
/// <param name="destinationStream">The stream to copy to</param>
internal static long CopyStream(Stream sourceStream, Stream destinationStream)
{
const int BufferSize = 0x10000;
byte[] buffer = new byte[BufferSize];
int n = 0;
long totalRead = 0;
do
{
n = sourceStream.Read(buffer, 0, BufferSize);
if (n > 0)
{
totalRead += n;
destinationStream.Write(buffer, 0, n);
}
} while (n > 0);
return totalRead;
}
internal static void CopyStream(Stream sourceStream, Stream destinationStream, long length)
{
const int BufferSize = 0x10000;
byte[] buffer = new byte[BufferSize];
int n = 0;
long amountLeft = length;
do
{
amountLeft -= n;
n = sourceStream.Read(buffer, 0, (int)Math.Min(BufferSize, amountLeft));
if (n > 0)
{
destinationStream.Write(buffer, 0, n);
}
} while (n > 0);
}
internal static int CopyStreamToBuffer(Stream sourceStream, byte[] buffer, int bytesToRead)
{
int n = 0;
int amountLeft = bytesToRead;
do
{
n = sourceStream.Read(buffer, bytesToRead - amountLeft, amountLeft);
amountLeft -= n;
} while (n > 0);
return bytesToRead - amountLeft;
}
internal static Uri CreateRequestUri(
Uri baseUri,
bool usePathStyleUris,
string accountName,
string containerName,
string blobName,
TimeSpan Timeout,
NameValueCollection queryParameters,
out ResourceUriComponents uriComponents
)
{
uriComponents =
new ResourceUriComponents(accountName, containerName, blobName);
Uri uri = HttpRequestAccessor.ConstructResourceUri(baseUri, uriComponents, usePathStyleUris);
if (queryParameters != null)
{
UriBuilder builder = new UriBuilder(uri);
if (queryParameters.Get(StorageHttpConstants.QueryParams.QueryParamTimeout) == null)
{
queryParameters.Add(StorageHttpConstants.QueryParams.QueryParamTimeout,
Timeout.TotalSeconds.ToString(CultureInfo.InvariantCulture));
}
StringBuilder sb = new StringBuilder();
bool firstParam = true;
foreach (string queryKey in queryParameters.AllKeys)
{
if (!firstParam)
sb.Append("&");
sb.Append(HttpUtility.UrlEncode(queryKey));
sb.Append('=');
sb.Append(HttpUtility.UrlEncode(queryParameters[queryKey]));
firstParam = false;
}
if (sb.Length > 0)
{
builder.Query = sb.ToString();
}
return builder.Uri;
}
else
{
return uri;
}
}
internal static bool StringIsIPAddress(string address)
{
IPAddress outIPAddress;
return IPAddress.TryParse(address, out outIPAddress);
}
internal static void AddMetadataHeaders(HttpWebRequest request, NameValueCollection metadata)
{
foreach (string key in metadata.Keys)
{
request.Headers.Add(
StorageHttpConstants.HeaderNames.PrefixForMetadata + key,
metadata[key]
);
}
}
internal static bool IsValidTableName(string name)
{
if (string.IsNullOrEmpty(name))
{
return false;
}
Regex reg = new Regex(StorageHttpConstants.RegularExpressionStrings.ValidTableNameRegex);
if (reg.IsMatch(name))
{
return true;
}
else
{
return false;
}
}
internal static bool IsValidContainerOrQueueName(string name)
{
if (string.IsNullOrEmpty(name))
{
return false;
}
Regex reg = new Regex(StorageHttpConstants.RegularExpressionStrings.ValidContainerNameRegex);
if (reg.IsMatch(name))
{
return true;
}
else
{
return false;
}
}
}
}

View file

@ -0,0 +1,977 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <copyright file="RestQueue.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.Web;
using System.Net;
using System.Collections.Specialized;
using System.IO;
using System.Xml;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
namespace Microsoft.Samples.ServiceHosting.StorageClient
{
internal class QueueStorageRest : QueueStorage
{
private SharedKeyCredentials _credentials;
internal QueueStorageRest(StorageAccountInfo accountInfo,string version)
: base(accountInfo,version)
{
byte[] key = null;
if (accountInfo.Base64Key != null)
{
key = Convert.FromBase64String(accountInfo.Base64Key);
}
_credentials = new SharedKeyCredentials(accountInfo.AccountName, key);
}
/// <summary>
/// Get a reference to a Queue object with a specified name. This method does not make a call to
/// the queue service.
/// </summary>
/// <param name="queueName">The name of the queue</param>
/// <returns>A newly created queue object</returns>
public override MessageQueue GetQueue(string queueName)
{
return new QueueRest(queueName,
AccountInfo,
Timeout,
RetryPolicy,
Version
);
}
internal class ListQueueResult
{
internal ListQueueResult(IEnumerable<string> names, IEnumerable<string> urls, string nextMarker)
{
Names = names;
Urls = urls;
NextMarker = nextMarker;
}
internal IEnumerable<string> Names
{
get;
private set;
}
internal IEnumerable<string> Urls
{
get;
private set;
}
internal string NextMarker
{
get;
private set;
}
}
/// <summary>
/// Lists all queues with a given prefix within an account.
/// </summary>
/// <param name="prefix"></param>
/// <returns>The list of queue names.</returns>
public override IEnumerable<MessageQueue> ListQueues(string prefix)
{
string marker = "";
const int maxResults = StorageHttpConstants.ListingConstants.MaxQueueListResults;
do
{
ListQueueResult result = ListQueuesImpl(prefix, marker, maxResults);
if (result == null)
{
marker = null;
}
else
{
marker = result.NextMarker;
foreach (string name in result.Names)
{
yield return new QueueRest(name, AccountInfo, this.Timeout, this.RetryPolicy,this.Version);
}
}
} while (marker != null);
}
/// <summary>
/// Lists the queues within the account.
/// </summary>
/// <returns>A list of queues</returns>
private ListQueueResult ListQueuesImpl(string prefix, string marker, int maxResult)
{
ListQueueResult result = null;
RetryPolicy(() =>
{
NameValueCollection col = new NameValueCollection();
col.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.List);
if (!string.IsNullOrEmpty(prefix))
{
col.Add(StorageHttpConstants.QueryParams.QueryParamPrefix, prefix);
}
if (!string.IsNullOrEmpty(marker))
{
col.Add(StorageHttpConstants.QueryParams.QueryParamMarker, marker);
}
col.Add(StorageHttpConstants.QueryParams.QueryParamMaxResults, maxResult.ToString(CultureInfo.InvariantCulture));
ResourceUriComponents uriComponents;
Uri uri = Utilities.CreateRequestUri(
AccountInfo.BaseUri,
AccountInfo.UsePathStyleUris,
AccountInfo.AccountName,
null,
null,
Timeout,
col,
out uriComponents
);
HttpWebRequest request = Utilities.CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, Timeout);
_credentials.SignRequest(request, uriComponents);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream stream = response.GetResponseStream())
{
result = GetQueuesFromResponse(stream);
stream.Close();
}
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
catch (WebException we)
{
throw Utilities.TranslateWebException(we);
}
});
return result;
}
private static ListQueueResult GetQueuesFromResponse(Stream stream)
{
ListQueueResult result = null;
List<string> names = new List<string>();
List<string> urls = new List<string>();
string nextMarker = null;
XmlDocument doc = new XmlDocument();
try
{
doc.Load(stream);
}
catch (XmlException xe)
{
throw new StorageServerException(StorageErrorCode.ServiceBadResponse,
"The result of a ListQueue operation could not be parsed", default(HttpStatusCode), xe);
}
// get queue names and urls
XmlNodeList queueNameNodes = doc.SelectNodes(XPathQueryHelper.QueueListQuery);
foreach (XmlNode queueNameNode in queueNameNodes)
{
string queueName = XPathQueryHelper.LoadSingleChildStringValue(queueNameNode, StorageHttpConstants.XmlElementNames.QueueName, true);
names.Add(queueName);
string url = XPathQueryHelper.LoadSingleChildStringValue(queueNameNode, StorageHttpConstants.XmlElementNames.Url, true);
urls.Add(url);
}
// Get the nextMarker
XmlNode nextMarkerNode = doc.SelectSingleNode(XPathQueryHelper.NextMarkerQuery);
if (nextMarkerNode != null && nextMarkerNode.FirstChild != null)
{
nextMarker = nextMarkerNode.FirstChild.Value;
}
if (names.Count > 0)
{
Debug.Assert(names.Count == urls.Count);
result = new ListQueueResult(names, urls, nextMarker);
}
return result;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
Justification = "Disposable types are only used for automatic receiving of messages, which handle the disposal themselves.")]
internal class QueueRest : MessageQueue
{
#region Member variables and constructors
private Uri _queueUri;
private SharedKeyCredentials _credentials;
private int _pollInterval = DefaultPollInterval;
private string version;
internal QueueRest(
string name,
StorageAccountInfo account,
TimeSpan timeout,
RetryPolicy retryPolicy,
string version
)
: base(name, account)
{
byte[] key = null;
if (AccountInfo.Base64Key != null)
{
key = Convert.FromBase64String(AccountInfo.Base64Key);
}
ResourceUriComponents uriComponents = new ResourceUriComponents(account.AccountName, name, null);
_credentials = new SharedKeyCredentials(AccountInfo.AccountName, key);
_queueUri = HttpRequestAccessor.ConstructResourceUri(account.BaseUri, uriComponents, account.UsePathStyleUris);
Timeout = timeout;
RetryPolicy = retryPolicy;
this.version = version;
}
#endregion
#region Public interface
public override Uri QueueUri
{
get
{
return _queueUri;
}
}
public override bool CreateQueue(out bool queueAlreadyExists)
{
bool result = false;
queueAlreadyExists = false;
// cannot use ref or out parameters in the retry expression below
bool exists = false;
RetryPolicy(() =>
{
ResourceUriComponents uriComponents;
Uri uri = CreateRequestUri(null, new NameValueCollection(), false, out uriComponents);
HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Put);
_credentials.SignRequest(request, uriComponents);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.Created)
{
// as an addition we could parse the result and retrieve
// queue properties at this point
exists = false;
result = true;
}
else if (response.StatusCode == HttpStatusCode.NoContent)
{
exists = true;
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
catch (WebException we)
{
if (we.Response != null &&
((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Conflict)
{
exists = true;
}
else
{
throw Utilities.TranslateWebException(we);
}
}
});
queueAlreadyExists = exists;
return result;
}
public override bool DeleteQueue()
{
bool result = false;
RetryPolicy(() =>
{
NameValueCollection col = new NameValueCollection();
ResourceUriComponents uriComponents;
Uri uri = CreateRequestUri(null, col, false, out uriComponents);
HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Delete);
_credentials.SignRequest(request, uriComponents);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.NoContent)
{
result = true;
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
catch (WebException we)
{
if (we.Response != null &&
(((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.NotFound ||
((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.PreconditionFailed ||
((HttpWebResponse)we.Response).StatusCode == HttpStatusCode.Conflict))
{
result = false;
}
else
{
throw Utilities.TranslateWebException(we);
}
}
});
return result;
}
public override bool SetProperties(QueueProperties properties)
{
if (properties == null)
{
throw new ArgumentNullException("properties");
}
bool result = false;
RetryPolicy(() =>
{
NameValueCollection col = new NameValueCollection();
col.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.Metadata);
ResourceUriComponents uriComponents;
Uri uri = CreateRequestUri(null, col, out uriComponents);
HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Put, properties.Metadata);
_credentials.SignRequest(request, uriComponents);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.NoContent)
{
result = true;
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
catch (WebException we)
{
throw Utilities.TranslateWebException(we);
}
});
return result;
}
public override QueueProperties GetProperties()
{
QueueProperties result = null;
RetryPolicy(() =>
{
NameValueCollection col = new NameValueCollection();
col.Add(StorageHttpConstants.QueryParams.QueryParamComp, StorageHttpConstants.CompConstants.Metadata);
ResourceUriComponents uriComponents;
Uri uri = CreateRequestUri(null, col, out uriComponents);
HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, null);
_credentials.SignRequest(request, uriComponents);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
result = GetPropertiesFromHeaders(response);
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
catch (WebException we)
{
throw Utilities.TranslateWebException(we);
}
});
return result;
}
public override int ApproximateCount()
{
QueueProperties props = GetProperties();
return props.ApproximateMessageCount;
}
// getting and putting messages
public override bool PutMessage(Message msg)
{
return PutMessage(msg, -1);
}
public override bool PutMessage(Message msg, int timeToLiveInSeconds)
{
if (timeToLiveInSeconds < -1)
{
throw new ArgumentException("ttl parameter must be equal or larger than 0.");
}
else if (timeToLiveInSeconds > Message.MaxTimeToLive)
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
"timeToLiveHours parameter must be smaller or equal than {0}, which is 7 days in hours.", Message.MaxTimeToLive));
}
if (msg == null || msg.ContentAsBytes() == null)
{
throw new ArgumentNullException("msg");
}
if (Convert.ToBase64String(msg.ContentAsBytes()).Length > Message.MaxMessageSize)
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Messages cannot be larger than {0} bytes.", Message.MaxMessageSize));
}
bool result = false;
RetryPolicy(() =>
{
NameValueCollection col = new NameValueCollection();
if (timeToLiveInSeconds != -1)
{
col.Add(StorageHttpConstants.RequestParams.MessageTtl, timeToLiveInSeconds.ToString(CultureInfo.InvariantCulture));
}
ResourceUriComponents uriComponents;
Uri uri = CreateRequestUri(StorageHttpConstants.RequestParams.Messages, col, out uriComponents);
HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Post, null);
int len;
byte[] body = msg.GetContentXMLRepresentation(out len);
request.ContentLength = len;
_credentials.SignRequest(request, uriComponents);
try
{
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(body, 0, body.Length);
requestStream.Close();
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.Created)
{
result = true;
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
}
catch (WebException we)
{
throw Utilities.TranslateWebException(we);
}
});
return result;
}
public override Message GetMessage()
{
IEnumerable<Message> result = GetMessages(1);
if (result == null || result.Count() == 0)
{
return null;
}
return result.First();
}
public override Message GetMessage(int visibilityTimeoutInSeconds)
{
IEnumerable<Message> result = GetMessages(1, visibilityTimeoutInSeconds);
if (result == null || result.Count() == 0)
{
return null;
}
return result.First();
}
public override IEnumerable<Message> GetMessages(int numberOfMessages)
{
return GetMessages(numberOfMessages, -1);
}
public override IEnumerable<Message> GetMessages(int numberOfMessages, int visibilityTimeout)
{
return InternalGet(numberOfMessages, visibilityTimeout, false);
}
public override Message PeekMessage()
{
IEnumerable<Message> result = PeekMessages(1);
if (result == null || result.Count() == 0)
{
return null;
}
return result.First();
}
public override IEnumerable<Message> PeekMessages(int numberOfMessages)
{
return InternalGet(numberOfMessages, -1, true);
}
// deleting messages
public override bool DeleteMessage(Message msg)
{
if (msg.PopReceipt == null)
{
throw new ArgumentException("No PopReceipt for the given message!");
}
bool result = false;
RetryPolicy(() =>
{
NameValueCollection col = new NameValueCollection();
col.Add(StorageHttpConstants.RequestParams.PopReceipt, msg.PopReceipt.ToString());
ResourceUriComponents uriComponents;
Uri uri = CreateRequestUri(StorageHttpConstants.RequestParams.Messages + "/" + msg.Id, col, out uriComponents);
HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Delete, null);
_credentials.SignRequest(request, uriComponents);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.NoContent)
{
result = true;
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
catch (WebException we)
{
throw Utilities.TranslateWebException(we);
}
});
return result;
}
public override bool Clear()
{
bool result = false;
RetryPolicy(() =>
{
NameValueCollection col = new NameValueCollection();
ResourceUriComponents uriComponents;
Uri uri = CreateRequestUri(StorageHttpConstants.RequestParams.Messages, col, out uriComponents);
HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Delete, null);
_credentials.SignRequest(request, uriComponents);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.NoContent)
{
result = true;
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
catch (WebException we)
{
throw Utilities.TranslateWebException(we);
}
});
return result;
}
// automatic receiving of messages
public override int PollInterval
{
get
{
return _pollInterval;
}
set
{
if (value < 0)
{
throw new ArgumentException("The send threshold must be a positive value.");
}
if (_run)
{
throw new ArgumentException("You cannot set the poll interval while the receive thread is running");
}
_pollInterval = value;
}
}
private AutoResetEvent _evStarted;
private AutoResetEvent _evStopped;
private AutoResetEvent _evQuit;
private bool _run;
private Thread _receiveThread;
private int _internalPollInterval;
private void PeriodicReceive()
{
Message msg;
_evStarted.Set();
_internalPollInterval = PollInterval;
while (!_evQuit.WaitOne(_internalPollInterval, false))
{
// time is up, so we get the message and continue
msg = GetMessage();
if (msg != null)
{
MessageReceived(this, new MessageReceivedEventArgs(msg));
// continue receiving fast until we get no message
_internalPollInterval = 10;
}
else
{
// we got no message, so we can fall back to the normal speed
_internalPollInterval = PollInterval;
}
}
_evStopped.Set();
}
public override bool StartReceiving()
{
lock (this)
{
if (_run)
{
return true;
}
_run = true;
}
if (_evStarted == null) {
_evStarted = new AutoResetEvent(false);
}
if (_evStopped == null) {
_evStopped = new AutoResetEvent(false);
}
if (_evQuit == null) {
_evQuit = new AutoResetEvent(false);
}
_receiveThread = new Thread(new ThreadStart(this.PeriodicReceive));
_receiveThread.Start();
if (!_evStarted.WaitOne(10000, false))
{
_receiveThread.Abort();
CloseEvents();
_run = false;
return false;
}
return true;
}
public override void StopReceiving()
{
_evQuit.Set();
if (!_evStopped.WaitOne(10000, false))
{
_receiveThread.Abort();
}
CloseEvents();
_run = false;
}
private void CloseEvents()
{
if (_evStarted != null)
{
_evStarted.Close();
}
if (_evStopped != null)
{
_evStopped.Close();
}
if (_evQuit != null)
{
_evQuit.Close();
}
}
public override event MessageReceivedEventHandler MessageReceived;
#endregion
#region Helper methods
private static QueueProperties GetPropertiesFromHeaders(HttpWebResponse response)
{
QueueProperties properties = new QueueProperties();
int prefixLength = StorageHttpConstants.HeaderNames.PrefixForMetadata.Length;
foreach (string key in response.Headers.AllKeys)
{
if (key.Equals(StorageHttpConstants.HeaderNames.ApproximateMessagesCount, StringComparison.OrdinalIgnoreCase))
{
properties.ApproximateMessageCount = Convert.ToInt32(response.Headers[key], CultureInfo.InvariantCulture);
}
else if (key.StartsWith(StorageHttpConstants.HeaderNames.PrefixForMetadata, StringComparison.OrdinalIgnoreCase))
{
if (properties.Metadata == null)
{
properties.Metadata = new NameValueCollection();
}
properties.Metadata.Add(key.Substring(prefixLength), response.Headers[key]);
}
}
return properties;
}
private IEnumerable<Message> InternalGet(int numberOfMessages, int visibilityTimeout, bool peekOnly)
{
if (peekOnly && visibilityTimeout != -1)
{
throw new ArgumentException("A peek operation does not change the visibility of messages", "visibilityTimeout");
}
if (numberOfMessages < 1)
{
throw new ArgumentException("numberOfMessages must be a positive integer", "numberOfMessages");
}
if (visibilityTimeout < -1)
{
throw new ArgumentException("Visibility Timeout must be 0 or a positive integer", "visibilityTimeout");
}
IEnumerable<Message> result = null;
RetryPolicy(() =>
{
NameValueCollection col = new NameValueCollection();
col.Add(StorageHttpConstants.RequestParams.NumOfMessages, numberOfMessages.ToString(CultureInfo.InvariantCulture));
if (visibilityTimeout != -1)
{
col.Add(StorageHttpConstants.RequestParams.VisibilityTimeout,
visibilityTimeout.ToString(CultureInfo.InvariantCulture));
}
if (peekOnly)
{
col.Add(StorageHttpConstants.RequestParams.PeekOnly,
peekOnly.ToString(CultureInfo.InvariantCulture));
}
ResourceUriComponents uriComponents;
Uri uri = CreateRequestUri(StorageHttpConstants.RequestParams.Messages, col, out uriComponents);
HttpWebRequest request = CreateHttpRequest(uri, StorageHttpConstants.HttpMethod.Get, null);
_credentials.SignRequest(request, uriComponents);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream stream = response.GetResponseStream())
{
result = GetMessageFromResponse(stream);
stream.Close();
}
}
else
{
Utilities.ProcessUnexpectedStatusCode(response);
}
response.Close();
}
}
catch (WebException we)
{
throw Utilities.TranslateWebException(we);
}
});
return result;
}
private static IEnumerable<Message> GetMessageFromResponse(Stream stream)
{
List<Message> result = null;
Message msg;
XmlDocument doc = new XmlDocument();
try
{
doc.Load(stream);
}
catch (XmlException xe)
{
throw new StorageServerException(StorageErrorCode.ServiceBadResponse,
"The result of a get message opertation could not be parsed", default(HttpStatusCode), xe);
}
XmlNodeList messagesNodes = doc.SelectNodes(XPathQueryHelper.MessagesListQuery);
if (messagesNodes.Count > 0)
{
result = new List<Message>();
}
foreach (XmlNode messageNode in messagesNodes)
{
msg = new Message();
msg.Id = messageNode.SelectSingleNode(StorageHttpConstants.XmlElementNames.MessageId).FirstChild.Value.Trim();
Debug.Assert(msg.Id != null);
if (messageNode.SelectSingleNode(StorageHttpConstants.XmlElementNames.PopReceipt) != null)
{
msg.PopReceipt = messageNode.SelectSingleNode(StorageHttpConstants.XmlElementNames.PopReceipt).FirstChild.Value.Trim();
Debug.Assert(msg.PopReceipt != null);
}
msg.InsertionTime = XPathQueryHelper.LoadSingleChildDateTimeValue(messageNode, StorageHttpConstants.XmlElementNames.InsertionTime, false).Value;
msg.ExpirationTime = XPathQueryHelper.LoadSingleChildDateTimeValue(messageNode, StorageHttpConstants.XmlElementNames.ExpirationTime, false).Value;
if (XPathQueryHelper.LoadSingleChildDateTimeValue(messageNode, StorageHttpConstants.XmlElementNames.TimeNextVisible, false) != null)
{
msg.TimeNextVisible = XPathQueryHelper.LoadSingleChildDateTimeValue(messageNode, StorageHttpConstants.XmlElementNames.TimeNextVisible, false).Value;
}
msg.SetContentFromBase64String(XPathQueryHelper.LoadSingleChildStringValue(messageNode, StorageHttpConstants.XmlElementNames.MessageText, false));
result.Add(msg);
}
return result.AsEnumerable();
}
private HttpWebRequest CreateHttpRequest(Uri uri, string httpMethod)
{
return CreateHttpRequest(uri, httpMethod, null);
}
private HttpWebRequest CreateHttpRequest(Uri uri, string httpMethod, NameValueCollection metadata)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Timeout = (int)Timeout.TotalMilliseconds;
request.ReadWriteTimeout = (int)Timeout.TotalMilliseconds;
request.Method = httpMethod;
request.ContentLength = 0;
request.Headers.Add(StorageHttpConstants.HeaderNames.StorageDateTime,
Utilities.ConvertDateTimeToHttpString(DateTime.UtcNow));
if (!String.IsNullOrEmpty(this.version))
{
request.Headers.Add(StorageHttpConstants.HeaderNames.Version, this.version);
}
if (metadata != null)
{
Utilities.AddMetadataHeaders(request, metadata);
}
return request;
}
private Uri CreateRequestUri(
string uriSuffix,
NameValueCollection queryParameters,
out ResourceUriComponents uriComponents
)
{
return CreateRequestUri(uriSuffix, queryParameters, false, out uriComponents);
}
private Uri CreateRequestUri(
string uriSuffix,
NameValueCollection queryParameters,
bool accountOperation,
out ResourceUriComponents uriComponents
)
{
return Utilities.CreateRequestUri(
this.AccountInfo.BaseUri,
this.AccountInfo.UsePathStyleUris,
this.AccountInfo.AccountName,
accountOperation ? null : this.Name,
uriSuffix,
this.Timeout,
queryParameters,
out uriComponents
);
}
#endregion
}
}

View file

@ -0,0 +1,628 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
//
// <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
}
}

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{C6F30C10-E1C2-4327-BB6B-3160B479CCA1}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Microsoft.Samples.ServiceHosting.StorageClient</RootNamespace>
<AssemblyName>StorageClient</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;CODE_ANALYSIS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\StorageClient.XML</DocumentationFile>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Data.Services.Client">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="Microsoft.ServiceHosting.ServiceRuntime">
<SpecificVersion>False</SpecificVersion>
</Reference>
</ItemGroup>
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup>
<Compile Include="Authentication.cs" />
<Compile Include="BlobStorage.cs" />
<Compile Include="Errors.cs" />
<Compile Include="Queue.cs" />
<Compile Include="RestBlobStorage.cs" />
<Compile Include="RestHelpers.cs" />
<Compile Include="RestQueue.cs" />
<Compile Include="StorageAccountInfo.cs" />
<Compile Include="TableStorage.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\bin\Debug\StorageClient.dll
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\bin\Debug\StorageClient.pdb
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\obj\Debug\ResolveAssemblyReference.cache
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\obj\Debug\StorageClient.dll
G:\TrainingKits\WindowsAzurePlatformKit\Labs\BuildingMVCAppsWithWindowsAzure\Assets\StorageClient\obj\Debug\StorageClient.pdb
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\bin\Debug\StorageClient.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\bin\Debug\StorageClient.pdb
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Debug\ResolveAssemblyReference.cache
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Debug\StorageClient.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Debug\StorageClient.pdb

View file

@ -0,0 +1,5 @@
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\bin\Release\StorageClient.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\bin\Release\StorageClient.pdb
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Release\ResolveAssemblyReference.cache
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Release\StorageClient.dll
F:\projects\dotnet35\VBParser80\AzureStoreAsp\Assets\StorageClient\obj\Release\StorageClient.pdb

View file

@ -0,0 +1,40 @@
setlocal enabledelayedexpansion
@echo off
%~d0
cd "%~dp0"
IF "%1%"=="" GOTO ERROR
echo.
echo ========================================
echo Updating HOSTS file...
echo ========================================
echo.
for /f "useback tokens=*" %%a in ('%1') do set STR=%%~a
for /l %%a in (1,1,5) do if "!STR:~-1!"=="/" set STR=!STR:~0,-1!
copy %systemroot%\System32\drivers\etc\hosts %systemroot%\System32\drivers\etc\hosts.bak
echo. >> %systemroot%\System32\drivers\etc\hosts
echo 127.0.0.1 %STR% >> %systemroot%\System32\drivers\etc\hosts
echo.
echo ========================================
echo Updating HOSTS file done.
echo ========================================
echo.
GOTO FINISH
:ERROR
echo.
echo ========================================
echo Domain parameter missing.
echo (required for updating the hosts file).
echo ========================================
echo.
:FINISH

View file

@ -0,0 +1,49 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{CC5FD16D-436D-48AD-A40C-5A424C6E3E79}") = "AzureStoreService", "AzureStoreService\AzureStoreService.ccproj", "{B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MVCAzureStore", "AzureStore\MVCAzureStore.csproj", "{D4109103-5396-4DE5-8042-1ACAAB367C32}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Assets", "Assets", "{103A3D88-1691-4659-AE90-A252552BC85B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspProviders", "Assets\AspProviders\AspProviders.csproj", "{306D2F9E-D6D0-4D96-94F1-173C60A13875}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StorageClient", "Assets\StorageClient\StorageClient.csproj", "{C6F30C10-E1C2-4327-BB6B-3160B479CCA1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9C75B39B-52AF-4887-8FD6-C8F23AE0ACCD}"
ProjectSection(SolutionItems) = preProject
Readme.txt = Readme.txt
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D4109103-5396-4DE5-8042-1ACAAB367C32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4109103-5396-4DE5-8042-1ACAAB367C32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4109103-5396-4DE5-8042-1ACAAB367C32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4109103-5396-4DE5-8042-1ACAAB367C32}.Release|Any CPU.Build.0 = Release|Any CPU
{B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8FEB21E-A4D5-441C-B5BB-5CEB8A1BE537}.Release|Any CPU.Build.0 = Release|Any CPU
{306D2F9E-D6D0-4D96-94F1-173C60A13875}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{306D2F9E-D6D0-4D96-94F1-173C60A13875}.Debug|Any CPU.Build.0 = Debug|Any CPU
{306D2F9E-D6D0-4D96-94F1-173C60A13875}.Release|Any CPU.ActiveCfg = Release|Any CPU
{306D2F9E-D6D0-4D96-94F1-173C60A13875}.Release|Any CPU.Build.0 = Release|Any CPU
{C6F30C10-E1C2-4327-BB6B-3160B479CCA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C6F30C10-E1C2-4327-BB6B-3160B479CCA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6F30C10-E1C2-4327-BB6B-3160B479CCA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6F30C10-E1C2-4327-BB6B-3160B479CCA1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{306D2F9E-D6D0-4D96-94F1-173C60A13875} = {103A3D88-1691-4659-AE90-A252552BC85B}
{C6F30C10-E1C2-4327-BB6B-3160B479CCA1} = {103A3D88-1691-4659-AE90-A252552BC85B}
EndGlobalSection
EndGlobal

Binary file not shown.

View file

@ -0,0 +1,755 @@
/* 9 of 12 Microsoft Internet Explorer Design Templates
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HTML TAGS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
body {
font-family:Arial, Tahoma, Helvetica, sans-serif;
margin:0;
padding:0;
}
p, li {
font-size:.75em;
line-height:160%;
}
a img {
border:none;
}
/* HEADINGS */
h1,h2,h3,h4,h5,h6 {
margin:15px 0;
font-family:"trebuchet MS", verdana, sans-serif;
color:#e99400;
}
.content-left h1, .content-right h1,
.content-left h2, .content-right h2,
.content-left h3, .content-right h3,
.content-left h4, .content-right h4,
.content-left h5, .content-right h5,
.content-left h6, .content-right h6 {
color:#000;
margin:5px 0;
}
h1.first,
h2.first,
h3.first,
h4.first,
h5.first,
h6.first {
margin-top:0;
}
h1 {
font-size:1.2em;
}
h2 {
font-size:1em;
}
h3 {
font-size:.9em;
background:url(images/bg-line.png) repeat-x bottom;
}
h4 {
font-size:.8em;
}
h5 {
font-size:.8em;
}
h6 {
font-size:.8em;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FORMS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
fieldset {
padding:10px 20px;
margin:15px 0;
position:relative;
}
fieldset fieldset {
margin:10px 0;
width:90%;
}
legend {
font-weight:bold;
font-size:1em;
padding:10px;
color:#000;
margin:0 0 10px 30px;
font-family:"trebuchet MS", verdana, sans-serif;
}
fieldset fieldset legend {
font-size:.8em;
}
.input-box {
display:block;
margin-bottom:5px;
vertical-align:top;
padding:2px;
color:#666666;
font-size:.8em;
border:1px solid #82b26e;
background:url(images/bg-input.png) no-repeat;
}
label {
font-weight:bold;
font-size:.7em;
font-weight:bold;
display:block;
font-family:Arial, Helvetica, sans-serif;
}
.checkbox {
margin:12px 0;
padding:4px 0 2px 3px;
border:1px dotted #585858;
width:95%;
}
.checkbox input {
margin-right:6px;
margin-bottom:2px;
}
.forgot-password {
margin:2px 0;
font-size:.65em;
}
input.button {
color:#FFF;
background:#317f0c url(images/bg-button.png) repeat-x;
border:2px groove #FFF;
font-size:.7em;
font-weight:bold;
padding:0 5px 2px 5px;
cursor:pointer;
}
input.button-big {
padding:6px 20px;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LINKS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
a:link {
color:#33840c;
text-decoration:underline;
}
a:visited {
color:#72840c;
text-decoration:underline;
}
a:hover {
text-decoration:none;
}
a:active {
text-decoration:underline;
color:#86c400;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HEADER
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.header-container {
position:relative;
padding:6px 0 0 0;
background:#373737;
color:#FFF;
border-bottom:1px dotted #585858;
min-height:1px;
_height:1px;
}
/* LOGO */
.logo {
font-size:1.5em;
width:60%;
float:left;
font-family:"trebuchet MS", verdana, sans-serif;
padding:5px 0 10px 15px;
letter-spacing:.2em;
}
/* LOGO LINK */
.logo a:link,
.logo a:visited {
text-decoration:none;
color:#FFF;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LOGIN
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.nav-login {
font-size:.9em;
text-align:right;
margin:10px 15px 3px 0;
float:right;
width:20%;
}
/* LOGIN LISTS */
.nav-login ul {
margin:0;
padding:0;
}
.nav-login li {
list-style:none;
padding:3px 0 3px 17px;
display:inline;
margin-left:15px;
background:url(images/bullet.png) no-repeat 0 4px;
}
.nav-login li.first {
list-style:none;
margin-left:0;
}
/* LOGIN LINKS */
.nav-login a:link,
.nav-login a:visited{
color:#FFF;
text-decoration:none;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MAIN NAV
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.nav-main {
margin:-41px 0 0 199px;
padding:12px 15px 0 15px;
background:#f6f6f6;
min-height:28px;
_height:28px;
border-top:1px dotted #7a7a7a;
border-bottom:1px dotted #7a7a7a;
border-left:1px dotted #7a7a7a;
}
/* MAIN NAV LISTS */
.nav-main ul {
margin:0;
padding:0;
}
.nav-main li {
list-style:none;
float:left;
background:none;
padding:0;
}
.nav-main li a {
display:block;
padding:0 10px;
border-right:1px dotted #d2d2d2;
}
.nav-main li.first a {
padding-left:0;
}
/* NAV MAIN LINKS */
.nav-main a:link,
.nav-main a:visited {
color:#33840c;
text-decoration:none;
}
.nav-main .active a:link,
.nav-main .active a:visited {
color:#000;
}
.nav-main a:hover {
text-decoration:underline;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FOOTER
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.footer {
padding:10px 0;
_height:1px;
background:#373737;
clear:both;
}
.nav-footer {
position:relative;
_height:1px;
margin:0 200px;
text-align:center;
clear:both;
}
.nav-footer ul{
padding:0;
margin:0;
text-align:center;
}
.nav-footer li{
background:none;
display:inline;
border-right:1px dotted #686868;
padding:0 10px;
}
.nav-footer li.first {
border-left:1px dotted #686868;
}
.copyright {
color:#999;
font-size:.7em;
clear:both;
margin:5px 0 0 0;
}
.nav-footer a:link,
.nav-footer a:visited {
color:#CCC;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
POSTER
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.poster-container {
background:url(images/bg-poster-tile.jpg) repeat-x;
min-height:200px;
_height:200px;
margin-top:-1px;
}
.poster-container-no-image {
background:url(images/bg-poster-tile.jpg) repeat-x;
min-height:70px;
_height:70px;
margin-top:-1px;
}
.poster-inner {
background:url(images/bg-poster.jpg) no-repeat left top;
font-family:"trebuchet MS", verdana, sans-serif;
min-height:70px;
_height:70px;
}
.poster-photo {
background:url(images/bg-poster-photo.jpg) no-repeat left;
min-height:170px;
_height:170px;
padding:30px 30% 0 200px;
font-style:italic;
}
.poster-container h1 {
margin:0;
font-size:1.2em;
color:#000;
}
.poster-container p {
margin:10px 0 0 0;
line-height:normal;
font-size:1em;
}
/* POSTER LINKS */
.poster-links {
font-weight:bold;
}
.poster-container a:link,
.poster-container a:visited {
color:#33840c;
}
.poster-container a:hover {
text-decoration:none;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BREADCRUMB
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.breadcrumb {
min-height:1px;
_height:1px;
padding:0 0 15px 0;
margin:0 0 15px 0;
font-size:.7em;
border-bottom:1px dotted #686868;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PRIMARY LAYOUT STYLES
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.content-container {
position:relative;
_height:1px;
min-height:1px;
background:url(images/bg-column-left.png) repeat-y;
}
.content-container-inner {
background:url(images/bg-column-right.png) repeat-y right;
_height:1px;
min-height:1px;
padding:0 200px;
position:relative;
}
.content-main {
padding:15px 2% 20px 2%;
position:relative;
min-height:1px;
_height:1px;
float:left;
width:96%;
}
.content-left {
padding:20px 10px;
float:left;
width:180px;
margin-top:-1px;
position:relative;
margin-left:-100%;
right:200px;
_left:200px;
border-top:1px dotted #797979;
}
.content-right {
padding:15px 10px 20px 10px;
float:left;
width:180px;
position:relative;
margin-right:-200px;
}
.ads {
text-align:center;
margin:20px 0;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SIDE BUCKET
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.side-bucket {
margin-bottom:20px;
line-height:normal;
}
/* SIDE BUCKET LISTS */
.side-bucket ul {
margin:0;
padding:0;
_height:1px;
}
.side-bucket li {
list-style:none;
border-bottom:1px dotted #7a7a7a;
background-position:0 4px;
}
.side-bucket li.first {
border-top:1px dotted #7a7a7a;
}
/* SIDE BUCKET LINKS */
.side-bucket a:link,
.side-bucket a:visited {
text-decoration:none;
display:block;
_display:inline;
}
.side-bucket a:hover {
text-decoration:underline;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PHOTOS & CAPTIONS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.photo-container {
padding:10px;
border:1px dotted #7a7a7a;
}
.photo-container img {
display:block;
border:1px solid #666;
}
.photo-caption {
font-size:.7em;
padding:10px 10px 0 10px;
text-align:center;
color:#999;
font-style:italic
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
THREE-COLUMN SECTION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.three-column-container {
margin:10px 0px;
}
.three-column-left,
.three-column-right,
.three-column-middle {
float:left;
width:30%;
margin-right:5%;
}
.three-column-right {
margin-right:0;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DATA TABLES
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
table.data-table {
background:#f6f6f6;
font-size:.7em;
border-top:1px dotted #7a7a7a;
border-left:1px dotted #7a7a7a;
}
table.data-table caption{
background:#b6b6b6;
padding:10px;
text-align:center;
font-weight:bold;
font-family:Arial, Helvetica, sans-serif;
border:1px dotted #7a7a7a;
border-bottom:none;
}
table.data-table td,
table.data-table th{
padding:8px;
border-right:1px dotted #7a7a7a;
border-bottom:1px dotted #7a7a7a;
}
table.data-table th{
text-align:left;
font-family:Arial, Helvetica, sans-serif;
background:#ececec;
}
table.data-table td{
}
table.data-table tr.row-alternating td{
background:#e0e0e0;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LISTS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
li {
list-style:none;
background:url(images/bullet.png) no-repeat 0 5px;
padding:3px 0 3px 17px;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MISC STYLES
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.none {
display:none;
}
.clear {
clear:both;
font-size:1px;
margin:0;
padding:0;
}
.align-left {
float:left;
margin:0 20px 15px 0;
}
.align-right {
float:right;
margin:0 0 15px 20px;
}
.align-middle {
vertical-align:middle;
}
.inline {
display:inline;
}
.no-margin {
margin:0;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MODIFIED
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.login
{
margin:auto auto;
}
.login input[type=text], .login input[type=password]
{
width: 10em;
margin: 0.5em;
}
.content-main
{
_height:400px;
min-height:400px;
}
.product-category
{
color: #666666;
}
.product-list
{
padding:0.25em;
border: solid 1px #999;
font-size:10pt;
width:100%;
margin-top:0.5em;
margin-bottom: 0.5em;
height:150px;
}
.identity
{
margin-left:0.25em;
max-width:6.5em;
display:inline-block;
overflow:hidden;
vertical-align:middle;
}
.login label
{
display: inline;
}
label
{
font-size:1em;
}
.role label
{
margin:1em;
}
.role input
{
margin:1em 0 0.5em 0;
vertical-align:middle;
}
.login-status, .login-status iframe
{
height: 2em !important;
width: 5em !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,422 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
namespace MVCAzureStore.Controllers
{
using System;
using System.Globalization;
using System.Security.Principal;
using System.Web.Mvc;
using System.Web.Security;
[HandleError]
public class AccountController : Controller
{
// This constructor is used by the MVC framework to instantiate the controller using
// the default forms authentication and membership providers.
public AccountController()
: this(null, null, null)
{
}
// This constructor is not used by the MVC framework but is instead provided for ease
// of unit testing this type. See the comments at the end of this file for more
// information.
public AccountController(IFormsAuthentication formsAuth, IMembershipService service, IRoleService roleService)
{
FormsAuth = formsAuth ?? new FormsAuthenticationService();
MembershipService = service ?? new AccountMembershipService();
RoleService = roleService ?? new AccountRoleService();
}
public IFormsAuthentication FormsAuth
{
get;
private set;
}
public IMembershipService MembershipService
{
get;
private set;
}
public IRoleService RoleService
{
get;
private set;
}
public ActionResult LogOn()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
Justification = "Needs to take same parameter type as Controller.Redirect()")]
public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl)
{
if (!ValidateLogOn(userName, password))
{
return View();
}
FormsAuth.SignIn(userName, rememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
public ActionResult LogOff()
{
FormsAuth.SignOut();
// ensure that selected items are cleared from session when users logs out
Session.Abandon();
return RedirectToAction("Index", "Home");
}
public ActionResult Register()
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
ViewData["Roles"] = RoleService.GetAllRoles();
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(string userName, string email, string password, string confirmPassword, string roleName)
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
if (ValidateRegistration(userName, email, password, confirmPassword, roleName))
{
// Attempt to register the user
MembershipCreateStatus createStatus = MembershipService.CreateUser(userName, password, email);
if (createStatus == MembershipCreateStatus.Success)
{
// assign user to role
this.RoleService.AddUserToRole(userName, roleName);
FormsAuth.SignIn(userName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View();
}
[Authorize]
public ActionResult ChangePassword()
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View();
}
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Exceptions result in password not being changed.")]
public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword)
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
if (!ValidateChangePassword(currentPassword, newPassword, confirmPassword))
{
return View();
}
try
{
if (MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword))
{
return RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError("_FORM", "The current password is incorrect or the new password is invalid.");
return View();
}
}
catch
{
ModelState.AddModelError("_FORM", "The current password is incorrect or the new password is invalid.");
return View();
}
}
public ActionResult ChangePasswordSuccess()
{
return View();
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity is WindowsIdentity)
{
throw new InvalidOperationException("Windows authentication is not supported.");
}
}
#region Validation Methods
private bool ValidateChangePassword(string currentPassword, string newPassword, string confirmPassword)
{
if (String.IsNullOrEmpty(currentPassword))
{
ModelState.AddModelError("currentPassword", "You must specify a current password.");
}
if (newPassword == null || newPassword.Length < MembershipService.MinPasswordLength)
{
ModelState.AddModelError("newPassword",
String.Format(CultureInfo.CurrentCulture,
"You must specify a new password of {0} or more characters.",
MembershipService.MinPasswordLength));
}
if (!String.Equals(newPassword, confirmPassword, StringComparison.Ordinal))
{
ModelState.AddModelError("_FORM", "The new password and confirmation password do not match.");
}
return ModelState.IsValid;
}
private bool ValidateLogOn(string userName, string password)
{
if (String.IsNullOrEmpty(userName))
{
ModelState.AddModelError("username", "You must specify a username.");
}
if (String.IsNullOrEmpty(password))
{
ModelState.AddModelError("password", "You must specify a password.");
}
if (!MembershipService.ValidateUser(userName, password))
{
ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
}
return ModelState.IsValid;
}
private bool ValidateRegistration(string userName, string email, string password, string confirmPassword, string roleName)
{
if (String.IsNullOrEmpty(userName))
{
ModelState.AddModelError("username", "You must specify a username.");
}
if (String.IsNullOrEmpty(email))
{
ModelState.AddModelError("email", "You must specify an email address.");
}
if (password == null || password.Length < MembershipService.MinPasswordLength)
{
ModelState.AddModelError("password",
String.Format(CultureInfo.CurrentCulture,
"You must specify a password of {0} or more characters.",
MembershipService.MinPasswordLength));
}
if (!String.Equals(password, confirmPassword, StringComparison.Ordinal))
{
ModelState.AddModelError("_FORM", "The new password and confirmation password do not match.");
}
if (this.RoleService.GetAllRoles().Length > 0 && String.IsNullOrEmpty(roleName))
{
ModelState.AddModelError("roleName", "You must specify a role.");
}
return ModelState.IsValid;
}
private static string ErrorCodeToString(MembershipCreateStatus createStatus)
{
// See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for
// a full list of status codes.
switch (createStatus)
{
case MembershipCreateStatus.DuplicateUserName:
return "Username already exists. Please enter a different user name.";
case MembershipCreateStatus.DuplicateEmail:
return "A username for that e-mail address already exists. Please enter a different e-mail address.";
case MembershipCreateStatus.InvalidPassword:
return "The password provided is invalid. Please enter a valid password value.";
case MembershipCreateStatus.InvalidEmail:
return "The e-mail address provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidAnswer:
return "The password retrieval answer provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidQuestion:
return "The password retrieval question provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidUserName:
return "The user name provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.ProviderError:
return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
case MembershipCreateStatus.UserRejected:
return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
default:
return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
}
}
#endregion
}
// The FormsAuthentication type is sealed and contains static members, so it is difficult to
// unit test code that calls its members. The interface and helper class below demonstrate
// how to create an abstract wrapper around such a type in order to make the AccountController
// code unit testable.
public interface IFormsAuthentication
{
void SignIn(string userName, bool createPersistentCookie);
void SignOut();
}
public class FormsAuthenticationService : IFormsAuthentication
{
public void SignIn(string userName, bool createPersistentCookie)
{
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
public interface IMembershipService
{
int MinPasswordLength { get; }
bool ValidateUser(string userName, string password);
MembershipCreateStatus CreateUser(string userName, string password, string email);
bool ChangePassword(string userName, string oldPassword, string newPassword);
}
public class AccountMembershipService : IMembershipService
{
private MembershipProvider _provider;
public AccountMembershipService()
: this(null)
{
}
public AccountMembershipService(MembershipProvider provider)
{
_provider = provider ?? Membership.Provider;
}
public int MinPasswordLength
{
get
{
return _provider.MinRequiredPasswordLength;
}
}
public bool ValidateUser(string userName, string password)
{
return _provider.ValidateUser(userName, password);
}
public MembershipCreateStatus CreateUser(string userName, string password, string email)
{
MembershipCreateStatus status;
_provider.CreateUser(userName, password, email, null, null, true, null, out status);
return status;
}
public bool ChangePassword(string userName, string oldPassword, string newPassword)
{
MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
return currentUser.ChangePassword(oldPassword, newPassword);
}
}
public interface IRoleService
{
bool IsUserInRole(string userName, string roleName);
void AddUserToRole(string userName, string roleName);
string[] GetAllRoles();
}
public class AccountRoleService : IRoleService
{
private readonly RoleProvider roleProvider;
public AccountRoleService()
: this(null)
{
}
public AccountRoleService(RoleProvider provider)
{
this.roleProvider = provider ?? (Roles.Enabled ? Roles.Provider : null);
}
public void AddUserToRole(string userName, string roleName)
{
if (this.roleProvider != null)
{
this.roleProvider.AddUsersToRoles(new string[] { userName }, new string[] { roleName });
}
}
public bool IsUserInRole(string userName, string roleName)
{
if (this.roleProvider != null)
{
return this.roleProvider.IsUserInRole(userName, roleName);
}
return false;
}
public string[] GetAllRoles()
{
if (this.roleProvider != null)
{
return this.roleProvider.GetAllRoles();
}
return new string[0];
}
}
}

View file

@ -0,0 +1,84 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
namespace MVCAzureStore.Controllers
{
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
[HandleError]
[Authorize]
public class HomeController : Controller
{
public ActionResult About()
{
return View();
}
public ActionResult Index()
{
var products = this.HttpContext.Application["Products"] as List<string>;
var itemsInSession = this.Session["Cart"] as List<string> ?? new List<string>();
// add all products currently not in session
var filteredProducts = products.Where(item => !itemsInSession.Contains(item));
// Add additional filters here
// filter product list for home users
if (User.IsInRole("Home"))
{
filteredProducts = filteredProducts.Where(item => item.Contains("Home"));
}
return View(filteredProducts);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Add(string selectedItem)
{
if (selectedItem != null)
{
List<string> cart = this.Session["Cart"] as List<string> ?? new List<string>();
cart.Add(selectedItem);
Session["Cart"] = cart;
}
return RedirectToAction("Index");
}
public ActionResult Checkout()
{
var itemsInSession = this.Session["Cart"] as List<string> ?? new List<string>();
return View(itemsInSession);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Remove(string selectedItem)
{
if (selectedItem != null)
{
var itemsInSession = this.Session["Cart"] as List<string>;
if (itemsInSession != null)
{
itemsInSession.Remove(selectedItem);
}
}
return RedirectToAction("Checkout");
}
}
}

View file

@ -0,0 +1,3 @@
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="MVCAzureStore._Default" %>
<%-- Please do not delete this file. It is used to ensure that ASP.NET MVC is activated by IIS when a user makes a "/" request to the server. --%>

View file

@ -0,0 +1,38 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
namespace MVCAzureStore
{
public partial class _Default : Page
{
public void Page_Load(object sender, System.EventArgs e)
{
// Change the current path so that the Routing handler can correctly interpret
// the request, then restore the original path so that the OutputCache module
// can correctly process the response (if caching is enabled).
string originalPath = Request.Path;
HttpContext.Current.RewritePath(Request.ApplicationPath, false);
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(HttpContext.Current);
HttpContext.Current.RewritePath(originalPath, false);
}
}
}

View file

@ -0,0 +1 @@
<%@ Application Codebehind="Global.asax.cs" Inherits="MVCAzureStore.MvcApplication" Language="C#" %>

View file

@ -0,0 +1,105 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
namespace MVCAzureStore
{
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using Dlrsoft.Asp.Mvc;
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Add(new AspViewEngine());
}
private static bool initialized = false;
private static object gate = new object();
// The Windows Azure fabric runs IIS 7.0 in integrated mode. In integrated
// mode, the Application_Start event does not support access to the request
// context or to the members of the RoleManager class provided by the Windows
// Azure SDK runtime API. If you are writing an ASP.NET application that
// accesses the request context or calls methods of the RoleManager class from
// the Application_Start event, you should modify it to initialize in the
// Application_BeginRequest event instead.
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (initialized)
{
return;
}
lock (gate)
{
if (initialized)
{
return;
}
LoadProducts();
// Initialize application roles here
// (requires access to RoleManager to read configuration)
if (!Roles.RoleExists("Home"))
{
Roles.CreateRole("Home");
}
if (!Roles.RoleExists("Enterprise"))
{
Roles.CreateRole("Enterprise");
}
initialized = true;
}
}
private void LoadProducts()
{
this.Application["Products"] = new List<string> {
"Microsoft Office 2007 Ultimate",
"Microsoft Office Communications Server Enterprise CAL",
"Microsoft Core CAL - License & software assurance - 1 user CAL",
"Windows Server 2008 Enterprise",
"Windows Vista Home Premium (Upgrade)",
"Windows XP Home Edition w/SP2 (OEM)",
"Windows Home Server - 10 Client (OEM License)",
"Console XBOX 360 Arcade"
};
}
}
}

View file

@ -0,0 +1,35 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
namespace Helpers
{
using System;
using System.Web.Mvc;
public static class IsCurrentActionHelper
{
public static bool IsCurrentAction(this HtmlHelper helper, string actionName, string controllerName)
{
string currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"];
string currentActionName = (string)helper.ViewContext.RouteData.Values["action"];
if (currentControllerName.Equals(controllerName, StringComparison.CurrentCultureIgnoreCase) && currentActionName.Equals(actionName, StringComparison.CurrentCultureIgnoreCase))
return true;
return false;
}
}
}

View file

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{D4109103-5396-4DE5-8042-1ACAAB367C32}</ProjectGuid>
<ProjectTypeGuids>{603c0e0b-db56-11dc-be95-000d561079b0};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MVCAzureStore</RootNamespace>
<AssemblyName>MVCAzureStore</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
<RoleType>Web</RoleType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dlrsoft.Asp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\asp\bin\Release\Dlrsoft.Asp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Routing" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web.Services" />
<Reference Include="System.EnterpriseServices" />
<Reference Include="System.Web.Mobile" />
</ItemGroup>
<ItemGroup>
<Compile Include="Controllers\AccountController.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Default.aspx.cs">
<DependentUpon>Default.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Helpers\IsCurrentActionHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Content\images\bg-button.png" />
<Content Include="Content\images\bg-column-left.png" />
<Content Include="Content\images\bg-column-right.png" />
<Content Include="Content\images\bg-input.png" />
<Content Include="Content\images\bg-poster-tile.jpg" />
<Content Include="Content\images\bg-poster.jpg" />
<Content Include="Content\images\bullet.png" />
<Content Include="Content\images\favicon.ico" />
<Content Include="Content\Site.css" />
<Content Include="Default.aspx" />
<Content Include="Global.asax" />
<Content Include="Views\Home\Checkout.aspx" />
<Content Include="Web.config" />
<Content Include="Scripts\jquery-1.3.2.js" />
<Content Include="Scripts\jquery-1.3.2.min.js" />
<Content Include="Scripts\jquery-1.3.2-vsdoc.js" />
<Content Include="Scripts\jquery-1.3.2.min-vsdoc.js" />
<Content Include="Scripts\MicrosoftAjax.js" />
<Content Include="Scripts\MicrosoftAjax.debug.js" />
<Content Include="Scripts\MicrosoftMvcAjax.js" />
<Content Include="Scripts\MicrosoftMvcAjax.debug.js" />
<Content Include="Views\Account\ChangePassword.aspx" />
<Content Include="Views\Account\ChangePasswordSuccess.aspx" />
<Content Include="Views\Account\LogOn.aspx" />
<Content Include="Views\Account\Register.aspx" />
<Content Include="Views\Home\About.aspx" />
<None Include="Views\Home\Index.aspx.bak">
<SubType>ASPXCodeBehind</SubType>
</None>
<Content Include="Views\Shared\Error.aspx" />
<Content Include="Views\Shared\LogOnUserControl.ascx" />
<Content Include="Views\Shared\Site.Master" />
<Content Include="Views\Web.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Assets\AspProviders\AspProviders.csproj">
<Project>{306D2F9E-D6D0-4D96-94F1-173C60A13875}</Project>
<Name>AspProviders</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Views\Home\Index.asp" />
<Content Include="Views\Shared\template.asp" />
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
<Folder Include="Models\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target> -->
<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
</Target>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>6004</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>
</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

View file

@ -0,0 +1,36 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ShowAllFiles</ProjectView>
</PropertyGroup>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<StartPageUrl>
</StartPageUrl>
<StartAction>CurrentPage</StartAction>
<AspNetDebugging>True</AspNetDebugging>
<SilverlightDebugging>False</SilverlightDebugging>
<NativeDebugging>False</NativeDebugging>
<SQLDebugging>False</SQLDebugging>
<PublishCopyOption>RunFiles</PublishCopyOption>
<PublishTargetLocation>
</PublishTargetLocation>
<PublishDeleteAllFiles>False</PublishDeleteAllFiles>
<PublishCopyAppData>True</PublishCopyAppData>
<ExternalProgram>
</ExternalProgram>
<StartExternalURL>
</StartExternalURL>
<StartCmdLineArguments>
</StartCmdLineArguments>
<StartWorkingDirectory>
</StartWorkingDirectory>
<EnableENC>False</EnableENC>
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
<EnableWcfTestClientForSVC>False</EnableWcfTestClientForSVC>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

View file

@ -0,0 +1,51 @@
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MVC Azure Store")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Azure Services Training Kit")]
[assembly: AssemblyCopyright("Copyright © 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b125afdd-6de0-4884-b46f-9942153ba86c")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,337 @@
//!----------------------------------------------------------
//! Copyright (C) Microsoft Corporation. All rights reserved.
//!----------------------------------------------------------
//! MicrosoftMvcAjax.js
Type.registerNamespace('Sys.Mvc');
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.AjaxOptions
Sys.Mvc.$create_AjaxOptions = function Sys_Mvc_AjaxOptions() { return {}; }
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.InsertionMode
Sys.Mvc.InsertionMode = function() {
/// <field name="replace" type="Number" integer="true" static="true">
/// </field>
/// <field name="insertBefore" type="Number" integer="true" static="true">
/// </field>
/// <field name="insertAfter" type="Number" integer="true" static="true">
/// </field>
};
Sys.Mvc.InsertionMode.prototype = {
replace: 0,
insertBefore: 1,
insertAfter: 2
}
Sys.Mvc.InsertionMode.registerEnum('Sys.Mvc.InsertionMode', false);
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.AjaxContext
Sys.Mvc.AjaxContext = function Sys_Mvc_AjaxContext(request, updateTarget, loadingElement, insertionMode) {
/// <param name="request" type="Sys.Net.WebRequest">
/// </param>
/// <param name="updateTarget" type="Object" domElement="true">
/// </param>
/// <param name="loadingElement" type="Object" domElement="true">
/// </param>
/// <param name="insertionMode" type="Sys.Mvc.InsertionMode">
/// </param>
/// <field name="_insertionMode" type="Sys.Mvc.InsertionMode">
/// </field>
/// <field name="_loadingElement" type="Object" domElement="true">
/// </field>
/// <field name="_response" type="Sys.Net.WebRequestExecutor">
/// </field>
/// <field name="_request" type="Sys.Net.WebRequest">
/// </field>
/// <field name="_updateTarget" type="Object" domElement="true">
/// </field>
this._request = request;
this._updateTarget = updateTarget;
this._loadingElement = loadingElement;
this._insertionMode = insertionMode;
}
Sys.Mvc.AjaxContext.prototype = {
_insertionMode: 0,
_loadingElement: null,
_response: null,
_request: null,
_updateTarget: null,
get_data: function Sys_Mvc_AjaxContext$get_data() {
/// <value type="String"></value>
if (this._response) {
return this._response.get_responseData();
}
else {
return null;
}
},
get_insertionMode: function Sys_Mvc_AjaxContext$get_insertionMode() {
/// <value type="Sys.Mvc.InsertionMode"></value>
return this._insertionMode;
},
get_loadingElement: function Sys_Mvc_AjaxContext$get_loadingElement() {
/// <value type="Object" domElement="true"></value>
return this._loadingElement;
},
get_response: function Sys_Mvc_AjaxContext$get_response() {
/// <value type="Sys.Net.WebRequestExecutor"></value>
return this._response;
},
set_response: function Sys_Mvc_AjaxContext$set_response(value) {
/// <value type="Sys.Net.WebRequestExecutor"></value>
this._response = value;
return value;
},
get_request: function Sys_Mvc_AjaxContext$get_request() {
/// <value type="Sys.Net.WebRequest"></value>
return this._request;
},
get_updateTarget: function Sys_Mvc_AjaxContext$get_updateTarget() {
/// <value type="Object" domElement="true"></value>
return this._updateTarget;
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.AsyncHyperlink
Sys.Mvc.AsyncHyperlink = function Sys_Mvc_AsyncHyperlink() {
}
Sys.Mvc.AsyncHyperlink.handleClick = function Sys_Mvc_AsyncHyperlink$handleClick(anchor, evt, ajaxOptions) {
/// <param name="anchor" type="Object" domElement="true">
/// </param>
/// <param name="evt" type="Sys.UI.DomEvent">
/// </param>
/// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
/// </param>
evt.preventDefault();
Sys.Mvc.MvcHelpers._asyncRequest(anchor.href, 'post', '', anchor, ajaxOptions);
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.MvcHelpers
Sys.Mvc.MvcHelpers = function Sys_Mvc_MvcHelpers() {
}
Sys.Mvc.MvcHelpers._serializeForm = function Sys_Mvc_MvcHelpers$_serializeForm(form) {
/// <param name="form" type="Object" domElement="true">
/// </param>
/// <returns type="String"></returns>
var formElements = form.elements;
var formBody = new Sys.StringBuilder();
var count = formElements.length;
for (var i = 0; i < count; i++) {
var element = formElements[i];
var name = element.name;
if (!name || !name.length) {
continue;
}
var tagName = element.tagName.toUpperCase();
if (tagName === 'INPUT') {
var inputElement = element;
var type = inputElement.type;
if ((type === 'text') || (type === 'password') || (type === 'hidden') || (((type === 'checkbox') || (type === 'radio')) && element.checked)) {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent(inputElement.value));
formBody.append('&');
}
}
else if (tagName === 'SELECT') {
var selectElement = element;
var optionCount = selectElement.options.length;
for (var j = 0; j < optionCount; j++) {
var optionElement = selectElement.options[j];
if (optionElement.selected) {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent(optionElement.value));
formBody.append('&');
}
}
}
else if (tagName === 'TEXTAREA') {
formBody.append(encodeURIComponent(name));
formBody.append('=');
formBody.append(encodeURIComponent((element.value)));
formBody.append('&');
}
}
return formBody.toString();
}
Sys.Mvc.MvcHelpers._asyncRequest = function Sys_Mvc_MvcHelpers$_asyncRequest(url, verb, body, triggerElement, ajaxOptions) {
/// <param name="url" type="String">
/// </param>
/// <param name="verb" type="String">
/// </param>
/// <param name="body" type="String">
/// </param>
/// <param name="triggerElement" type="Object" domElement="true">
/// </param>
/// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
/// </param>
if (ajaxOptions.confirm) {
if (!confirm(ajaxOptions.confirm)) {
return;
}
}
if (ajaxOptions.url) {
url = ajaxOptions.url;
}
if (ajaxOptions.httpMethod) {
verb = ajaxOptions.httpMethod;
}
if (body.length > 0 && !body.endsWith('&')) {
body += '&';
}
body += 'X-Requested-With=XMLHttpRequest';
var requestBody = '';
if (verb.toUpperCase() === 'GET' || verb.toUpperCase() === 'DELETE') {
if (url.indexOf('?') > -1) {
if (!url.endsWith('&')) {
url += '&';
}
url += body;
}
else {
url += '?';
url += body;
}
}
else {
requestBody = body;
}
var request = new Sys.Net.WebRequest();
request.set_url(url);
request.set_httpVerb(verb);
request.set_body(requestBody);
if (verb.toUpperCase() === 'PUT') {
request.get_headers()['Content-Type'] = 'application/x-www-form-urlencoded;';
}
request.get_headers()['X-Requested-With'] = 'XMLHttpRequest';
var updateElement = null;
if (ajaxOptions.updateTargetId) {
updateElement = $get(ajaxOptions.updateTargetId);
}
var loadingElement = null;
if (ajaxOptions.loadingElementId) {
loadingElement = $get(ajaxOptions.loadingElementId);
}
var ajaxContext = new Sys.Mvc.AjaxContext(request, updateElement, loadingElement, ajaxOptions.insertionMode);
var continueRequest = true;
if (ajaxOptions.onBegin) {
continueRequest = ajaxOptions.onBegin(ajaxContext) !== false;
}
if (loadingElement) {
Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), true);
}
if (continueRequest) {
request.add_completed(Function.createDelegate(null, function(executor) {
Sys.Mvc.MvcHelpers._onComplete(request, ajaxOptions, ajaxContext);
}));
request.invoke();
}
}
Sys.Mvc.MvcHelpers._onComplete = function Sys_Mvc_MvcHelpers$_onComplete(request, ajaxOptions, ajaxContext) {
/// <param name="request" type="Sys.Net.WebRequest">
/// </param>
/// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
/// </param>
/// <param name="ajaxContext" type="Sys.Mvc.AjaxContext">
/// </param>
ajaxContext.set_response(request.get_executor());
if (ajaxOptions.onComplete && ajaxOptions.onComplete(ajaxContext) === false) {
return;
}
var statusCode = ajaxContext.get_response().get_statusCode();
if ((statusCode >= 200 && statusCode < 300) || statusCode === 304 || statusCode === 1223) {
if (statusCode !== 204 && statusCode !== 304 && statusCode !== 1223) {
var contentType = ajaxContext.get_response().getResponseHeader('Content-Type');
if ((contentType) && (contentType.indexOf('application/x-javascript') !== -1)) {
eval(ajaxContext.get_data());
}
else {
Sys.Mvc.MvcHelpers.updateDomElement(ajaxContext.get_updateTarget(), ajaxContext.get_insertionMode(), ajaxContext.get_data());
}
}
if (ajaxOptions.onSuccess) {
ajaxOptions.onSuccess(ajaxContext);
}
}
else {
if (ajaxOptions.onFailure) {
ajaxOptions.onFailure(ajaxContext);
}
}
if (ajaxContext.get_loadingElement()) {
Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), false);
}
}
Sys.Mvc.MvcHelpers.updateDomElement = function Sys_Mvc_MvcHelpers$updateDomElement(target, insertionMode, content) {
/// <param name="target" type="Object" domElement="true">
/// </param>
/// <param name="insertionMode" type="Sys.Mvc.InsertionMode">
/// </param>
/// <param name="content" type="String">
/// </param>
if (target) {
switch (insertionMode) {
case Sys.Mvc.InsertionMode.replace:
target.innerHTML = content;
break;
case Sys.Mvc.InsertionMode.insertBefore:
if (content && content.length > 0) {
target.innerHTML = content + target.innerHTML.trimStart();
}
break;
case Sys.Mvc.InsertionMode.insertAfter:
if (content && content.length > 0) {
target.innerHTML = target.innerHTML.trimEnd() + content;
}
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Sys.Mvc.AsyncForm
Sys.Mvc.AsyncForm = function Sys_Mvc_AsyncForm() {
}
Sys.Mvc.AsyncForm.handleSubmit = function Sys_Mvc_AsyncForm$handleSubmit(form, evt, ajaxOptions) {
/// <param name="form" type="Object" domElement="true">
/// </param>
/// <param name="evt" type="Sys.UI.DomEvent">
/// </param>
/// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
/// </param>
evt.preventDefault();
var body = Sys.Mvc.MvcHelpers._serializeForm(form);
Sys.Mvc.MvcHelpers._asyncRequest(form.action, form.method || 'post', body, form, ajaxOptions);
}
Sys.Mvc.AjaxContext.registerClass('Sys.Mvc.AjaxContext');
Sys.Mvc.AsyncHyperlink.registerClass('Sys.Mvc.AsyncHyperlink');
Sys.Mvc.MvcHelpers.registerClass('Sys.Mvc.MvcHelpers');
Sys.Mvc.AsyncForm.registerClass('Sys.Mvc.AsyncForm');
// ---- Do not remove this footer ----
// Generated using Script# v0.5.0.0 (http://projects.nikhilk.net)
// -----------------------------------

View file

@ -0,0 +1,23 @@
//----------------------------------------------------------
// Copyright (C) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------
// MicrosoftMvcAjax.js
Type.registerNamespace('Sys.Mvc');Sys.Mvc.$create_AjaxOptions=function(){return {};}
Sys.Mvc.InsertionMode=function(){};Sys.Mvc.InsertionMode.prototype = {replace:0,insertBefore:1,insertAfter:2}
Sys.Mvc.InsertionMode.registerEnum('Sys.Mvc.InsertionMode',false);Sys.Mvc.AjaxContext=function(request,updateTarget,loadingElement,insertionMode){this.$3=request;this.$4=updateTarget;this.$1=loadingElement;this.$0=insertionMode;}
Sys.Mvc.AjaxContext.prototype={$0:0,$1:null,$2:null,$3:null,$4:null,get_data:function(){if(this.$2){return this.$2.get_responseData();}else{return null;}},get_insertionMode:function(){return this.$0;},get_loadingElement:function(){return this.$1;},get_response:function(){return this.$2;},set_response:function(value){this.$2=value;return value;},get_request:function(){return this.$3;},get_updateTarget:function(){return this.$4;}}
Sys.Mvc.AsyncHyperlink=function(){}
Sys.Mvc.AsyncHyperlink.handleClick=function(anchor,evt,ajaxOptions){evt.preventDefault();Sys.Mvc.MvcHelpers.$1(anchor.href,'post','',anchor,ajaxOptions);}
Sys.Mvc.MvcHelpers=function(){}
Sys.Mvc.MvcHelpers.$0=function($p0){var $0=$p0.elements;var $1=new Sys.StringBuilder();var $2=$0.length;for(var $3=0;$3<$2;$3++){var $4=$0[$3];var $5=$4.name;if(!$5||!$5.length){continue;}var $6=$4.tagName.toUpperCase();if($6==='INPUT'){var $7=$4;var $8=$7.type;if(($8==='text')||($8==='password')||($8==='hidden')||((($8==='checkbox')||($8==='radio'))&&$4.checked)){$1.append(encodeURIComponent($5));$1.append('=');$1.append(encodeURIComponent($7.value));$1.append('&');}}else if($6==='SELECT'){var $9=$4;var $A=$9.options.length;for(var $B=0;$B<$A;$B++){var $C=$9.options[$B];if($C.selected){$1.append(encodeURIComponent($5));$1.append('=');$1.append(encodeURIComponent($C.value));$1.append('&');}}}else if($6==='TEXTAREA'){$1.append(encodeURIComponent($5));$1.append('=');$1.append(encodeURIComponent(($4.value)));$1.append('&');}}return $1.toString();}
Sys.Mvc.MvcHelpers.$1=function($p0,$p1,$p2,$p3,$p4){if($p4.confirm){if(!confirm($p4.confirm)){return;}}if($p4.url){$p0=$p4.url;}if($p4.httpMethod){$p1=$p4.httpMethod;}if($p2.length>0&&!$p2.endsWith('&')){$p2+='&';}$p2+='X-Requested-With=XMLHttpRequest';var $0='';if($p1.toUpperCase()==='GET'||$p1.toUpperCase()==='DELETE'){if($p0.indexOf('?')>-1){if(!$p0.endsWith('&')){$p0+='&';}$p0+=$p2;}else{$p0+='?';$p0+=$p2;}}else{$0=$p2;}var $1=new Sys.Net.WebRequest();$1.set_url($p0);$1.set_httpVerb($p1);$1.set_body($0);if($p1.toUpperCase()==='PUT'){$1.get_headers()['Content-Type']='application/x-www-form-urlencoded;';}$1.get_headers()['X-Requested-With']='XMLHttpRequest';var $2=null;if($p4.updateTargetId){$2=$get($p4.updateTargetId);}var $3=null;if($p4.loadingElementId){$3=$get($p4.loadingElementId);}var $4=new Sys.Mvc.AjaxContext($1,$2,$3,$p4.insertionMode);var $5=true;if($p4.onBegin){$5=$p4.onBegin($4)!==false;}if($3){Sys.UI.DomElement.setVisible($4.get_loadingElement(),true);}if($5){$1.add_completed(Function.createDelegate(null,function($p1_0){
Sys.Mvc.MvcHelpers.$2($1,$p4,$4);}));$1.invoke();}}
Sys.Mvc.MvcHelpers.$2=function($p0,$p1,$p2){$p2.set_response($p0.get_executor());if($p1.onComplete&&$p1.onComplete($p2)===false){return;}var $0=$p2.get_response().get_statusCode();if(($0>=200&&$0<300)||$0===304||$0===1223){if($0!==204&&$0!==304&&$0!==1223){var $1=$p2.get_response().getResponseHeader('Content-Type');if(($1)&&($1.indexOf('application/x-javascript')!==-1)){eval($p2.get_data());}else{Sys.Mvc.MvcHelpers.updateDomElement($p2.get_updateTarget(),$p2.get_insertionMode(),$p2.get_data());}}if($p1.onSuccess){$p1.onSuccess($p2);}}else{if($p1.onFailure){$p1.onFailure($p2);}}if($p2.get_loadingElement()){Sys.UI.DomElement.setVisible($p2.get_loadingElement(),false);}}
Sys.Mvc.MvcHelpers.updateDomElement=function(target,insertionMode,content){if(target){switch(insertionMode){case 0:target.innerHTML=content;break;case 1:if(content&&content.length>0){target.innerHTML=content+target.innerHTML.trimStart();}break;case 2:if(content&&content.length>0){target.innerHTML=target.innerHTML.trimEnd()+content;}break;}}}
Sys.Mvc.AsyncForm=function(){}
Sys.Mvc.AsyncForm.handleSubmit=function(form,evt,ajaxOptions){evt.preventDefault();var $0=Sys.Mvc.MvcHelpers.$0(form);Sys.Mvc.MvcHelpers.$1(form.action,form.method||'post',$0,form,ajaxOptions);}
Sys.Mvc.AjaxContext.registerClass('Sys.Mvc.AjaxContext');Sys.Mvc.AsyncHyperlink.registerClass('Sys.Mvc.AsyncHyperlink');Sys.Mvc.MvcHelpers.registerClass('Sys.Mvc.MvcHelpers');Sys.Mvc.AsyncForm.registerClass('Sys.Mvc.AsyncForm');
// ---- Do not remove this footer ----
// Generated using Script# v0.5.0.0 (http://projects.nikhilk.net)
// -----------------------------------

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,42 @@
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="changePasswordTitle" ContentPlaceHolderID="TitleContent" runat="server">
Change Password
</asp:Content>
<asp:Content ID="changePasswordContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>Change Password</h2>
<p>
Use the form below to change your password.
</p>
<p>
New passwords are required to be a minimum of <%=Html.Encode(ViewData["PasswordLength"])%> characters in length.
</p>
<%= Html.ValidationSummary("Password change was unsuccessful. Please correct the errors and try again.")%>
<% using (Html.BeginForm()) { %>
<div>
<fieldset>
<legend>Account Information</legend>
<p>
<label for="currentPassword">Current password:</label>
<%= Html.Password("currentPassword") %>
<%= Html.ValidationMessage("currentPassword") %>
</p>
<p>
<label for="newPassword">New password:</label>
<%= Html.Password("newPassword") %>
<%= Html.ValidationMessage("newPassword") %>
</p>
<p>
<label for="confirmPassword">Confirm new password:</label>
<%= Html.Password("confirmPassword") %>
<%= Html.ValidationMessage("confirmPassword") %>
</p>
<p>
<input type="submit" value="Change Password" />
</p>
</fieldset>
</div>
<% } %>
</asp:Content>

View file

@ -0,0 +1,12 @@
<%@Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="changePasswordTitle" ContentPlaceHolderID="TitleContent" runat="server">
Change Password
</asp:Content>
<asp:Content ID="changePasswordSuccessContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>Change Password</h2>
<p>
Your password has been changed successfully.
</p>
</asp:Content>

View file

@ -0,0 +1,37 @@
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="loginTitle" ContentPlaceHolderID="TitleContent" runat="server">
Log On
</asp:Content>
<asp:Content ID="loginContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>Log On</h2>
<p>
Please enter your username and password. <%= Html.ActionLink("Register", "Register") %> if you don't have an account.
</p>
<%= Html.ValidationSummary("Login was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) { %>
<div>
<fieldset>
<legend>Account Information</legend>
<p>
<label for="username">Username:</label>
<%= Html.TextBox("username") %>
<%= Html.ValidationMessage("username") %>
</p>
<p>
<label for="password">Password:</label>
<%= Html.Password("password") %>
<%= Html.ValidationMessage("password") %>
</p>
<p>
<%= Html.CheckBox("rememberMe") %> <label class="inline" for="rememberMe">Remember me?</label>
</p>
<p>
<input type="submit" value="Log On" />
</p>
</fieldset>
</div>
<% } %>
</asp:Content>

View file

@ -0,0 +1,58 @@
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="registerTitle" ContentPlaceHolderID="TitleContent" runat="server">
Register
</asp:Content>
<asp:Content ID="registerContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create a New Account</h2>
<p>
Use the form below to create a new account.
</p>
<p>
Passwords are required to be a minimum of <%=Html.Encode(ViewData["PasswordLength"])%> characters in length.
</p>
<%= Html.ValidationSummary("Account creation was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) { %>
<div>
<fieldset>
<legend>Account Information</legend>
<p>
<label for="username">Username:</label>
<%= Html.TextBox("username") %>
<%= Html.ValidationMessage("username") %>
</p>
<p>
<label for="email">Email:</label>
<%= Html.TextBox("email") %>
<%= Html.ValidationMessage("email") %>
</p>
<p>
<label for="password">Password:</label>
<%= Html.Password("password") %>
<%= Html.ValidationMessage("password") %>
</p>
<p>
<label for="confirmPassword">Confirm password:</label>
<%= Html.Password("confirmPassword") %>
<%= Html.ValidationMessage("confirmPassword") %>
</p>
<% string[] roles = ViewData["Roles"] as string[]; if (roles != null && roles.Length > 0)
{%>
<p>
<label for="roleName">Role:</label>
<% foreach (string role in roles)
{ %>
<%= Html.RadioButton("roleName", role)%>&nbsp;
<span><%=role%></span>
<% } %>
</p>
<%} %>
<p>
<input type="submit" value="Register" />
</p>
</fieldset>
</div>
<% } %>
</asp:Content>

View file

@ -0,0 +1,12 @@
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="aboutTitle" ContentPlaceHolderID="TitleContent" runat="server">
About Us
</asp:Content>
<asp:Content ID="aboutContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>About</h2>
<p>
Azure Store
</p>
</asp:Content>

View file

@ -0,0 +1,19 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<string>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Azure Store Check Out
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h1>Your Order</h1>
<label for="cart">You have selected the following products:</label>
<% using (Html.BeginForm("Remove", "Home")) { %>
<select name="selectedItem" class="product-list" id="items" size="4">
<% foreach (string product in ViewData.Model)
{ %>
<option value="<%=product%>"><%=product%></option>
<% } %>
</select>
<a href="javascript:document.forms[0].submit();">Remove product from cart</a>
<% } %>
</asp:Content>

View file

@ -0,0 +1,81 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Azure Store Products</title>
<link rel="shortcut icon" href="../../Content/images/favicon.ico" />
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="header-container">
<div class="nav-login">
<ul>
<%if Request.IsAuthenticated then %>
<li class="first">User:<span class="identity"><%=Html.Encode(context.User.Identity.Name)%></span></li>&nbsp;
<li><%= Html.ActionLink("Logout", "LogOff", "Account") %></li>
<% else %>
<li class="first"><%= Html.ActionLink("Register", "Register", "Account") %></li>
<%end if %>
</ul>
</div>
<div class="logo">Azure Store</div>
<div class="clear"></div>
</div>
<div class="poster-container-no-image">
<div class="poster-inner"> </div>
</div>
<div class="nav-main">
<ul>
<% if Html.IsCurrentAction("Index", "Home") then %>
<li class="first active">
<% else %>
<li class="first">
<% end if %>
<%=Html.ActionLink("Products", "Index", "Home")%>
</li>
<% if Html.IsCurrentAction("Checkout", "Home") then %>
<li class="active">
<% else %>
<li class="">
<% end if %>
<%=Html.ActionLink("Checkout", "Checkout", "Home")%>
</li>
</ul>
</div>
<div class="content-container">
<div class="content-container-inner">
<div class="content-main">
<%if context.User.IsInRole("Home") then %>
<h1><span class="product-category">Home</span> Products</h1>
<% else %>
<h1><span class="product-category">Enterprise</span> Products</h1>
<% end if %>
<label for="items">Select a product from the list:</label>
<%
dim product
dim myForm = Html.BeginForm("Add", "Home") %>
<select name="selectedItem" class="product-list" id="items" size="4">
<% for each product in ViewData.Model %>
<option value="<%=product%>"><%=product%></option>
<% next %>
</select>
<a href="javascript:document.forms[0].submit();">Add item to cart</a>
<% myForm.dispose() %>
</div>
<div class="clear" />
</div>
</div>
<div class="footer">
<div class="nav-footer">
<ul>
<li class="first"><%=Html.ActionLink("Products", "Index", "Home")%></li>
<li><%=Html.ActionLink("Checkout", "Checkout", "Home")%></li>
</ul>
<p class="copyright">Azure Store</p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,19 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<string>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Azure Store Products
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h1><span class="product-category"><%=User.IsInRole("Home") ? "Home" : "Enterprise"%></span> Products</h1>
<label for="items">Select a product from the list:</label>
<% using (Html.BeginForm("Add", "Home")) { %>
<select name="selectedItem" class="product-list" id="items" size="4">
<% foreach (string product in ViewData.Model)
{ %>
<option value="<%=product%>"><%=product%></option>
<% } %>
</select>
<a href="javascript:document.forms[0].submit();">Add item to cart</a>
<% } %>
</asp:Content>

View file

@ -0,0 +1,11 @@
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>
<asp:Content ID="errorTitle" ContentPlaceHolderID="TitleContent" runat="server">
Error
</asp:Content>
<asp:Content ID="errorContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Sorry, an error occurred while processing your request.
</h2>
</asp:Content>

View file

@ -0,0 +1,14 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
if (Request.IsAuthenticated) {
%>
Welcome <b><%= Html.Encode(Page.User.Identity.Name) %></b>!
[ <%= Html.ActionLink("Log Off", "LogOff", "Account") %> ]
<%
}
else {
%>
[ <%= Html.ActionLink("Log On", "LogOn", "Account") %> ]
<%
}
%>

View file

@ -0,0 +1,63 @@
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.Master.cs" Inherits="System.Web.Mvc.ViewMasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link rel="shortcut icon" href="../../Content/images/favicon.ico" />
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="header-container">
<div class="nav-login">
<ul>
<%if (Request.IsAuthenticated)
{ %>
<li class="first">User:<span class="identity"><%=Html.Encode(Page.User.Identity.Name)%></span></li>&nbsp;
<li><%= Html.ActionLink("Logout", "LogOff", "Account") %></li>
<%}
else
{ %>
<li class="first"><%= Html.ActionLink("Register", "Register", "Account") %></li>
<%} %>
</ul>
</div>
<div class="logo">Azure Store</div>
<div class="clear"></div>
</div>
<div class="poster-container-no-image">
<div class="poster-inner"> </div>
</div>
<div class="nav-main">
<ul>
<li class="first <%=Html.IsCurrentAction("Index", "Home") ? "active" : ""%>">
<%=Html.ActionLink("Products", "Index", "Home")%>
</li>
<li class="<%=Html.IsCurrentAction("Checkout", "Home") ? "active" : ""%>">
<%=Html.ActionLink("Checkout", "Checkout", "Home")%>
</li>
</ul>
</div>
<div class="content-container">
<div class="content-container-inner">
<div class="content-main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
<div class="clear" />
</div>
</div>
<div class="footer">
<div class="nav-footer">
<ul>
<li class="first"><%=Html.ActionLink("Products", "Index", "Home")%></li>
<li><%=Html.ActionLink("Checkout", "Checkout", "Home")%></li>
</ul>
<p class="copyright">Azure Store</p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,66 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link rel="shortcut icon" href="../../Content/images/favicon.ico" />
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="header-container">
<div class="nav-login">
<ul>
<%if Request.IsAuthenticated then %>
<li class="first">User:<span class="identity"><%=Html.Encode(Request.LogonUserIdentity.Name)%></span></li>&nbsp;
<li><%= Html.ActionLink("Logout", "LogOff", "Account") %></li>
<% else %>
<li class="first"><%= Html.ActionLink("Register", "Register", "Account") %></li>
<%end if %>
</ul>
</div>
<div class="logo">Azure Store</div>
<div class="clear"></div>
</div>
<div class="poster-container-no-image">
<div class="poster-inner"> </div>
</div>
<div class="nav-main">
<ul>
<% if Html.IsCurrentAction("Index", "Home") then %>
<li class="first active">
<% else %>
<li class="first">
<% end if %>
<%=Html.ActionLink("Products", "Index", "Home")%>
</li>
<% if Html.IsCurrentAction("Checkout", "Home") then %>
<li class="active">
<% else %>
<li class="">
<% end if %>
<%=Html.ActionLink("Checkout", "Checkout", "Home")%>
</li>
</ul>
</div>
<div class="content-container">
<div class="content-container-inner">
<div class="content-main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
<div class="clear" />
</div>
</div>
<div class="footer">
<div class="nav-footer">
<ul>
<li class="first"><%=Html.ActionLink("Products", "Index", "Home")%></li>
<li><%=Html.ActionLink("Checkout", "Checkout", "Home")%></li>
</ul>
<p class="copyright">Azure Store</p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,34 @@
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add path="*" verb="*"
type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
<!--
Enabling request validation in view pages would cause validation to occur
after the input has already been processed by the controller. By default
MVC performs request validation before a controller processes the input.
To change this behavior apply the ValidateInputAttribute to a
controller or action.
-->
<pages
validateRequest="false"
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<controls>
<add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
</controls>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler"/>
</handlers>
</system.webServer>
</configuration>

View file

@ -0,0 +1,205 @@
<?xml version="1.0"?>
<!--
Note: As an alternative to hand editing this file you can use the
web admin tool to configure settings for your application. Use
the Website->Asp.Net Configuration option in Visual Studio.
A full list of settings and comments can be found in
machine.config.comments usually located in
\Windows\Microsoft.Net\Framework\v2.x\Config
-->
<configuration>
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>
<appSettings>
<!-- account configuration -->
<add key="BlobStorageEndpoint" value="http://127.0.0.1:10000"/>
<add key="TableStorageEndpoint" value="http://127.0.0.1:10002"/>
<add key="AccountName" value="devstoreaccount1"/>
<add key="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/>
<!-- provider configuration -->
<!-- When using the local development table storage service only the default values given
below will work for the tables (Membership, Roles and Sessions) since these are the names
of the properties on the DataServiceContext class -->
<add key="DefaultMembershipTableName" value="Membership"/>
<add key="DefaultRoleTableName" value="Roles"/>
<add key="DefaultSessionTableName" value="Sessions"/>
</appSettings>
<connectionStrings>
<add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
<!--
Set compilation debug="true" to insert debugging
symbols into the compiled page. Because this
affects performance, set this value to true only
during development.
-->
<compilation debug="true">
<assemblies>
<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</assemblies>
</compilation>
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
<!-- Membership Provider Configuration -->
<membership defaultProvider="TableStorageMembershipProvider" userIsOnlineTimeWindow="20">
<providers>
<clear/>
<add name="TableStorageMembershipProvider"
type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageMembershipProvider"
description="Membership provider using table storage"
applicationName="MVCAzureStore"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
minRequiredPasswordLength="1"
minRequiredNonalphanumericCharacters="0"
requiresUniqueEmail="false"
passwordFormat="Hashed"/>
</providers>
</membership>
<!-- RoleManager Provider Configuration -->
<roleManager enabled="true"
defaultProvider="TableStorageRoleProvider"
cacheRolesInCookie="true"
cookieName=".ASPXROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="false"
cookieSlidingExpiration="true"
cookieProtection="All">
<providers>
<clear/>
<add name="TableStorageRoleProvider"
type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageRoleProvider"
description="Role provider using table storage"
applicationName="MVCAzureStore" />
</providers>
</roleManager>
<!-- SessionState Provider Configuration -->
<sessionState mode="Custom"
customProvider="TableStorageSessionStateProvider">
<providers>
<clear/>
<add name="TableStorageSessionStateProvider"
type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageSessionStateProvider"
applicationName="MVCAzureStore" />
</providers>
</sessionState>
<profile>
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" applicationName="/"/>
</providers>
</profile>
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</controls>
<namespaces>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Linq"/>
<add namespace="System.Collections.Generic"/>
<add namespace="Helpers" />
</namespaces>
</pages>
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
<add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpHandlers>
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<providerOption name="CompilerVersion" value="v3.5"/>
<providerOption name="WarnAsError" value="false"/>
</compiler>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" warningLevel="4" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<providerOption name="CompilerVersion" value="v3.5"/>
<providerOption name="OptionInfer" value="true"/>
<providerOption name="WarnAsError" value="false"/>
</compiler>
</compilers>
</system.codedom>
<system.web.extensions/>
<!--
The system.webServer section is required for running ASP.NET AJAX under Internet
Information Services 7.0. It is not necessary for previous version of IIS.
-->
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true">
<remove name="ScriptModule"/>
<remove name="UrlRoutingModule"/>
<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</modules>
<handlers>
<remove name="WebServiceHandlerFactory-Integrated"/>
<remove name="ScriptHandlerFactory"/>
<remove name="ScriptHandlerFactoryAppServices"/>
<remove name="ScriptResource"/>
<remove name="MvcHttpHandler"/>
<remove name="UrlRoutingHandler"/>
<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</handlers>
</system.webServer>
</configuration>

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more