5.3.32871 AzureStoreAsp sample initial release
--HG-- extra : convert_revision : svn%3Aa83551a4-30f6-4d81-a974-c6ced450ddbf%4030945
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
502
aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/SecUtil.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
767
aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Errors.cs
Normal 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
|
||||
}
|
705
aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/Queue.cs
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
40
aspclassiccompiler/AzureStoreAsp/Assets/updateHostsFile.cmd
Normal 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
|
||||
|
49
aspclassiccompiler/AzureStoreAsp/AzureStore.sln
Normal 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
|
BIN
aspclassiccompiler/AzureStoreAsp/AzureStore.suo
Normal file
755
aspclassiccompiler/AzureStoreAsp/AzureStore/Content/Site.css
Normal 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;
|
||||
}
|
After Width: | Height: | Size: 220 B |
After Width: | Height: | Size: 166 B |
After Width: | Height: | Size: 138 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 465 B |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 162 B |
After Width: | Height: | Size: 1.1 KiB |
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
3
aspclassiccompiler/AzureStoreAsp/AzureStore/Default.aspx
Normal 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. --%>
|
38
aspclassiccompiler/AzureStoreAsp/AzureStore/Default.aspx.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
1
aspclassiccompiler/AzureStoreAsp/AzureStore/Global.asax
Normal file
|
@ -0,0 +1 @@
|
|||
<%@ Application Codebehind="Global.asax.cs" Inherits="MVCAzureStore.MvcApplication" Language="C#" %>
|
105
aspclassiccompiler/AzureStoreAsp/AzureStore/Global.asax.cs
Normal 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"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
153
aspclassiccompiler/AzureStoreAsp/AzureStore/MVCAzureStore.csproj
Normal 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>
|
|
@ -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>
|
|
@ -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")]
|
6850
aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftAjax.debug.js
vendored
Normal file
7
aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftAjax.js
vendored
Normal file
337
aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftMvcAjax.debug.js
vendored
Normal 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)
|
||||
// -----------------------------------
|
23
aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/MicrosoftMvcAjax.js
vendored
Normal 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)
|
||||
// -----------------------------------
|
6255
aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/jquery-1.3.2-vsdoc.js
vendored
Normal file
4410
aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/jquery-1.3.2.js
vendored
Normal file
6255
aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/jquery-1.3.2.min-vsdoc.js
vendored
Normal file
53
aspclassiccompiler/AzureStoreAsp/AzureStore/Scripts/jquery-1.3.2.min.js
vendored
Normal 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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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)%>
|
||||
<span><%=role%></span>
|
||||
<% } %>
|
||||
</p>
|
||||
<%} %>
|
||||
<p>
|
||||
<input type="submit" value="Register" />
|
||||
</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
<% } %>
|
||||
</asp:Content>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
<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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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") %> ]
|
||||
<%
|
||||
}
|
||||
%>
|
|
@ -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>
|
||||
<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>
|
|
@ -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>
|
||||
<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>
|
34
aspclassiccompiler/AzureStoreAsp/AzureStore/Views/Web.config
Normal 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>
|
205
aspclassiccompiler/AzureStoreAsp/AzureStore/Web.config
Normal 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>
|