summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.Core
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth.Core')
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuthSection.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/HostNameElement.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/HostNameOrRegexCollection.cs5
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/MessagingElement.cs3
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/ReportingElement.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/TrustedProviderConfigurationCollection.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/TypeConfigurationCollection.cs15
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/TypeConfigurationElement.cs44
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/UntrustedWebRequestElement.cs1
-rw-r--r--src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj70
-rw-r--r--src/DotNetOpenAuth.Core/IHostFactories.cs41
-rw-r--r--src/DotNetOpenAuth.Core/IRequireHostFactories.cs20
-rw-r--r--src/DotNetOpenAuth.Core/Logger.cs4
-rw-r--r--src/DotNetOpenAuth.Core/Loggers/NLogLogger.cs222
-rw-r--r--src/DotNetOpenAuth.Core/MachineKeyUtil.cs354
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/BinaryDataBagFormatter.cs4
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/AsymmetricCryptoKeyStoreWrapper.cs20
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKey.cs6
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKeyCollisionException.cs4
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/ExpiredMessageException.cs4
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/HardCodedKeyCryptoKeyStore.cs98
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyAndNonceStore.cs14
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyStore.cs61
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/MemoryCryptoKeyAndNonceStore.cs126
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/MemoryNonceStore.cs (renamed from src/DotNetOpenAuth.Core/Messaging/Bindings/NonceMemoryStore.cs)14
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/StandardExpirationBindingElement.cs30
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Bindings/StandardReplayProtectionBindingElement.cs32
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/CachedDirectWebResponse.cs184
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Channel.cs678
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/ChannelContract.cs54
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/ChannelEventArgs.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/DataBag.cs4
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs17
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/EnumerableCacheExtensions.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs58
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/HmacAlgorithms.cs3
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs60
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/HttpResponseMessageWithOriginal.cs36
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IChannelBindingElement.cs91
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs49
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IDirectWebRequestHandler.cs223
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs4
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs75
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponse.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponseContract.cs43
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IHttpIndirectResponse.cs1
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IMessage.cs53
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IMessageFactory.cs52
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IMessageOriginalPayload.cs16
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs130
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IProtocolMessageWithExtensions.cs93
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IStreamSerializingDataBag.cs28
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IncomingWebResponse.cs191
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IncomingWebResponseContract.cs54
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/KeyedCollectionDelegate.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MessageProtectionTasks.cs41
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MessageReceivingEndpoint.cs10
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs16
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs497
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MultipartContentMember.cs56
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/MultipartPostPart.cs223
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/NetworkDirectWebResponse.cs116
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponse.cs387
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs45
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs5
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs14
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs3
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/IMessagePartEncoder.cs44
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescription.cs5
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescriptionCollection.cs9
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDictionary.cs5
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs18
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/ValueMapping.cs3
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/StandardMessageFactory.cs9
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs16
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/StandardWebRequestHandler.cs260
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/UntrustedWebRequestHandler.cs476
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs4
-rw-r--r--src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs5
-rw-r--r--src/DotNetOpenAuth.Core/Reporting.cs104
-rw-r--r--src/DotNetOpenAuth.Core/Requires.cs255
-rw-r--r--src/DotNetOpenAuth.Core/RequiresEx.cs94
-rw-r--r--src/DotNetOpenAuth.Core/Strings.Designer.cs20
-rw-r--r--src/DotNetOpenAuth.Core/Strings.resx6
-rw-r--r--src/DotNetOpenAuth.Core/UriUtil.cs17
-rw-r--r--src/DotNetOpenAuth.Core/Util.cs67
-rw-r--r--src/DotNetOpenAuth.Core/packages.config11
87 files changed, 2064 insertions, 4182 deletions
diff --git a/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuthSection.cs b/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuthSection.cs
index 7c03c48..cdcd670 100644
--- a/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuthSection.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuthSection.cs
@@ -7,7 +7,6 @@
namespace DotNetOpenAuth.Configuration {
using System;
using System.Configuration;
- using System.Diagnostics.Contracts;
using System.Web;
using System.Web.Configuration;
@@ -15,7 +14,6 @@ namespace DotNetOpenAuth.Configuration {
/// Represents the section in the host's .config file that configures
/// this library's settings.
/// </summary>
- [ContractVerification(true)]
public class DotNetOpenAuthSection : ConfigurationSectionGroup {
/// <summary>
/// The name of the section under which this library's settings must be found.
diff --git a/src/DotNetOpenAuth.Core/Configuration/HostNameElement.cs b/src/DotNetOpenAuth.Core/Configuration/HostNameElement.cs
index b46ece9..5386314 100644
--- a/src/DotNetOpenAuth.Core/Configuration/HostNameElement.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/HostNameElement.cs
@@ -6,12 +6,10 @@
namespace DotNetOpenAuth.Configuration {
using System.Configuration;
- using System.Diagnostics.Contracts;
/// <summary>
/// Represents the name of a single host or a regex pattern for host names.
/// </summary>
- [ContractVerification(true)]
internal class HostNameElement : ConfigurationElement {
/// <summary>
/// Gets the name of the @name attribute.
diff --git a/src/DotNetOpenAuth.Core/Configuration/HostNameOrRegexCollection.cs b/src/DotNetOpenAuth.Core/Configuration/HostNameOrRegexCollection.cs
index f009ce5..8f537c3 100644
--- a/src/DotNetOpenAuth.Core/Configuration/HostNameOrRegexCollection.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/HostNameOrRegexCollection.cs
@@ -7,13 +7,12 @@
namespace DotNetOpenAuth.Configuration {
using System.Collections.Generic;
using System.Configuration;
- using System.Diagnostics.Contracts;
using System.Text.RegularExpressions;
+ using Validation;
/// <summary>
/// Represents a collection of child elements that describe host names either as literal host names or regex patterns.
/// </summary>
- [ContractVerification(true)]
internal class HostNameOrRegexCollection : ConfigurationElementCollection {
/// <summary>
/// Initializes a new instance of the <see cref="HostNameOrRegexCollection"/> class.
@@ -63,7 +62,7 @@ namespace DotNetOpenAuth.Configuration {
/// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </returns>
protected override object GetElementKey(ConfigurationElement element) {
- Contract.Assume(element != null); // this should be Contract.Requires in base class.
+ Requires.NotNull(element, "element");
return ((HostNameElement)element).Name ?? string.Empty;
}
}
diff --git a/src/DotNetOpenAuth.Core/Configuration/MessagingElement.cs b/src/DotNetOpenAuth.Core/Configuration/MessagingElement.cs
index ff98d36..64dfba3 100644
--- a/src/DotNetOpenAuth.Core/Configuration/MessagingElement.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/MessagingElement.cs
@@ -7,14 +7,12 @@
namespace DotNetOpenAuth.Configuration {
using System;
using System.Configuration;
- using System.Diagnostics.Contracts;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
/// <summary>
/// Represents the &lt;messaging&gt; element in the host's .config file.
/// </summary>
- [ContractVerification(true)]
public class MessagingElement : ConfigurationSection {
/// <summary>
/// The name of the &lt;webResourceUrlProvider&gt; sub-element.
@@ -75,7 +73,6 @@ namespace DotNetOpenAuth.Configuration {
/// </summary>
public static MessagingElement Configuration {
get {
- Contract.Ensures(Contract.Result<MessagingElement>() != null);
return (MessagingElement)ConfigurationManager.GetSection(MessagingElementName) ?? new MessagingElement();
}
}
diff --git a/src/DotNetOpenAuth.Core/Configuration/ReportingElement.cs b/src/DotNetOpenAuth.Core/Configuration/ReportingElement.cs
index 0af8205..f0184a6 100644
--- a/src/DotNetOpenAuth.Core/Configuration/ReportingElement.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/ReportingElement.cs
@@ -8,7 +8,6 @@ namespace DotNetOpenAuth.Configuration {
using System;
using System.Collections.Generic;
using System.Configuration;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
@@ -76,7 +75,6 @@ namespace DotNetOpenAuth.Configuration {
/// </summary>
public static ReportingElement Configuration {
get {
- Contract.Ensures(Contract.Result<ReportingElement>() != null);
return (ReportingElement)ConfigurationManager.GetSection(ReportingElementName) ?? new ReportingElement();
}
}
diff --git a/src/DotNetOpenAuth.Core/Configuration/TrustedProviderConfigurationCollection.cs b/src/DotNetOpenAuth.Core/Configuration/TrustedProviderConfigurationCollection.cs
index 96f60bf..de70f64 100644
--- a/src/DotNetOpenAuth.Core/Configuration/TrustedProviderConfigurationCollection.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/TrustedProviderConfigurationCollection.cs
@@ -9,7 +9,7 @@ namespace DotNetOpenAuth.Configuration {
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// A configuration collection of trusted OP Endpoints.
diff --git a/src/DotNetOpenAuth.Core/Configuration/TypeConfigurationCollection.cs b/src/DotNetOpenAuth.Core/Configuration/TypeConfigurationCollection.cs
index 3e72722..fa146a2 100644
--- a/src/DotNetOpenAuth.Core/Configuration/TypeConfigurationCollection.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/TypeConfigurationCollection.cs
@@ -8,16 +8,15 @@ namespace DotNetOpenAuth.Configuration {
using System;
using System.Collections.Generic;
using System.Configuration;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
+ using Validation;
/// <summary>
/// A collection of <see cref="TypeConfigurationElement&lt;T&gt;"/>.
/// </summary>
/// <typeparam name="T">The type that all types specified in the elements must derive from.</typeparam>
- [ContractVerification(true)]
internal class TypeConfigurationCollection<T> : ConfigurationElementCollection
where T : class {
/// <summary>
@@ -42,12 +41,14 @@ namespace DotNetOpenAuth.Configuration {
/// Creates instances of all the types listed in the collection.
/// </summary>
/// <param name="allowInternals">if set to <c>true</c> then internal types may be instantiated.</param>
- /// <returns>A sequence of instances generated from types in this collection. May be empty, but never null.</returns>
- internal IEnumerable<T> CreateInstances(bool allowInternals) {
- Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
+ /// <param name="hostFactories">The host factories.</param>
+ /// <returns>
+ /// A sequence of instances generated from types in this collection. May be empty, but never null.
+ /// </returns>
+ internal IEnumerable<T> CreateInstances(bool allowInternals, IHostFactories hostFactories) {
return from element in this.Cast<TypeConfigurationElement<T>>()
where !element.IsEmpty
- select element.CreateInstance(default(T), allowInternals);
+ select element.CreateInstance(default(T), allowInternals, hostFactories);
}
/// <summary>
@@ -68,7 +69,7 @@ namespace DotNetOpenAuth.Configuration {
/// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </returns>
protected override object GetElementKey(ConfigurationElement element) {
- Contract.Assume(element != null); // this should be Contract.Requires in base class.
+ Requires.NotNull(element, "element");
TypeConfigurationElement<T> typedElement = (TypeConfigurationElement<T>)element;
return (!string.IsNullOrEmpty(typedElement.TypeName) ? typedElement.TypeName : typedElement.XamlSource) ?? string.Empty;
}
diff --git a/src/DotNetOpenAuth.Core/Configuration/TypeConfigurationElement.cs b/src/DotNetOpenAuth.Core/Configuration/TypeConfigurationElement.cs
index edbb614..bcf199f 100644
--- a/src/DotNetOpenAuth.Core/Configuration/TypeConfigurationElement.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/TypeConfigurationElement.cs
@@ -8,15 +8,10 @@ namespace DotNetOpenAuth.Configuration {
using System;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.IO;
using System.Reflection;
using System.Web;
-#if CLR4
using System.Xaml;
-#else
- using System.Windows.Markup;
-#endif
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -80,11 +75,12 @@ namespace DotNetOpenAuth.Configuration {
/// Creates an instance of the type described in the .config file.
/// </summary>
/// <param name="defaultValue">The value to return if no type is given in the .config file.</param>
- /// <returns>The newly instantiated type.</returns>
- public T CreateInstance(T defaultValue) {
- Contract.Ensures(Contract.Result<T>() != null || Contract.Result<T>() == defaultValue);
-
- return this.CreateInstance(defaultValue, false);
+ /// <param name="hostFactories">The host factories.</param>
+ /// <returns>
+ /// The newly instantiated type.
+ /// </returns>
+ public T CreateInstance(T defaultValue, IHostFactories hostFactories) {
+ return this.CreateInstance(defaultValue, false, hostFactories);
}
/// <summary>
@@ -92,11 +88,13 @@ namespace DotNetOpenAuth.Configuration {
/// </summary>
/// <param name="defaultValue">The value to return if no type is given in the .config file.</param>
/// <param name="allowInternals">if set to <c>true</c> then internal types may be instantiated.</param>
- /// <returns>The newly instantiated type.</returns>
+ /// <param name="hostFactories">The host factories.</param>
+ /// <returns>
+ /// The newly instantiated type.
+ /// </returns>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")]
- public T CreateInstance(T defaultValue, bool allowInternals) {
- Contract.Ensures(Contract.Result<T>() != null || Contract.Result<T>() == defaultValue);
-
+ public T CreateInstance(T defaultValue, bool allowInternals, IHostFactories hostFactories) {
+ T instance;
if (this.CustomType != null) {
if (!allowInternals) {
// Although .NET will usually prevent our instantiating non-public types,
@@ -104,7 +102,7 @@ namespace DotNetOpenAuth.Configuration {
// But we don't want the host site to be able to do this, so we check ourselves.
ErrorUtilities.VerifyArgument((this.CustomType.Attributes & TypeAttributes.Public) != 0, Strings.ConfigurationTypeMustBePublic, this.CustomType.FullName);
}
- return (T)Activator.CreateInstance(this.CustomType);
+ instance = (T)Activator.CreateInstance(this.CustomType);
} else if (!string.IsNullOrEmpty(this.XamlSource)) {
string source = this.XamlSource;
if (source.StartsWith("~/", StringComparison.Ordinal)) {
@@ -112,11 +110,18 @@ namespace DotNetOpenAuth.Configuration {
source = HttpContext.Current.Server.MapPath(source);
}
using (Stream xamlFile = File.OpenRead(source)) {
- return CreateInstanceFromXaml(xamlFile);
+ instance = CreateInstanceFromXaml(xamlFile);
}
} else {
- return defaultValue;
+ instance = defaultValue;
}
+
+ var requiresHostFactories = instance as IRequireHostFactories;
+ if (requiresHostFactories != null) {
+ requiresHostFactories.HostFactories = hostFactories;
+ }
+
+ return instance;
}
/// <summary>
@@ -132,12 +137,7 @@ namespace DotNetOpenAuth.Configuration {
/// be present.
/// </remarks>
private static T CreateInstanceFromXaml(Stream xaml) {
- Contract.Ensures(Contract.Result<T>() != null);
-#if CLR4
return (T)XamlServices.Load(xaml);
-#else
- return (T)XamlReader.Load(xaml);
-#endif
}
}
}
diff --git a/src/DotNetOpenAuth.Core/Configuration/UntrustedWebRequestElement.cs b/src/DotNetOpenAuth.Core/Configuration/UntrustedWebRequestElement.cs
index b49452a..a16522a 100644
--- a/src/DotNetOpenAuth.Core/Configuration/UntrustedWebRequestElement.cs
+++ b/src/DotNetOpenAuth.Core/Configuration/UntrustedWebRequestElement.cs
@@ -7,7 +7,6 @@
namespace DotNetOpenAuth.Configuration {
using System;
using System.Configuration;
- using System.Diagnostics.Contracts;
/// <summary>
/// Represents the section of a .config file where security policies regarding web requests
diff --git a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
index dc47259..ebfd000 100644
--- a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
+++ b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
@@ -4,6 +4,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
</PropertyGroup>
<Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" />
<PropertyGroup>
@@ -19,20 +20,26 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Assumes.cs" />
+ <Compile Include="IHostFactories.cs" />
+ <Compile Include="IRequireHostFactories.cs" />
+ <Compile Include="MachineKeyUtil.cs" />
<Compile Include="Messaging\Base64WebEncoder.cs" />
<Compile Include="Messaging\Bindings\AsymmetricCryptoKeyStoreWrapper.cs" />
<Compile Include="Messaging\Bindings\CryptoKey.cs" />
<Compile Include="Messaging\Bindings\CryptoKeyCollisionException.cs" />
+ <Compile Include="Messaging\Bindings\HardCodedKeyCryptoKeyStore.cs" />
<Compile Include="Messaging\Bindings\ICryptoKeyStore.cs" />
+ <Compile Include="Messaging\Bindings\ICryptoKeyAndNonceStore.cs" />
<Compile Include="Messaging\Bindings\MemoryCryptoKeyStore.cs" />
<Compile Include="Messaging\BinaryDataBagFormatter.cs" />
- <Compile Include="Messaging\CachedDirectWebResponse.cs" />
- <Compile Include="Messaging\ChannelContract.cs" />
+ <Compile Include="Messaging\Bindings\MemoryCryptoKeyAndNonceStore.cs" />
+ <Compile Include="Messaging\HttpResponseMessageWithOriginal.cs" />
+ <Compile Include="Messaging\MessageProtectionTasks.cs" />
+ <Compile Include="Messaging\MultipartContentMember.cs" />
<Compile Include="Messaging\DataBagFormatterBase.cs" />
<Compile Include="Messaging\HmacAlgorithms.cs" />
<Compile Include="Messaging\HttpRequestHeaders.cs" />
<Compile Include="Messaging\IHttpDirectRequest.cs" />
- <Compile Include="Messaging\IHttpDirectRequestContract.cs" />
<Compile Include="Messaging\IHttpIndirectResponse.cs" />
<Compile Include="Messaging\IMessageOriginalPayload.cs" />
<Compile Include="Messaging\DirectWebRequestOptions.cs" />
@@ -40,23 +47,17 @@
<Compile Include="Messaging\HostErrorException.cs" />
<Compile Include="Messaging\IHttpDirectResponse.cs" />
<Compile Include="Messaging\IExtensionMessage.cs" />
- <Compile Include="Messaging\IHttpDirectResponseContract.cs" />
<Compile Include="Messaging\IMessage.cs" />
- <Compile Include="Messaging\IncomingWebResponse.cs" />
<Compile Include="Messaging\IDirectResponseProtocolMessage.cs" />
<Compile Include="Messaging\EmptyDictionary.cs" />
<Compile Include="Messaging\EmptyEnumerator.cs" />
<Compile Include="Messaging\EmptyList.cs" />
<Compile Include="Messaging\ErrorUtilities.cs" />
<Compile Include="Messaging\IMessageWithEvents.cs" />
- <Compile Include="Messaging\IncomingWebResponseContract.cs" />
<Compile Include="Messaging\IProtocolMessageWithExtensions.cs" />
<Compile Include="Messaging\InternalErrorException.cs" />
<Compile Include="Messaging\IStreamSerializingDataBag.cs" />
<Compile Include="Messaging\KeyedCollectionDelegate.cs" />
- <Compile Include="Messaging\MultipartPostPart.cs" />
- <Compile Include="Messaging\NetworkDirectWebResponse.cs" />
- <Compile Include="Messaging\OutgoingWebResponseActionResult.cs" />
<Compile Include="Messaging\ProtocolFaultResponseException.cs" />
<Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" />
<Compile Include="Messaging\Reflection\IMessagePartFormattingEncoder.cs" />
@@ -72,8 +73,7 @@
<Compile Include="Messaging\TimestampEncoder.cs" />
<Compile Include="Messaging\IMessageWithBinaryData.cs" />
<Compile Include="Messaging\ChannelEventArgs.cs" />
- <Compile Include="Messaging\Bindings\NonceMemoryStore.cs" />
- <Compile Include="Messaging\IDirectWebRequestHandler.cs" />
+ <Compile Include="Messaging\Bindings\MemoryNonceStore.cs" />
<Compile Include="Messaging\Bindings\INonceStore.cs" />
<Compile Include="Messaging\Bindings\StandardReplayProtectionBindingElement.cs" />
<Compile Include="Messaging\MessagePartAttribute.cs" />
@@ -102,20 +102,18 @@
<Compile Include="Messaging\Reflection\MessageDictionary.cs" />
<Compile Include="Messaging\Reflection\MessagePart.cs" />
<Compile Include="Messaging\UnprotectedMessageException.cs" />
- <Compile Include="Messaging\OutgoingWebResponse.cs" />
<Compile Include="Messaging\IProtocolMessage.cs" />
<Compile Include="Messaging\HttpDeliveryMethods.cs" />
<Compile Include="Messaging\MessageTransport.cs" />
<Compile Include="Messaging\ProtocolException.cs" />
<Compile Include="Messaging\TimespanSecondsEncoder.cs" />
- <Compile Include="Messaging\UntrustedWebRequestHandler.cs" />
- <Compile Include="Messaging\StandardWebRequestHandler.cs" />
<Compile Include="Messaging\MessageReceivingEndpoint.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Messaging\Bindings\Bindings.cd" />
<None Include="Messaging\Exceptions.cd" />
<None Include="Messaging\Messaging.cd" />
+ <None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration\DotNetOpenAuthSection.cs" />
@@ -132,12 +130,13 @@
<Compile Include="Logger.cs" />
<Compile Include="Loggers\ILog.cs" />
<Compile Include="Loggers\Log4NetLogger.cs" />
+ <Compile Include="Loggers\NLogLogger.cs" />
<Compile Include="Loggers\NoOpLogger.cs" />
<Compile Include="Loggers\TraceLogger.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Messaging\ReadOnlyDictionary.cs" />
<Compile Include="Reporting.cs" />
- <Compile Include="Requires.cs" />
+ <Compile Include="RequiresEx.cs" />
<Compile Include="Strings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@@ -170,11 +169,50 @@
<EmbeddedResource Include="Messaging\MessagingStrings.sr.resx" />
</ItemGroup>
<ItemGroup>
- <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
+ <Reference Include="log4net, Version=1.2.11.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\log4net.2.0.0\lib\net40-full\log4net.dll</HintPath>
+ </Reference>
+ <Reference Include="NLog">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Net.Http.WebRequest" />
+ <Reference Include="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="System.Web.WebPages.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <Private>True</Private>
+ <HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll</HintPath>
+ </Reference>
+ <Reference Include="Validation, Version=2.0.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\Validation.2.0.2.13022\lib\portable-windows8+net40+sl5+windowsphone8\Validation.dll</HintPath>
</Reference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " />
+ <Import Project="$(SolutionDir)\.nuget\nuget.targets" />
</Project> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.Core/IHostFactories.cs b/src/DotNetOpenAuth.Core/IHostFactories.cs
new file mode 100644
index 0000000..d171ed8
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/IHostFactories.cs
@@ -0,0 +1,41 @@
+//-----------------------------------------------------------------------
+// <copyright file="IHostFactories.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net.Http;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ /// <summary>
+ /// Provides the host application or tests with the ability to create standard message handlers or stubs.
+ /// </summary>
+ public interface IHostFactories {
+ /// <summary>
+ /// Initializes a new instance of a concrete derivation of <see cref="HttpMessageHandler"/>
+ /// to be used for outbound HTTP traffic.
+ /// </summary>
+ /// <returns>An instance of <see cref="HttpMessageHandler"/>.</returns>
+ /// <remarks>
+ /// An instance of <see cref="WebRequestHandler"/> is recommended where available;
+ /// otherwise an instance of <see cref="HttpClientHandler"/> is recommended.
+ /// </remarks>
+ HttpMessageHandler CreateHttpMessageHandler();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HttpClient"/> class
+ /// to be used for outbound HTTP traffic.
+ /// </summary>
+ /// <param name="handler">
+ /// The handler to pass to the <see cref="HttpClient"/> constructor.
+ /// May be null to use the default that would be provided by <see cref="CreateHttpMessageHandler"/>.
+ /// </param>
+ /// <returns>An instance of <see cref="HttpClient"/>.</returns>
+ HttpClient CreateHttpClient(HttpMessageHandler handler = null);
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/IRequireHostFactories.cs b/src/DotNetOpenAuth.Core/IRequireHostFactories.cs
new file mode 100644
index 0000000..60fa970
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/IRequireHostFactories.cs
@@ -0,0 +1,20 @@
+//-----------------------------------------------------------------------
+// <copyright file="IRequireHostFactories.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth {
+ /// <summary>
+ /// An interface implemented by DotNetOpenAuth extensions that need to create host-specific instances of specific interfaces.
+ /// </summary>
+ public interface IRequireHostFactories {
+ /// <summary>
+ /// Gets or sets the host factories used by this instance.
+ /// </summary>
+ /// <value>
+ /// The host factories.
+ /// </value>
+ IHostFactories HostFactories { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Logger.cs b/src/DotNetOpenAuth.Core/Logger.cs
index 975b391..d14fd8a 100644
--- a/src/DotNetOpenAuth.Core/Logger.cs
+++ b/src/DotNetOpenAuth.Core/Logger.cs
@@ -6,11 +6,11 @@
namespace DotNetOpenAuth {
using System;
- using System.Diagnostics.Contracts;
using System.Globalization;
using DotNetOpenAuth.Loggers;
using DotNetOpenAuth.Messaging;
using log4net.Core;
+ using Validation;
/// <summary>
/// A general logger for the entire DotNetOpenAuth library.
@@ -177,7 +177,7 @@ namespace DotNetOpenAuth {
/// <param name="name">The name of the log to initialize.</param>
/// <returns>The <see cref="ILog"/> instance of the logger to use.</returns>
private static ILog InitializeFacade(string name) {
- ILog result = Log4NetLogger.Initialize(name) ?? TraceLogger.Initialize(name) ?? NoOpLogger.Initialize();
+ ILog result = Log4NetLogger.Initialize(name) ?? NLogLogger.Initialize(name) ?? TraceLogger.Initialize(name) ?? NoOpLogger.Initialize();
return result;
}
}
diff --git a/src/DotNetOpenAuth.Core/Loggers/NLogLogger.cs b/src/DotNetOpenAuth.Core/Loggers/NLogLogger.cs
new file mode 100644
index 0000000..b5d883d
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Loggers/NLogLogger.cs
@@ -0,0 +1,222 @@
+// <auto-generated />
+
+namespace DotNetOpenAuth.Loggers {
+ using System;
+ using System.Globalization;
+ using System.IO;
+ using System.Reflection;
+
+ internal class NLogLogger : ILog {
+ private NLog.Logger nLogLogger;
+
+ private NLogLogger(NLog.Logger logger) {
+ this.nLogLogger = logger;
+ }
+
+ #region ILog Members
+
+ public bool IsDebugEnabled {
+ get { return this.nLogLogger.IsDebugEnabled; }
+ }
+
+ public bool IsInfoEnabled {
+ get { return this.nLogLogger.IsInfoEnabled; }
+ }
+
+ public bool IsWarnEnabled {
+ get { return this.nLogLogger.IsWarnEnabled; }
+ }
+
+ public bool IsErrorEnabled {
+ get { return this.nLogLogger.IsErrorEnabled; }
+ }
+
+ public bool IsFatalEnabled {
+ get { return this.nLogLogger.IsFatalEnabled; }
+ }
+
+ #endregion
+
+ private static bool IsNLogPresent {
+ get {
+ try {
+ Assembly.Load("NLog");
+ return true;
+ } catch (FileNotFoundException) {
+ return false;
+ }
+ }
+ }
+
+ #region ILog methods
+
+ public void Debug(object message) {
+ this.nLogLogger.Debug(message);
+ }
+
+ public void Debug(object message, Exception exception) {
+ this.nLogLogger.DebugException(String.Format("{0}", message), exception);
+ }
+
+ public void DebugFormat(string format, params object[] args) {
+ this.nLogLogger.Debug(CultureInfo.InvariantCulture, format, args);
+ }
+
+ public void DebugFormat(string format, object arg0) {
+ this.nLogLogger.Debug(format, arg0);
+ }
+
+ public void DebugFormat(string format, object arg0, object arg1) {
+ this.nLogLogger.Debug(format, arg0, arg1);
+ }
+
+ public void DebugFormat(string format, object arg0, object arg1, object arg2) {
+ this.nLogLogger.Debug(format, arg0, arg1, arg2);
+ }
+
+ public void DebugFormat(IFormatProvider provider, string format, params object[] args) {
+ this.nLogLogger.Debug(provider, format, args);
+ }
+
+ public void Info(object message) {
+ this.nLogLogger.Info(message);
+ }
+
+ public void Info(object message, Exception exception) {
+ this.nLogLogger.InfoException(String.Format("{0}", message), exception);
+ }
+
+ public void InfoFormat(string format, params object[] args) {
+ this.nLogLogger.Info(CultureInfo.InvariantCulture, format, args);
+ }
+
+ public void InfoFormat(string format, object arg0) {
+ this.nLogLogger.Info(format, arg0);
+ }
+
+ public void InfoFormat(string format, object arg0, object arg1) {
+ this.nLogLogger.Info(format, arg0, arg1);
+ }
+
+ public void InfoFormat(string format, object arg0, object arg1, object arg2) {
+ this.nLogLogger.Info(format, arg0, arg1, arg2);
+ }
+
+ public void InfoFormat(IFormatProvider provider, string format, params object[] args) {
+ this.nLogLogger.Info(provider, format, args);
+ }
+
+ public void Warn(object message) {
+ this.nLogLogger.Warn(message);
+ }
+
+ public void Warn(object message, Exception exception) {
+ this.nLogLogger.Warn(String.Format("{0}", message), exception);
+ }
+
+ public void WarnFormat(string format, params object[] args) {
+ this.nLogLogger.Warn(CultureInfo.InvariantCulture, format, args);
+ }
+
+ public void WarnFormat(string format, object arg0) {
+ this.nLogLogger.Warn(format, arg0);
+ }
+
+ public void WarnFormat(string format, object arg0, object arg1) {
+ this.nLogLogger.Warn(format, arg0, arg1);
+ }
+
+ public void WarnFormat(string format, object arg0, object arg1, object arg2) {
+ this.nLogLogger.Warn(format, arg0, arg1, arg2);
+ }
+
+ public void WarnFormat(IFormatProvider provider, string format, params object[] args) {
+ this.nLogLogger.Warn(provider, format, args);
+ }
+
+ public void Error(object message) {
+ this.nLogLogger.Error(message);
+ }
+
+ public void Error(object message, Exception exception) {
+ this.nLogLogger.Error(String.Format("{0}", message), exception);
+ }
+
+ public void ErrorFormat(string format, params object[] args) {
+ this.nLogLogger.Error(CultureInfo.InvariantCulture, format, args);
+ }
+
+ public void ErrorFormat(string format, object arg0) {
+ this.nLogLogger.Error(format, arg0);
+ }
+
+ public void ErrorFormat(string format, object arg0, object arg1) {
+ this.nLogLogger.Error(format, arg0, arg1);
+ }
+
+ public void ErrorFormat(string format, object arg0, object arg1, object arg2) {
+ this.nLogLogger.Error(format, arg0, arg1, arg2);
+ }
+
+ public void ErrorFormat(IFormatProvider provider, string format, params object[] args) {
+ this.nLogLogger.Error(provider, format, args);
+ }
+
+ public void Fatal(object message) {
+ this.nLogLogger.Fatal(message);
+ }
+
+ public void Fatal(object message, Exception exception) {
+ this.nLogLogger.Fatal(String.Format("{0}", message), exception);
+ }
+
+ public void FatalFormat(string format, params object[] args) {
+ this.nLogLogger.Fatal(CultureInfo.InvariantCulture, format, args);
+ }
+
+ public void FatalFormat(string format, object arg0) {
+ this.nLogLogger.Fatal(format, arg0);
+ }
+
+ public void FatalFormat(string format, object arg0, object arg1) {
+ this.nLogLogger.Fatal(format, arg0, arg1);
+ }
+
+ public void FatalFormat(string format, object arg0, object arg1, object arg2) {
+ this.nLogLogger.Fatal(format, arg0, arg1, arg2);
+ }
+
+ public void FatalFormat(IFormatProvider provider, string format, params object[] args) {
+ this.nLogLogger.Fatal(provider, format, args);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Returns a new NLog logger if it exists, or returns null if the assembly cannot be found.
+ /// </summary>
+ /// <returns>The created <see cref="ILog"/> instance.</returns>
+ internal static ILog Initialize(string name) {
+ try {
+ return IsNLogPresent ? CreateLogger(name) : null;
+ } catch (FileLoadException) {
+ // wrong NLog.dll version
+ return null;
+ } catch (TargetInvocationException) {
+ // Thrown due to some security issues on .NET 4.5.
+ return null;
+ } catch (TypeLoadException) {
+ // Thrown by mono (http://stackoverflow.com/questions/10805773/error-when-pushing-dotnetopenauth-to-staging-or-production-environment)
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Creates the NLogLogger. Call ONLY after NLog.dll is known to be present.
+ /// </summary>
+ /// <returns>The created <see cref="ILog"/> instance.</returns>
+ private static ILog CreateLogger(string name) {
+ return new NLogLogger(NLog.LogManager.GetLogger(name));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/MachineKeyUtil.cs b/src/DotNetOpenAuth.Core/MachineKeyUtil.cs
new file mode 100644
index 0000000..eceb38c
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/MachineKeyUtil.cs
@@ -0,0 +1,354 @@
+//-----------------------------------------------------------------------
+// <copyright file="MachineKeyUtil.cs" company="Microsoft">
+// Copyright (c) Microsoft. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Net;
+ using System.Reflection;
+ using System.Security.Cryptography;
+ using System.Text;
+ using System.Web;
+ using System.Web.Security;
+
+ /// <summary>
+ /// Provides helpers that mimic the ASP.NET 4.5 MachineKey.Protect / Unprotect APIs,
+ /// even when running on ASP.NET 4.0. Consumers are expected to follow the same
+ /// conventions used by the MachineKey.Protect / Unprotect APIs (consult MSDN docs
+ /// for how these are meant to be used). Additionally, since this helper class
+ /// dynamically switches between the two based on whether the current application is
+ /// .NET 4.0 or 4.5, consumers should never persist output from the Protect method
+ /// since the implementation will change when upgrading 4.0 -> 4.5. This should be
+ /// used for transient data only.
+ /// </summary>
+ internal static class MachineKeyUtil {
+ /// <summary>
+ /// MachineKey implementation depending on the target .NET framework version
+ /// </summary>
+ private static readonly IMachineKey MachineKeyImpl = GetMachineKeyImpl();
+
+ /// <summary>
+ /// ProtectUnprotect delegate.
+ /// </summary>
+ /// <param name="data">The data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>Result of either Protect or Unprotect methods.</returns>
+ private delegate byte[] ProtectUnprotect(byte[] data, string[] purposes);
+
+ /// <summary>
+ /// Abstract the MachineKey implementation in .NET 4.0 and 4.5
+ /// </summary>
+ private interface IMachineKey {
+ /// <summary>
+ /// Protects the specified user data.
+ /// </summary>
+ /// <param name="userData">The user data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>The protected data.</returns>
+ byte[] Protect(byte[] userData, string[] purposes);
+
+ /// <summary>
+ /// Unprotects the specified protected data.
+ /// </summary>
+ /// <param name="protectedData">The protected data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>The unprotected data.</returns>
+ byte[] Unprotect(byte[] protectedData, string[] purposes);
+ }
+
+ /// <summary>
+ /// Protects the specified user data.
+ /// </summary>
+ /// <param name="userData">The user data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>The encrypted data</returns>
+ public static byte[] Protect(byte[] userData, params string[] purposes) {
+ return MachineKeyImpl.Protect(userData, purposes);
+ }
+
+ /// <summary>
+ /// Unprotects the specified protected data.
+ /// </summary>
+ /// <param name="protectedData">The protected data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>The unencrypted data</returns>
+ public static byte[] Unprotect(byte[] protectedData, params string[] purposes) {
+ return MachineKeyImpl.Unprotect(protectedData, purposes);
+ }
+
+ /// <summary>
+ /// Gets the machine key implementation based on the runtime framework version.
+ /// </summary>
+ /// <returns>The machine key implementation</returns>
+ private static IMachineKey GetMachineKeyImpl() {
+ // Late bind to the MachineKey.Protect / Unprotect methods only if <httpRuntime targetFramework="4.5" />.
+ // This helps ensure that round-tripping the payloads continues to work even if the application is
+ // deployed to a mixed 4.0 / 4.5 farm environment.
+ PropertyInfo targetFrameworkProperty = typeof(HttpRuntime).GetProperty("TargetFramework", typeof(Version));
+ Version targetFramework = (targetFrameworkProperty != null) ? targetFrameworkProperty.GetValue(null, null) as Version : null;
+ if (targetFramework != null && targetFramework >= new Version(4, 5)) {
+ ProtectUnprotect protectThunk = (ProtectUnprotect)Delegate.CreateDelegate(typeof(ProtectUnprotect), typeof(MachineKey), "Protect", ignoreCase: false, throwOnBindFailure: false);
+ ProtectUnprotect unprotectThunk = (ProtectUnprotect)Delegate.CreateDelegate(typeof(ProtectUnprotect), typeof(MachineKey), "Unprotect", ignoreCase: false, throwOnBindFailure: false);
+ if (protectThunk != null && unprotectThunk != null) {
+ return new MachineKey45(protectThunk, unprotectThunk); // ASP.NET 4.5
+ }
+ }
+
+ return new MachineKey40(); // ASP.NET 4.0
+ }
+
+ /// <summary>
+ /// On ASP.NET 4.0, we perform some transforms which mimic the behaviors of MachineKey.Protect
+ /// and Unprotect.
+ /// </summary>
+ private sealed class MachineKey40 : IMachineKey {
+ /// <summary>
+ /// This is the magic header that identifies a MachineKey40 payload.
+ /// It helps differentiate this from other encrypted payloads.</summary>
+ private const uint MagicHeader = 0x8519140c;
+
+ /// <summary>
+ /// The SHA-256 factory to be used.
+ /// </summary>
+ private static readonly Func<SHA256> sha256Factory = GetSHA256Factory();
+
+ /// <summary>
+ /// Protects the specified user data.
+ /// </summary>
+ /// <param name="userData">The user data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>The protected data</returns>
+ public byte[] Protect(byte[] userData, string[] purposes) {
+ if (userData == null) {
+ throw new ArgumentNullException("userData");
+ }
+
+ // dataWithHeader = {magic header} .. {purposes} .. {userData}
+ byte[] dataWithHeader = new byte[checked(4 /* magic header */ + (256 / 8) /* purposes */ + userData.Length)];
+ unchecked {
+ dataWithHeader[0] = (byte)(MagicHeader >> 24);
+ dataWithHeader[1] = (byte)(MagicHeader >> 16);
+ dataWithHeader[2] = (byte)(MagicHeader >> 8);
+ dataWithHeader[3] = (byte)MagicHeader;
+ }
+ byte[] purposeHash = ComputeSHA256(purposes);
+ Buffer.BlockCopy(purposeHash, 0, dataWithHeader, 4, purposeHash.Length);
+ Buffer.BlockCopy(userData, 0, dataWithHeader, 4 + (256 / 8), userData.Length);
+
+ // encrypt + sign
+ string hexValue = MachineKey.Encode(dataWithHeader, MachineKeyProtection.All);
+
+ // convert hex -> binary
+ byte[] binary = HexToBinary(hexValue);
+ return binary;
+ }
+
+ /// <summary>
+ /// Unprotects the specified protected data.
+ /// </summary>
+ /// <param name="protectedData">The protected data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>The unprotected data</returns>
+ public byte[] Unprotect(byte[] protectedData, string[] purposes) {
+ if (protectedData == null) {
+ throw new ArgumentNullException("protectedData");
+ }
+
+ // convert binary -> hex and calculate what the purpose should read
+ string hexEncodedData = BinaryToHex(protectedData);
+ byte[] purposeHash = ComputeSHA256(purposes);
+
+ try {
+ // decrypt / verify signature
+ byte[] dataWithHeader = MachineKey.Decode(hexEncodedData, MachineKeyProtection.All);
+
+ // validate magic header and purpose string
+ if (dataWithHeader != null
+ && dataWithHeader.Length >= (4 + (256 / 8))
+ && (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(dataWithHeader, 0)) == MagicHeader
+ && AreByteArraysEqual(new ArraySegment<byte>(purposeHash), new ArraySegment<byte>(dataWithHeader, 4, 256 / 8))) {
+ // validation succeeded
+ byte[] userData = new byte[dataWithHeader.Length - 4 - (256 / 8)];
+ Buffer.BlockCopy(dataWithHeader, 4 + (256 / 8), userData, 0, userData.Length);
+ return userData;
+ }
+ }
+ catch {
+ // swallow since will be rethrown immediately below
+ }
+
+ // if we reached this point, some cryptographic operation failed
+ throw new CryptographicException(Strings.Generic_CryptoFailure);
+ }
+
+ /// <summary>
+ /// Convert bytes to hex string.
+ /// </summary>
+ /// <param name="binary">The input array.</param>
+ /// <returns>Hex string</returns>
+ internal static string BinaryToHex(byte[] binary) {
+ StringBuilder builder = new StringBuilder(checked(binary.Length * 2));
+ for (int i = 0; i < binary.Length; i++) {
+ byte b = binary[i];
+ builder.Append(HexDigit(b >> 4));
+ builder.Append(HexDigit(b & 0x0F));
+ }
+ string result = builder.ToString();
+ return result;
+ }
+
+ /// <summary>
+ /// This method is specially written to take the same amount of time
+ /// regardless of where 'a' and 'b' differ. Please do not optimize it.</summary>
+ /// <param name="a">first array.</param>
+ /// <param name="b">second array.</param>
+ /// <returns><c href="true" /> if equal, others <c href="false" /></returns>
+ private static bool AreByteArraysEqual(ArraySegment<byte> a, ArraySegment<byte> b) {
+ if (a.Count != b.Count) {
+ return false;
+ }
+
+ bool areEqual = true;
+ for (int i = 0; i < a.Count; i++) {
+ areEqual &= a.Array[a.Offset + i] == b.Array[b.Offset + i];
+ }
+ return areEqual;
+ }
+
+ /// <summary>
+ /// Computes a SHA256 hash over all of the input parameters.
+ /// Each parameter is UTF8 encoded and preceded by a 7-bit encoded</summary>
+ /// integer describing the encoded byte length of the string.
+ /// <param name="parameters">The parameters.</param>
+ /// <returns>The output hash</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "MemoryStream is resilient to double-Dispose")]
+ private static byte[] ComputeSHA256(IList<string> parameters) {
+ using (MemoryStream ms = new MemoryStream()) {
+ using (BinaryWriter bw = new BinaryWriter(ms)) {
+ if (parameters != null) {
+ foreach (string parameter in parameters) {
+ bw.Write(parameter); // also writes the length as a prefix; unambiguous
+ }
+ bw.Flush();
+ }
+
+ using (SHA256 sha256 = sha256Factory()) {
+ byte[] retVal = sha256.ComputeHash(ms.GetBuffer(), 0, checked((int)ms.Length));
+ return retVal;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the SHA-256 factory.
+ /// </summary>
+ /// <returns>SHA256 factory</returns>
+ private static Func<SHA256> GetSHA256Factory() {
+ // Note: ASP.NET 4.5 always prefers CNG, but the CNG algorithms are not that
+ // performant on 4.0 and below. The following list is optimized for speed
+ // given our scenarios.
+ if (!CryptoConfig.AllowOnlyFipsAlgorithms) {
+ // This provider is not FIPS-compliant, so we can't use it if FIPS compliance
+ // is mandatory.
+ return () => new SHA256Managed();
+ }
+
+ try {
+ using (SHA256Cng sha256 = new SHA256Cng()) {
+ return () => new SHA256Cng();
+ }
+ }
+ catch (PlatformNotSupportedException) {
+ // CNG not supported (perhaps because we're not on Windows Vista or above); move on
+ }
+
+ // If all else fails, fall back to CAPI.
+ return () => new SHA256CryptoServiceProvider();
+ }
+
+ /// <summary>
+ /// Convert to hex character
+ /// </summary>
+ /// <param name="value">The value to be converted.</param>
+ /// <returns>Hex character</returns>
+ private static char HexDigit(int value) {
+ return (char)(value > 9 ? value + '7' : value + '0');
+ }
+
+ /// <summary>
+ /// Convert hdex string to bytes.
+ /// </summary>
+ /// <param name="hex">Input hex string.</param>
+ /// <returns>The bytes</returns>
+ private static byte[] HexToBinary(string hex) {
+ int size = hex.Length / 2;
+ byte[] bytes = new byte[size];
+ for (int idx = 0; idx < size; idx++) {
+ bytes[idx] = (byte)((HexValue(hex[idx * 2]) << 4) + HexValue(hex[(idx * 2) + 1]));
+ }
+ return bytes;
+ }
+
+ /// <summary>
+ /// Convert hex digit to byte.
+ /// </summary>
+ /// <param name="digit">The hex digit.</param>
+ /// <returns>The byte</returns>
+ private static int HexValue(char digit) {
+ return digit > '9' ? digit - '7' : digit - '0';
+ }
+ }
+
+ /// <summary>
+ /// On ASP.NET 4.5, we can just delegate to MachineKey.Protect and MachineKey.Unprotect directly,
+ /// which contain optimized code paths.
+ /// </summary>
+ private sealed class MachineKey45 : IMachineKey {
+ /// <summary>
+ /// Protect thunk
+ /// </summary>
+ private readonly ProtectUnprotect protectThunk;
+
+ /// <summary>
+ /// Unprotect thunk
+ /// </summary>
+ private readonly ProtectUnprotect unprotectThunk;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MachineKey45"/> class.
+ /// </summary>
+ /// <param name="protectThunk">The protect thunk.</param>
+ /// <param name="unprotectThunk">The unprotect thunk.</param>
+ public MachineKey45(ProtectUnprotect protectThunk, ProtectUnprotect unprotectThunk) {
+ this.protectThunk = protectThunk;
+ this.unprotectThunk = unprotectThunk;
+ }
+
+ /// <summary>
+ /// Protects the specified user data.
+ /// </summary>
+ /// <param name="userData">The user data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>The protected data</returns>
+ public byte[] Protect(byte[] userData, string[] purposes) {
+ return this.protectThunk(userData, purposes);
+ }
+
+ /// <summary>
+ /// Unprotects the specified protected data.
+ /// </summary>
+ /// <param name="protectedData">The protected data.</param>
+ /// <param name="purposes">The purposes.</param>
+ /// <returns>The unprotected data</returns>
+ public byte[] Unprotect(byte[] protectedData, string[] purposes) {
+ return this.unprotectThunk(protectedData, purposes);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Messaging/BinaryDataBagFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/BinaryDataBagFormatter.cs
index 4f4bf0e..554205a 100644
--- a/src/DotNetOpenAuth.Core/Messaging/BinaryDataBagFormatter.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/BinaryDataBagFormatter.cs
@@ -8,12 +8,12 @@ namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using DotNetOpenAuth.Messaging.Bindings;
+ using Validation;
/// <summary>
/// A compact binary <see cref="DataBag"/> serialization class.
@@ -45,7 +45,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
protected internal BinaryDataBagFormatter(ICryptoKeyStore cryptoKeyStore = null, string bucket = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? minimumAge = null, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
: base(cryptoKeyStore, bucket, signed, encrypted, compressed, minimumAge, maximumAge, decodeOnceOnly) {
- Requires.True((cryptoKeyStore != null && bucket != null) || (!signed && !encrypted), null);
+ Requires.That((cryptoKeyStore != null && bucket != null) || (!signed && !encrypted), null, "Signing or encryption requires a crypto key store and bucket.");
}
/// <summary>
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/AsymmetricCryptoKeyStoreWrapper.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/AsymmetricCryptoKeyStoreWrapper.cs
index 4cb5337..0439908 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Bindings/AsymmetricCryptoKeyStoreWrapper.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/AsymmetricCryptoKeyStoreWrapper.cs
@@ -8,11 +8,11 @@ namespace DotNetOpenAuth.Messaging.Bindings {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using DotNetOpenAuth.Messaging;
+ using Validation;
/// <summary>
/// Provides RSA encryption of symmetric keys to protect them from a theft of
@@ -42,7 +42,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
public AsymmetricCryptoKeyStoreWrapper(ICryptoKeyStore dataStore, RSACryptoServiceProvider asymmetricCrypto) {
Requires.NotNull(dataStore, "dataStore");
Requires.NotNull(asymmetricCrypto, "asymmetricCrypto");
- Requires.True(!asymmetricCrypto.PublicOnly, "asymmetricCrypto");
+ Requires.That(!asymmetricCrypto.PublicOnly, "asymmetricCrypto", "Private key required.");
this.dataStore = dataStore;
this.asymmetricCrypto = asymmetricCrypto;
}
@@ -138,9 +138,9 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <param name="decrypted">The decrypted key.</param>
internal CachedCryptoKey(CryptoKey encrypted, CryptoKey decrypted)
: base(decrypted.Key, decrypted.ExpiresUtc) {
- Contract.Requires(encrypted != null);
- Contract.Requires(decrypted != null);
- Contract.Requires(encrypted.ExpiresUtc == decrypted.ExpiresUtc);
+ Requires.NotNull(encrypted, "encrypted");
+ Requires.NotNull(decrypted, "decrypted");
+ Requires.That(encrypted.ExpiresUtc == decrypted.ExpiresUtc, "encrypted", "encrypted and decrypted expirations must equal.");
this.EncryptedKey = encrypted.Key;
}
@@ -149,16 +149,6 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// Gets the encrypted key.
/// </summary>
internal byte[] EncryptedKey { get; private set; }
-
- /// <summary>
- /// Invariant conditions.
- /// </summary>
- [ContractInvariantMethod]
- [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Code contracts")]
- [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
- private void ObjectInvariant() {
- Contract.Invariant(this.EncryptedKey != null);
- }
}
}
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKey.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKey.cs
index 3fa50d4..d6fef62 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKey.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKey.cs
@@ -8,10 +8,10 @@ namespace DotNetOpenAuth.Messaging.Bindings {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
+ using Validation;
/// <summary>
/// A cryptographic key and metadata concerning it.
@@ -34,7 +34,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <param name="expiresUtc">The expires UTC.</param>
public CryptoKey(byte[] key, DateTime expiresUtc) {
Requires.NotNull(key, "key");
- Requires.True(expiresUtc.Kind == DateTimeKind.Utc, "expiresUtc");
+ Requires.That(expiresUtc.Kind == DateTimeKind.Utc, "expiresUtc", "Time must be expressed in UTC.");
this.key = key;
this.expiresUtc = expiresUtc;
}
@@ -45,7 +45,6 @@ namespace DotNetOpenAuth.Messaging.Bindings {
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "It's a buffer")]
public byte[] Key {
get {
- Contract.Ensures(Contract.Result<byte[]>() != null);
return this.key;
}
}
@@ -55,7 +54,6 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// </summary>
public DateTime ExpiresUtc {
get {
- Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Utc);
return this.expiresUtc;
}
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKeyCollisionException.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKeyCollisionException.cs
index 1fe4493..7507b31 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKeyCollisionException.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/CryptoKeyCollisionException.cs
@@ -56,11 +56,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter"/>
/// </PermissionSet>
-#if CLR4
[System.Security.SecurityCritical]
-#else
- [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
-#endif
public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) {
base.GetObjectData(info, context);
throw new NotImplementedException();
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/ExpiredMessageException.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/ExpiredMessageException.cs
index 88b8fed..8c5db3c 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Bindings/ExpiredMessageException.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/ExpiredMessageException.cs
@@ -6,8 +6,8 @@
namespace DotNetOpenAuth.Messaging.Bindings {
using System;
- using System.Diagnostics.Contracts;
using System.Globalization;
+ using Validation;
/// <summary>
/// An exception thrown when a message is received that exceeds the maximum message age limit.
@@ -21,7 +21,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <param name="faultedMessage">The expired message.</param>
public ExpiredMessageException(DateTime utcExpirationDate, IProtocolMessage faultedMessage)
: base(string.Format(CultureInfo.CurrentCulture, MessagingStrings.ExpiredMessage, utcExpirationDate.ToLocalTime(), DateTime.Now), faultedMessage) {
- Requires.True(utcExpirationDate.Kind == DateTimeKind.Utc, "utcExpirationDate");
+ Requires.Argument(utcExpirationDate.Kind == DateTimeKind.Utc, "utcExpirationDate", "Time must be expressed as UTC.");
}
/// <summary>
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/HardCodedKeyCryptoKeyStore.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/HardCodedKeyCryptoKeyStore.cs
new file mode 100644
index 0000000..9bea16f
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/HardCodedKeyCryptoKeyStore.cs
@@ -0,0 +1,98 @@
+//-----------------------------------------------------------------------
+// <copyright file="HardCodedKeyCryptoKeyStore.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ using System;
+ using System.Collections.Generic;
+ using Validation;
+
+ /// <summary>
+ /// A trivial implementation of <see cref="ICryptoKeyStore"/> that has only one fixed key.
+ /// This is meant for simple, low-security applications. Greater security requires an
+ /// implementation of <see cref="ICryptoKeyStore"/> that actually stores and retrieves
+ /// keys from a persistent store.
+ /// </summary>
+ public class HardCodedKeyCryptoKeyStore : ICryptoKeyStore {
+ /// <summary>
+ /// The handle to report for the hard-coded key.
+ /// </summary>
+ private const string HardCodedKeyHandle = "fxd";
+
+ /// <summary>
+ /// The one crypto key singleton instance.
+ /// </summary>
+ private readonly CryptoKey OneCryptoKey;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HardCodedKeyCryptoKeyStore"/> class.
+ /// </summary>
+ /// <param name="secretAsBase64">The 256-bit secret as a base64 encoded string.</param>
+ public HardCodedKeyCryptoKeyStore(string secretAsBase64)
+ : this(Convert.FromBase64String(Requires.NotNull(secretAsBase64, "secretAsBase64"))) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HardCodedKeyCryptoKeyStore"/> class.
+ /// </summary>
+ /// <param name="secret">The 256-bit secret.</param>
+ public HardCodedKeyCryptoKeyStore(byte[] secret) {
+ Requires.NotNull(secret, "secret");
+ this.OneCryptoKey = new CryptoKey(secret, DateTime.MaxValue.AddDays(-2).ToUniversalTime());
+ }
+
+ #region ICryptoKeyStore Members
+
+ /// <summary>
+ /// Gets the key in a given bucket and handle.
+ /// </summary>
+ /// <param name="bucket">The bucket name. Case sensitive.</param>
+ /// <param name="handle">The key handle. Case sensitive.</param>
+ /// <returns>
+ /// The cryptographic key, or <c>null</c> if no matching key was found.
+ /// </returns>
+ public CryptoKey GetKey(string bucket, string handle) {
+ if (handle == HardCodedKeyHandle) {
+ return this.OneCryptoKey;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Gets a sequence of existing keys within a given bucket.
+ /// </summary>
+ /// <param name="bucket">The bucket name. Case sensitive.</param>
+ /// <returns>
+ /// A sequence of handles and keys, ordered by descending <see cref="CryptoKey.ExpiresUtc" />.
+ /// </returns>
+ public IEnumerable<KeyValuePair<string, CryptoKey>> GetKeys(string bucket) {
+ return new[] { new KeyValuePair<string, CryptoKey>(HardCodedKeyHandle, this.OneCryptoKey) };
+ }
+
+ /// <summary>
+ /// Stores a cryptographic key.
+ /// </summary>
+ /// <param name="bucket">The name of the bucket to store the key in. Case sensitive.</param>
+ /// <param name="handle">The handle to the key, unique within the bucket. Case sensitive.</param>
+ /// <param name="key">The key to store.</param>
+ /// <exception cref="System.NotSupportedException">Always thrown.</exception>
+ public void StoreKey(string bucket, string handle, CryptoKey key) {
+ throw new NotSupportedException();
+ }
+
+ /// <summary>
+ /// Removes the key.
+ /// </summary>
+ /// <param name="bucket">The bucket name. Case sensitive.</param>
+ /// <param name="handle">The key handle. Case sensitive.</param>
+ /// <exception cref="System.NotSupportedException">Always thrown.</exception>
+ public void RemoveKey(string bucket, string handle) {
+ throw new NotSupportedException();
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyAndNonceStore.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyAndNonceStore.cs
new file mode 100644
index 0000000..aa03504
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyAndNonceStore.cs
@@ -0,0 +1,14 @@
+//-----------------------------------------------------------------------
+// <copyright file="ICryptoKeyAndNonceStore.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ /// <summary>
+ /// A hybrid of the store interfaces that an OpenID Provider must implement, and
+ /// an OpenID Relying Party may implement to operate in stateful (smart) mode.
+ /// </summary>
+ public interface ICryptoKeyAndNonceStore : ICryptoKeyStore, INonceStore {
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyStore.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyStore.cs
index 2e43bba..ce7bf42 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyStore.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/ICryptoKeyStore.cs
@@ -8,10 +8,10 @@ namespace DotNetOpenAuth.Messaging.Bindings {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
+ using Validation;
/// <summary>
/// A persistent store for rotating symmetric cryptographic keys.
@@ -23,7 +23,6 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// of the confidentiality of the keys. One possible mitigation is to asymmetrically encrypt
/// each key using a certificate installed in the server's certificate store.
/// </remarks>
- [ContractClass(typeof(ICryptoKeyStoreContract))]
public interface ICryptoKeyStore {
/// <summary>
/// Gets the key in a given bucket and handle.
@@ -57,62 +56,4 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// <param name="handle">The key handle. Case sensitive.</param>
void RemoveKey(string bucket, string handle);
}
-
- /// <summary>
- /// Code contract for the <see cref="ICryptoKeyStore"/> interface.
- /// </summary>
- [ContractClassFor(typeof(ICryptoKeyStore))]
- internal abstract class ICryptoKeyStoreContract : ICryptoKeyStore {
- /// <summary>
- /// Gets the key in a given bucket and handle.
- /// </summary>
- /// <param name="bucket">The bucket name. Case sensitive.</param>
- /// <param name="handle">The key handle. Case sensitive.</param>
- /// <returns>
- /// The cryptographic key, or <c>null</c> if no matching key was found.
- /// </returns>
- CryptoKey ICryptoKeyStore.GetKey(string bucket, string handle) {
- Requires.NotNullOrEmpty(bucket, "bucket");
- Requires.NotNullOrEmpty(handle, "handle");
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Gets a sequence of existing keys within a given bucket.
- /// </summary>
- /// <param name="bucket">The bucket name. Case sensitive.</param>
- /// <returns>
- /// A sequence of handles and keys, ordered by descending <see cref="CryptoKey.ExpiresUtc"/>.
- /// </returns>
- IEnumerable<KeyValuePair<string, CryptoKey>> ICryptoKeyStore.GetKeys(string bucket) {
- Requires.NotNullOrEmpty(bucket, "bucket");
- Contract.Ensures(Contract.Result<IEnumerable<KeyValuePair<string, CryptoKey>>>() != null);
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Stores a cryptographic key.
- /// </summary>
- /// <param name="bucket">The name of the bucket to store the key in. Case sensitive.</param>
- /// <param name="handle">The handle to the key, unique within the bucket. Case sensitive.</param>
- /// <param name="key">The key to store.</param>
- /// <exception cref="CryptoKeyCollisionException">Thrown in the event of a conflict with an existing key in the same bucket and with the same handle.</exception>
- void ICryptoKeyStore.StoreKey(string bucket, string handle, CryptoKey key) {
- Requires.NotNullOrEmpty(bucket, "bucket");
- Requires.NotNullOrEmpty(handle, "handle");
- Requires.NotNull(key, "key");
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Removes the key.
- /// </summary>
- /// <param name="bucket">The bucket name. Case sensitive.</param>
- /// <param name="handle">The key handle. Case sensitive.</param>
- void ICryptoKeyStore.RemoveKey(string bucket, string handle) {
- Requires.NotNullOrEmpty(bucket, "bucket");
- Requires.NotNullOrEmpty(handle, "handle");
- throw new NotImplementedException();
- }
- }
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/MemoryCryptoKeyAndNonceStore.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/MemoryCryptoKeyAndNonceStore.cs
new file mode 100644
index 0000000..1484ec7
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/MemoryCryptoKeyAndNonceStore.cs
@@ -0,0 +1,126 @@
+//-----------------------------------------------------------------------
+// <copyright file="MemoryCryptoKeyAndNonceStore.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ using System;
+ using System.Collections.Generic;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging.Bindings;
+
+ /// <summary>
+ /// An in-memory store for Providers, suitable for single server, single process
+ /// ASP.NET web sites.
+ /// </summary>
+ /// <remarks>
+ /// This class provides only a basic implementation that is likely to work
+ /// out of the box on most single-server web sites. It is highly recommended
+ /// that high traffic web sites consider using a database to store the information
+ /// used by an OpenID Provider and write a custom implementation of the
+ /// <see cref="ICryptoKeyAndNonceStore"/> interface to use instead of this
+ /// class.
+ /// </remarks>
+ public class MemoryCryptoKeyAndNonceStore : ICryptoKeyAndNonceStore {
+ /// <summary>
+ /// The nonce store to use.
+ /// </summary>
+ private readonly INonceStore nonceStore;
+
+ /// <summary>
+ /// The crypto key store where symmetric keys are persisted.
+ /// </summary>
+ private readonly ICryptoKeyStore cryptoKeyStore;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemoryCryptoKeyAndNonceStore" /> class
+ /// with a default max nonce lifetime of 5 minutes.
+ /// </summary>
+ public MemoryCryptoKeyAndNonceStore()
+ : this(TimeSpan.FromMinutes(5)) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemoryCryptoKeyAndNonceStore"/> class.
+ /// </summary>
+ /// <param name="maximumMessageAge">The maximum time to live of a message that might carry a nonce.</param>
+ public MemoryCryptoKeyAndNonceStore(TimeSpan maximumMessageAge) {
+ this.nonceStore = new MemoryNonceStore(maximumMessageAge);
+ this.cryptoKeyStore = new MemoryCryptoKeyStore();
+ }
+
+ #region INonceStore Members
+
+ /// <summary>
+ /// Stores a given nonce and timestamp.
+ /// </summary>
+ /// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param>
+ /// <param name="nonce">A series of random characters.</param>
+ /// <param name="timestampUtc">The timestamp that together with the nonce string make it unique.
+ /// The timestamp may also be used by the data store to clear out old nonces.</param>
+ /// <returns>
+ /// True if the nonce+timestamp (combination) was not previously in the database.
+ /// False if the nonce was stored previously with the same timestamp.
+ /// </returns>
+ /// <remarks>
+ /// The nonce must be stored for no less than the maximum time window a message may
+ /// be processed within before being discarded as an expired message.
+ /// If the binding element is applicable to your channel, this expiration window
+ /// is retrieved or set using the
+ /// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property.
+ /// </remarks>
+ public bool StoreNonce(string context, string nonce, DateTime timestampUtc) {
+ return this.nonceStore.StoreNonce(context, nonce, timestampUtc);
+ }
+
+ #endregion
+
+ #region ICryptoKeyStore
+
+ /// <summary>
+ /// Gets the key in a given bucket and handle.
+ /// </summary>
+ /// <param name="bucket">The bucket name. Case sensitive.</param>
+ /// <param name="handle">The key handle. Case sensitive.</param>
+ /// <returns>
+ /// The cryptographic key, or <c>null</c> if no matching key was found.
+ /// </returns>
+ public CryptoKey GetKey(string bucket, string handle) {
+ return this.cryptoKeyStore.GetKey(bucket, handle);
+ }
+
+ /// <summary>
+ /// Gets a sequence of existing keys within a given bucket.
+ /// </summary>
+ /// <param name="bucket">The bucket name. Case sensitive.</param>
+ /// <returns>
+ /// A sequence of handles and keys, ordered by descending <see cref="CryptoKey.ExpiresUtc"/>.
+ /// </returns>
+ public IEnumerable<KeyValuePair<string, CryptoKey>> GetKeys(string bucket) {
+ return this.cryptoKeyStore.GetKeys(bucket);
+ }
+
+ /// <summary>
+ /// Stores a cryptographic key.
+ /// </summary>
+ /// <param name="bucket">The name of the bucket to store the key in. Case sensitive.</param>
+ /// <param name="handle">The handle to the key, unique within the bucket. Case sensitive.</param>
+ /// <param name="key">The key to store.</param>
+ /// <exception cref="CryptoKeyCollisionException">Thrown in the event of a conflict with an existing key in the same bucket and with the same handle.</exception>
+ public void StoreKey(string bucket, string handle, CryptoKey key) {
+ this.cryptoKeyStore.StoreKey(bucket, handle, key);
+ }
+
+ /// <summary>
+ /// Removes the key.
+ /// </summary>
+ /// <param name="bucket">The bucket name. Case sensitive.</param>
+ /// <param name="handle">The key handle. Case sensitive.</param>
+ public void RemoveKey(string bucket, string handle) {
+ this.cryptoKeyStore.RemoveKey(bucket, handle);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/NonceMemoryStore.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/MemoryNonceStore.cs
index d069d66..f1d1d3e 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Bindings/NonceMemoryStore.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/MemoryNonceStore.cs
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------
-// <copyright file="NonceMemoryStore.cs" company="Outercurve Foundation">
+// <copyright file="MemoryNonceStore.cs" company="Outercurve Foundation">
// Copyright (c) Outercurve Foundation. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
@@ -8,14 +8,12 @@ namespace DotNetOpenAuth.Messaging.Bindings {
using System;
using System.Collections.Generic;
using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging.Bindings;
/// <summary>
/// An in-memory nonce store. Useful for single-server web applications.
/// NOT for web farms.
/// </summary>
- internal class NonceMemoryStore : INonceStore {
+ internal class MemoryNonceStore : INonceStore {
/// <summary>
/// How frequently we should take time to clear out old nonces.
/// </summary>
@@ -45,17 +43,17 @@ namespace DotNetOpenAuth.Messaging.Bindings {
private int nonceClearingCounter;
/// <summary>
- /// Initializes a new instance of the <see cref="NonceMemoryStore"/> class.
+ /// Initializes a new instance of the <see cref="MemoryNonceStore"/> class.
/// </summary>
- internal NonceMemoryStore()
+ internal MemoryNonceStore()
: this(StandardExpirationBindingElement.MaximumMessageAge) {
}
/// <summary>
- /// Initializes a new instance of the <see cref="NonceMemoryStore"/> class.
+ /// Initializes a new instance of the <see cref="MemoryNonceStore"/> class.
/// </summary>
/// <param name="maximumMessageAge">The maximum age a message can be before it is discarded.</param>
- internal NonceMemoryStore(TimeSpan maximumMessageAge) {
+ internal MemoryNonceStore(TimeSpan maximumMessageAge) {
this.maximumMessageAge = maximumMessageAge;
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/StandardExpirationBindingElement.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/StandardExpirationBindingElement.cs
index 7ab78db..f19d4bd 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Bindings/StandardExpirationBindingElement.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/StandardExpirationBindingElement.cs
@@ -6,6 +6,8 @@
namespace DotNetOpenAuth.Messaging.Bindings {
using System;
+ using System.Threading;
+ using System.Threading.Tasks;
using DotNetOpenAuth.Configuration;
/// <summary>
@@ -14,6 +16,16 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// </summary>
internal class StandardExpirationBindingElement : IChannelBindingElement {
/// <summary>
+ /// A reusable pre-completed task that may be returned multiple times to reduce GC pressure.
+ /// </summary>
+ private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null);
+
+ /// <summary>
+ /// A reusable pre-completed task that may be returned multiple times to reduce GC pressure.
+ /// </summary>
+ private static readonly Task<MessageProtections?> CompletedExpirationTask = Task.FromResult<MessageProtections?>(MessageProtections.Expiration);
+
+ /// <summary>
/// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class.
/// </summary>
internal StandardExpirationBindingElement() {
@@ -51,24 +63,30 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// Sets the timestamp on an outgoing message.
/// </summary>
/// <param name="message">The outgoing message.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The protections (if any) that this binding element applied to the message.
/// Null if this binding element did not even apply to this binding element.
/// </returns>
- public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection" /> properties where applicable.
+ /// </remarks>
+ public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) {
IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
if (expiringMessage != null) {
expiringMessage.UtcCreationDate = DateTime.UtcNow;
- return MessageProtections.Expiration;
+ return CompletedExpirationTask;
}
- return null;
+ return NullTask;
}
/// <summary>
/// Reads the timestamp on a message and throws an exception if the message is too old.
/// </summary>
/// <param name="message">The incoming message.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The protections (if any) that this binding element applied to the message.
/// Null if this binding element did not even apply to this binding element.
@@ -78,7 +96,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// Thrown when the binding element rules indicate that this message is invalid and should
/// NOT be processed.
/// </exception>
- public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
+ public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) {
IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
if (expiringMessage != null) {
// Yes the UtcCreationDate is supposed to always be in UTC already,
@@ -96,10 +114,10 @@ namespace DotNetOpenAuth.Messaging.Bindings {
MessagingStrings.MessageTimestampInFuture,
creationDate);
- return MessageProtections.Expiration;
+ return CompletedExpirationTask;
}
- return null;
+ return NullTask;
}
#endregion
diff --git a/src/DotNetOpenAuth.Core/Messaging/Bindings/StandardReplayProtectionBindingElement.cs b/src/DotNetOpenAuth.Core/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
index 7e39536..65c7882 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
@@ -7,13 +7,25 @@
namespace DotNetOpenAuth.Messaging.Bindings {
using System;
using System.Diagnostics;
- using System.Diagnostics.Contracts;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Validation;
/// <summary>
/// A binding element that checks/verifies a nonce message part.
/// </summary>
internal class StandardReplayProtectionBindingElement : IChannelBindingElement {
/// <summary>
+ /// A reusable, precompleted task that can be returned many times to reduce GC pressure.
+ /// </summary>
+ private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null);
+
+ /// <summary>
+ /// A reusable, precompleted task that can be returned many times to reduce GC pressure.
+ /// </summary>
+ private static readonly Task<MessageProtections?> CompletedReplayProtectionTask = Task.FromResult<MessageProtections?>(MessageProtections.ReplayProtection);
+
+ /// <summary>
/// These are the characters that may be chosen from when forming a random nonce.
/// </summary>
private const string AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
@@ -96,30 +108,36 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// Applies a nonce to the message.
/// </summary>
/// <param name="message">The message to apply replay protection to.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The protections (if any) that this binding element applied to the message.
/// Null if this binding element did not even apply to this binding element.
/// </returns>
- public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
+ public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) {
IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
if (nonceMessage != null) {
nonceMessage.Nonce = this.GenerateUniqueFragment();
- return MessageProtections.ReplayProtection;
+ return CompletedReplayProtectionTask;
}
- return null;
+ return NullTask;
}
/// <summary>
/// Verifies that the nonce in an incoming message has not been seen before.
/// </summary>
/// <param name="message">The incoming message.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The protections (if any) that this binding element applied to the message.
/// Null if this binding element did not even apply to this binding element.
/// </returns>
/// <exception cref="ReplayedMessageException">Thrown when the nonce check revealed a replayed message.</exception>
- public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection" /> properties where applicable.
+ /// </remarks>
+ public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) {
IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
if (nonceMessage != null && nonceMessage.Nonce != null) {
ErrorUtilities.VerifyProtocol(nonceMessage.Nonce.Length > 0 || this.AllowZeroLengthNonce, MessagingStrings.InvalidNonceReceived);
@@ -129,10 +147,10 @@ namespace DotNetOpenAuth.Messaging.Bindings {
throw new ReplayedMessageException(message);
}
- return MessageProtections.ReplayProtection;
+ return CompletedReplayProtectionTask;
}
- return null;
+ return NullTask;
}
#endregion
diff --git a/src/DotNetOpenAuth.Core/Messaging/CachedDirectWebResponse.cs b/src/DotNetOpenAuth.Core/Messaging/CachedDirectWebResponse.cs
deleted file mode 100644
index 16e92a8..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/CachedDirectWebResponse.cs
+++ /dev/null
@@ -1,184 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="CachedDirectWebResponse.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
- using System.IO;
- using System.Net;
- using System.Text;
-
- /// <summary>
- /// Cached details on the response from a direct web request to a remote party.
- /// </summary>
- [ContractVerification(true)]
- [DebuggerDisplay("{Status} {ContentType.MediaType}, length: {ResponseStream.Length}")]
- internal class CachedDirectWebResponse : IncomingWebResponse {
- /// <summary>
- /// A seekable, repeatable response stream.
- /// </summary>
- private MemoryStream responseStream;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CachedDirectWebResponse"/> class.
- /// </summary>
- internal CachedDirectWebResponse() {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CachedDirectWebResponse"/> class.
- /// </summary>
- /// <param name="requestUri">The request URI.</param>
- /// <param name="response">The response.</param>
- /// <param name="maximumBytesToRead">The maximum bytes to read.</param>
- internal CachedDirectWebResponse(Uri requestUri, HttpWebResponse response, int maximumBytesToRead)
- : base(requestUri, response) {
- Requires.NotNull(requestUri, "requestUri");
- Requires.NotNull(response, "response");
- this.responseStream = CacheNetworkStreamAndClose(response, maximumBytesToRead);
-
- // BUGBUG: if the response was exactly maximumBytesToRead, we'll incorrectly believe it was truncated.
- this.ResponseTruncated = this.responseStream.Length == maximumBytesToRead;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CachedDirectWebResponse"/> class.
- /// </summary>
- /// <param name="requestUri">The request URI.</param>
- /// <param name="responseUri">The final URI to respond to the request.</param>
- /// <param name="headers">The headers.</param>
- /// <param name="statusCode">The status code.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="contentEncoding">The content encoding.</param>
- /// <param name="responseStream">The response stream.</param>
- internal CachedDirectWebResponse(Uri requestUri, Uri responseUri, WebHeaderCollection headers, HttpStatusCode statusCode, string contentType, string contentEncoding, MemoryStream responseStream)
- : base(requestUri, responseUri, headers, statusCode, contentType, contentEncoding) {
- Requires.NotNull(requestUri, "requestUri");
- Requires.NotNull(responseStream, "responseStream");
- this.responseStream = responseStream;
- }
-
- /// <summary>
- /// Gets a value indicating whether the cached response stream was
- /// truncated to a maximum allowable length.
- /// </summary>
- public bool ResponseTruncated { get; private set; }
-
- /// <summary>
- /// Gets the body of the HTTP response.
- /// </summary>
- public override Stream ResponseStream {
- get { return this.responseStream; }
- }
-
- /// <summary>
- /// Gets or sets the cached response stream.
- /// </summary>
- internal MemoryStream CachedResponseStream {
- get { return this.responseStream; }
- set { this.responseStream = value; }
- }
-
- /// <summary>
- /// Creates a text reader for the response stream.
- /// </summary>
- /// <returns>The text reader, initialized for the proper encoding.</returns>
- public override StreamReader GetResponseReader() {
- this.ResponseStream.Seek(0, SeekOrigin.Begin);
- string contentEncoding = this.Headers[HttpResponseHeader.ContentEncoding];
- Encoding encoding = null;
- if (!string.IsNullOrEmpty(contentEncoding)) {
- try {
- encoding = Encoding.GetEncoding(contentEncoding);
- } catch (ArgumentException ex) {
- Logger.Messaging.ErrorFormat("Encoding.GetEncoding(\"{0}\") threw ArgumentException: {1}", contentEncoding, ex);
- }
- }
-
- return encoding != null ? new StreamReader(this.ResponseStream, encoding) : new StreamReader(this.ResponseStream);
- }
-
- /// <summary>
- /// Gets the body of the response as a string.
- /// </summary>
- /// <returns>The entire body of the response.</returns>
- internal string GetResponseString() {
- if (this.ResponseStream != null) {
- string value = this.GetResponseReader().ReadToEnd();
- this.ResponseStream.Seek(0, SeekOrigin.Begin);
- return value;
- } else {
- return null;
- }
- }
-
- /// <summary>
- /// Gets an offline snapshot version of this instance.
- /// </summary>
- /// <param name="maximumBytesToCache">The maximum bytes from the response stream to cache.</param>
- /// <returns>A snapshot version of this instance.</returns>
- /// <remarks>
- /// If this instance is a <see cref="NetworkDirectWebResponse"/> creating a snapshot
- /// will automatically close and dispose of the underlying response stream.
- /// If this instance is a <see cref="CachedDirectWebResponse"/>, the result will
- /// be the self same instance.
- /// </remarks>
- internal override CachedDirectWebResponse GetSnapshot(int maximumBytesToCache) {
- return this;
- }
-
- /// <summary>
- /// Sets the response to some string, encoded as UTF-8.
- /// </summary>
- /// <param name="body">The string to set the response to.</param>
- internal void SetResponse(string body) {
- if (body == null) {
- this.responseStream = null;
- return;
- }
-
- Encoding encoding = Encoding.UTF8;
- this.Headers[HttpResponseHeader.ContentEncoding] = encoding.HeaderName;
- this.responseStream = new MemoryStream();
- StreamWriter writer = new StreamWriter(this.ResponseStream, encoding);
- writer.Write(body);
- writer.Flush();
- this.ResponseStream.Seek(0, SeekOrigin.Begin);
- }
-
- /// <summary>
- /// Caches the network stream and closes it if it is open.
- /// </summary>
- /// <param name="response">The response whose stream is to be cloned.</param>
- /// <param name="maximumBytesToRead">The maximum bytes to cache.</param>
- /// <returns>The seekable Stream instance that contains a copy of what was returned in the HTTP response.</returns>
- [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Assume(System.Boolean,System.String,System.String)", Justification = "No localization required.")]
- private static MemoryStream CacheNetworkStreamAndClose(HttpWebResponse response, int maximumBytesToRead) {
- Requires.NotNull(response, "response");
- Contract.Ensures(Contract.Result<MemoryStream>() != null);
-
- // Now read and cache the network stream
- Stream networkStream = response.GetResponseStream();
- MemoryStream cachedStream = new MemoryStream(response.ContentLength < 0 ? 4 * 1024 : Math.Min((int)response.ContentLength, maximumBytesToRead));
- try {
- Contract.Assume(networkStream.CanRead, "HttpWebResponse.GetResponseStream() always returns a readable stream."); // CC missing
- Contract.Assume(cachedStream.CanWrite, "This is a MemoryStream -- it's always writable."); // CC missing
- networkStream.CopyTo(cachedStream);
- cachedStream.Seek(0, SeekOrigin.Begin);
-
- networkStream.Dispose();
- response.Close();
-
- return cachedStream;
- } catch {
- cachedStream.Dispose();
- throw;
- }
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs
index f8ac6a1..cc61b25 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs
@@ -17,20 +17,23 @@ namespace DotNetOpenAuth.Messaging {
using System.Linq;
using System.Net;
using System.Net.Cache;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
using System.Net.Mime;
+ using System.Net.Sockets;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading;
+ using System.Threading.Tasks;
using System.Web;
using System.Xml;
using DotNetOpenAuth.Messaging.Reflection;
+ using Validation;
/// <summary>
/// Manages sending direct messages to a remote party and receiving responses.
/// </summary>
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unavoidable.")]
- [ContractVerification(true)]
- [ContractClass(typeof(ChannelContract))]
public abstract class Channel : IDisposable {
/// <summary>
/// The encoding to use when writing out POST entity strings.
@@ -137,31 +140,25 @@ namespace DotNetOpenAuth.Messaging {
private IMessageFactory messageTypeProvider;
/// <summary>
- /// Backing store for the <see cref="CachePolicy"/> property.
- /// </summary>
- private RequestCachePolicy cachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
-
- /// <summary>
/// Backing field for the <see cref="MaximumIndirectMessageUrlLength"/> property.
/// </summary>
private int maximumIndirectMessageUrlLength = Configuration.DotNetOpenAuthSection.Messaging.MaximumIndirectMessageUrlLength;
/// <summary>
- /// Initializes a new instance of the <see cref="Channel"/> class.
+ /// Initializes a new instance of the <see cref="Channel" /> class.
/// </summary>
- /// <param name="messageTypeProvider">
- /// A class prepared to analyze incoming messages and indicate what concrete
- /// message types can deserialize from it.
- /// </param>
- /// <param name="bindingElements">
- /// The binding elements to use in sending and receiving messages.
- /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages.
- /// </param>
- protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingElement[] bindingElements) {
+ /// <param name="messageTypeProvider">A class prepared to analyze incoming messages and indicate what concrete
+ /// message types can deserialize from it.</param>
+ /// <param name="bindingElements">The binding elements to use in sending and receiving messages.
+ /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages.</param>
+ /// <param name="hostFactories">The host factories.</param>
+ protected Channel(IMessageFactory messageTypeProvider, IChannelBindingElement[] bindingElements, IHostFactories hostFactories) {
Requires.NotNull(messageTypeProvider, "messageTypeProvider");
+ Requires.NotNull(bindingElements, "bindingElements");
+ Requires.NotNull(hostFactories, "hostFactories");
this.messageTypeProvider = messageTypeProvider;
- this.WebRequestHandler = new StandardWebRequestHandler();
+ this.HostFactories = hostFactories;
this.XmlDictionaryReaderQuotas = DefaultUntrustedXmlDictionaryReaderQuotas;
this.outgoingBindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
@@ -179,14 +176,9 @@ namespace DotNetOpenAuth.Messaging {
internal event EventHandler<ChannelEventArgs> Sending;
/// <summary>
- /// Gets or sets an instance to a <see cref="IDirectWebRequestHandler"/> that will be used when
- /// submitting HTTP requests and waiting for responses.
+ /// Gets the host factories instance to use.
/// </summary>
- /// <remarks>
- /// This defaults to a straightforward implementation, but can be set
- /// to a mock object for testing purposes.
- /// </remarks>
- public IDirectWebRequestHandler WebRequestHandler { get; set; }
+ public IHostFactories HostFactories { get; private set; }
/// <summary>
/// Gets or sets the maximum allowable size for a 301 Redirect response before we send
@@ -201,7 +193,7 @@ namespace DotNetOpenAuth.Messaging {
}
set {
- Requires.InRange(value >= 500 && value <= 4096, "value");
+ Requires.Range(value >= 500 && value <= 4096, "value");
this.maximumIndirectMessageUrlLength = value;
}
}
@@ -229,13 +221,28 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets or sets the outgoing message filter.
+ /// </summary>
+ /// <value>
+ /// The outgoing message filter.
+ /// </value>
+ internal Action<IProtocolMessage> OutgoingMessageFilter { get; set; }
+
+ /// <summary>
+ /// Gets or sets the incoming message filter.
+ /// </summary>
+ /// <value>
+ /// The incoming message filter.
+ /// </value>
+ internal Action<IProtocolMessage> IncomingMessageFilter { get; set; }
+
+ /// <summary>
/// Gets the binding elements used by this channel, in no particular guaranteed order.
/// </summary>
protected internal ReadOnlyCollection<IChannelBindingElement> BindingElements {
get {
- Contract.Ensures(Contract.Result<ReadOnlyCollection<IChannelBindingElement>>() != null);
var result = this.outgoingBindingElements.AsReadOnly();
- Contract.Assume(result != null); // should be an implicit BCL contract
+ Assumes.True(result != null); // should be an implicit BCL contract
return result;
}
}
@@ -252,8 +259,6 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
protected internal ReadOnlyCollection<IChannelBindingElement> IncomingBindingElements {
get {
- Contract.Ensures(Contract.Result<ReadOnlyCollection<IChannelBindingElement>>().All(be => be.Channel != null));
- Contract.Ensures(Contract.Result<ReadOnlyCollection<IChannelBindingElement>>().All(be => be != null));
return this.incomingBindingElements.AsReadOnly();
}
}
@@ -276,76 +281,25 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Gets or sets the cache policy to use for direct message requests.
- /// </summary>
- /// <value>Default is <see cref="HttpRequestCacheLevel.NoCacheNoStore"/>.</value>
- protected RequestCachePolicy CachePolicy {
- get {
- return this.cachePolicy;
- }
-
- set {
- Requires.NotNull(value, "value");
- this.cachePolicy = value;
- }
- }
-
- /// <summary>
/// Gets or sets the XML dictionary reader quotas.
/// </summary>
/// <value>The XML dictionary reader quotas.</value>
protected virtual XmlDictionaryReaderQuotas XmlDictionaryReaderQuotas { get; set; }
/// <summary>
- /// Sends an indirect message (either a request or response)
- /// or direct message response for transmission to a remote party
- /// and ends execution on the current page or handler.
- /// </summary>
- /// <param name="message">The one-way message to send</param>
- /// <exception cref="ThreadAbortException">Thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void Send(IProtocolMessage message) {
- Requires.ValidState(HttpContext.Current != null, MessagingStrings.CurrentHttpContextRequired);
- Requires.NotNull(message, "message");
- this.PrepareResponse(message).Respond(HttpContext.Current, true);
- }
-
- /// <summary>
- /// Sends an indirect message (either a request or response)
- /// or direct message response for transmission to a remote party
- /// and skips most of the remaining ASP.NET request handling pipeline.
- /// Not safe to call from ASP.NET web forms.
- /// </summary>
- /// <param name="message">The one-way message to send</param>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// This call is not safe to make from an ASP.NET web form (.aspx file or code-behind) because
- /// ASP.NET will render HTML after the protocol message has been sent, which will corrupt the response.
- /// Use the <see cref="Send"/> method instead for web forms.
- /// </remarks>
- public void Respond(IProtocolMessage message) {
- Requires.ValidState(HttpContext.Current != null, MessagingStrings.CurrentHttpContextRequired);
- Requires.NotNull(message, "message");
- this.PrepareResponse(message).Respond();
- }
-
- /// <summary>
/// Prepares an indirect message (either a request or response)
/// or direct message response for transmission to a remote party.
/// </summary>
/// <param name="message">The one-way message to send</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- public OutgoingWebResponse PrepareResponse(IProtocolMessage message) {
+ public async Task<HttpResponseMessage> PrepareResponseAsync(IProtocolMessage message, CancellationToken cancellationToken = default(CancellationToken)) {
Requires.NotNull(message, "message");
- Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
- this.ProcessOutgoingMessage(message);
+ await this.ProcessOutgoingMessageAsync(message, cancellationToken);
Logger.Channel.DebugFormat("Sending message: {0}", message.GetType().Name);
- OutgoingWebResponse result;
+ HttpResponseMessage result;
switch (message.Transport) {
case MessageTransport.Direct:
// This is a response to a direct message.
@@ -374,8 +328,13 @@ namespace DotNetOpenAuth.Messaging {
// Apply caching policy to any response. We want to disable all caching because in auth* protocols,
// caching can be utilized in identity spoofing attacks.
- result.Headers[HttpResponseHeader.CacheControl] = "no-cache, no-store, max-age=0, must-revalidate";
- result.Headers[HttpResponseHeader.Pragma] = "no-cache";
+ result.Headers.CacheControl = new CacheControlHeaderValue {
+ NoCache = true,
+ NoStore = true,
+ MaxAge = TimeSpan.Zero,
+ MustRevalidate = true,
+ };
+ result.Headers.Pragma.Add(new NameValueHeaderValue("no-cache"));
return result;
}
@@ -383,71 +342,26 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Gets the protocol message embedded in the given HTTP request, if present.
/// </summary>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- public IDirectedProtocolMessage ReadFromRequest() {
- return this.ReadFromRequest(this.GetRequestFromContext());
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
- /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
- public bool TryReadFromRequest<TRequest>(out TRequest request)
- where TRequest : class, IProtocolMessage {
- return TryReadFromRequest<TRequest>(this.GetRequestFromContext(), out request);
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
/// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
/// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
- /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// True if the expected message was recognized and deserialized. False otherwise.
+ /// </returns>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current" /> is null.</exception>
/// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
- public bool TryReadFromRequest<TRequest>(HttpRequestBase httpRequest, out TRequest request)
+ public async Task<TRequest> TryReadFromRequestAsync<TRequest>(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
where TRequest : class, IProtocolMessage {
Requires.NotNull(httpRequest, "httpRequest");
- Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn<TRequest>(out request) != null));
- IProtocolMessage untypedRequest = this.ReadFromRequest(httpRequest);
+ IProtocolMessage untypedRequest = await this.ReadFromRequestAsync(httpRequest, cancellationToken);
if (untypedRequest == null) {
- request = null;
- return false;
+ return null;
}
- request = untypedRequest as TRequest;
+ var request = untypedRequest as TRequest;
ErrorUtilities.VerifyProtocol(request != null, MessagingStrings.UnexpectedMessageReceived, typeof(TRequest), untypedRequest.GetType());
-
- return true;
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the current HTTP request.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <returns>The deserialized message. Never null.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TRequest ReadFromRequest<TRequest>()
- where TRequest : class, IProtocolMessage {
- return this.ReadFromRequest<TRequest>(this.GetRequestFromContext());
+ return request;
}
/// <summary>
@@ -455,43 +369,47 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
/// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <returns>The deserialized message. Never null.</returns>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The deserialized message. Never null.
+ /// </returns>
/// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TRequest ReadFromRequest<TRequest>(HttpRequestBase httpRequest)
+ public async Task<TRequest> ReadFromRequestAsync<TRequest>(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
where TRequest : class, IProtocolMessage {
Requires.NotNull(httpRequest, "httpRequest");
- TRequest request;
- if (this.TryReadFromRequest<TRequest>(httpRequest, out request)) {
- return request;
- } else {
- throw ErrorUtilities.ThrowProtocol(MessagingStrings.ExpectedMessageNotReceived, typeof(TRequest));
- }
+
+ TRequest request = await this.TryReadFromRequestAsync<TRequest>(httpRequest, cancellationToken);
+ ErrorUtilities.VerifyProtocol(request != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TRequest));
+ return request;
}
/// <summary>
/// Gets the protocol message that may be embedded in the given HTTP request.
/// </summary>
/// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- public IDirectedProtocolMessage ReadFromRequest(HttpRequestBase httpRequest) {
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The deserialized message, if one is found. Null otherwise.
+ /// </returns>
+ public async Task<IDirectedProtocolMessage> ReadFromRequestAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken) {
Requires.NotNull(httpRequest, "httpRequest");
- if (Logger.Channel.IsInfoEnabled && httpRequest.GetPublicFacingUrl() != null) {
- Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.GetPublicFacingUrl().AbsoluteUri);
+ if (Logger.Channel.IsInfoEnabled && httpRequest.RequestUri != null) {
+ Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.RequestUri.AbsoluteUri);
}
- IDirectedProtocolMessage requestMessage = this.ReadFromRequestCore(httpRequest);
+ IDirectedProtocolMessage requestMessage = await this.ReadFromRequestCoreAsync(httpRequest, cancellationToken);
if (requestMessage != null) {
Logger.Channel.DebugFormat("Incoming request received: {0}", requestMessage.GetType().Name);
var directRequest = requestMessage as IHttpDirectRequest;
if (directRequest != null) {
- foreach (string header in httpRequest.Headers) {
- directRequest.Headers[header] = httpRequest.Headers[header];
+ foreach (var header in httpRequest.Headers) {
+ directRequest.Headers.Add(header.Key, header.Value);
}
}
- this.ProcessIncomingMessage(requestMessage);
+ await this.ProcessIncomingMessageAsync(requestMessage, cancellationToken);
}
return requestMessage;
@@ -502,18 +420,18 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <typeparam name="TResponse">The expected type of the message to be received.</typeparam>
/// <param name="requestMessage">The message to send.</param>
- /// <returns>The remote party's response.</returns>
- /// <exception cref="ProtocolException">
- /// Thrown if no message is recognized in the response
- /// or an unexpected type of message is received.
- /// </exception>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The remote party's response.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown if no message is recognized in the response
+ /// or an unexpected type of message is received.</exception>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TResponse Request<TResponse>(IDirectedProtocolMessage requestMessage)
+ public async Task<TResponse> RequestAsync<TResponse>(IDirectedProtocolMessage requestMessage, CancellationToken cancellationToken)
where TResponse : class, IProtocolMessage {
Requires.NotNull(requestMessage, "requestMessage");
- Contract.Ensures(Contract.Result<TResponse>() != null);
- IProtocolMessage response = this.Request(requestMessage);
+ IProtocolMessage response = await this.RequestAsync(requestMessage, cancellationToken);
ErrorUtilities.VerifyProtocol(response != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TResponse));
var expectedResponse = response as TResponse;
@@ -526,18 +444,21 @@ namespace DotNetOpenAuth.Messaging {
/// Sends a direct message to a remote party and waits for the response.
/// </summary>
/// <param name="requestMessage">The message to send.</param>
- /// <returns>The remote party's response. Guaranteed to never be null.</returns>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The remote party's response. Guaranteed to never be null.
+ /// </returns>
/// <exception cref="ProtocolException">Thrown if the response does not include a protocol message.</exception>
- public IProtocolMessage Request(IDirectedProtocolMessage requestMessage) {
+ public async Task<IProtocolMessage> RequestAsync(IDirectedProtocolMessage requestMessage, CancellationToken cancellationToken) {
Requires.NotNull(requestMessage, "requestMessage");
- this.ProcessOutgoingMessage(requestMessage);
+ await this.ProcessOutgoingMessageAsync(requestMessage, cancellationToken);
Logger.Channel.DebugFormat("Sending {0} request.", requestMessage.GetType().Name);
- var responseMessage = this.RequestCore(requestMessage);
+ var responseMessage = await this.RequestCoreAsync(requestMessage, cancellationToken);
ErrorUtilities.VerifyProtocol(responseMessage != null, MessagingStrings.ExpectedMessageNotReceived, typeof(IProtocolMessage).Name);
Logger.Channel.DebugFormat("Received {0} response.", responseMessage.GetType().Name);
- this.ProcessIncomingMessage(responseMessage);
+ await this.ProcessIncomingMessageAsync(responseMessage, cancellationToken);
return responseMessage;
}
@@ -558,12 +479,14 @@ namespace DotNetOpenAuth.Messaging {
/// Verifies the integrity and applicability of an incoming message.
/// </summary>
/// <param name="message">The message just received.</param>
- /// <exception cref="ProtocolException">
- /// Thrown when the message is somehow invalid.
- /// This can be due to tampering, replay attack or expiration, among other things.
- /// </exception>
- internal void ProcessIncomingMessageTestHook(IProtocolMessage message) {
- this.ProcessIncomingMessage(message);
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A task that completes with the asynchronous operation.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown when the message is somehow invalid.
+ /// This can be due to tampering, replay attack or expiration, among other things.</exception>
+ internal Task ProcessIncomingMessageTestHookAsync(IProtocolMessage message, CancellationToken cancellationToken) {
+ return this.ProcessIncomingMessageAsync(message, cancellationToken);
}
/// <summary>
@@ -572,10 +495,10 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The message to send.</param>
/// <returns>The <see cref="HttpWebRequest"/> prepared to send the request.</returns>
/// <remarks>
- /// This method must be overridden by a derived class, unless the <see cref="RequestCore"/> method
+ /// This method must be overridden by a derived class, unless the <see cref="RequestCoreAsync"/> method
/// is overridden and does not require this method.
/// </remarks>
- internal HttpWebRequest CreateHttpRequestTestHook(IDirectedProtocolMessage request) {
+ internal HttpRequestMessage CreateHttpRequestTestHook(IDirectedProtocolMessage request) {
return this.CreateHttpRequest(request);
}
@@ -588,7 +511,7 @@ namespace DotNetOpenAuth.Messaging {
/// <remarks>
/// This method implements spec OAuth V1.0 section 5.3.
/// </remarks>
- internal OutgoingWebResponse PrepareDirectResponseTestHook(IProtocolMessage response) {
+ internal HttpResponseMessage PrepareDirectResponseTestHook(IProtocolMessage response) {
return this.PrepareDirectResponse(response);
}
@@ -596,22 +519,44 @@ namespace DotNetOpenAuth.Messaging {
/// Gets the protocol message that may be in the given HTTP response.
/// </summary>
/// <param name="response">The response that is anticipated to contain an protocol message.</param>
- /// <returns>The deserialized message parts, if found. Null otherwise.</returns>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The deserialized message parts, if found. Null otherwise.
+ /// </returns>
/// <exception cref="ProtocolException">Thrown when the response is not valid.</exception>
- internal IDictionary<string, string> ReadFromResponseCoreTestHook(IncomingWebResponse response) {
- return this.ReadFromResponseCore(response);
+ internal Task<IDictionary<string, string>> ReadFromResponseCoreAsyncTestHook(HttpResponseMessage response, CancellationToken cancellationToken) {
+ return this.ReadFromResponseCoreAsync(response, cancellationToken);
}
- /// <remarks>
- /// This method should NOT be called by derived types
- /// except when sending ONE WAY request messages.
- /// </remarks>
/// <summary>
/// Prepares a message for transmit by applying signatures, nonces, etc.
/// </summary>
/// <param name="message">The message to prepare for sending.</param>
- internal void ProcessOutgoingMessageTestHook(IProtocolMessage message) {
- this.ProcessOutgoingMessage(message);
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A task that completes with the asynchronous operation.
+ /// </returns>
+ /// <remarks>
+ /// This method should NOT be called by derived types
+ /// except when sending ONE WAY request messages.
+ /// </remarks>
+ internal Task ProcessOutgoingMessageTestHookAsync(IProtocolMessage message, CancellationToken cancellationToken = default(CancellationToken)) {
+ return this.ProcessOutgoingMessageAsync(message, cancellationToken);
+ }
+
+ /// <summary>
+ /// Parses the URL encoded form content.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>A sequence of key=value pairs found in the request's entity; or an empty sequence if none are found.</returns>
+ protected internal static async Task<IEnumerable<KeyValuePair<string, string>>> ParseUrlEncodedFormContentAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
+ if (request.Content != null && request.Content.Headers.ContentType != null
+ && request.Content.Headers.ContentType.MediaType.Equals(HttpFormUrlEncoded)) {
+ return HttpUtility.ParseQueryString(await request.Content.ReadAsStringAsync()).AsKeyValuePairs();
+ }
+
+ return Enumerable.Empty<KeyValuePair<string, string>>();
}
/// <summary>
@@ -620,7 +565,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>An HttpContextBase instance.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Allocates memory")]
protected internal virtual HttpContextBase GetHttpContext() {
- Requires.ValidState(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ RequiresEx.ValidState(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
return new HttpContextWrapper(HttpContext.Current);
}
@@ -634,28 +579,51 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly call should not be a property.")]
protected internal virtual HttpRequestBase GetRequestFromContext() {
- Requires.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
- Contract.Ensures(Contract.Result<HttpRequestBase>() != null);
+ RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
- Contract.Assume(HttpContext.Current.Request.Url != null);
- Contract.Assume(HttpContext.Current.Request.RawUrl != null);
+ Assumes.True(HttpContext.Current.Request.Url != null);
+ Assumes.True(HttpContext.Current.Request.RawUrl != null);
return new HttpRequestWrapper(HttpContext.Current.Request);
}
/// <summary>
+ /// Adds just the binary data part of a message to a multipart form content object.
+ /// </summary>
+ /// <param name="requestMessageWithBinaryData">The request message with binary data.</param>
+ /// <returns>The initialized HttpContent.</returns>
+ protected static MultipartFormDataContent InitializeMultipartFormDataContent(IMessageWithBinaryData requestMessageWithBinaryData) {
+ Requires.NotNull(requestMessageWithBinaryData, "requestMessageWithBinaryData");
+
+ var content = new MultipartFormDataContent();
+ foreach (var part in requestMessageWithBinaryData.BinaryData) {
+ if (string.IsNullOrEmpty(part.Name)) {
+ content.Add(part.Content);
+ } else if (string.IsNullOrEmpty(part.FileName)) {
+ content.Add(part.Content, part.Name);
+ } else {
+ content.Add(part.Content, part.Name, part.FileName);
+ }
+ }
+
+ return content;
+ }
+
+ /// <summary>
/// Checks whether a given HTTP method is expected to include an entity body in its request.
/// </summary>
/// <param name="httpMethod">The HTTP method.</param>
/// <returns><c>true</c> if the HTTP method is supposed to have an entity; <c>false</c> otherwise.</returns>
- protected static bool HttpMethodHasEntity(string httpMethod) {
- if (string.Equals(httpMethod, "GET", StringComparison.Ordinal) ||
- string.Equals(httpMethod, "HEAD", StringComparison.Ordinal) ||
- string.Equals(httpMethod, "DELETE", StringComparison.Ordinal) ||
- string.Equals(httpMethod, "OPTIONS", StringComparison.Ordinal)) {
+ protected static bool HttpMethodHasEntity(HttpMethod httpMethod) {
+ Requires.NotNull(httpMethod, "httpMethod");
+
+ if (httpMethod == HttpMethod.Get ||
+ httpMethod == HttpMethod.Head ||
+ httpMethod == HttpMethod.Delete ||
+ httpMethod == HttpMethod.Options) {
return false;
- } else if (string.Equals(httpMethod, "POST", StringComparison.Ordinal) ||
- string.Equals(httpMethod, "PUT", StringComparison.Ordinal) ||
- string.Equals(httpMethod, "PATCH", StringComparison.Ordinal)) {
+ } else if (httpMethod == HttpMethod.Post ||
+ httpMethod == HttpMethod.Put ||
+ string.Equals(httpMethod.Method, "PATCH", StringComparison.Ordinal)) {
return true;
} else {
throw ErrorUtilities.ThrowArgumentNamed("httpMethod", MessagingStrings.UnsupportedHttpVerb, httpMethod);
@@ -667,11 +635,11 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="message">The message.</param>
/// <param name="response">The HTTP response.</param>
- protected static void ApplyMessageTemplate(IMessage message, OutgoingWebResponse response) {
+ protected static void ApplyMessageTemplate(IMessage message, HttpResponseMessage response) {
Requires.NotNull(message, "message");
var httpMessage = message as IHttpDirectResponse;
if (httpMessage != null) {
- response.Status = httpMessage.HttpStatusCode;
+ response.StatusCode = httpMessage.HttpStatusCode;
foreach (string headerName in httpMessage.Headers) {
response.Headers.Add(headerName, httpMessage.Headers[headerName]);
}
@@ -707,63 +675,63 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Gets the direct response of a direct HTTP request.
- /// </summary>
- /// <param name="webRequest">The web request.</param>
- /// <returns>The response to the web request.</returns>
- /// <exception cref="ProtocolException">Thrown on network or protocol errors.</exception>
- protected virtual IncomingWebResponse GetDirectResponse(HttpWebRequest webRequest) {
- Requires.NotNull(webRequest, "webRequest");
- return this.WebRequestHandler.GetResponse(webRequest);
- }
-
- /// <summary>
/// Submits a direct request message to some remote party and blocks waiting for an immediately reply.
/// </summary>
/// <param name="request">The request message.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The response message, or null if the response did not carry a message.</returns>
/// <remarks>
/// Typically a deriving channel will override <see cref="CreateHttpRequest"/> to customize this method's
/// behavior. However in non-HTTP frameworks, such as unit test mocks, it may be appropriate to override
/// this method to eliminate all use of an HTTP transport.
/// </remarks>
- protected virtual IProtocolMessage RequestCore(IDirectedProtocolMessage request) {
+ protected virtual async Task<IProtocolMessage> RequestCoreAsync(IDirectedProtocolMessage request, CancellationToken cancellationToken) {
Requires.NotNull(request, "request");
- Requires.True(request.Recipient != null, "request", MessagingStrings.DirectedMessageMissingRecipient);
+ Requires.That(request.Recipient != null, "request", MessagingStrings.DirectedMessageMissingRecipient);
- HttpWebRequest webRequest = this.CreateHttpRequest(request);
+ if (this.OutgoingMessageFilter != null) {
+ this.OutgoingMessageFilter(request);
+ }
+
+ var webRequest = this.CreateHttpRequest(request);
var directRequest = request as IHttpDirectRequest;
if (directRequest != null) {
- foreach (string header in directRequest.Headers) {
- webRequest.Headers[header] = directRequest.Headers[header];
+ foreach (var header in directRequest.Headers) {
+ webRequest.Headers.Add(header.Key, header.Value);
}
}
- IDictionary<string, string> responseFields;
- IDirectResponseProtocolMessage responseMessage;
-
- using (IncomingWebResponse response = this.GetDirectResponse(webRequest)) {
- if (response.ResponseStream == null) {
- return null;
- }
-
- responseFields = this.ReadFromResponseCore(response);
- if (responseFields == null) {
- return null;
- }
-
- responseMessage = this.MessageFactory.GetNewResponseMessage(request, responseFields);
- if (responseMessage == null) {
- return null;
+ try {
+ using (var httpClient = this.HostFactories.CreateHttpClient()) {
+ using (var response = await httpClient.SendAsync(webRequest, cancellationToken)) {
+ if (response.Content != null) {
+ var responseFields = await this.ReadFromResponseCoreAsync(response, cancellationToken);
+ if (responseFields != null) {
+ var responseMessage = this.MessageFactory.GetNewResponseMessage(request, responseFields);
+ if (responseMessage != null) {
+ this.OnReceivingDirectResponse(response, responseMessage);
+
+ var messageAccessor = this.MessageDescriptions.GetAccessor(responseMessage);
+ messageAccessor.Deserialize(responseFields);
+
+ return responseMessage;
+ }
+ }
+ }
+
+ if (!response.IsSuccessStatusCode) {
+ var errorContent = (response.Content != null) ? await response.Content.ReadAsStringAsync() : null;
+ Logger.Http.ErrorFormat(
+ "Error received in HTTP response: {0} {1}\n{2}", (int)response.StatusCode, response.ReasonPhrase, errorContent);
+ response.EnsureSuccessStatusCode(); // throw so we can wrap it in our catch block.
+ }
+
+ return null;
+ }
}
-
- this.OnReceivingDirectResponse(response, responseMessage);
+ } catch (HttpRequestException requestException) {
+ throw ErrorUtilities.Wrap(requestException, "Error sending HTTP request or receiving response.");
}
-
- var messageAccessor = this.MessageDescriptions.GetAccessor(responseMessage);
- messageAccessor.Deserialize(responseFields);
-
- return responseMessage;
}
/// <summary>
@@ -771,24 +739,27 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="response">The HTTP direct response.</param>
/// <param name="message">The newly instantiated message, prior to deserialization.</param>
- protected virtual void OnReceivingDirectResponse(IncomingWebResponse response, IDirectResponseProtocolMessage message) {
+ protected virtual void OnReceivingDirectResponse(HttpResponseMessage response, IDirectResponseProtocolMessage message) {
}
/// <summary>
/// Gets the protocol message that may be embedded in the given HTTP request.
/// </summary>
/// <param name="request">The request to search for an embedded message.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- protected virtual IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request) {
+ protected virtual async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
Requires.NotNull(request, "request");
- Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.GetPublicFacingUrl().AbsoluteUri);
+ Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.Method, request.RequestUri.AbsoluteUri);
+
+ var fields = new Dictionary<string, string>();
// Search Form data first, and if nothing is there search the QueryString
- Contract.Assume(request.Form != null && request.GetQueryStringBeforeRewriting() != null);
- var fields = request.Form.ToDictionary();
- if (fields.Count == 0 && request.HttpMethod != "POST") { // OpenID 2.0 section 4.1.2
- fields = request.GetQueryStringBeforeRewriting().ToDictionary();
+ fields.AddRange(await ParseUrlEncodedFormContentAsync(request, cancellationToken));
+
+ if (fields.Count == 0 && request.Method.Method != "POST") { // OpenID 2.0 section 4.1.2
+ fields.AddRange(HttpUtility.ParseQueryString(request.RequestUri.Query).AsKeyValuePairs());
}
MessageReceivingEndpoint recipient;
@@ -835,18 +806,17 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="message">The message to send.</param>
/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- protected virtual OutgoingWebResponse PrepareIndirectResponse(IDirectedProtocolMessage message) {
+ protected virtual HttpResponseMessage PrepareIndirectResponse(IDirectedProtocolMessage message) {
Requires.NotNull(message, "message");
- Requires.True(message.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient);
- Requires.True((message.HttpMethods & (HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest)) != 0, "message");
- Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
+ Requires.That(message.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient);
+ Requires.That((message.HttpMethods & (HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest)) != 0, "message", "GET or POST expected.");
- Contract.Assert(message != null && message.Recipient != null);
+ Assumes.True(message != null && message.Recipient != null);
var messageAccessor = this.MessageDescriptions.GetAccessor(message);
- Contract.Assert(message != null && message.Recipient != null);
+ Assumes.True(message != null && message.Recipient != null);
var fields = messageAccessor.Serialize();
- OutgoingWebResponse response = null;
+ HttpResponseMessage response = null;
bool tooLargeForGet = false;
if ((message.HttpMethods & HttpDeliveryMethods.GetRequest) == HttpDeliveryMethods.GetRequest) {
bool payloadInFragment = false;
@@ -858,7 +828,7 @@ namespace DotNetOpenAuth.Messaging {
// First try creating a 301 redirect, and fallback to a form POST
// if the message is too big.
response = this.Create301RedirectResponse(message, fields, payloadInFragment);
- tooLargeForGet = response.Headers[HttpResponseHeader.Location].Length > this.MaximumIndirectMessageUrlLength;
+ tooLargeForGet = response.Headers.Location.PathAndQuery.Length > this.MaximumIndirectMessageUrlLength;
}
// Make sure that if the message is too large for GET that POST is allowed.
@@ -885,15 +855,13 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="payloadInFragment">if set to <c>true</c> the redirect will contain the message payload in the #fragment portion of the URL rather than the ?querystring.</param>
/// <returns>The encoded HTTP response.</returns>
[Pure]
- protected virtual OutgoingWebResponse Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields, bool payloadInFragment = false) {
+ protected virtual HttpResponseMessage Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields, bool payloadInFragment = false) {
Requires.NotNull(message, "message");
- Requires.True(message.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient);
+ Requires.That(message.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient);
Requires.NotNull(fields, "fields");
- Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
// As part of this redirect, we include an HTML body in order to get passed some proxy filters
// such as WebSense.
- WebHeaderCollection headers = new WebHeaderCollection();
UriBuilder builder = new UriBuilder(message.Recipient);
if (payloadInFragment) {
builder.AppendFragmentArgs(fields);
@@ -901,16 +869,14 @@ namespace DotNetOpenAuth.Messaging {
builder.AppendQueryArgs(fields);
}
- headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri);
- headers.Add(HttpResponseHeader.ContentType, "text/html; charset=utf-8");
Logger.Http.DebugFormat("Redirecting to {0}", builder.Uri.AbsoluteUri);
- OutgoingWebResponse response = new OutgoingWebResponse {
- Status = HttpStatusCode.Redirect,
- Headers = headers,
- Body = string.Format(CultureInfo.InvariantCulture, RedirectResponseBodyFormat, builder.Uri.AbsoluteUri),
- OriginalMessage = message
+ HttpResponseMessage response = new HttpResponseMessageWithOriginal(message) {
+ StatusCode = HttpStatusCode.Redirect,
+ Content = new StringContent(string.Format(CultureInfo.InvariantCulture, RedirectResponseBodyFormat, builder.Uri.AbsoluteUri)),
};
+ response.Headers.Location = builder.Uri;
+ response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html") { CharSet = "utf-8" };
return response;
}
@@ -922,14 +888,11 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="fields">The pre-serialized fields from the message.</param>
/// <returns>The encoded HTTP response.</returns>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")]
- protected virtual OutgoingWebResponse CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
+ protected virtual HttpResponseMessage CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
Requires.NotNull(message, "message");
- Requires.True(message.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient);
+ Requires.That(message.Recipient != null, "message", MessagingStrings.DirectedMessageMissingRecipient);
Requires.NotNull(fields, "fields");
- Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
- WebHeaderCollection headers = new WebHeaderCollection();
- headers.Add(HttpResponseHeader.ContentType, "text/html");
using (StringWriter bodyWriter = new StringWriter(CultureInfo.InvariantCulture)) {
StringBuilder hiddenFields = new StringBuilder();
foreach (var field in fields) {
@@ -943,12 +906,11 @@ namespace DotNetOpenAuth.Messaging {
HttpUtility.HtmlEncode(message.Recipient.AbsoluteUri),
hiddenFields);
bodyWriter.Flush();
- OutgoingWebResponse response = new OutgoingWebResponse {
- Status = HttpStatusCode.OK,
- Headers = headers,
- Body = bodyWriter.ToString(),
- OriginalMessage = message
+ HttpResponseMessage response = new HttpResponseMessageWithOriginal(message) {
+ StatusCode = HttpStatusCode.OK,
+ Content = new StringContent(bodyWriter.ToString()),
};
+ response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
@@ -958,9 +920,12 @@ namespace DotNetOpenAuth.Messaging {
/// Gets the protocol message that may be in the given HTTP response.
/// </summary>
/// <param name="response">The response that is anticipated to contain an protocol message.</param>
- /// <returns>The deserialized message parts, if found. Null otherwise.</returns>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The deserialized message parts, if found. Null otherwise.
+ /// </returns>
/// <exception cref="ProtocolException">Thrown when the response is not valid.</exception>
- protected abstract IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response);
+ protected abstract Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken);
/// <summary>
/// Prepares an HTTP request that carries a given message.
@@ -968,13 +933,12 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The message to send.</param>
/// <returns>The <see cref="HttpWebRequest"/> prepared to send the request.</returns>
/// <remarks>
- /// This method must be overridden by a derived class, unless the <see cref="Channel.RequestCore"/> method
+ /// This method must be overridden by a derived class, unless the <see cref="Channel.RequestCoreAsync"/> method
/// is overridden and does not require this method.
/// </remarks>
- protected virtual HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) {
+ protected virtual HttpRequestMessage CreateHttpRequest(IDirectedProtocolMessage request) {
Requires.NotNull(request, "request");
- Requires.True(request.Recipient != null, "request", MessagingStrings.DirectedMessageMissingRecipient);
- Contract.Ensures(Contract.Result<HttpWebRequest>() != null);
+ Requires.That(request.Recipient != null, "request", MessagingStrings.DirectedMessageMissingRecipient);
throw new NotImplementedException();
}
@@ -987,7 +951,7 @@ namespace DotNetOpenAuth.Messaging {
/// <remarks>
/// This method implements spec OAuth V1.0 section 5.3.
/// </remarks>
- protected abstract OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response);
+ protected abstract HttpResponseMessage PrepareDirectResponse(IProtocolMessage response);
/// <summary>
/// Serializes the given message as a JSON string.
@@ -1021,11 +985,16 @@ namespace DotNetOpenAuth.Messaging {
/// Prepares a message for transmit by applying signatures, nonces, etc.
/// </summary>
/// <param name="message">The message to prepare for sending.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A task that completes with the asynchronous operation.
+ /// </returns>
+ /// <exception cref="UnprotectedMessageException">Thrown if the message does not have the minimal required protections applied.</exception>
/// <remarks>
/// This method should NOT be called by derived types
/// except when sending ONE WAY request messages.
/// </remarks>
- protected void ProcessOutgoingMessage(IProtocolMessage message) {
+ protected async Task ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) {
Requires.NotNull(message, "message");
Logger.Channel.DebugFormat("Preparing to send {0} ({1}) message.", message.GetType().Name, message.Version);
@@ -1039,8 +1008,8 @@ namespace DotNetOpenAuth.Messaging {
MessageProtections appliedProtection = MessageProtections.None;
foreach (IChannelBindingElement bindingElement in this.outgoingBindingElements) {
- Contract.Assume(bindingElement.Channel != null);
- MessageProtections? elementProtection = bindingElement.ProcessOutgoingMessage(message);
+ Assumes.True(bindingElement.Channel != null);
+ MessageProtections? elementProtection = await bindingElement.ProcessOutgoingMessageAsync(message, cancellationToken);
if (elementProtection.HasValue) {
Logger.Bindings.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
@@ -1061,6 +1030,10 @@ namespace DotNetOpenAuth.Messaging {
this.EnsureValidMessageParts(message);
message.EnsureValidMessage();
+ if (this.OutgoingMessageFilter != null) {
+ this.OutgoingMessageFilter(message);
+ }
+
if (Logger.Channel.IsInfoEnabled) {
var directedMessage = message as IDirectedProtocolMessage;
string recipient = (directedMessage != null && directedMessage.Recipient != null) ? directedMessage.Recipient.AbsoluteUri : "<response>";
@@ -1084,16 +1057,16 @@ namespace DotNetOpenAuth.Messaging {
/// This method is simply a standard HTTP Get request with the message parts serialized to the query string.
/// This method satisfies OAuth 1.0 section 5.2, item #3.
/// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
+ protected virtual HttpRequestMessage InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
Requires.NotNull(requestMessage, "requestMessage");
- Requires.True(requestMessage.Recipient != null, "requestMessage", MessagingStrings.DirectedMessageMissingRecipient);
+ Requires.That(requestMessage.Recipient != null, "requestMessage", MessagingStrings.DirectedMessageMissingRecipient);
var messageAccessor = this.MessageDescriptions.GetAccessor(requestMessage);
var fields = messageAccessor.Serialize();
UriBuilder builder = new UriBuilder(requestMessage.Recipient);
MessagingUtilities.AppendQueryArgs(builder, fields);
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
+ var httpRequest = new HttpRequestMessage(HttpMethod.Get, builder.Uri);
this.PrepareHttpWebRequest(httpRequest);
return httpRequest;
@@ -1108,12 +1081,12 @@ namespace DotNetOpenAuth.Messaging {
/// This method is simply a standard HTTP HEAD request with the message parts serialized to the query string.
/// This method satisfies OAuth 1.0 section 5.2, item #3.
/// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsHead(IDirectedProtocolMessage requestMessage) {
+ protected virtual HttpRequestMessage InitializeRequestAsHead(IDirectedProtocolMessage requestMessage) {
Requires.NotNull(requestMessage, "requestMessage");
- Requires.True(requestMessage.Recipient != null, "requestMessage", MessagingStrings.DirectedMessageMissingRecipient);
+ Requires.That(requestMessage.Recipient != null, "requestMessage", MessagingStrings.DirectedMessageMissingRecipient);
- HttpWebRequest request = this.InitializeRequestAsGet(requestMessage);
- request.Method = "HEAD";
+ var request = this.InitializeRequestAsGet(requestMessage);
+ request.Method = HttpMethod.Head;
return request;
}
@@ -1127,28 +1100,28 @@ namespace DotNetOpenAuth.Messaging {
/// with the application/x-www-form-urlencoded content type
/// This method satisfies OAuth 1.0 section 5.2, item #2 and OpenID 2.0 section 4.1.2.
/// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
+ protected virtual HttpRequestMessage InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
Requires.NotNull(requestMessage, "requestMessage");
- Contract.Ensures(Contract.Result<HttpWebRequest>() != null);
var messageAccessor = this.MessageDescriptions.GetAccessor(requestMessage);
var fields = messageAccessor.Serialize();
- var httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
+ var httpRequest = new HttpRequestMessage(HttpMethod.Post, requestMessage.Recipient);
this.PrepareHttpWebRequest(httpRequest);
- httpRequest.CachePolicy = this.CachePolicy;
- httpRequest.Method = "POST";
var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData;
if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) {
- var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData);
+ var content = InitializeMultipartFormDataContent(requestMessageWithBinaryData);
// When sending multi-part, all data gets send as multi-part -- even the non-binary data.
- multiPartFields.AddRange(fields.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value)));
- this.SendParametersInEntityAsMultipart(httpRequest, multiPartFields);
+ foreach (var field in fields) {
+ content.Add(new StringContent(field.Value), field.Key);
+ }
+
+ httpRequest.Content = content;
} else {
ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart);
- this.SendParametersInEntity(httpRequest, fields);
+ httpRequest.Content = new FormUrlEncodedContent(fields);
}
return httpRequest;
@@ -1162,12 +1135,11 @@ namespace DotNetOpenAuth.Messaging {
/// <remarks>
/// This method is simply a standard HTTP PUT request with the message parts serialized to the query string.
/// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsPut(IDirectedProtocolMessage requestMessage) {
+ protected virtual HttpRequestMessage InitializeRequestAsPut(IDirectedProtocolMessage requestMessage) {
Requires.NotNull(requestMessage, "requestMessage");
- Contract.Ensures(Contract.Result<HttpWebRequest>() != null);
- HttpWebRequest request = this.InitializeRequestAsGet(requestMessage);
- request.Method = "PUT";
+ var request = this.InitializeRequestAsGet(requestMessage);
+ request.Method = HttpMethod.Put;
return request;
}
@@ -1179,67 +1151,26 @@ namespace DotNetOpenAuth.Messaging {
/// <remarks>
/// This method is simply a standard HTTP DELETE request with the message parts serialized to the query string.
/// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsDelete(IDirectedProtocolMessage requestMessage) {
+ protected virtual HttpRequestMessage InitializeRequestAsDelete(IDirectedProtocolMessage requestMessage) {
Requires.NotNull(requestMessage, "requestMessage");
- Contract.Ensures(Contract.Result<HttpWebRequest>() != null);
- HttpWebRequest request = this.InitializeRequestAsGet(requestMessage);
- request.Method = "DELETE";
+ var request = this.InitializeRequestAsGet(requestMessage);
+ request.Method = HttpMethod.Delete;
return request;
}
/// <summary>
- /// Sends the given parameters in the entity stream of an HTTP request.
- /// </summary>
- /// <param name="httpRequest">The HTTP request.</param>
- /// <param name="fields">The parameters to send.</param>
- /// <remarks>
- /// This method calls <see cref="HttpWebRequest.GetRequestStream()"/> and closes
- /// the request stream, but does not call <see cref="HttpWebRequest.GetResponse"/>.
- /// </remarks>
- protected void SendParametersInEntity(HttpWebRequest httpRequest, IDictionary<string, string> fields) {
- Requires.NotNull(httpRequest, "httpRequest");
- Requires.NotNull(fields, "fields");
-
- string requestBody = MessagingUtilities.CreateQueryString(fields);
- byte[] requestBytes = PostEntityEncoding.GetBytes(requestBody);
- httpRequest.ContentType = HttpFormUrlEncodedContentType.ToString();
- httpRequest.ContentLength = requestBytes.Length;
- Stream requestStream = this.WebRequestHandler.GetRequestStream(httpRequest);
- try {
- requestStream.Write(requestBytes, 0, requestBytes.Length);
- } finally {
- // We need to be sure to close the request stream...
- // unless it is a MemoryStream, which is a clue that we're in
- // a mock stream situation and closing it would preclude reading it later.
- if (!(requestStream is MemoryStream)) {
- requestStream.Dispose();
- }
- }
- }
-
- /// <summary>
- /// Sends the given parameters in the entity stream of an HTTP request in multi-part format.
- /// </summary>
- /// <param name="httpRequest">The HTTP request.</param>
- /// <param name="fields">The parameters to send.</param>
- /// <remarks>
- /// This method calls <see cref="HttpWebRequest.GetRequestStream()"/> and closes
- /// the request stream, but does not call <see cref="HttpWebRequest.GetResponse"/>.
- /// </remarks>
- protected void SendParametersInEntityAsMultipart(HttpWebRequest httpRequest, IEnumerable<MultipartPostPart> fields) {
- httpRequest.PostMultipartNoGetResponse(this.WebRequestHandler, fields);
- }
-
- /// <summary>
/// Verifies the integrity and applicability of an incoming message.
/// </summary>
/// <param name="message">The message just received.</param>
- /// <exception cref="ProtocolException">
- /// Thrown when the message is somehow invalid.
- /// This can be due to tampering, replay attack or expiration, among other things.
- /// </exception>
- protected virtual void ProcessIncomingMessage(IProtocolMessage message) {
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A task that completes with the asynchronous operation.
+ /// </returns>
+ /// <exception cref="UnprotectedMessageException">Thrown if the message does not have the minimal required protections applied.</exception>
+ /// <exception cref="ProtocolException">Thrown when the message is somehow invalid.
+ /// This can be due to tampering, replay attack or expiration, among other things.</exception>
+ protected virtual async Task ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) {
Requires.NotNull(message, "message");
if (Logger.Channel.IsInfoEnabled) {
@@ -1252,10 +1183,14 @@ namespace DotNetOpenAuth.Messaging {
messageAccessor.ToStringDeferred());
}
+ if (this.IncomingMessageFilter != null) {
+ this.IncomingMessageFilter(message);
+ }
+
MessageProtections appliedProtection = MessageProtections.None;
foreach (IChannelBindingElement bindingElement in this.IncomingBindingElements) {
- Contract.Assume(bindingElement.Channel != null); // CC bug: this.IncomingBindingElements ensures this... why must we assume it here?
- MessageProtections? elementProtection = bindingElement.ProcessIncomingMessage(message);
+ Assumes.True(bindingElement.Channel != null); // CC bug: this.IncomingBindingElements ensures this... why must we assume it here?
+ MessageProtections? elementProtection = await bindingElement.ProcessIncomingMessageAsync(message, cancellationToken);
if (elementProtection.HasValue) {
Logger.Bindings.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
@@ -1316,7 +1251,7 @@ namespace DotNetOpenAuth.Messaging {
/// Performs additional processing on an outgoing web request before it is sent to the remote server.
/// </summary>
/// <param name="request">The request.</param>
- protected virtual void PrepareHttpWebRequest(HttpWebRequest request) {
+ protected virtual void PrepareHttpWebRequest(HttpRequestMessage request) {
Requires.NotNull(request, "request");
}
@@ -1350,8 +1285,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The properly ordered list of elements.</returns>
/// <exception cref="ProtocolException">Thrown when the binding elements are incomplete or inconsistent with each other.</exception>
private static IEnumerable<IChannelBindingElement> ValidateAndPrepareBindingElements(IEnumerable<IChannelBindingElement> elements) {
- Requires.NullOrWithNoNullElements(elements, "elements");
- Contract.Ensures(Contract.Result<IEnumerable<IChannelBindingElement>>() != null);
+ Requires.NullOrNotNullElements(elements, "elements");
if (elements == null) {
return new IChannelBindingElement[0];
}
@@ -1402,18 +1336,6 @@ namespace DotNetOpenAuth.Messaging {
return -((int)protection1).CompareTo((int)protection2); // descending flag ordinal order
}
-#if CONTRACTS_FULL
- /// <summary>
- /// Verifies conditions that should be true for any valid state of this object.
- /// </summary>
- [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Called by code contracts.")]
- [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
- [ContractInvariantMethod]
- private void ObjectInvariant() {
- Contract.Invariant(this.MessageDescriptions != null);
- }
-#endif
-
/// <summary>
/// Verifies that all required message parts are initialized to values
/// prior to sending the message to a remote party.
diff --git a/src/DotNetOpenAuth.Core/Messaging/ChannelContract.cs b/src/DotNetOpenAuth.Core/Messaging/ChannelContract.cs
deleted file mode 100644
index b48d45b..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/ChannelContract.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="ChannelContract.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
-
- /// <summary>
- /// Code contract for the <see cref="Channel"/> class.
- /// </summary>
- [ContractClassFor(typeof(Channel))]
- internal abstract class ChannelContract : Channel {
- /// <summary>
- /// Prevents a default instance of the ChannelContract class from being created.
- /// </summary>
- private ChannelContract()
- : base(null, null) {
- }
-
- /// <summary>
- /// Gets the protocol message that may be in the given HTTP response.
- /// </summary>
- /// <param name="response">The response that is anticipated to contain an protocol message.</param>
- /// <returns>
- /// The deserialized message parts, if found. Null otherwise.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception>
- protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) {
- Requires.NotNull(response, "response");
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Queues a message for sending in the response stream where the fields
- /// are sent in the response stream in querystring style.
- /// </summary>
- /// <param name="response">The message to send as a response.</param>
- /// <returns>
- /// The pending user agent redirect based message to be sent as an HttpResponse.
- /// </returns>
- /// <remarks>
- /// This method implements spec V1.0 section 5.3.
- /// </remarks>
- protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) {
- Requires.NotNull(response, "response");
- Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
- throw new NotImplementedException();
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/ChannelEventArgs.cs b/src/DotNetOpenAuth.Core/Messaging/ChannelEventArgs.cs
index f3ebc04..5c69e4d 100644
--- a/src/DotNetOpenAuth.Core/Messaging/ChannelEventArgs.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/ChannelEventArgs.cs
@@ -6,7 +6,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// The data packet sent with Channel events.
diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBag.cs b/src/DotNetOpenAuth.Core/Messaging/DataBag.cs
index 0800840..8469676 100644
--- a/src/DotNetOpenAuth.Core/Messaging/DataBag.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/DataBag.cs
@@ -8,7 +8,7 @@ namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// A collection of message parts that will be serialized into a single string,
@@ -42,7 +42,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="version">The DataBag version.</param>
protected DataBag(Version version) {
- Contract.Requires(version != null);
+ Requires.NotNull(version, "version");
this.version = version;
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs
index e7ac254..210a95e 100644
--- a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs
@@ -8,7 +8,6 @@ namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
@@ -17,6 +16,7 @@ namespace DotNetOpenAuth.Messaging {
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.Messaging.Reflection;
+ using Validation;
/// <summary>
/// A serializer for <see cref="DataBag"/>-derived types
@@ -110,8 +110,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
protected DataBagFormatterBase(ICryptoKeyStore cryptoKeyStore = null, string bucket = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? minimumAge = null, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
: this(signed, encrypted, compressed, maximumAge, decodeOnceOnly) {
- Requires.True(!string.IsNullOrEmpty(bucket) || cryptoKeyStore == null, null);
- Requires.True(cryptoKeyStore != null || (!signed && !encrypted), null);
+ Requires.That(!string.IsNullOrEmpty(bucket) || cryptoKeyStore == null, "bucket", "Bucket name required when cryptoKeyStore is non-null.");
+ Requires.That(cryptoKeyStore != null || (!signed && !encrypted), "cryptoKeyStore", "cryptoKeyStore required if signing or encrypting.");
this.cryptoKeyStore = cryptoKeyStore;
this.cryptoKeyBucket = bucket;
@@ -129,8 +129,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <paramref name="decodeOnceOnly"/> is <c>true</c>.</param>
/// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
private DataBagFormatterBase(bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) {
- Requires.True(signed || decodeOnceOnly == null, null);
- Requires.True(maximumAge.HasValue || decodeOnceOnly == null, null);
+ Requires.That(signed || decodeOnceOnly == null, "decodeOnceOnly", "Nonce only valid with signing.");
+ Requires.That(maximumAge.HasValue || decodeOnceOnly == null, "decodeOnceOnly", "Nonce requires a maximum message age.");
this.signed = signed;
this.maximumAge = maximumAge;
@@ -303,8 +303,7 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")]
private byte[] CalculateSignature(byte[] bytesToSign, string symmetricSecretHandle) {
Requires.NotNull(bytesToSign, "bytesToSign");
- Requires.ValidState(this.asymmetricSigning != null || this.cryptoKeyStore != null);
- Contract.Ensures(Contract.Result<byte[]>() != null);
+ RequiresEx.ValidState(this.asymmetricSigning != null || this.cryptoKeyStore != null);
if (this.asymmetricSigning != null) {
using (var hasher = SHA1.Create()) {
@@ -328,7 +327,7 @@ namespace DotNetOpenAuth.Messaging {
/// The encrypted value.
/// </returns>
private byte[] Encrypt(byte[] value, out string symmetricSecretHandle) {
- Requires.ValidState(this.asymmetricEncrypting != null || this.cryptoKeyStore != null);
+ Assumes.True(this.asymmetricEncrypting != null || this.cryptoKeyStore != null);
if (this.asymmetricEncrypting != null) {
symmetricSecretHandle = null;
@@ -349,7 +348,7 @@ namespace DotNetOpenAuth.Messaging {
/// The decrypted value.
/// </returns>
private byte[] Decrypt(byte[] value, string symmetricSecretHandle) {
- Requires.ValidState(this.asymmetricEncrypting != null || symmetricSecretHandle != null);
+ RequiresEx.ValidState(this.asymmetricEncrypting != null || symmetricSecretHandle != null);
if (this.asymmetricEncrypting != null) {
return this.asymmetricEncrypting.DecryptWithRandomSymmetricKey(value);
diff --git a/src/DotNetOpenAuth.Core/Messaging/EnumerableCacheExtensions.cs b/src/DotNetOpenAuth.Core/Messaging/EnumerableCacheExtensions.cs
index 5e9cf93..0886ef2 100644
--- a/src/DotNetOpenAuth.Core/Messaging/EnumerableCacheExtensions.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/EnumerableCacheExtensions.cs
@@ -9,7 +9,7 @@ namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// Extension methods for <see cref="IEnumerable&lt;T&gt;"/> types.
diff --git a/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs
index 2237cc7..71c904b 100644
--- a/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs
@@ -11,11 +11,11 @@ namespace DotNetOpenAuth.Messaging {
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Web;
+ using Validation;
/// <summary>
/// A collection of error checking and reporting methods.
/// </summary>
- [ContractVerification(true)]
[Pure]
internal static class ErrorUtilities {
/// <summary>
@@ -28,7 +28,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static Exception Wrap(Exception inner, string errorMessage, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Assume(errorMessage != null);
+ Assumes.True(errorMessage != null);
return new ProtocolException(string.Format(CultureInfo.CurrentCulture, errorMessage, args), inner);
}
@@ -58,8 +58,6 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="InternalErrorException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyInternal(bool condition, string errorMessage) {
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<InternalErrorException>(!condition);
if (!condition) {
ThrowInternal(errorMessage);
}
@@ -75,9 +73,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyInternal(bool condition, string errorMessage, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<InternalErrorException>(!condition);
- Contract.Assume(errorMessage != null);
+ Assumes.True(errorMessage != null);
if (!condition) {
errorMessage = string.Format(CultureInfo.CurrentCulture, errorMessage, args);
throw new InternalErrorException(errorMessage);
@@ -92,8 +88,6 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="InvalidOperationException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifyOperation(bool condition, string errorMessage) {
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<InvalidOperationException>(!condition);
if (!condition) {
throw new InvalidOperationException(errorMessage);
}
@@ -107,8 +101,6 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="NotSupportedException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
internal static void VerifySupported(bool condition, string errorMessage) {
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<NotSupportedException>(!condition);
if (!condition) {
throw new NotSupportedException(errorMessage);
}
@@ -124,9 +116,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifySupported(bool condition, string errorMessage, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<NotSupportedException>(!condition);
- Contract.Assume(errorMessage != null);
+ Assumes.True(errorMessage != null);
if (!condition) {
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, errorMessage, args));
}
@@ -142,9 +132,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyOperation(bool condition, string errorMessage, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<InvalidOperationException>(!condition);
- Contract.Assume(errorMessage != null);
+ Assumes.True(errorMessage != null);
if (!condition) {
errorMessage = string.Format(CultureInfo.CurrentCulture, errorMessage, args);
throw new InvalidOperationException(errorMessage);
@@ -161,9 +149,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyHost(bool condition, string errorMessage, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<ProtocolException>(!condition);
- Contract.Assume(errorMessage != null);
+ Assumes.True(errorMessage != null);
if (!condition) {
throw new HostErrorException(string.Format(CultureInfo.CurrentCulture, errorMessage, args));
}
@@ -181,9 +167,7 @@ namespace DotNetOpenAuth.Messaging {
internal static void VerifyProtocol(bool condition, IProtocolMessage faultedMessage, string errorMessage, params object[] args) {
Requires.NotNull(args, "args");
Requires.NotNull(faultedMessage, "faultedMessage");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<ProtocolException>(!condition);
- Contract.Assume(errorMessage != null);
+ Assumes.True(errorMessage != null);
if (!condition) {
throw new ProtocolException(string.Format(CultureInfo.CurrentCulture, errorMessage, args), faultedMessage);
}
@@ -199,9 +183,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyProtocol(bool condition, string unformattedMessage, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<ProtocolException>(!condition);
- Contract.Assume(unformattedMessage != null);
+ Assumes.True(unformattedMessage != null);
if (!condition) {
var exception = new ProtocolException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args));
if (Logger.Messaging.IsErrorEnabled) {
@@ -231,7 +213,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static Exception ThrowProtocol(string unformattedMessage, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Assume(unformattedMessage != null);
+ Assumes.True(unformattedMessage != null);
VerifyProtocol(false, unformattedMessage, args);
// we never reach here, but this allows callers to "throw" this method.
@@ -247,7 +229,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static Exception ThrowFormat(string message, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Assume(message != null);
+ Assumes.True(message != null);
throw new FormatException(string.Format(CultureInfo.CurrentCulture, message, args));
}
@@ -261,9 +243,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyFormat(bool condition, string message, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<FormatException>(!condition);
- Contract.Assume(message != null);
+ Assumes.True(message != null);
if (!condition) {
throw ThrowFormat(message, args);
}
@@ -279,9 +259,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyArgument(bool condition, string message, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<ArgumentException>(!condition);
- Contract.Assume(message != null);
+ Assumes.True(message != null);
if (!condition) {
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, message, args));
}
@@ -297,7 +275,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static Exception ThrowArgumentNamed(string parameterName, string message, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Assume(message != null);
+ Assumes.True(message != null);
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, message, args), parameterName);
}
@@ -312,9 +290,7 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyArgumentNamed(bool condition, string parameterName, string message, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Ensures(condition);
- Contract.EnsuresOnThrow<ArgumentException>(!condition);
- Contract.Assume(message != null);
+ Assumes.True(message != null);
if (!condition) {
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, message, args), parameterName);
}
@@ -328,8 +304,6 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
[Pure]
internal static void VerifyArgumentNotNull(object value, string paramName) {
- Contract.Ensures(value != null);
- Contract.EnsuresOnThrow<ArgumentNullException>(value == null);
if (value == null) {
throw new ArgumentNullException(paramName);
}
@@ -344,8 +318,6 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> has zero length.</exception>
[Pure]
internal static void VerifyNonZeroLength(string value, string paramName) {
- Contract.Ensures((value != null && value.Length > 0) && !string.IsNullOrEmpty(value));
- Contract.EnsuresOnThrow<ArgumentException>(value == null || value.Length == 0);
VerifyArgumentNotNull(value, paramName);
if (value.Length == 0) {
throw new ArgumentException(MessagingStrings.UnexpectedEmptyString, paramName);
@@ -358,8 +330,6 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current"/> == <c>null</c></exception>
[Pure]
internal static void VerifyHttpContext() {
- Contract.Ensures(HttpContext.Current != null);
- Contract.Ensures(HttpContext.Current.Request != null);
ErrorUtilities.VerifyOperation(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/HmacAlgorithms.cs b/src/DotNetOpenAuth.Core/Messaging/HmacAlgorithms.cs
index 872b4ac..be7e96b 100644
--- a/src/DotNetOpenAuth.Core/Messaging/HmacAlgorithms.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/HmacAlgorithms.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.Messaging {
using System.Linq;
using System.Security.Cryptography;
using System.Text;
+ using Validation;
/// <summary>
/// HMAC-SHA algorithm names that can be passed to the <see cref="HMAC.Create(string)"/> method.
@@ -50,9 +51,7 @@ namespace DotNetOpenAuth.Messaging {
hmac.Key = key;
return hmac;
} catch {
-#if CLR4
hmac.Dispose();
-#endif
throw;
}
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs
index 75da833..ba1faab 100644
--- a/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs
@@ -9,17 +9,15 @@ namespace DotNetOpenAuth.Messaging {
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Net;
-#if CLR4
using System.Net.Http;
using System.Net.Http.Headers;
-#endif
using System.Net.Mime;
using System.ServiceModel.Channels;
using System.Web;
+ using Validation;
/// <summary>
/// A property store of details of an incoming HTTP request.
@@ -124,7 +122,6 @@ namespace DotNetOpenAuth.Messaging {
Reporting.RecordRequestStatistics(this);
}
-#if CLR4
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestInfo" /> class.
/// </summary>
@@ -144,7 +141,6 @@ namespace DotNetOpenAuth.Messaging {
Reporting.RecordRequestStatistics(this);
}
-#endif
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestInfo"/> class.
@@ -226,6 +222,58 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// When overridden in a derived class, gets an array of client-supported MIME accept types.
+ /// </summary>
+ /// <returns>An array of client-supported MIME accept types.</returns>
+ public override string[] AcceptTypes {
+ get {
+ if (this.Headers["Accept"] != null) {
+ return this.headers["Accept"].Split(',');
+ }
+
+ return new string[0];
+ }
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, gets information about the URL of the client request that linked to the current URL.
+ /// </summary>
+ /// <returns>The URL of the page that linked to the current request.</returns>
+ public override Uri UrlReferrer {
+ get {
+ if (this.Headers["Referer"] != null) { // misspelled word intentional, per RFC
+ return new Uri(this.Headers["Referer"]);
+ }
+
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, gets the length, in bytes, of content that was sent by the client.
+ /// </summary>
+ /// <returns>The length, in bytes, of content that was sent by the client.</returns>
+ public override int ContentLength {
+ get {
+ if (this.Headers["Content-Length"] != null) {
+ return int.Parse(this.headers["Content-Length"]);
+ }
+
+ return 0;
+ }
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, gets or sets the MIME content type of the request.
+ /// </summary>
+ /// <returns>The MIME content type of the request, such as "text/html".</returns>
+ /// <exception cref="System.NotImplementedException">Setter always throws</exception>
+ public override string ContentType {
+ get { return this.Headers["Content-Type"]; }
+ set { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
/// Creates an <see cref="HttpRequestBase"/> instance that describes the specified HTTP request.
/// </summary>
/// <param name="request">The request.</param>
@@ -309,7 +357,6 @@ namespace DotNetOpenAuth.Messaging {
return new NameValueCollection();
}
-#if CLR4
/// <summary>
/// Adds HTTP headers to a <see cref="NameValueCollection"/>.
/// </summary>
@@ -325,6 +372,5 @@ namespace DotNetOpenAuth.Messaging {
}
}
}
-#endif
}
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/HttpResponseMessageWithOriginal.cs b/src/DotNetOpenAuth.Core/Messaging/HttpResponseMessageWithOriginal.cs
new file mode 100644
index 0000000..c6d2cc3
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/HttpResponseMessageWithOriginal.cs
@@ -0,0 +1,36 @@
+//-----------------------------------------------------------------------
+// <copyright file="HttpResponseMessageWithOriginal.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System.Net;
+ using System.Net.Http;
+
+ using Validation;
+
+ /// <summary>
+ /// An HttpResponseMessage that includes the original DNOA semantic message as a property.
+ /// </summary>
+ /// <remarks>
+ /// This is used to assist in testing.
+ /// </remarks>
+ internal class HttpResponseMessageWithOriginal : HttpResponseMessage {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HttpResponseMessageWithOriginal"/> class.
+ /// </summary>
+ /// <param name="originalMessage">The original message.</param>
+ /// <param name="statusCode">The status code.</param>
+ internal HttpResponseMessageWithOriginal(IMessage originalMessage, HttpStatusCode statusCode = HttpStatusCode.OK)
+ : base(statusCode) {
+ this.OriginalMessage = originalMessage;
+ Requires.NotNull(originalMessage, "originalMessage");
+ }
+
+ /// <summary>
+ /// Gets the original message.
+ /// </summary>
+ internal IMessage OriginalMessage { get; private set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IChannelBindingElement.cs b/src/DotNetOpenAuth.Core/Messaging/IChannelBindingElement.cs
index 1047ec5..fe2cf3d 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IChannelBindingElement.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IChannelBindingElement.cs
@@ -6,13 +6,14 @@
namespace DotNetOpenAuth.Messaging {
using System;
- using System.Diagnostics.Contracts;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Validation;
/// <summary>
/// An interface that must be implemented by message transforms/validators in order
/// to be included in the channel stack.
/// </summary>
- [ContractClass(typeof(IChannelBindingElementContract))]
public interface IChannelBindingElement {
/// <summary>
/// Gets or sets the channel that this binding element belongs to.
@@ -34,6 +35,7 @@ namespace DotNetOpenAuth.Messaging {
/// Prepares a message for sending based on the rules of this channel binding element.
/// </summary>
/// <param name="message">The message to prepare for sending.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The protections (if any) that this binding element applied to the message.
/// Null if this binding element did not even apply to this binding element.
@@ -42,13 +44,14 @@ namespace DotNetOpenAuth.Messaging {
/// Implementations that provide message protection must honor the
/// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
/// </remarks>
- MessageProtections? ProcessOutgoingMessage(IProtocolMessage message);
+ Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken);
/// <summary>
/// Performs any transformation on an incoming message that may be necessary and/or
/// validates an incoming message based on the rules of this channel binding element.
/// </summary>
/// <param name="message">The incoming message to process.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The protections (if any) that this binding element applied to the message.
/// Null if this binding element did not even apply to this binding element.
@@ -61,86 +64,6 @@ namespace DotNetOpenAuth.Messaging {
/// Implementations that provide message protection must honor the
/// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
/// </remarks>
- MessageProtections? ProcessIncomingMessage(IProtocolMessage message);
- }
-
- /// <summary>
- /// Code Contract for the <see cref="IChannelBindingElement"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IChannelBindingElement))]
- internal abstract class IChannelBindingElementContract : IChannelBindingElement {
- /// <summary>
- /// Prevents a default instance of the <see cref="IChannelBindingElementContract"/> class from being created.
- /// </summary>
- private IChannelBindingElementContract() {
- }
-
- #region IChannelBindingElement Members
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// This property is set by the channel when it is first constructed.
- /// </remarks>
- Channel IChannelBindingElement.Channel {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Gets the protection commonly offered (if any) by this binding element.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// This value is used to assist in sorting binding elements in the channel stack.
- /// </remarks>
- MessageProtections IChannelBindingElement.Protection {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Prepares a message for sending based on the rules of this channel binding element.
- /// </summary>
- /// <param name="message">The message to prepare for sending.</param>
- /// <returns>
- /// The protections (if any) that this binding element applied to the message.
- /// Null if this binding element did not even apply to this binding element.
- /// </returns>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- MessageProtections? IChannelBindingElement.ProcessOutgoingMessage(IProtocolMessage message) {
- Requires.NotNull(message, "message");
- Requires.ValidState(((IChannelBindingElement)this).Channel != null);
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Performs any transformation on an incoming message that may be necessary and/or
- /// validates an incoming message based on the rules of this channel binding element.
- /// </summary>
- /// <param name="message">The incoming message to process.</param>
- /// <returns>
- /// The protections (if any) that this binding element applied to the message.
- /// Null if this binding element did not even apply to this binding element.
- /// </returns>
- /// <exception cref="ProtocolException">
- /// Thrown when the binding element rules indicate that this message is invalid and should
- /// NOT be processed.
- /// </exception>
- /// <remarks>
- /// Implementations that provide message protection must honor the
- /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
- /// </remarks>
- MessageProtections? IChannelBindingElement.ProcessIncomingMessage(IProtocolMessage message) {
- Requires.NotNull(message, "message");
- Requires.ValidState(((IChannelBindingElement)this).Channel != null);
- throw new NotImplementedException();
- }
-
- #endregion
+ Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken);
}
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs
index 0d1ab03..955d7c0 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs
@@ -6,13 +6,12 @@
namespace DotNetOpenAuth.Messaging {
using System;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// A serializer for <see cref="DataBag"/>-derived types
/// </summary>
/// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam>
- [ContractClass(typeof(IDataBagFormatterContract<>))]
internal interface IDataBagFormatter<in T> where T : DataBag {
/// <summary>
/// Serializes the specified message.
@@ -30,50 +29,4 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="messagePartName">The name of the parameter whose value is to be deserialized. Used for error message generation, but may be <c>null</c>.</param>
void Deserialize(T message, string data, IProtocolMessage containingMessage = null, string messagePartName = null);
}
-
- /// <summary>
- /// Contract class for the IDataBagFormatter interface.
- /// </summary>
- /// <typeparam name="T">The type of DataBag to serialize.</typeparam>
- [ContractClassFor(typeof(IDataBagFormatter<>))]
- internal abstract class IDataBagFormatterContract<T> : IDataBagFormatter<T> where T : DataBag, new() {
- /// <summary>
- /// Prevents a default instance of the <see cref="IDataBagFormatterContract&lt;T&gt;"/> class from being created.
- /// </summary>
- private IDataBagFormatterContract() {
- }
-
- #region IDataBagFormatter<T> Members
-
- /// <summary>
- /// Serializes the specified message.
- /// </summary>
- /// <param name="message">The message to serialize. Must not be null.</param>
- /// <returns>A non-null, non-empty value.</returns>
- string IDataBagFormatter<T>.Serialize(T message) {
- Requires.NotNull(message, "message");
- Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
-
- throw new System.NotImplementedException();
- }
-
- /// <summary>
- /// Deserializes a <see cref="DataBag"/>.
- /// </summary>
- /// <param name="message">The instance to deserialize into</param>
- /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param>
- /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param>
- /// <param name="messagePartName">Name of the message part whose value is to be deserialized. Used for exception messages.</param>
- void IDataBagFormatter<T>.Deserialize(T message, string data, IProtocolMessage containingMessage, string messagePartName) {
- Requires.NotNull(message, "message");
- Requires.NotNull(containingMessage, "containingMessage");
- Requires.NotNullOrEmpty(data, "data");
- Requires.NotNullOrEmpty(messagePartName, "messagePartName");
- Contract.Ensures(Contract.Result<T>() != null);
-
- throw new System.NotImplementedException();
- }
-
- #endregion
- }
} \ No newline at end of file
diff --git a/src/DotNetOpenAuth.Core/Messaging/IDirectWebRequestHandler.cs b/src/DotNetOpenAuth.Core/Messaging/IDirectWebRequestHandler.cs
deleted file mode 100644
index 7878405..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/IDirectWebRequestHandler.cs
+++ /dev/null
@@ -1,223 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="IDirectWebRequestHandler.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using System.IO;
- using System.Net;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// A contract for <see cref="HttpWebRequest"/> handling.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface must be thread safe.
- /// </remarks>
- [ContractClass(typeof(IDirectWebRequestHandlerContract))]
- public interface IDirectWebRequestHandler {
- /// <summary>
- /// Determines whether this instance can support the specified options.
- /// </summary>
- /// <param name="options">The set of options that might be given in a subsequent web request.</param>
- /// <returns>
- /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
- /// </returns>
- [Pure]
- bool CanSupport(DirectWebRequestOptions options);
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <returns>
- /// The stream the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.
- /// Callers <i>must</i> close and dispose of the request stream when they are done
- /// writing to it to avoid taking up the connection too long and causing long waits on
- /// subsequent requests.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- Stream GetRequestStream(HttpWebRequest request);
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>
- /// The stream the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.
- /// Callers <i>must</i> close and dispose of the request stream when they are done
- /// writing to it to avoid taking up the connection too long and causing long waits on
- /// subsequent requests.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options);
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <returns>An instance of <see cref="IncomingWebResponse"/> describing the response.</returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.</para>
- /// </remarks>
- IncomingWebResponse GetResponse(HttpWebRequest request);
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>An instance of <see cref="IncomingWebResponse"/> describing the response.</returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.</para>
- /// </remarks>
- IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options);
- }
-
- /// <summary>
- /// Code contract for the <see cref="IDirectWebRequestHandler"/> type.
- /// </summary>
- [ContractClassFor(typeof(IDirectWebRequestHandler))]
- internal abstract class IDirectWebRequestHandlerContract : IDirectWebRequestHandler {
- #region IDirectWebRequestHandler Members
-
- /// <summary>
- /// Determines whether this instance can support the specified options.
- /// </summary>
- /// <param name="options">The set of options that might be given in a subsequent web request.</param>
- /// <returns>
- /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
- /// </returns>
- bool IDirectWebRequestHandler.CanSupport(DirectWebRequestOptions options) {
- throw new System.NotImplementedException();
- }
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <returns>
- /// The stream the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.
- /// Callers <i>must</i> close and dispose of the request stream when they are done
- /// writing to it to avoid taking up the connection too long and causing long waits on
- /// subsequent requests.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- Stream IDirectWebRequestHandler.GetRequestStream(HttpWebRequest request) {
- Requires.NotNull(request, "request");
- throw new System.NotImplementedException();
- }
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>
- /// The stream the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.
- /// Callers <i>must</i> close and dispose of the request stream when they are done
- /// writing to it to avoid taking up the connection too long and causing long waits on
- /// subsequent requests.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- Stream IDirectWebRequestHandler.GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) {
- Requires.NotNull(request, "request");
- Requires.Support(((IDirectWebRequestHandler)this).CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported);
- ////ErrorUtilities.VerifySupported(((IDirectWebRequestHandler)this).CanSupport(options), string.Format(MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name));
- throw new System.NotImplementedException();
- }
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <returns>
- /// An instance of <see cref="IncomingWebResponse"/> describing the response.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.
- /// </remarks>
- IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) {
- Requires.NotNull(request, "request");
- Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
- Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null);
- throw new System.NotImplementedException();
- }
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>
- /// An instance of <see cref="IncomingWebResponse"/> describing the response.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.
- /// </remarks>
- IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
- Requires.NotNull(request, "request");
- Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);
- Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null);
- Requires.Support(((IDirectWebRequestHandler)this).CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported);
-
- ////ErrorUtilities.VerifySupported(((IDirectWebRequestHandler)this).CanSupport(options), string.Format(MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name));
- throw new System.NotImplementedException();
- }
-
- #endregion
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs
index 7153334..226102d 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs
@@ -5,18 +5,16 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Messaging {
- using System.Diagnostics.Contracts;
using System.Net;
/// <summary>
/// An interface that allows direct request messages to capture the details of the HTTP request they arrived on.
/// </summary>
- [ContractClass(typeof(IHttpDirectRequestContract))]
public interface IHttpDirectRequest : IMessage {
/// <summary>
/// Gets the HTTP headers of the request.
/// </summary>
/// <value>May be an empty collection, but must not be <c>null</c>.</value>
- WebHeaderCollection Headers { get; }
+ System.Net.Http.Headers.HttpRequestHeaders Headers { get; }
}
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs
deleted file mode 100644
index cfde6cf..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="IHttpDirectRequestContract.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using System.Linq;
- using System.Net;
- using System.Text;
-
- /// <summary>
- /// Contract class for the <see cref="IHttpDirectRequest"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IHttpDirectRequest))]
- public abstract class IHttpDirectRequestContract : IHttpDirectRequest {
- #region IHttpDirectRequest Members
-
- /// <summary>
- /// Gets the HTTP headers of the request.
- /// </summary>
- /// <value>May be an empty collection, but must not be <c>null</c>.</value>
- WebHeaderCollection IHttpDirectRequest.Headers {
- get {
- Contract.Ensures(Contract.Result<WebHeaderCollection>() != null);
- throw new NotImplementedException();
- }
- }
-
- #endregion
-
- #region IMessage Members
-
- /// <summary>
- /// Gets the version of the protocol or extension this message is prepared to implement.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- Version IMessage.Version {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Gets the extra, non-standard Protocol parameters included in the message.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IDictionary<string, string> IMessage.ExtraData {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- void IMessage.EnsureValidMessage() {
- throw new NotImplementedException();
- }
-
- #endregion
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponse.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponse.cs
index d942366..f455fcf 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponse.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponse.cs
@@ -5,14 +5,12 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Messaging {
- using System.Diagnostics.Contracts;
using System.Net;
/// <summary>
/// An interface that allows direct response messages to specify
/// HTTP transport specific properties.
/// </summary>
- [ContractClass(typeof(IHttpDirectResponseContract))]
public interface IHttpDirectResponse {
/// <summary>
/// Gets the HTTP status code that the direct response should be sent with.
diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponseContract.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponseContract.cs
deleted file mode 100644
index a04ba62..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectResponseContract.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="IHttpDirectResponseContract.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using System.Linq;
- using System.Net;
- using System.Text;
-
- /// <summary>
- /// Contract class for the <see cref="IHttpDirectResponse"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IHttpDirectResponse))]
- public abstract class IHttpDirectResponseContract : IHttpDirectResponse {
- #region IHttpDirectResponse Members
-
- /// <summary>
- /// Gets the HTTP status code that the direct response should be sent with.
- /// </summary>
- /// <value></value>
- HttpStatusCode IHttpDirectResponse.HttpStatusCode {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Gets the HTTP headers to add to the response.
- /// </summary>
- /// <value>May be an empty collection, but must not be <c>null</c>.</value>
- WebHeaderCollection IHttpDirectResponse.Headers {
- get {
- Contract.Ensures(Contract.Result<WebHeaderCollection>() != null);
- throw new NotImplementedException();
- }
- }
-
- #endregion
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpIndirectResponse.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpIndirectResponse.cs
index e0e8665..c9ab73b 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IHttpIndirectResponse.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IHttpIndirectResponse.cs
@@ -5,7 +5,6 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Messaging {
- using System.Diagnostics.Contracts;
using System.Net;
/// <summary>
diff --git a/src/DotNetOpenAuth.Core/Messaging/IMessage.cs b/src/DotNetOpenAuth.Core/Messaging/IMessage.cs
index 62673ef..c007913 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IMessage.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IMessage.cs
@@ -7,14 +7,12 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
using System.Text;
/// <summary>
/// The interface that classes must implement to be serialized/deserialized
/// as protocol or extension messages.
/// </summary>
- [ContractClass(typeof(IMessageContract))]
public interface IMessage {
/// <summary>
/// Gets the version of the protocol or extension this message is prepared to implement.
@@ -46,55 +44,4 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
void EnsureValidMessage();
}
-
- /// <summary>
- /// Code contract for the <see cref="IMessage"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IMessage))]
- internal abstract class IMessageContract : IMessage {
- /// <summary>
- /// Prevents a default instance of the <see cref="IMessageContract"/> class from being created.
- /// </summary>
- private IMessageContract() {
- }
-
- /// <summary>
- /// Gets the version of the protocol or extension this message is prepared to implement.
- /// </summary>
- Version IMessage.Version {
- get {
- Contract.Ensures(Contract.Result<Version>() != null);
- return default(Version); // dummy return
- }
- }
-
- /// <summary>
- /// Gets the extra, non-standard Protocol parameters included in the message.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IDictionary<string, string> IMessage.ExtraData {
- get {
- Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
- return default(IDictionary<string, string>);
- }
- }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- void IMessage.EnsureValidMessage() {
- }
- }
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IMessageFactory.cs b/src/DotNetOpenAuth.Core/Messaging/IMessageFactory.cs
index e45ac1d..1e86328 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IMessageFactory.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IMessageFactory.cs
@@ -7,13 +7,12 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// A tool to analyze an incoming message to figure out what concrete class
/// is designed to deserialize it and instantiates that class.
/// </summary>
- [ContractClass(typeof(IMessageFactoryContract))]
public interface IMessageFactory {
/// <summary>
/// Analyzes an incoming request message payload to discover what kind of
@@ -41,53 +40,4 @@ namespace DotNetOpenAuth.Messaging {
/// </returns>
IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields);
}
-
- /// <summary>
- /// Code contract for the <see cref="IMessageFactory"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IMessageFactory))]
- internal abstract class IMessageFactoryContract : IMessageFactory {
- /// <summary>
- /// Prevents a default instance of the <see cref="IMessageFactoryContract"/> class from being created.
- /// </summary>
- private IMessageFactoryContract() {
- }
-
- #region IMessageFactory Members
-
- /// <summary>
- /// Analyzes an incoming request message payload to discover what kind of
- /// message is embedded in it and returns the type, or null if no match is found.
- /// </summary>
- /// <param name="recipient">The intended or actual recipient of the request message.</param>
- /// <param name="fields">The name/value pairs that make up the message payload.</param>
- /// <returns>
- /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
- /// deserialize to. Null if the request isn't recognized as a valid protocol message.
- /// </returns>
- IDirectedProtocolMessage IMessageFactory.GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
- Requires.NotNull(recipient, "recipient");
- Requires.NotNull(fields, "fields");
-
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Analyzes an incoming request message payload to discover what kind of
- /// message is embedded in it and returns the type, or null if no match is found.
- /// </summary>
- /// <param name="request">The message that was sent as a request that resulted in the response.</param>
- /// <param name="fields">The name/value pairs that make up the message payload.</param>
- /// <returns>
- /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
- /// deserialize to. Null if the request isn't recognized as a valid protocol message.
- /// </returns>
- IDirectResponseProtocolMessage IMessageFactory.GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) {
- Requires.NotNull(request, "request");
- Requires.NotNull(fields, "fields");
- throw new NotImplementedException();
- }
-
- #endregion
- }
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IMessageOriginalPayload.cs b/src/DotNetOpenAuth.Core/Messaging/IMessageOriginalPayload.cs
index 099f54b..33fa860 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IMessageOriginalPayload.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IMessageOriginalPayload.cs
@@ -8,14 +8,12 @@ namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Text;
/// <summary>
/// An interface that appears on messages that need to retain a description of
/// what their literal payload was when they were deserialized.
/// </summary>
- [ContractClass(typeof(IMessageOriginalPayloadContract))]
public interface IMessageOriginalPayload {
/// <summary>
/// Gets or sets the original message parts, before any normalization or default values were assigned.
@@ -23,18 +21,4 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "By design")]
IDictionary<string, string> OriginalPayload { get; set; }
}
-
- /// <summary>
- /// Code contract for the <see cref="IMessageOriginalPayload"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IMessageOriginalPayload))]
- internal abstract class IMessageOriginalPayloadContract : IMessageOriginalPayload {
- /// <summary>
- /// Gets or sets the original message parts, before any normalization or default values were assigned.
- /// </summary>
- IDictionary<string, string> IMessageOriginalPayload.OriginalPayload {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
- }
- }
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs b/src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs
index 60e1f50..84a7760 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs
@@ -7,150 +7,24 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
using System.Linq;
+ using System.Net.Http;
using System.Text;
/// <summary>
/// The interface that classes must implement to be serialized/deserialized
/// as protocol or extension messages that uses POST multi-part data for binary content.
/// </summary>
- [ContractClass(typeof(IMessageWithBinaryDataContract))]
public interface IMessageWithBinaryData : IDirectedProtocolMessage {
/// <summary>
/// Gets the parts of the message that carry binary data.
/// </summary>
/// <value>A list of parts. Never null.</value>
- IList<MultipartPostPart> BinaryData { get; }
+ IList<MultipartContentMember> BinaryData { get; }
/// <summary>
/// Gets a value indicating whether this message should be sent as multi-part POST.
/// </summary>
bool SendAsMultipart { get; }
}
-
- /// <summary>
- /// The contract class for the <see cref="IMessageWithBinaryData"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IMessageWithBinaryData))]
- internal abstract class IMessageWithBinaryDataContract : IMessageWithBinaryData {
- /// <summary>
- /// Prevents a default instance of the <see cref="IMessageWithBinaryDataContract"/> class from being created.
- /// </summary>
- private IMessageWithBinaryDataContract() {
- }
-
- #region IMessageWithBinaryData Members
-
- /// <summary>
- /// Gets the parts of the message that carry binary data.
- /// </summary>
- /// <value>A list of parts. Never null.</value>
- IList<MultipartPostPart> IMessageWithBinaryData.BinaryData {
- get {
- Contract.Ensures(Contract.Result<IList<MultipartPostPart>>() != null);
- throw new NotImplementedException();
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message should be sent as multi-part POST.
- /// </summary>
- bool IMessageWithBinaryData.SendAsMultipart {
- get { throw new NotImplementedException(); }
- }
-
- #endregion
-
- #region IMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol or extension this message is prepared to implement.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- Version IMessage.Version {
- get {
- return default(Version); // dummy return
- }
- }
-
- /// <summary>
- /// Gets the extra, non-standard Protocol parameters included in the message.
- /// </summary>
- /// <value></value>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IDictionary<string, string> IMessage.ExtraData {
- get {
- return default(IDictionary<string, string>);
- }
- }
-
- #endregion
-
- #region IDirectedProtocolMessage Members
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- /// <remarks>
- /// For indirect messages this will likely be GET+POST, which both can be simulated in the user agent:
- /// the GET with a simple 301 Redirect, and the POST with an HTML form in the response with javascript
- /// to automate submission.
- /// </remarks>
- HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Gets the URL of the intended receiver of this message.
- /// </summary>
- Uri IDirectedProtocolMessage.Recipient {
- get { throw new NotImplementedException(); }
- }
-
- #endregion
-
- #region IProtocolMessage Members
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- MessageProtections IProtocolMessage.RequiredProtection {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- MessageTransport IProtocolMessage.Transport {
- get { throw new NotImplementedException(); }
- }
-
- #endregion
-
- #region IMessage methods
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- void IMessage.EnsureValidMessage() {
- throw new NotImplementedException();
- }
-
- #endregion
- }
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IProtocolMessageWithExtensions.cs b/src/DotNetOpenAuth.Core/Messaging/IProtocolMessageWithExtensions.cs
index c492e65..436c7a9 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IProtocolMessageWithExtensions.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IProtocolMessageWithExtensions.cs
@@ -7,12 +7,10 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
/// <summary>
/// A protocol message that supports adding extensions to the payload for transmission.
/// </summary>
- [ContractClass(typeof(IProtocolMessageWithExtensionsContract))]
public interface IProtocolMessageWithExtensions : IProtocolMessage {
/// <summary>
/// Gets the list of extensions that are included with this message.
@@ -22,95 +20,4 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
IList<IExtensionMessage> Extensions { get; }
}
-
- /// <summary>
- /// Code contract for the <see cref="IProtocolMessageWithExtensions"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IProtocolMessageWithExtensions))]
- internal abstract class IProtocolMessageWithExtensionsContract : IProtocolMessageWithExtensions {
- /// <summary>
- /// Prevents a default instance of the <see cref="IProtocolMessageWithExtensionsContract"/> class from being created.
- /// </summary>
- private IProtocolMessageWithExtensionsContract() {
- }
-
- #region IProtocolMessageWithExtensions Members
-
- /// <summary>
- /// Gets the list of extensions that are included with this message.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IList<IExtensionMessage> IProtocolMessageWithExtensions.Extensions {
- get {
- Contract.Ensures(Contract.Result<IList<IExtensionMessage>>() != null);
- throw new NotImplementedException();
- }
- }
-
- #endregion
-
- #region IProtocolMessage Members
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- MessageProtections IProtocolMessage.RequiredProtection {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- MessageTransport IProtocolMessage.Transport {
- get { throw new NotImplementedException(); }
- }
-
- #endregion
-
- #region IMessage Members
-
- /// <summary>
- /// Gets the version of the protocol or extension this message is prepared to implement.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- Version IMessage.Version {
- get {
- throw new NotImplementedException();
- }
- }
-
- /// <summary>
- /// Gets the extra, non-standard Protocol parameters included in the message.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IDictionary<string, string> IMessage.ExtraData {
- get {
- throw new NotImplementedException();
- }
- }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- void IMessage.EnsureValidMessage() {
- throw new NotImplementedException();
- }
-
- #endregion
- }
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IStreamSerializingDataBag.cs b/src/DotNetOpenAuth.Core/Messaging/IStreamSerializingDataBag.cs
index cc82d6a..16fed67 100644
--- a/src/DotNetOpenAuth.Core/Messaging/IStreamSerializingDataBag.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/IStreamSerializingDataBag.cs
@@ -6,13 +6,11 @@
namespace DotNetOpenAuth.Messaging {
using System;
- using System.Diagnostics.Contracts;
using System.IO;
/// <summary>
/// An interface implemented by <see cref="DataBag"/>-derived types that support binary serialization.
/// </summary>
- [ContractClass(typeof(IStreamSerializingDataBaContract))]
internal interface IStreamSerializingDataBag {
/// <summary>
/// Serializes the instance to the specified stream.
@@ -26,30 +24,4 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="stream">The stream.</param>
void Deserialize(Stream stream);
}
-
- /// <summary>
- /// Code Contract for the <see cref="IStreamSerializingDataBag"/> interface.
- /// </summary>
- [ContractClassFor(typeof(IStreamSerializingDataBag))]
- internal abstract class IStreamSerializingDataBaContract : IStreamSerializingDataBag {
- /// <summary>
- /// Serializes the instance to the specified stream.
- /// </summary>
- /// <param name="stream">The stream.</param>
- void IStreamSerializingDataBag.Serialize(Stream stream) {
- Contract.Requires(stream != null);
- Contract.Requires(stream.CanWrite);
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Initializes the fields on this instance from the specified stream.
- /// </summary>
- /// <param name="stream">The stream.</param>
- void IStreamSerializingDataBag.Deserialize(Stream stream) {
- Contract.Requires(stream != null);
- Contract.Requires(stream.CanRead);
- throw new NotImplementedException();
- }
- }
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IncomingWebResponse.cs b/src/DotNetOpenAuth.Core/Messaging/IncomingWebResponse.cs
deleted file mode 100644
index cdb26ae..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/IncomingWebResponse.cs
+++ /dev/null
@@ -1,191 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="IncomingWebResponse.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
- using System.Globalization;
- using System.IO;
- using System.Net;
- using System.Net.Mime;
- using System.Text;
-
- /// <summary>
- /// Details on the incoming response from a direct web request to a remote party.
- /// </summary>
- [ContractVerification(true)]
- [ContractClass(typeof(IncomingWebResponseContract))]
- public abstract class IncomingWebResponse : IDisposable {
- /// <summary>
- /// The encoding to use in reading a response that does not declare its own content encoding.
- /// </summary>
- private const string DefaultContentEncoding = "ISO-8859-1";
-
- /// <summary>
- /// Initializes a new instance of the <see cref="IncomingWebResponse"/> class.
- /// </summary>
- protected internal IncomingWebResponse() {
- this.Status = HttpStatusCode.OK;
- this.Headers = new WebHeaderCollection();
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="IncomingWebResponse"/> class.
- /// </summary>
- /// <param name="requestUri">The original request URI.</param>
- /// <param name="response">The response to initialize from. The network stream is used by this class directly.</param>
- protected IncomingWebResponse(Uri requestUri, HttpWebResponse response) {
- Requires.NotNull(requestUri, "requestUri");
- Requires.NotNull(response, "response");
-
- this.RequestUri = requestUri;
- if (!string.IsNullOrEmpty(response.ContentType)) {
- try {
- this.ContentType = new ContentType(response.ContentType);
- } catch (FormatException) {
- Logger.Messaging.ErrorFormat("HTTP response to {0} included an invalid Content-Type header value: {1}", response.ResponseUri.AbsoluteUri, response.ContentType);
- }
- }
- this.ContentEncoding = string.IsNullOrEmpty(response.ContentEncoding) ? DefaultContentEncoding : response.ContentEncoding;
- this.FinalUri = response.ResponseUri;
- this.Status = response.StatusCode;
- this.Headers = response.Headers;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="IncomingWebResponse"/> class.
- /// </summary>
- /// <param name="requestUri">The request URI.</param>
- /// <param name="responseUri">The final URI to respond to the request.</param>
- /// <param name="headers">The headers.</param>
- /// <param name="statusCode">The status code.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="contentEncoding">The content encoding.</param>
- protected IncomingWebResponse(Uri requestUri, Uri responseUri, WebHeaderCollection headers, HttpStatusCode statusCode, string contentType, string contentEncoding) {
- Requires.NotNull(requestUri, "requestUri");
-
- this.RequestUri = requestUri;
- this.Status = statusCode;
- if (!string.IsNullOrEmpty(contentType)) {
- try {
- this.ContentType = new ContentType(contentType);
- } catch (FormatException) {
- Logger.Messaging.ErrorFormat("HTTP response to {0} included an invalid Content-Type header value: {1}", responseUri.AbsoluteUri, contentType);
- }
- }
- this.ContentEncoding = string.IsNullOrEmpty(contentEncoding) ? DefaultContentEncoding : contentEncoding;
- this.Headers = headers;
- this.FinalUri = responseUri;
- }
-
- /// <summary>
- /// Gets the type of the content.
- /// </summary>
- public ContentType ContentType { get; private set; }
-
- /// <summary>
- /// Gets the content encoding.
- /// </summary>
- public string ContentEncoding { get; private set; }
-
- /// <summary>
- /// Gets the URI of the initial request.
- /// </summary>
- public Uri RequestUri { get; private set; }
-
- /// <summary>
- /// Gets the URI that finally responded to the request.
- /// </summary>
- /// <remarks>
- /// This can be different from the <see cref="RequestUri"/> in cases of
- /// redirection during the request.
- /// </remarks>
- public Uri FinalUri { get; internal set; }
-
- /// <summary>
- /// Gets the headers that must be included in the response to the user agent.
- /// </summary>
- /// <remarks>
- /// The headers in this collection are not meant to be a comprehensive list
- /// of exactly what should be sent, but are meant to augment whatever headers
- /// are generally included in a typical response.
- /// </remarks>
- public WebHeaderCollection Headers { get; internal set; }
-
- /// <summary>
- /// Gets the HTTP status code to use in the HTTP response.
- /// </summary>
- public HttpStatusCode Status { get; internal set; }
-
- /// <summary>
- /// Gets the body of the HTTP response.
- /// </summary>
- public abstract Stream ResponseStream { get; }
-
- /// <summary>
- /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
- /// </returns>
- public override string ToString() {
- StringBuilder sb = new StringBuilder();
- sb.AppendLine(string.Format(CultureInfo.CurrentCulture, "RequestUri = {0}", this.RequestUri));
- sb.AppendLine(string.Format(CultureInfo.CurrentCulture, "ResponseUri = {0}", this.FinalUri));
- sb.AppendLine(string.Format(CultureInfo.CurrentCulture, "StatusCode = {0}", this.Status));
- sb.AppendLine(string.Format(CultureInfo.CurrentCulture, "ContentType = {0}", this.ContentType));
- sb.AppendLine(string.Format(CultureInfo.CurrentCulture, "ContentEncoding = {0}", this.ContentEncoding));
- sb.AppendLine("Headers:");
- foreach (string header in this.Headers) {
- sb.AppendLine(string.Format(CultureInfo.CurrentCulture, "\t{0}: {1}", header, this.Headers[header]));
- }
-
- return sb.ToString();
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose() {
- this.Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Creates a text reader for the response stream.
- /// </summary>
- /// <returns>The text reader, initialized for the proper encoding.</returns>
- [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly operation")]
- public abstract StreamReader GetResponseReader();
-
- /// <summary>
- /// Gets an offline snapshot version of this instance.
- /// </summary>
- /// <param name="maximumBytesToCache">The maximum bytes from the response stream to cache.</param>
- /// <returns>A snapshot version of this instance.</returns>
- /// <remarks>
- /// If this instance is a <see cref="NetworkDirectWebResponse"/> creating a snapshot
- /// will automatically close and dispose of the underlying response stream.
- /// If this instance is a <see cref="CachedDirectWebResponse"/>, the result will
- /// be the self same instance.
- /// </remarks>
- internal abstract CachedDirectWebResponse GetSnapshot(int maximumBytesToCache);
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool disposing) {
- if (disposing) {
- Stream responseStream = this.ResponseStream;
- if (responseStream != null) {
- responseStream.Dispose();
- }
- }
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IncomingWebResponseContract.cs b/src/DotNetOpenAuth.Core/Messaging/IncomingWebResponseContract.cs
deleted file mode 100644
index 5c94e47..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/IncomingWebResponseContract.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="IncomingWebResponseContract.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Diagnostics.Contracts;
- using System.IO;
-
- /// <summary>
- /// Code contract for the <see cref="IncomingWebResponse"/> class.
- /// </summary>
- [ContractClassFor(typeof(IncomingWebResponse))]
- internal abstract class IncomingWebResponseContract : IncomingWebResponse {
- /// <summary>
- /// Gets the body of the HTTP response.
- /// </summary>
- /// <value></value>
- public override Stream ResponseStream {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Creates a text reader for the response stream.
- /// </summary>
- /// <returns>
- /// The text reader, initialized for the proper encoding.
- /// </returns>
- public override StreamReader GetResponseReader() {
- Contract.Ensures(Contract.Result<StreamReader>() != null);
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Gets an offline snapshot version of this instance.
- /// </summary>
- /// <param name="maximumBytesToCache">The maximum bytes from the response stream to cache.</param>
- /// <returns>A snapshot version of this instance.</returns>
- /// <remarks>
- /// If this instance is a <see cref="NetworkDirectWebResponse"/> creating a snapshot
- /// will automatically close and dispose of the underlying response stream.
- /// If this instance is a <see cref="CachedDirectWebResponse"/>, the result will
- /// be the self same instance.
- /// </remarks>
- internal override CachedDirectWebResponse GetSnapshot(int maximumBytesToCache) {
- Requires.InRange(maximumBytesToCache >= 0, "maximumBytesToCache");
- Requires.ValidState(this.RequestUri != null);
- Contract.Ensures(Contract.Result<CachedDirectWebResponse>() != null);
- throw new NotImplementedException();
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/KeyedCollectionDelegate.cs b/src/DotNetOpenAuth.Core/Messaging/KeyedCollectionDelegate.cs
index d0988c8..251ff30 100644
--- a/src/DotNetOpenAuth.Core/Messaging/KeyedCollectionDelegate.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/KeyedCollectionDelegate.cs
@@ -7,7 +7,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.ObjectModel;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// A KeyedCollection whose item -&gt; key transform is provided via a delegate
diff --git a/src/DotNetOpenAuth.Core/Messaging/MessageProtectionTasks.cs b/src/DotNetOpenAuth.Core/Messaging/MessageProtectionTasks.cs
new file mode 100644
index 0000000..37c86ca
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/MessageProtectionTasks.cs
@@ -0,0 +1,41 @@
+//-----------------------------------------------------------------------
+// <copyright file="MessageProtectionTasks.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ /// <summary>
+ /// Reusable pre-completed tasks that may be returned multiple times to reduce GC pressure.
+ /// </summary>
+ internal static class MessageProtectionTasks {
+ /// <summary>
+ /// A task whose result is <c>null</c>
+ /// </summary>
+ internal static readonly Task<MessageProtections?> Null = Task.FromResult<MessageProtections?>(null);
+
+ /// <summary>
+ /// A task whose result is <see cref="MessageProtections.None"/>
+ /// </summary>
+ internal static readonly Task<MessageProtections?> None =
+ Task.FromResult<MessageProtections?>(MessageProtections.None);
+
+ /// <summary>
+ /// A task whose result is <see cref="MessageProtections.TamperProtection"/>
+ /// </summary>
+ internal static readonly Task<MessageProtections?> TamperProtection =
+ Task.FromResult<MessageProtections?>(MessageProtections.TamperProtection);
+
+ /// <summary>
+ /// A task whose result is <see cref="MessageProtections.ReplayProtection"/>
+ /// </summary>
+ internal static readonly Task<MessageProtections?> ReplayProtection =
+ Task.FromResult<MessageProtections?>(MessageProtections.ReplayProtection);
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Messaging/MessageReceivingEndpoint.cs b/src/DotNetOpenAuth.Core/Messaging/MessageReceivingEndpoint.cs
index cf5ea92..34be92d 100644
--- a/src/DotNetOpenAuth.Core/Messaging/MessageReceivingEndpoint.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/MessageReceivingEndpoint.cs
@@ -7,7 +7,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Diagnostics;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// An immutable description of a URL that receives messages.
@@ -23,8 +23,8 @@ namespace DotNetOpenAuth.Messaging {
public MessageReceivingEndpoint(string locationUri, HttpDeliveryMethods method)
: this(new Uri(locationUri), method) {
Requires.NotNull(locationUri, "locationUri");
- Requires.InRange(method != HttpDeliveryMethods.None, "method");
- Requires.InRange((method & HttpDeliveryMethods.HttpVerbMask) != 0, "method", MessagingStrings.GetOrPostFlagsRequired);
+ Requires.Range(method != HttpDeliveryMethods.None, "method");
+ Requires.Range((method & HttpDeliveryMethods.HttpVerbMask) != 0, "method", MessagingStrings.GetOrPostFlagsRequired);
}
/// <summary>
@@ -34,8 +34,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="method">The HTTP method(s) allowed.</param>
public MessageReceivingEndpoint(Uri location, HttpDeliveryMethods method) {
Requires.NotNull(location, "location");
- Requires.InRange(method != HttpDeliveryMethods.None, "method");
- Requires.InRange((method & HttpDeliveryMethods.HttpVerbMask) != 0, "method", MessagingStrings.GetOrPostFlagsRequired);
+ Requires.Range(method != HttpDeliveryMethods.None, "method");
+ Requires.Range((method & HttpDeliveryMethods.HttpVerbMask) != 0, "method", MessagingStrings.GetOrPostFlagsRequired);
this.Location = location;
this.AllowedMethods = method;
diff --git a/src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs b/src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs
index 7391867..1b30748 100644
--- a/src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs
@@ -14,11 +14,11 @@ namespace DotNetOpenAuth.Messaging {
using System.Reflection;
using System.Xml;
using DotNetOpenAuth.Messaging.Reflection;
+ using Validation;
/// <summary>
/// Serializes/deserializes OAuth messages for/from transit.
/// </summary>
- [ContractVerification(true)]
internal class MessageSerializer {
/// <summary>
/// The specific <see cref="IMessage"/>-derived type
@@ -31,10 +31,8 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="messageType">The specific <see cref="IMessage"/>-derived type
/// that will be serialized and deserialized using this class.</param>
- [ContractVerification(false)] // bugs/limitations in CC static analysis
private MessageSerializer(Type messageType) {
- Requires.NotNullSubtype<IMessage>(messageType, "messageType");
- Contract.Ensures(this.messageType != null);
+ RequiresEx.NotNullSubtype<IMessage>(messageType, "messageType");
this.messageType = messageType;
}
@@ -43,9 +41,8 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="messageType">The type of message that will be serialized/deserialized.</param>
/// <returns>A message serializer for the given message type.</returns>
- [ContractVerification(false)] // bugs/limitations in CC static analysis
- internal static MessageSerializer Get(Type messageType) {
- Requires.NotNullSubtype<IMessage>(messageType, "messageType");
+ internal static MessageSerializer Get(Type messageType) {
+ RequiresEx.NotNullSubtype<IMessage>(messageType, "messageType");
return new MessageSerializer(messageType);
}
@@ -94,7 +91,7 @@ namespace DotNetOpenAuth.Messaging {
string type = "string";
MessagePart partDescription;
if (messageDictionary.Description.Mapping.TryGetValue(pair.Key, out partDescription)) {
- Contract.Assume(partDescription != null);
+ Assumes.True(partDescription != null);
if (partDescription.IsRequired || partDescription.IsNondefaultValueSet(messageDictionary.Message)) {
include = true;
Type formattingType = partDescription.PreferredFormattingType;
@@ -150,7 +147,6 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Parallel design with Deserialize method.")]
internal IDictionary<string, string> Serialize(MessageDictionary messageDictionary) {
Requires.NotNull(messageDictionary, "messageDictionary");
- Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
// Rather than hand back the whole message dictionary (which
// includes keys with blank values), create a new dictionary
@@ -160,7 +156,7 @@ namespace DotNetOpenAuth.Messaging {
foreach (var pair in messageDictionary) {
MessagePart partDescription;
if (messageDictionary.Description.Mapping.TryGetValue(pair.Key, out partDescription)) {
- Contract.Assume(partDescription != null);
+ Assumes.True(partDescription != null);
if (partDescription.IsRequired || partDescription.IsNondefaultValueSet(messageDictionary.Message)) {
result.Add(pair.Key, pair.Value);
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs
index 0bcde95..9885eb2 100644
--- a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs
@@ -9,26 +9,26 @@ namespace DotNetOpenAuth.Messaging {
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
-#if CLR4
using System.Net.Http;
-#endif
+ using System.Net.Http.Headers;
using System.Net.Mime;
using System.Runtime.Serialization.Json;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
+ using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Xml;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.Messaging.Reflection;
+ using Validation;
/// <summary>
/// A grab-bag of utility methods useful for the channel stack of the protocol.
@@ -88,6 +88,11 @@ namespace DotNetOpenAuth.Messaging {
private const int SymmetricSecretHandleLength = 4;
/// <summary>
+ /// A pre-completed task.
+ /// </summary>
+ private static readonly Task CompletedTaskField = Task.FromResult<object>(null);
+
+ /// <summary>
/// The default lifetime of a private secret.
/// </summary>
private static readonly TimeSpan SymmetricSecretKeyLifespan = Configuration.DotNetOpenAuthSection.Messaging.PrivateSecretMaximumAge;
@@ -151,44 +156,18 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Gets a random number generator for use on the current thread only.
+ /// Gets a pre-completed task.
/// </summary>
- internal static Random NonCryptoRandomDataGenerator {
- get { return ThreadSafeRandom.RandomNumberGenerator; }
- }
-
- /// <summary>
- /// Transforms an OutgoingWebResponse to an MVC-friendly ActionResult.
- /// </summary>
- /// <param name="response">The response to send to the user agent.</param>
- /// <returns>The <see cref="ActionResult"/> instance to be returned by the Controller's action method.</returns>
- public static ActionResult AsActionResult(this OutgoingWebResponse response) {
- Requires.NotNull(response, "response");
- return new OutgoingWebResponseActionResult(response);
+ internal static Task CompletedTask {
+ get { return CompletedTaskField; }
}
-#if CLR4
/// <summary>
- /// Transforms an OutgoingWebResponse to a Web API-friendly HttpResponseMessage.
+ /// Gets a random number generator for use on the current thread only.
/// </summary>
- /// <param name="outgoingResponse">The response to send to the user agent.</param>
- /// <returns>The <see cref="HttpResponseMessage"/> instance to be returned by the Web API method.</returns>
- public static HttpResponseMessage AsHttpResponseMessage(this OutgoingWebResponse outgoingResponse) {
- HttpResponseMessage response = new HttpResponseMessage(outgoingResponse.Status);
- if (outgoingResponse.ResponseStream != null) {
- response.Content = new StreamContent(outgoingResponse.ResponseStream);
- }
-
- var responseHeaders = outgoingResponse.Headers;
- foreach (var header in responseHeaders.AllKeys) {
- if (!response.Headers.TryAddWithoutValidation(header, responseHeaders[header])) {
- response.Content.Headers.TryAddWithoutValidation(header, responseHeaders[header]);
- }
- }
-
- return response;
+ internal static Random NonCryptoRandomDataGenerator {
+ get { return ThreadSafeRandom.RandomNumberGenerator; }
}
-#endif
/// <summary>
/// Gets the original request URL, as seen from the browser before any URL rewrites on the server if any.
@@ -198,7 +177,7 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "The Uri merging requires use of a string value.")]
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call should not be a property.")]
public static Uri GetRequestUrlFromContext() {
- Requires.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
+ RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
return new HttpRequestWrapper(HttpContext.Current.Request).GetPublicFacingUrl();
}
@@ -227,22 +206,6 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Sends a multipart HTTP POST request (useful for posting files).
- /// </summary>
- /// <param name="request">The HTTP request.</param>
- /// <param name="requestHandler">The request handler.</param>
- /// <param name="parts">The parts to include in the POST entity.</param>
- /// <returns>The HTTP response.</returns>
- public static IncomingWebResponse PostMultipart(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable<MultipartPostPart> parts) {
- Requires.NotNull(request, "request");
- Requires.NotNull(requestHandler, "requestHandler");
- Requires.NotNull(parts, "parts");
-
- PostMultipartNoGetResponse(request, requestHandler, parts);
- return requestHandler.GetResponse(request);
- }
-
- /// <summary>
/// Assembles a message comprised of the message on a given exception and all inner exceptions.
/// </summary>
/// <param name="exception">The exception.</param>
@@ -397,7 +360,15 @@ namespace DotNetOpenAuth.Messaging {
// HttpRequest.Url gives us the internal URL in a cloud environment,
// So we use a variable that (at least from what I can tell) gives us
// the public URL:
- if (serverVariables["HTTP_HOST"] != null) {
+ string httpHost;
+ try {
+ httpHost = serverVariables["HTTP_HOST"];
+ } catch (NullReferenceException) {
+ // The VS dev web server can throw this. :(
+ httpHost = null;
+ }
+
+ if (httpHost != null) {
ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols.");
string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ??
(string.Equals(serverVariables["HTTP_FRONT_END_HTTPS"], "on", StringComparison.OrdinalIgnoreCase) ? Uri.UriSchemeHttps : request.Url.Scheme);
@@ -431,6 +402,115 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets the public facing URL for the given incoming HTTP request.
+ /// </summary>
+ /// <returns>The URI that the outside world used to create this request.</returns>
+ public static Uri GetPublicFacingUrl() {
+ ErrorUtilities.VerifyHttpContext();
+ return GetPublicFacingUrl(new HttpRequestWrapper(HttpContext.Current.Request));
+ }
+
+ /// <summary>
+ /// Wraps a response message as an MVC <see cref="ActionResult"/> so it can be conveniently returned from an MVC controller's action method.
+ /// </summary>
+ /// <param name="response">The response message.</param>
+ /// <returns>An <see cref="ActionResult"/> instance.</returns>
+ public static ActionResult AsActionResult(this HttpResponseMessage response) {
+ Requires.NotNull(response, "response");
+ return new HttpResponseMessageActionResult(response);
+ }
+
+ /// <summary>
+ /// Wraps an instance of <see cref="HttpRequestBase"/> as an <see cref="HttpRequestMessage"/> instance.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>An instance of <see cref="HttpRequestMessage"/></returns>
+ public static HttpRequestMessage AsHttpRequestMessage(this HttpRequestBase request) {
+ Requires.NotNull(request, "request");
+
+ Uri publicFacingUrl = request.GetPublicFacingUrl();
+ var httpRequest = new HttpRequestMessage(new HttpMethod(request.HttpMethod), publicFacingUrl);
+
+ if (request.Form != null) {
+ // Avoid a request message that will try to read the request stream twice for already parsed data.
+ httpRequest.Content = new FormUrlEncodedContent(request.Form.AsKeyValuePairs());
+ } else if (request.InputStream != null) {
+ httpRequest.Content = new StreamContent(request.InputStream);
+ }
+
+ httpRequest.CopyHeadersFrom(request);
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Sends a response message to the HTTP client.
+ /// </summary>
+ /// <param name="response">The response message.</param>
+ /// <param name="context">The HTTP context to send the response with.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A task that completes with the asynchronous operation.
+ /// </returns>
+ public static async Task SendAsync(this HttpResponseMessage response, HttpContextBase context = null, CancellationToken cancellationToken = default(CancellationToken)) {
+ Requires.NotNull(response, "response");
+ if (context == null) {
+ ErrorUtilities.VerifyHttpContext();
+ context = new HttpContextWrapper(HttpContext.Current);
+ }
+
+ var responseContext = context.Response;
+ responseContext.StatusCode = (int)response.StatusCode;
+ responseContext.StatusDescription = response.ReasonPhrase;
+ foreach (var header in response.Headers) {
+ foreach (var value in header.Value) {
+ responseContext.AddHeader(header.Key, value);
+ }
+ }
+
+ if (response.Content != null) {
+ await response.Content.CopyToAsync(responseContext.OutputStream).ConfigureAwait(false);
+ }
+ }
+
+ /// <summary>
+ /// Disposes a value if it is not null.
+ /// </summary>
+ /// <param name="disposable">The disposable value.</param>
+ internal static void DisposeIfNotNull(this IDisposable disposable) {
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Clones the specified <see cref="HttpRequestMessage"/> so it can be re-sent.
+ /// </summary>
+ /// <param name="original">The original message.</param>
+ /// <returns>The cloned message</returns>
+ /// <remarks>
+ /// This is useful when an HTTP request fails, and after a little tweaking should be resent.
+ /// Since <see cref="HttpRequestMessage"/> remembers it was already sent, it will not permit being
+ /// sent a second time. This method clones the message so its contents are identical but allows
+ /// re-sending.
+ /// </remarks>
+ internal static HttpRequestMessage Clone(this HttpRequestMessage original) {
+ Requires.NotNull(original, "original");
+
+ var clone = new HttpRequestMessage(original.Method, original.RequestUri);
+ clone.Content = original.Content;
+ foreach (var header in original.Headers) {
+ clone.Headers.Add(header.Key, header.Value);
+ }
+
+ foreach (var property in original.Properties) {
+ clone.Properties[property.Key] = property.Value;
+ }
+
+ clone.Version = original.Version;
+ return clone;
+ }
+
+ /// <summary>
/// Gets the URL to the root of a web site, which may include a virtual directory path.
/// </summary>
/// <returns>An absolute URI.</returns>
@@ -457,11 +537,7 @@ namespace DotNetOpenAuth.Messaging {
return new XmlReaderSettings {
MaxCharactersFromEntities = 1024,
XmlResolver = null,
-#if CLR4
DtdProcessing = DtdProcessing.Prohibit,
-#else
- ProhibitDtd = true,
-#endif
};
}
@@ -505,59 +581,6 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Sends a multipart HTTP POST request (useful for posting files) but doesn't call GetResponse on it.
- /// </summary>
- /// <param name="request">The HTTP request.</param>
- /// <param name="requestHandler">The request handler.</param>
- /// <param name="parts">The parts to include in the POST entity.</param>
- internal static void PostMultipartNoGetResponse(this HttpWebRequest request, IDirectWebRequestHandler requestHandler, IEnumerable<MultipartPostPart> parts) {
- Requires.NotNull(request, "request");
- Requires.NotNull(requestHandler, "requestHandler");
- Requires.NotNull(parts, "parts");
-
- Reporting.RecordFeatureUse("MessagingUtilities.PostMultipart");
- parts = parts.CacheGeneratedResults();
- string boundary = Guid.NewGuid().ToString();
- string initialPartLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "--{0}\r\n", boundary);
- string partLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}\r\n", boundary);
- string finalTrailingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}--\r\n", boundary);
- var contentType = new ContentType("multipart/form-data") {
- Boundary = boundary,
- CharSet = Channel.PostEntityEncoding.WebName,
- };
-
- request.Method = "POST";
- request.ContentType = contentType.ToString();
- long contentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length;
- if (parts.Any()) {
- contentLength -= 2; // the initial part leading boundary has no leading \r\n
- }
- request.ContentLength = contentLength;
-
- var requestStream = requestHandler.GetRequestStream(request);
- try {
- StreamWriter writer = new StreamWriter(requestStream, Channel.PostEntityEncoding);
- bool firstPart = true;
- foreach (var part in parts) {
- writer.Write(firstPart ? initialPartLeadingBoundary : partLeadingBoundary);
- firstPart = false;
- part.Serialize(writer);
- part.Dispose();
- }
-
- writer.Write(finalTrailingBoundary);
- writer.Flush();
- } finally {
- // We need to be sure to close the request stream...
- // unless it is a MemoryStream, which is a clue that we're in
- // a mock stream situation and closing it would preclude reading it later.
- if (!(requestStream is MemoryStream)) {
- requestStream.Dispose();
- }
- }
- }
-
- /// <summary>
/// Assembles the content of the HTTP Authorization or WWW-Authenticate header.
/// </summary>
/// <param name="fields">The fields to include.</param>
@@ -603,25 +626,15 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="scheme">The scheme. Must not be null or empty.</param>
/// <param name="authorizationHeader">The authorization header. May be null or empty.</param>
/// <returns>A sequence of key=value pairs discovered in the header. Never null, but may be empty.</returns>
- internal static IEnumerable<KeyValuePair<string, string>> ParseAuthorizationHeader(string scheme, string authorizationHeader) {
+ internal static IEnumerable<KeyValuePair<string, string>> ParseAuthorizationHeader(string scheme, AuthenticationHeaderValue authorizationHeader) {
Requires.NotNullOrEmpty(scheme, "scheme");
- Contract.Ensures(Contract.Result<IEnumerable<KeyValuePair<string, string>>>() != null);
-
- string prefix = scheme + " ";
- if (authorizationHeader != null) {
- // The authorization header may have multiple sections. Look for the appropriate one.
- string[] authorizationSections = new string[] { authorizationHeader }; // what is the right delimiter, if any?
- foreach (string authorization in authorizationSections) {
- string trimmedAuth = authorization.Trim();
- if (trimmedAuth.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { // RFC 2617 says this is case INsensitive
- string data = trimmedAuth.Substring(prefix.Length);
- return from element in data.Split(CommaArray)
- let parts = element.Trim().Split(EqualsArray, 2)
- let key = Uri.UnescapeDataString(parts[0])
- let value = Uri.UnescapeDataString(parts[1].Trim(QuoteArray))
- select new KeyValuePair<string, string>(key, value);
- }
- }
+
+ if (authorizationHeader != null && authorizationHeader.Scheme.Equals(scheme, StringComparison.OrdinalIgnoreCase)) { // RFC 2617 says this is case INsensitive
+ return from element in authorizationHeader.Parameter.Split(CommaArray)
+ let parts = element.Trim().Split(EqualsArray, 2)
+ let key = Uri.UnescapeDataString(parts[0])
+ let value = Uri.UnescapeDataString(parts[1].Trim(QuoteArray))
+ select new KeyValuePair<string, string>(key, value);
}
return Enumerable.Empty<KeyValuePair<string, string>>();
@@ -637,7 +650,6 @@ namespace DotNetOpenAuth.Messaging {
internal static string CombineKeyHandleAndPayload(string handle, string payload) {
Requires.NotNullOrEmpty(handle, "handle");
Requires.NotNullOrEmpty(payload, "payload");
- Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
return handle + "!" + payload;
}
@@ -714,8 +726,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="allowableCharacters">The allowable characters.</param>
/// <returns>A random string.</returns>
internal static string GetRandomString(int length, string allowableCharacters) {
- Requires.InRange(length >= 0, "length");
- Requires.True(allowableCharacters != null && allowableCharacters.Length >= 2, "allowableCharacters");
+ Requires.Range(length >= 0, "length");
+ Requires.That(allowableCharacters != null && allowableCharacters.Length >= 2, "allowableCharacters", "At least two allowable characters required.");
char[] randomString = new char[length];
var random = NonCryptoRandomDataGenerator;
@@ -736,7 +748,6 @@ namespace DotNetOpenAuth.Messaging {
internal static string ComputeHash(this HashAlgorithm algorithm, string value, Encoding encoding = null) {
Requires.NotNull(algorithm, "algorithm");
Requires.NotNull(value, "value");
- Contract.Ensures(Contract.Result<string>() != null);
encoding = encoding ?? Encoding.UTF8;
byte[] bytesToHash = encoding.GetBytes(value);
@@ -755,7 +766,6 @@ namespace DotNetOpenAuth.Messaging {
internal static string ComputeHash(this HashAlgorithm algorithm, IDictionary<string, string> data, Encoding encoding = null) {
Requires.NotNull(algorithm, "algorithm");
Requires.NotNull(data, "data");
- Contract.Ensures(Contract.Result<string>() != null);
// Assemble the dictionary to sign, taking care to remove the signature itself
// in order to accurately reproduce the original signature (which of course didn't include
@@ -776,7 +786,6 @@ namespace DotNetOpenAuth.Messaging {
internal static string ComputeHash(this HashAlgorithm algorithm, IEnumerable<KeyValuePair<string, string>> sortedData, Encoding encoding = null) {
Requires.NotNull(algorithm, "algorithm");
Requires.NotNull(sortedData, "sortedData");
- Contract.Ensures(Contract.Result<string>() != null);
return ComputeHash(algorithm, CreateQueryString(sortedData), encoding);
}
@@ -955,7 +964,7 @@ namespace DotNetOpenAuth.Messaging {
internal static KeyValuePair<string, CryptoKey> GetCurrentKey(this ICryptoKeyStore cryptoKeyStore, string bucket, TimeSpan minimumRemainingLife, int keySize = 256) {
Requires.NotNull(cryptoKeyStore, "cryptoKeyStore");
Requires.NotNullOrEmpty(bucket, "bucket");
- Requires.True(keySize % 8 == 0, "keySize");
+ Requires.That(keySize % 8 == 0, "keySize", "Key size must be a multiple of 8.");
var cryptoKeyPair = cryptoKeyStore.GetKeys(bucket).FirstOrDefault(pair => pair.Value.Key.Length == keySize / 8);
if (cryptoKeyPair.Value == null || cryptoKeyPair.Value.ExpiresUtc < DateTime.UtcNow + minimumRemainingLife) {
@@ -998,7 +1007,6 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")]
internal static byte[] Compress(byte[] buffer, CompressionMethod method = CompressionMethod.Deflate) {
Requires.NotNull(buffer, "buffer");
- Contract.Ensures(Contract.Result<byte[]>() != null);
using (var ms = new MemoryStream()) {
Stream compressingStream = null;
@@ -1011,7 +1019,7 @@ namespace DotNetOpenAuth.Messaging {
compressingStream = new GZipStream(ms, CompressionMode.Compress, true);
break;
default:
- Requires.InRange(false, "method");
+ Requires.Range(false, "method");
break;
}
@@ -1035,7 +1043,6 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "This Dispose is safe.")]
internal static byte[] Decompress(byte[] buffer, CompressionMethod method = CompressionMethod.Deflate) {
Requires.NotNull(buffer, "buffer");
- Contract.Ensures(Contract.Result<byte[]>() != null);
using (var compressedDataStream = new MemoryStream(buffer)) {
using (var decompressedDataStream = new MemoryStream()) {
@@ -1049,7 +1056,7 @@ namespace DotNetOpenAuth.Messaging {
decompressingStream = new GZipStream(compressedDataStream, CompressionMode.Decompress, true);
break;
default:
- Requires.InRange(false, "method");
+ Requires.Range(false, "method");
break;
}
@@ -1089,7 +1096,6 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>A data buffer.</returns>
internal static byte[] FromBase64WebSafeString(string base64WebSafe) {
Requires.NotNullOrEmpty(base64WebSafe, "base64WebSafe");
- Contract.Ensures(Contract.Result<byte[]>() != null);
// Restore the padding characters and original URL-unsafe characters.
int missingPaddingCharacters;
@@ -1139,51 +1145,6 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Adds a set of HTTP headers to an <see cref="HttpResponse"/> instance,
- /// taking care to set some headers to the appropriate properties of
- /// <see cref="HttpResponse" />
- /// </summary>
- /// <param name="headers">The headers to add.</param>
- /// <param name="response">The <see cref="HttpListenerResponse"/> instance to set the appropriate values to.</param>
- internal static void ApplyHeadersToResponse(WebHeaderCollection headers, HttpListenerResponse response) {
- Requires.NotNull(headers, "headers");
- Requires.NotNull(response, "response");
-
- foreach (string headerName in headers) {
- switch (headerName) {
- case "Content-Type":
- response.ContentType = headers[HttpResponseHeader.ContentType];
- break;
-
- // Add more special cases here as necessary.
- default:
- response.AddHeader(headerName, headers[headerName]);
- break;
- }
- }
- }
-
-#if !CLR4
- /// <summary>
- /// Copies the contents of one stream to another.
- /// </summary>
- /// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param>
- /// <param name="copyTo">The stream to copy to, at the position where bytes should be written.</param>
- /// <returns>The total number of bytes copied.</returns>
- /// <remarks>
- /// Copying begins at the streams' current positions.
- /// The positions are NOT reset after copying is complete.
- /// </remarks>
- internal static int CopyTo(this Stream copyFrom, Stream copyTo) {
- Requires.NotNull(copyFrom, "copyFrom");
- Requires.NotNull(copyTo, "copyTo");
- Requires.True(copyFrom.CanRead, "copyFrom", MessagingStrings.StreamUnreadable);
- Requires.True(copyTo.CanWrite, "copyTo", MessagingStrings.StreamUnwritable);
- return CopyUpTo(copyFrom, copyTo, int.MaxValue);
- }
-#endif
-
- /// <summary>
/// Copies the contents of one stream to another.
/// </summary>
/// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param>
@@ -1197,8 +1158,8 @@ namespace DotNetOpenAuth.Messaging {
internal static int CopyUpTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) {
Requires.NotNull(copyFrom, "copyFrom");
Requires.NotNull(copyTo, "copyTo");
- Requires.True(copyFrom.CanRead, "copyFrom", MessagingStrings.StreamUnreadable);
- Requires.True(copyTo.CanWrite, "copyTo", MessagingStrings.StreamUnwritable);
+ Requires.That(copyFrom.CanRead, "copyFrom", MessagingStrings.StreamUnreadable);
+ Requires.That(copyTo.CanWrite, "copyTo", MessagingStrings.StreamUnwritable);
byte[] buffer = new byte[1024];
int readBytes;
@@ -1220,7 +1181,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>A seekable stream with the same contents as the original.</returns>
internal static Stream CreateSnapshot(this Stream copyFrom) {
Requires.NotNull(copyFrom, "copyFrom");
- Requires.True(copyFrom.CanRead, "copyFrom", MessagingStrings.StreamUnreadable);
+ Requires.That(copyFrom.CanRead, "copyFrom", MessagingStrings.StreamUnreadable);
MemoryStream copyTo = new MemoryStream(copyFrom.CanSeek ? (int)copyFrom.Length : 4 * 1024);
try {
@@ -1234,80 +1195,20 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Clones an <see cref="HttpWebRequest"/> in order to send it again.
+ /// Clones an <see cref="HttpWebRequest" /> in order to send it again.
/// </summary>
- /// <param name="request">The request to clone.</param>
- /// <returns>The newly created instance.</returns>
- internal static HttpWebRequest Clone(this HttpWebRequest request) {
+ /// <param name="message">The message to set headers on.</param>
+ /// <param name="request">The request with headers to clone.</param>
+ internal static void CopyHeadersFrom(this HttpRequestMessage message, HttpRequestBase request) {
Requires.NotNull(request, "request");
- Requires.True(request.RequestUri != null, "request");
- return Clone(request, request.RequestUri);
- }
-
- /// <summary>
- /// Clones an <see cref="HttpWebRequest"/> in order to send it again.
- /// </summary>
- /// <param name="request">The request to clone.</param>
- /// <param name="newRequestUri">The new recipient of the request.</param>
- /// <returns>The newly created instance.</returns>
- internal static HttpWebRequest Clone(this HttpWebRequest request, Uri newRequestUri) {
- Requires.NotNull(request, "request");
- Requires.NotNull(newRequestUri, "newRequestUri");
-
- var newRequest = (HttpWebRequest)WebRequest.Create(newRequestUri);
+ Requires.NotNull(message, "message");
- // First copy headers. Only set those that are explicitly set on the original request,
- // because some properties (like IfModifiedSince) activate special behavior when set,
- // even when set to their "original" values.
foreach (string headerName in request.Headers) {
- switch (headerName) {
- case "Accept": newRequest.Accept = request.Accept; break;
- case "Connection": break; // Keep-Alive controls this
- case "Content-Length": newRequest.ContentLength = request.ContentLength; break;
- case "Content-Type": newRequest.ContentType = request.ContentType; break;
- case "Expect": newRequest.Expect = request.Expect; break;
- case "Host": break; // implicitly copied as part of the RequestUri
- case "If-Modified-Since": newRequest.IfModifiedSince = request.IfModifiedSince; break;
- case "Keep-Alive": newRequest.KeepAlive = request.KeepAlive; break;
- case "Proxy-Connection": break; // no property equivalent?
- case "Referer": newRequest.Referer = request.Referer; break;
- case "Transfer-Encoding": newRequest.TransferEncoding = request.TransferEncoding; break;
- case "User-Agent": newRequest.UserAgent = request.UserAgent; break;
- default: newRequest.Headers[headerName] = request.Headers[headerName]; break;
+ string[] headerValues = request.Headers.GetValues(headerName);
+ if (!message.Headers.TryAddWithoutValidation(headerName, headerValues)) {
+ message.Content.Headers.TryAddWithoutValidation(headerName, headerValues);
}
}
-
- newRequest.AllowAutoRedirect = request.AllowAutoRedirect;
- newRequest.AllowWriteStreamBuffering = request.AllowWriteStreamBuffering;
- newRequest.AuthenticationLevel = request.AuthenticationLevel;
- newRequest.AutomaticDecompression = request.AutomaticDecompression;
- newRequest.CachePolicy = request.CachePolicy;
- newRequest.ClientCertificates = request.ClientCertificates;
- newRequest.ConnectionGroupName = request.ConnectionGroupName;
- newRequest.ContinueDelegate = request.ContinueDelegate;
- newRequest.CookieContainer = request.CookieContainer;
- newRequest.Credentials = request.Credentials;
- newRequest.ImpersonationLevel = request.ImpersonationLevel;
- newRequest.MaximumAutomaticRedirections = request.MaximumAutomaticRedirections;
- newRequest.MaximumResponseHeadersLength = request.MaximumResponseHeadersLength;
- newRequest.MediaType = request.MediaType;
- newRequest.Method = request.Method;
- newRequest.Pipelined = request.Pipelined;
- newRequest.PreAuthenticate = request.PreAuthenticate;
- newRequest.ProtocolVersion = request.ProtocolVersion;
- newRequest.ReadWriteTimeout = request.ReadWriteTimeout;
- newRequest.SendChunked = request.SendChunked;
- newRequest.Timeout = request.Timeout;
- newRequest.UseDefaultCredentials = request.UseDefaultCredentials;
-
- try {
- newRequest.Proxy = request.Proxy;
- newRequest.UnsafeAuthenticatedConnectionSharing = request.UnsafeAuthenticatedConnectionSharing;
- } catch (SecurityException) {
- Logger.Messaging.Warn("Unable to clone some HttpWebRequest properties due to partial trust.");
- }
-
- return newRequest;
}
/// <summary>
@@ -1453,7 +1354,6 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The formulated querystring style string.</returns>
internal static string CreateQueryString(IEnumerable<KeyValuePair<string, string>> args) {
Requires.NotNull(args, "args");
- Contract.Ensures(Contract.Result<string>() != null);
if (!args.Any()) {
return string.Empty;
@@ -1559,8 +1459,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The request to get recipient information from.</param>
/// <returns>The recipient.</returns>
/// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception>
- internal static MessageReceivingEndpoint GetRecipient(this HttpRequestBase request) {
- return new MessageReceivingEndpoint(request.GetPublicFacingUrl(), GetHttpDeliveryMethod(request.HttpMethod));
+ internal static MessageReceivingEndpoint GetRecipient(this HttpRequestMessage request) {
+ return new MessageReceivingEndpoint(request.RequestUri, GetHttpDeliveryMethod(request.Method.Method));
}
/// <summary>
@@ -1594,23 +1494,23 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="httpMethod">The HTTP method.</param>
/// <returns>An HTTP verb, such as GET, POST, PUT, DELETE, PATCH, or OPTION.</returns>
- internal static string GetHttpVerb(HttpDeliveryMethods httpMethod) {
+ internal static HttpMethod GetHttpVerb(HttpDeliveryMethods httpMethod) {
if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.GetRequest) {
- return "GET";
+ return HttpMethod.Get;
} else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PostRequest) {
- return "POST";
+ return HttpMethod.Post;
} else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PutRequest) {
- return "PUT";
+ return HttpMethod.Put;
} else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.DeleteRequest) {
- return "DELETE";
+ return HttpMethod.Delete;
} else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.HeadRequest) {
- return "HEAD";
+ return HttpMethod.Head;
} else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.PatchRequest) {
- return "PATCH";
+ return new HttpMethod("PATCH");
} else if ((httpMethod & HttpDeliveryMethods.HttpVerbMask) == HttpDeliveryMethods.OptionsRequest) {
- return "OPTIONS";
+ return HttpMethod.Options;
} else if ((httpMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) {
- return "GET"; // if AuthorizationHeaderRequest is specified without an explicit HTTP verb, assume GET.
+ return HttpMethod.Get; // if AuthorizationHeaderRequest is specified without an explicit HTTP verb, assume GET.
} else {
throw ErrorUtilities.ThrowArgumentNamed("httpMethod", MessagingStrings.UnsupportedHttpVerb, httpMethod);
}
@@ -1636,6 +1536,29 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets the URI that contains the entire payload that would be sent by the browser for the specified redirect-based request message.
+ /// </summary>
+ /// <param name="response">The redirecting response message.</param>
+ /// <returns>The absolute URI that could be retrieved to send the same message the browser would.</returns>
+ /// <exception cref="System.NotSupportedException">Thrown if the message is not a redirect message.</exception>
+ internal static Uri GetDirectUriRequest(this HttpResponseMessage response) {
+ Requires.NotNull(response, "response");
+ Requires.Argument(
+ response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectKeepVerb
+ || response.StatusCode == HttpStatusCode.RedirectMethod || response.StatusCode == HttpStatusCode.TemporaryRedirect,
+ "response",
+ "Redirecting response expected.");
+
+ if (response.Headers.Location != null) {
+ return response.Headers.Location;
+ } else {
+ // Some responses are so large that they're HTML/JS self-posting pages.
+ // We can't create long URLs for those, at present.
+ throw new NotSupportedException();
+ }
+ }
+
+ /// <summary>
/// Collects a sequence of key=value pairs into a dictionary.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
@@ -1672,7 +1595,6 @@ namespace DotNetOpenAuth.Messaging {
/// <c>Dictionary&lt;string, string&gt;</c> does not allow null keys.
/// </remarks>
internal static Dictionary<string, string> ToDictionary(this NameValueCollection nvc) {
- Contract.Ensures((nvc != null && Contract.Result<Dictionary<string, string>>() != null) || (nvc == null && Contract.Result<Dictionary<string, string>>() == null));
return ToDictionary(nvc, false);
}
@@ -1688,7 +1610,6 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The generated dictionary, or null if <paramref name="nvc"/> is null.</returns>
/// <exception cref="ArgumentException">Thrown if <paramref name="throwOnNullKey"/> is <c>true</c> and a null key is encountered.</exception>
internal static Dictionary<string, string> ToDictionary(this NameValueCollection nvc, bool throwOnNullKey) {
- Contract.Ensures((nvc != null && Contract.Result<Dictionary<string, string>>() != null) || (nvc == null && Contract.Result<Dictionary<string, string>>() == null));
if (nvc == null) {
return null;
}
@@ -1742,7 +1663,6 @@ namespace DotNetOpenAuth.Messaging {
Requires.NotNull(source, "source");
Requires.NotNull(comparer, "comparer");
Requires.NotNull(keySelector, "keySelector");
- Contract.Ensures(Contract.Result<IOrderedEnumerable<TSource>>() != null);
return System.Linq.Enumerable.OrderBy<TSource, TKey>(source, keySelector, new ComparisonHelper<TKey>(comparer));
}
@@ -1805,7 +1725,7 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The read buffer.</returns>
internal static byte[] ReadBuffer(this BinaryReader reader, int maxBufferSize) {
Requires.NotNull(reader, "reader");
- Requires.InRange(maxBufferSize > 0 && maxBufferSize < 1024 * 1024, "maxBufferSize");
+ Requires.Range(maxBufferSize > 0 && maxBufferSize < 1024 * 1024, "maxBufferSize");
int length = reader.ReadInt32();
ErrorUtilities.VerifyProtocol(length <= maxBufferSize, MessagingStrings.DataCorruptionDetected);
byte[] buffer = new byte[length];
@@ -1936,6 +1856,11 @@ namespace DotNetOpenAuth.Messaging {
internal static string EscapeUriDataStringRfc3986(string value) {
Requires.NotNull(value, "value");
+ // fast path for empty values.
+ if (value.Length == 0) {
+ return value;
+ }
+
// Start with RFC 2396 escaping by calling the .NET method to do the work.
// This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
// If it does, the escaping we do that follows it will be a no-op since the
@@ -2113,5 +2038,33 @@ namespace DotNetOpenAuth.Messaging {
#endregion
}
+
+ /// <summary>
+ /// An MVC <see cref="ActionResult"/> that wraps an <see cref="HttpResponseMessage"/>
+ /// </summary>
+ private class HttpResponseMessageActionResult : ActionResult {
+ /// <summary>
+ /// The wrapped response.
+ /// </summary>
+ private readonly HttpResponseMessage response;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HttpResponseMessageActionResult"/> class.
+ /// </summary>
+ /// <param name="response">The response.</param>
+ internal HttpResponseMessageActionResult(HttpResponseMessage response) {
+ Requires.NotNull(response, "response");
+ this.response = response;
+ }
+
+ /// <summary>
+ /// Enables processing of the result of an action method by a custom type that inherits from the <see cref="T:System.Web.Mvc.ActionResult" /> class.
+ /// </summary>
+ /// <param name="context">The context in which the result is executed. The context information includes the controller, HTTP content, request context, and route data.</param>
+ public override void ExecuteResult(ControllerContext context) {
+ // TODO: fix this to be asynchronous.
+ this.response.SendAsync(context.HttpContext).GetAwaiter().GetResult();
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/MultipartContentMember.cs b/src/DotNetOpenAuth.Core/Messaging/MultipartContentMember.cs
new file mode 100644
index 0000000..fd5bfb5
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/MultipartContentMember.cs
@@ -0,0 +1,56 @@
+//-----------------------------------------------------------------------
+// <copyright file="MultipartContentMember.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net.Http;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ /// <summary>
+ /// Describes a part from a multi-part POST.
+ /// </summary>
+ public struct MultipartContentMember {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MultipartContentMember"/> struct.
+ /// </summary>
+ /// <param name="content">The content.</param>
+ /// <param name="name">The name of this part as it may come from an HTML form.</param>
+ /// <param name="fileName">Name of the file.</param>
+ public MultipartContentMember(HttpContent content, string name = null, string fileName = null)
+ : this() {
+ this.Content = content;
+ this.Name = name;
+ this.FileName = fileName;
+ }
+
+ /// <summary>
+ /// Gets or sets the content.
+ /// </summary>
+ /// <value>
+ /// The content.
+ /// </value>
+ public HttpContent Content { get; set; }
+
+ /// <summary>
+ /// Gets or sets the HTML form name.
+ /// </summary>
+ /// <value>
+ /// The name.
+ /// </value>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name of the file.
+ /// </summary>
+ /// <value>
+ /// The name of the file.
+ /// </value>
+ public string FileName { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Messaging/MultipartPostPart.cs b/src/DotNetOpenAuth.Core/Messaging/MultipartPostPart.cs
deleted file mode 100644
index 055e4b9..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/MultipartPostPart.cs
+++ /dev/null
@@ -1,223 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="MultipartPostPart.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
- using System.IO;
- using System.Net;
- using System.Text;
-
- /// <summary>
- /// Represents a single part in a HTTP multipart POST request.
- /// </summary>
- public class MultipartPostPart : IDisposable {
- /// <summary>
- /// The "Content-Disposition" string.
- /// </summary>
- private const string ContentDispositionHeader = "Content-Disposition";
-
- /// <summary>
- /// The two-character \r\n newline character sequence to use.
- /// </summary>
- private const string NewLine = "\r\n";
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MultipartPostPart"/> class.
- /// </summary>
- /// <param name="contentDisposition">The content disposition of the part.</param>
- public MultipartPostPart(string contentDisposition) {
- Requires.NotNullOrEmpty(contentDisposition, "contentDisposition");
-
- this.ContentDisposition = contentDisposition;
- this.ContentAttributes = new Dictionary<string, string>();
- this.PartHeaders = new WebHeaderCollection();
- }
-
- /// <summary>
- /// Gets or sets the content disposition.
- /// </summary>
- /// <value>The content disposition.</value>
- public string ContentDisposition { get; set; }
-
- /// <summary>
- /// Gets the key=value attributes that appear on the same line as the Content-Disposition.
- /// </summary>
- /// <value>The content attributes.</value>
- public IDictionary<string, string> ContentAttributes { get; private set; }
-
- /// <summary>
- /// Gets the headers that appear on subsequent lines after the Content-Disposition.
- /// </summary>
- public WebHeaderCollection PartHeaders { get; private set; }
-
- /// <summary>
- /// Gets or sets the content of the part.
- /// </summary>
- public Stream Content { get; set; }
-
- /// <summary>
- /// Gets the length of this entire part.
- /// </summary>
- /// <remarks>Useful for calculating the ContentLength HTTP header to send before actually serializing the content.</remarks>
- public long Length {
- get {
- ErrorUtilities.VerifyOperation(this.Content != null && this.Content.Length >= 0, MessagingStrings.StreamMustHaveKnownLength);
-
- long length = 0;
- length += ContentDispositionHeader.Length;
- length += ": ".Length;
- length += this.ContentDisposition.Length;
- foreach (var pair in this.ContentAttributes) {
- length += "; ".Length + pair.Key.Length + "=\"".Length + pair.Value.Length + "\"".Length;
- }
-
- length += NewLine.Length;
- foreach (string headerName in this.PartHeaders) {
- length += headerName.Length;
- length += ": ".Length;
- length += this.PartHeaders[headerName].Length;
- length += NewLine.Length;
- }
-
- length += NewLine.Length;
- length += this.Content.Length;
-
- return length;
- }
- }
-
- /// <summary>
- /// Creates a part that represents a simple form field.
- /// </summary>
- /// <param name="name">The name of the form field.</param>
- /// <param name="value">The value.</param>
- /// <returns>The constructed part.</returns>
- public static MultipartPostPart CreateFormPart(string name, string value) {
- Requires.NotNullOrEmpty(name, "name");
- Requires.NotNull(value, "value");
-
- var part = new MultipartPostPart("form-data");
- try {
- part.ContentAttributes["name"] = name;
- part.Content = new MemoryStream(Encoding.UTF8.GetBytes(value));
- return part;
- } catch {
- part.Dispose();
- throw;
- }
- }
-
- /// <summary>
- /// Creates a part that represents a file attachment.
- /// </summary>
- /// <param name="name">The name of the form field.</param>
- /// <param name="filePath">The path to the file to send.</param>
- /// <param name="contentType">Type of the content in HTTP Content-Type format.</param>
- /// <returns>The constructed part.</returns>
- public static MultipartPostPart CreateFormFilePart(string name, string filePath, string contentType) {
- Requires.NotNullOrEmpty(name, "name");
- Requires.NotNullOrEmpty(filePath, "filePath");
- Requires.NotNullOrEmpty(contentType, "contentType");
-
- string fileName = Path.GetFileName(filePath);
- var fileStream = File.OpenRead(filePath);
- try {
- return CreateFormFilePart(name, fileName, contentType, fileStream);
- } catch {
- fileStream.Dispose();
- throw;
- }
- }
-
- /// <summary>
- /// Creates a part that represents a file attachment.
- /// </summary>
- /// <param name="name">The name of the form field.</param>
- /// <param name="fileName">Name of the file as the server should see it.</param>
- /// <param name="contentType">Type of the content in HTTP Content-Type format.</param>
- /// <param name="content">The content of the file.</param>
- /// <returns>The constructed part.</returns>
- public static MultipartPostPart CreateFormFilePart(string name, string fileName, string contentType, Stream content) {
- Requires.NotNullOrEmpty(name, "name");
- Requires.NotNullOrEmpty(fileName, "fileName");
- Requires.NotNullOrEmpty(contentType, "contentType");
- Requires.NotNull(content, "content");
-
- var part = new MultipartPostPart("file");
- try {
- part.ContentAttributes["name"] = name;
- part.ContentAttributes["filename"] = fileName;
- part.PartHeaders[HttpRequestHeader.ContentType] = contentType;
- if (!contentType.StartsWith("text/", StringComparison.Ordinal)) {
- part.PartHeaders["Content-Transfer-Encoding"] = "binary";
- }
-
- part.Content = content;
- return part;
- } catch {
- part.Dispose();
- throw;
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose() {
- this.Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Serializes the part to a stream.
- /// </summary>
- /// <param name="streamWriter">The stream writer.</param>
- internal void Serialize(StreamWriter streamWriter) {
- // VERY IMPORTANT: any changes at all made to this must be kept in sync with the
- // Length property which calculates exactly how many bytes this method will write.
- streamWriter.NewLine = NewLine;
- streamWriter.Write("{0}: {1}", ContentDispositionHeader, this.ContentDisposition);
- foreach (var pair in this.ContentAttributes) {
- streamWriter.Write("; {0}=\"{1}\"", pair.Key, pair.Value);
- }
-
- streamWriter.WriteLine();
- foreach (string headerName in this.PartHeaders) {
- streamWriter.WriteLine("{0}: {1}", headerName, this.PartHeaders[headerName]);
- }
-
- streamWriter.WriteLine();
- streamWriter.Flush();
- this.Content.CopyTo(streamWriter.BaseStream);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool disposing) {
- if (disposing) {
- this.Content.Dispose();
- }
- }
-
-#if CONTRACTS_FULL
- /// <summary>
- /// Verifies conditions that should be true for any valid state of this object.
- /// </summary>
- [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
- [ContractInvariantMethod]
- private void Invariant() {
- Contract.Invariant(!string.IsNullOrEmpty(this.ContentDisposition));
- Contract.Invariant(this.PartHeaders != null);
- Contract.Invariant(this.ContentAttributes != null);
- }
-#endif
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/NetworkDirectWebResponse.cs b/src/DotNetOpenAuth.Core/Messaging/NetworkDirectWebResponse.cs
deleted file mode 100644
index 2c3ddac..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/NetworkDirectWebResponse.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="NetworkDirectWebResponse.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Diagnostics;
- using System.Diagnostics.Contracts;
- using System.IO;
- using System.Net;
- using System.Text;
-
- /// <summary>
- /// A live network HTTP response
- /// </summary>
- [DebuggerDisplay("{Status} {ContentType.MediaType}")]
- [ContractVerification(true)]
- internal class NetworkDirectWebResponse : IncomingWebResponse, IDisposable {
- /// <summary>
- /// The network response object, used to initialize this instance, that still needs
- /// to be closed if applicable.
- /// </summary>
- private HttpWebResponse httpWebResponse;
-
- /// <summary>
- /// The incoming network response stream.
- /// </summary>
- private Stream responseStream;
-
- /// <summary>
- /// A value indicating whether a stream reader has already been
- /// created on this instance.
- /// </summary>
- private bool streamReadBegun;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="NetworkDirectWebResponse"/> class.
- /// </summary>
- /// <param name="requestUri">The request URI.</param>
- /// <param name="response">The response.</param>
- internal NetworkDirectWebResponse(Uri requestUri, HttpWebResponse response)
- : base(requestUri, response) {
- Requires.NotNull(requestUri, "requestUri");
- Requires.NotNull(response, "response");
- this.httpWebResponse = response;
- this.responseStream = response.GetResponseStream();
- }
-
- /// <summary>
- /// Gets the body of the HTTP response.
- /// </summary>
- public override Stream ResponseStream {
- get { return this.responseStream; }
- }
-
- /// <summary>
- /// Creates a text reader for the response stream.
- /// </summary>
- /// <returns>The text reader, initialized for the proper encoding.</returns>
- public override StreamReader GetResponseReader() {
- this.streamReadBegun = true;
- if (this.responseStream == null) {
- throw new ObjectDisposedException(GetType().Name);
- }
-
- string contentEncoding = this.Headers[HttpResponseHeader.ContentEncoding];
- if (string.IsNullOrEmpty(contentEncoding)) {
- return new StreamReader(this.ResponseStream);
- } else {
- return new StreamReader(this.ResponseStream, Encoding.GetEncoding(contentEncoding));
- }
- }
-
- /// <summary>
- /// Gets an offline snapshot version of this instance.
- /// </summary>
- /// <param name="maximumBytesToCache">The maximum bytes from the response stream to cache.</param>
- /// <returns>A snapshot version of this instance.</returns>
- /// <remarks>
- /// If this instance is a <see cref="NetworkDirectWebResponse"/> creating a snapshot
- /// will automatically close and dispose of the underlying response stream.
- /// If this instance is a <see cref="CachedDirectWebResponse"/>, the result will
- /// be the self same instance.
- /// </remarks>
- internal override CachedDirectWebResponse GetSnapshot(int maximumBytesToCache) {
- ErrorUtilities.VerifyOperation(!this.streamReadBegun, "Network stream reading has already begun.");
- ErrorUtilities.VerifyOperation(this.httpWebResponse != null, "httpWebResponse != null");
-
- this.streamReadBegun = true;
- var result = new CachedDirectWebResponse(this.RequestUri, this.httpWebResponse, maximumBytesToCache);
- this.Dispose();
- return result;
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected override void Dispose(bool disposing) {
- if (disposing) {
- if (this.responseStream != null) {
- this.responseStream.Dispose();
- this.responseStream = null;
- }
- if (this.httpWebResponse != null) {
- this.httpWebResponse.Close();
- this.httpWebResponse = null;
- }
- }
-
- base.Dispose(disposing);
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponse.cs b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponse.cs
deleted file mode 100644
index 3b9ab41..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponse.cs
+++ /dev/null
@@ -1,387 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="OutgoingWebResponse.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.ComponentModel;
- using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
- using System.Globalization;
- using System.IO;
- using System.Net;
- using System.Net.Mime;
- using System.ServiceModel.Web;
- using System.Text;
- using System.Threading;
- using System.Web;
-
- /// <summary>
- /// A protocol message (request or response) that passes from this
- /// to a remote party via the user agent using a redirect or form
- /// POST submission, OR a direct message response.
- /// </summary>
- /// <remarks>
- /// <para>An instance of this type describes the HTTP response that must be sent
- /// in response to the current HTTP request.</para>
- /// <para>It is important that this response make up the entire HTTP response.
- /// A hosting ASPX page should not be allowed to render its normal HTML output
- /// after this response is sent. The normal rendered output of an ASPX page
- /// can be canceled by calling <see cref="HttpResponse.End"/> after this message
- /// is sent on the response stream.</para>
- /// </remarks>
- public class OutgoingWebResponse {
- /// <summary>
- /// The encoder to use for serializing the response body.
- /// </summary>
- private static Encoding bodyStringEncoder = new UTF8Encoding(false);
-
- /// <summary>
- /// Initializes a new instance of the <see cref="OutgoingWebResponse"/> class.
- /// </summary>
- internal OutgoingWebResponse() {
- this.Status = HttpStatusCode.OK;
- this.Headers = new WebHeaderCollection();
- this.Cookies = new HttpCookieCollection();
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="OutgoingWebResponse"/> class
- /// based on the contents of an <see cref="HttpWebResponse"/>.
- /// </summary>
- /// <param name="response">The <see cref="HttpWebResponse"/> to clone.</param>
- /// <param name="maximumBytesToRead">The maximum bytes to read from the response stream.</param>
- protected internal OutgoingWebResponse(HttpWebResponse response, int maximumBytesToRead) {
- Requires.NotNull(response, "response");
-
- this.Status = response.StatusCode;
- this.Headers = response.Headers;
- this.Cookies = new HttpCookieCollection();
- this.ResponseStream = new MemoryStream(response.ContentLength < 0 ? 4 * 1024 : (int)response.ContentLength);
- using (Stream responseStream = response.GetResponseStream()) {
- // BUGBUG: strictly speaking, is the response were exactly the limit, we'd report it as truncated here.
- this.IsResponseTruncated = responseStream.CopyUpTo(this.ResponseStream, maximumBytesToRead) == maximumBytesToRead;
- this.ResponseStream.Seek(0, SeekOrigin.Begin);
- }
- }
-
- /// <summary>
- /// Gets the headers that must be included in the response to the user agent.
- /// </summary>
- /// <remarks>
- /// The headers in this collection are not meant to be a comprehensive list
- /// of exactly what should be sent, but are meant to augment whatever headers
- /// are generally included in a typical response.
- /// </remarks>
- public WebHeaderCollection Headers { get; internal set; }
-
- /// <summary>
- /// Gets the body of the HTTP response.
- /// </summary>
- public Stream ResponseStream { get; internal set; }
-
- /// <summary>
- /// Gets a value indicating whether the response stream is incomplete due
- /// to a length limitation imposed by the HttpWebRequest or calling method.
- /// </summary>
- public bool IsResponseTruncated { get; internal set; }
-
- /// <summary>
- /// Gets the cookies collection to add as headers to the HTTP response.
- /// </summary>
- public HttpCookieCollection Cookies { get; internal set; }
-
- /// <summary>
- /// Gets or sets the body of the response as a string.
- /// </summary>
- public string Body {
- get { return this.ResponseStream != null ? this.GetResponseReader().ReadToEnd() : null; }
- set { this.SetResponse(value, null); }
- }
-
- /// <summary>
- /// Gets the HTTP status code to use in the HTTP response.
- /// </summary>
- public HttpStatusCode Status { get; internal set; }
-
- /// <summary>
- /// Gets or sets a reference to the actual protocol message that
- /// is being sent via the user agent.
- /// </summary>
- internal IProtocolMessage OriginalMessage { get; set; }
-
- /// <summary>
- /// Creates a text reader for the response stream.
- /// </summary>
- /// <returns>The text reader, initialized for the proper encoding.</returns>
- [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly operation")]
- public StreamReader GetResponseReader() {
- this.ResponseStream.Seek(0, SeekOrigin.Begin);
- string contentEncoding = this.Headers[HttpResponseHeader.ContentEncoding];
- if (string.IsNullOrEmpty(contentEncoding)) {
- return new StreamReader(this.ResponseStream);
- } else {
- return new StreamReader(this.ResponseStream, Encoding.GetEncoding(contentEncoding));
- }
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent
- /// and ends execution on the current page or handler.
- /// </summary>
- /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
- /// <remarks>
- /// Requires a current HttpContext.
- /// </remarks>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public virtual void Send() {
- Requires.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
-
- this.Send(HttpContext.Current);
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent
- /// and ends execution on the current page or handler.
- /// </summary>
- /// <param name="context">The context of the HTTP request whose response should be set.
- /// Typically this is <see cref="HttpContext.Current"/>.</param>
- /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public virtual void Send(HttpContext context) {
- this.Respond(new HttpContextWrapper(context), true);
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent
- /// and ends execution on the current page or handler.
- /// </summary>
- /// <param name="context">The context of the HTTP request whose response should be set.
- /// Typically this is <see cref="HttpContext.Current"/>.</param>
- /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
- [EditorBrowsable(EditorBrowsableState.Never)]
- public virtual void Send(HttpContextBase context) {
- this.Respond(context, true);
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent
- /// and signals ASP.NET to short-circuit the page execution pipeline
- /// now that the response has been completed.
- /// Not safe to call from ASP.NET web forms.
- /// </summary>
- /// <remarks>
- /// Requires a current HttpContext.
- /// This call is not safe to make from an ASP.NET web form (.aspx file or code-behind) because
- /// ASP.NET will render HTML after the protocol message has been sent, which will corrupt the response.
- /// Use the <see cref="Send()"/> method instead for web forms.
- /// </remarks>
- public virtual void Respond() {
- Requires.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
-
- this.Respond(HttpContext.Current);
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent
- /// and signals ASP.NET to short-circuit the page execution pipeline
- /// now that the response has been completed.
- /// Not safe to call from ASP.NET web forms.
- /// </summary>
- /// <param name="context">The context of the HTTP request whose response should be set.
- /// Typically this is <see cref="HttpContext.Current"/>.</param>
- /// <remarks>
- /// This call is not safe to make from an ASP.NET web form (.aspx file or code-behind) because
- /// ASP.NET will render HTML after the protocol message has been sent, which will corrupt the response.
- /// Use the <see cref="Send()"/> method instead for web forms.
- /// </remarks>
- public void Respond(HttpContext context) {
- Requires.NotNull(context, "context");
- this.Respond(new HttpContextWrapper(context));
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent
- /// and signals ASP.NET to short-circuit the page execution pipeline
- /// now that the response has been completed.
- /// Not safe to call from ASP.NET web forms.
- /// </summary>
- /// <param name="context">The context of the HTTP request whose response should be set.
- /// Typically this is <see cref="HttpContext.Current"/>.</param>
- /// <remarks>
- /// This call is not safe to make from an ASP.NET web form (.aspx file or code-behind) because
- /// ASP.NET will render HTML after the protocol message has been sent, which will corrupt the response.
- /// Use the <see cref="Send()"/> method instead for web forms.
- /// </remarks>
- public virtual void Respond(HttpContextBase context) {
- Requires.NotNull(context, "context");
-
- this.Respond(context, false);
- }
-
- /// <summary>
- /// Submits this response to a WCF response context. Only available when no response body is included.
- /// </summary>
- /// <param name="responseContext">The response context to apply the response to.</param>
- public virtual void Respond(OutgoingWebResponseContext responseContext) {
- Requires.NotNull(responseContext, "responseContext");
- if (this.ResponseStream != null) {
- throw new NotSupportedException(Strings.ResponseBodyNotSupported);
- }
-
- responseContext.StatusCode = this.Status;
- responseContext.SuppressEntityBody = true;
- foreach (string header in this.Headers) {
- responseContext.Headers[header] = this.Headers[header];
- }
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent.
- /// </summary>
- /// <param name="response">The response to set to this message.</param>
- public virtual void Send(HttpListenerResponse response) {
- Requires.NotNull(response, "response");
-
- response.StatusCode = (int)this.Status;
- MessagingUtilities.ApplyHeadersToResponse(this.Headers, response);
- foreach (HttpCookie httpCookie in this.Cookies) {
- var cookie = new Cookie(httpCookie.Name, httpCookie.Value) {
- Expires = httpCookie.Expires,
- Path = httpCookie.Path,
- HttpOnly = httpCookie.HttpOnly,
- Secure = httpCookie.Secure,
- Domain = httpCookie.Domain,
- };
- response.AppendCookie(cookie);
- }
-
- if (this.ResponseStream != null) {
- response.ContentLength64 = this.ResponseStream.Length;
- this.ResponseStream.CopyTo(response.OutputStream);
- }
-
- response.OutputStream.Close();
- }
-
- /// <summary>
- /// Gets the URI that, when requested with an HTTP GET request,
- /// would transmit the message that normally would be transmitted via a user agent redirect.
- /// </summary>
- /// <param name="channel">The channel to use for encoding.</param>
- /// <returns>
- /// The URL that would transmit the original message. This URL may exceed the normal 2K limit,
- /// and should therefore be broken up manually and POSTed as form fields when it exceeds this length.
- /// </returns>
- /// <remarks>
- /// This is useful for desktop applications that will spawn a user agent to transmit the message
- /// rather than cause a redirect.
- /// </remarks>
- internal Uri GetDirectUriRequest(Channel channel) {
- Requires.NotNull(channel, "channel");
-
- var message = this.OriginalMessage as IDirectedProtocolMessage;
- if (message == null) {
- throw new InvalidOperationException(); // this only makes sense for directed messages (indirect responses)
- }
-
- var fields = channel.MessageDescriptions.GetAccessor(message).Serialize();
- UriBuilder builder = new UriBuilder(message.Recipient);
- MessagingUtilities.AppendQueryArgs(builder, fields);
- return builder.Uri;
- }
-
- /// <summary>
- /// Sets the response to some string, encoded as UTF-8.
- /// </summary>
- /// <param name="body">The string to set the response to.</param>
- /// <param name="contentType">Type of the content. May be null.</param>
- internal void SetResponse(string body, ContentType contentType) {
- if (body == null) {
- this.ResponseStream = null;
- return;
- }
-
- if (contentType == null) {
- contentType = new ContentType("text/html");
- contentType.CharSet = bodyStringEncoder.WebName;
- } else if (contentType.CharSet != bodyStringEncoder.WebName) {
- // clone the original so we're not tampering with our inputs if it came as a parameter.
- contentType = new ContentType(contentType.ToString());
- contentType.CharSet = bodyStringEncoder.WebName;
- }
-
- this.Headers[HttpResponseHeader.ContentType] = contentType.ToString();
- this.ResponseStream = new MemoryStream();
- StreamWriter writer = new StreamWriter(this.ResponseStream, bodyStringEncoder);
- writer.Write(body);
- writer.Flush();
- this.ResponseStream.Seek(0, SeekOrigin.Begin);
- this.Headers[HttpResponseHeader.ContentLength] = this.ResponseStream.Length.ToString(CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent
- /// and signals ASP.NET to short-circuit the page execution pipeline
- /// now that the response has been completed.
- /// </summary>
- /// <param name="context">The context of the HTTP request whose response should be set.
- /// Typically this is <see cref="HttpContext.Current"/>.</param>
- /// <param name="endRequest">If set to <c>false</c>, this method calls
- /// <see cref="HttpApplication.CompleteRequest"/> rather than <see cref="HttpResponse.End"/>
- /// to avoid a <see cref="ThreadAbortException"/>.</param>
- protected internal void Respond(HttpContext context, bool endRequest) {
- this.Respond(new HttpContextWrapper(context), endRequest);
- }
-
- /// <summary>
- /// Automatically sends the appropriate response to the user agent
- /// and signals ASP.NET to short-circuit the page execution pipeline
- /// now that the response has been completed.
- /// </summary>
- /// <param name="context">The context of the HTTP request whose response should be set.
- /// Typically this is <see cref="HttpContext.Current"/>.</param>
- /// <param name="endRequest">If set to <c>false</c>, this method calls
- /// <see cref="HttpApplication.CompleteRequest"/> rather than <see cref="HttpResponse.End"/>
- /// to avoid a <see cref="ThreadAbortException"/>.</param>
- protected internal virtual void Respond(HttpContextBase context, bool endRequest) {
- Requires.NotNull(context, "context");
-
- context.Response.Clear();
- context.Response.StatusCode = (int)this.Status;
- MessagingUtilities.ApplyHeadersToResponse(this.Headers, context.Response);
- if (this.ResponseStream != null) {
- try {
- this.ResponseStream.CopyTo(context.Response.OutputStream);
- } catch (HttpException ex) {
- if (ex.ErrorCode == -2147467259 && context.Response.Output != null) {
- // Test scenarios can generate this, since the stream is being spoofed:
- // System.Web.HttpException: OutputStream is not available when a custom TextWriter is used.
- context.Response.Output.Write(this.Body);
- } else {
- throw;
- }
- }
- }
-
- foreach (string cookieName in this.Cookies) {
- var cookie = this.Cookies[cookieName];
- context.Response.AppendCookie(cookie);
- }
-
- if (endRequest) {
- // This approach throws an exception in order that
- // no more code is executed in the calling page.
- // Microsoft no longer recommends this approach.
- context.Response.End();
- } else if (context.ApplicationInstance != null) {
- // This approach doesn't throw an exception, but
- // still tells ASP.NET to short-circuit most of the
- // request handling pipeline to speed things up.
- context.ApplicationInstance.CompleteRequest();
- }
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs
deleted file mode 100644
index 7691cc4..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="OutgoingWebResponseActionResult.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Diagnostics.Contracts;
- using System.Web.Mvc;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// An ASP.NET MVC structure to represent the response to send
- /// to the user agent when the controller has finished its work.
- /// </summary>
- internal class OutgoingWebResponseActionResult : ActionResult {
- /// <summary>
- /// The outgoing web response to send when the ActionResult is executed.
- /// </summary>
- private readonly OutgoingWebResponse response;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="OutgoingWebResponseActionResult"/> class.
- /// </summary>
- /// <param name="response">The response.</param>
- internal OutgoingWebResponseActionResult(OutgoingWebResponse response) {
- Requires.NotNull(response, "response");
- this.response = response;
- }
-
- /// <summary>
- /// Enables processing of the result of an action method by a custom type that inherits from <see cref="T:System.Web.Mvc.ActionResult"/>.
- /// </summary>
- /// <param name="context">The context in which to set the response.</param>
- public override void ExecuteResult(ControllerContext context) {
- this.response.Respond(context.HttpContext);
-
- // MVC likes to muck with our response. For example, when returning contrived 401 Unauthorized responses
- // MVC will rewrite our response and turn it into a redirect, which breaks OAuth 2 authorization server token endpoints.
- // It turns out we can prevent this unwanted behavior by flushing the response before returning from this method.
- context.HttpContext.Response.Flush();
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs b/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs
index 982e1c0..a45e397 100644
--- a/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs
@@ -7,7 +7,6 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
using System.Security;
using System.Security.Permissions;
@@ -81,11 +80,7 @@ namespace DotNetOpenAuth.Messaging {
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter"/>
/// </PermissionSet>
-#if CLR4
[SecurityCritical]
-#else
- [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
-#endif
public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) {
base.GetObjectData(info, context);
throw new NotImplementedException();
diff --git a/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs b/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs
index c2dc34e..b5cab3b 100644
--- a/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs
@@ -8,7 +8,11 @@ namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Net.Http;
using System.Text;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Validation;
/// <summary>
/// An exception to represent errors in the local or remote implementation of the protocol
@@ -60,10 +64,12 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Creates the HTTP response to forward to the client to report the error.
/// </summary>
- /// <returns>The HTTP response.</returns>
- public OutgoingWebResponse CreateErrorResponse() {
- var response = this.channel.PrepareResponse(this.ErrorResponseMessage);
- return response;
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The HTTP response.
+ /// </returns>
+ public Task<HttpResponseMessage> CreateErrorResponseAsync(CancellationToken cancellationToken) {
+ return this.channel.PrepareResponseAsync(this.ErrorResponseMessage, cancellationToken);
}
}
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs
index d827972..adf0f33 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Collections.Generic;
using System.Linq;
using System.Text;
+ using Validation;
/// <summary>
/// Allows a custom class or struct to be serializable between itself and a string representation.
@@ -21,7 +22,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="converterType">The <see cref="IMessagePartEncoder"/> implementing type to use for serializing this type.</param>
public DefaultEncoderAttribute(Type converterType) {
Requires.NotNull(converterType, "converterType");
- Requires.True(typeof(IMessagePartEncoder).IsAssignableFrom(converterType), "Argument must be a type that implements {0}.", typeof(IMessagePartEncoder).Name);
+ Requires.That(typeof(IMessagePartEncoder).IsAssignableFrom(converterType), "Argument must be a type that implements {0}.", typeof(IMessagePartEncoder).Name);
this.Encoder = (IMessagePartEncoder)Activator.CreateInstance(converterType);
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/IMessagePartEncoder.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/IMessagePartEncoder.cs
index 6186cd7..017c7d7 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Reflection/IMessagePartEncoder.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/IMessagePartEncoder.cs
@@ -7,9 +7,9 @@
namespace DotNetOpenAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
+ using Validation;
/// <summary>
/// An interface describing how various objects can be serialized and deserialized between their object and string forms.
@@ -17,7 +17,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <remarks>
/// Implementations of this interface must include a default constructor and must be thread-safe.
/// </remarks>
- [ContractClass(typeof(IMessagePartEncoderContract))]
public interface IMessagePartEncoder {
/// <summary>
/// Encodes the specified value.
@@ -34,45 +33,4 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
object Decode(string value);
}
-
- /// <summary>
- /// Code contract for the <see cref="IMessagePartEncoder"/> type.
- /// </summary>
- [ContractClassFor(typeof(IMessagePartEncoder))]
- internal abstract class IMessagePartEncoderContract : IMessagePartEncoder {
- /// <summary>
- /// Initializes a new instance of the <see cref="IMessagePartEncoderContract"/> class.
- /// </summary>
- protected IMessagePartEncoderContract() {
- }
-
- #region IMessagePartEncoder Members
-
- /// <summary>
- /// Encodes the specified value.
- /// </summary>
- /// <param name="value">The value. Guaranteed to never be null.</param>
- /// <returns>
- /// The <paramref name="value"/> in string form, ready for message transport.
- /// </returns>
- string IMessagePartEncoder.Encode(object value) {
- Requires.NotNull(value, "value");
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Decodes the specified value.
- /// </summary>
- /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param>
- /// <returns>
- /// The deserialized form of the given string.
- /// </returns>
- /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
- object IMessagePartEncoder.Decode(string value) {
- Requires.NotNull(value, "value");
- throw new NotImplementedException();
- }
-
- #endregion
- }
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescription.cs
index 7e67842..cd04e1d 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescription.cs
@@ -12,6 +12,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Globalization;
using System.Linq;
using System.Reflection;
+ using Validation;
/// <summary>
/// A mapping between serialized key names and <see cref="MessagePart"/> instances describing
@@ -30,7 +31,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="messageType">Type of the message.</param>
/// <param name="messageVersion">The message version.</param>
internal MessageDescription(Type messageType, Version messageVersion) {
- Requires.NotNullSubtype<IMessage>(messageType, "messageType");
+ RequiresEx.NotNullSubtype<IMessage>(messageType, "messageType");
Requires.NotNull(messageVersion, "messageVersion");
this.MessageType = messageType;
@@ -80,7 +81,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[Pure]
internal MessageDictionary GetDictionary(IMessage message) {
Requires.NotNull(message, "message");
- Contract.Ensures(Contract.Result<MessageDictionary>() != null);
return this.GetDictionary(message, false);
}
@@ -93,7 +93,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[Pure]
internal MessageDictionary GetDictionary(IMessage message, bool getOriginalValues) {
Requires.NotNull(message, "message");
- Contract.Ensures(Contract.Result<MessageDictionary>() != null);
return new MessageDictionary(message, this, getOriginalValues);
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescriptionCollection.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescriptionCollection.cs
index 3517abc..f27a7af 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescriptionCollection.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDescriptionCollection.cs
@@ -10,11 +10,11 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
+ using Validation;
/// <summary>
/// A cache of <see cref="MessageDescription"/> instances.
/// </summary>
- [ContractVerification(true)]
internal class MessageDescriptionCollection : IEnumerable<MessageDescription> {
/// <summary>
/// A dictionary of reflected message types and the generated reflection information.
@@ -68,9 +68,8 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Assume(System.Boolean,System.String,System.String)", Justification = "No localization required.")]
[Pure]
internal MessageDescription Get(Type messageType, Version messageVersion) {
- Requires.NotNullSubtype<IMessage>(messageType, "messageType");
+ RequiresEx.NotNullSubtype<IMessage>(messageType, "messageType");
Requires.NotNull(messageVersion, "messageVersion");
- Contract.Ensures(Contract.Result<MessageDescription>() != null);
MessageTypeAndVersion key = new MessageTypeAndVersion(messageType, messageVersion);
@@ -106,7 +105,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[Pure]
internal MessageDescription Get(IMessage message) {
Requires.NotNull(message, "message");
- Contract.Ensures(Contract.Result<MessageDescription>() != null);
return this.Get(message.GetType(), message.Version);
}
@@ -136,8 +134,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// A struct used as the key to bundle message type and version.
/// </summary>
- [ContractVerification(true)]
- private struct MessageTypeAndVersion {
+ private struct MessageTypeAndVersion {
/// <summary>
/// Backing store for the <see cref="Type"/> property.
/// </summary>
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDictionary.cs
index cf44863..a2dddb2 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessageDictionary.cs
@@ -11,13 +11,13 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// Wraps an <see cref="IMessage"/> instance in a dictionary that
/// provides access to both well-defined message properties and "extra"
/// name/value pairs that have no properties associated with them.
/// </summary>
- [ContractVerification(false)]
internal class MessageDictionary : IDictionary<string, string> {
/// <summary>
/// The <see cref="IMessage"/> instance manipulated by this dictionary.
@@ -55,7 +55,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
public IMessage Message {
get {
- Contract.Ensures(Contract.Result<IMessage>() != null);
return this.message;
}
}
@@ -65,7 +64,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
public MessageDescription Description {
get {
- Contract.Ensures(Contract.Result<MessageDescription>() != null);
return this.description;
}
}
@@ -380,7 +378,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <returns>The generated dictionary.</returns>
[Pure]
public IDictionary<string, string> Serialize() {
- Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
return this.Serializer.Serialize(this);
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs
index a6e8da2..add4beb 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs
@@ -9,18 +9,17 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Net.Security;
using System.Reflection;
using System.Xml;
using DotNetOpenAuth.Configuration;
+ using Validation;
/// <summary>
/// Describes an individual member of a message and assists in its serialization.
/// </summary>
- [ContractVerification(true)]
[DebuggerDisplay("MessagePart {Name}")]
internal class MessagePart {
/// <summary>
@@ -66,20 +65,20 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Much more efficient initialization when we can call methods.")]
static MessagePart() {
Func<string, Uri> safeUri = str => {
- Contract.Assume(str != null);
+ Assumes.True(str != null);
return new Uri(str);
};
Func<string, bool> safeBool = str => {
- Contract.Assume(str != null);
+ Assumes.True(str != null);
return bool.Parse(str);
};
Func<byte[], string> safeFromByteArray = bytes => {
- Contract.Assume(bytes != null);
+ Assumes.True(bytes != null);
return Convert.ToBase64String(bytes);
};
Func<string, byte[]> safeToByteArray = str => {
- Contract.Assume(str != null);
+ Assumes.True(str != null);
return Convert.FromBase64String(str);
};
Map<Uri>(uri => uri.AbsoluteUri, uri => uri.OriginalString, safeUri);
@@ -106,7 +105,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Unavoidable"), SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code contracts requires it.")]
internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
Requires.NotNull(member, "member");
- Requires.True(member is FieldInfo || member is PropertyInfo, "member");
+ Requires.That(member is FieldInfo || member is PropertyInfo, "member", "Member must be a property or field.");
Requires.NotNull(attribute, "attribute");
this.field = member as FieldInfo;
@@ -119,7 +118,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
this.defaultMemberValue = DeriveDefaultValue(this.memberDeclaredType);
- Contract.Assume(this.memberDeclaredType != null); // CC missing PropertyInfo.PropertyType ensures result != null
+ Assumes.True(this.memberDeclaredType != null); // CC missing PropertyInfo.PropertyType ensures result != null
if (attribute.Encoder == null) {
if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) {
if (this.memberDeclaredType.IsGenericType &&
@@ -203,7 +202,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
internal string StaticConstantValue {
get {
- Requires.ValidState(this.IsConstantValueAvailableStatically);
+ RequiresEx.ValidState(this.IsConstantValueAvailableStatically);
return this.ToString(this.field.GetValue(null), false);
}
}
@@ -394,7 +393,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <returns>An instance of the desired encoder.</returns>
private static IMessagePartEncoder GetEncoder(Type messagePartEncoder) {
Requires.NotNull(messagePartEncoder, "messagePartEncoder");
- Contract.Ensures(Contract.Result<IMessagePartEncoder>() != null);
IMessagePartEncoder encoder;
lock (encoders) {
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/ValueMapping.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/ValueMapping.cs
index 4139f52..c45eb5d 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Reflection/ValueMapping.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/ValueMapping.cs
@@ -6,12 +6,11 @@
namespace DotNetOpenAuth.Messaging.Reflection {
using System;
- using System.Diagnostics.Contracts;
+ using Validation;
/// <summary>
/// A pair of conversion functions to map some type to a string and back again.
/// </summary>
- [ContractVerification(true)]
internal struct ValueMapping {
/// <summary>
/// The mapping function that converts some custom type to a string.
diff --git a/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactory.cs b/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactory.cs
index 762b54b..fd35e5f 100644
--- a/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactory.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactory.cs
@@ -7,11 +7,11 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using System.Text;
using DotNetOpenAuth.Messaging.Reflection;
+ using Validation;
/// <summary>
/// A message factory that automatically selects the message type based on the incoming data.
@@ -42,7 +42,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="messageTypes">The message types that this factory may instantiate.</param>
public virtual void AddMessageTypes(IEnumerable<MessageDescription> messageTypes) {
Requires.NotNull(messageTypes, "messageTypes");
- Requires.True(messageTypes.All(msg => msg != null), "messageTypes");
+ Requires.NullOrNotNullElements(messageTypes, "messageTypes");
var unsupportedMessageTypes = new List<MessageDescription>(0);
foreach (MessageDescription messageDescription in messageTypes) {
@@ -208,7 +208,6 @@ namespace DotNetOpenAuth.Messaging {
protected virtual IDirectedProtocolMessage InstantiateAsRequest(MessageDescription messageDescription, MessageReceivingEndpoint recipient) {
Requires.NotNull(messageDescription, "messageDescription");
Requires.NotNull(recipient, "recipient");
- Contract.Ensures(Contract.Result<IDirectedProtocolMessage>() != null);
ConstructorInfo ctor = this.requestMessageTypes[messageDescription];
return (IDirectedProtocolMessage)ctor.Invoke(new object[] { recipient.Location, messageDescription.MessageVersion });
@@ -223,7 +222,6 @@ namespace DotNetOpenAuth.Messaging {
protected virtual IDirectResponseProtocolMessage InstantiateAsResponse(MessageDescription messageDescription, IDirectedProtocolMessage request) {
Requires.NotNull(messageDescription, "messageDescription");
Requires.NotNull(request, "request");
- Contract.Ensures(Contract.Result<IDirectResponseProtocolMessage>() != null);
Type requestType = request.GetType();
var ctors = this.FindMatchingResponseConstructors(messageDescription, requestType);
@@ -249,7 +247,7 @@ namespace DotNetOpenAuth.Messaging {
private static int GetDerivationDistance(Type assignableType, Type derivedType) {
Requires.NotNull(assignableType, "assignableType");
Requires.NotNull(derivedType, "derivedType");
- Requires.True(assignableType.IsAssignableFrom(derivedType), "assignableType");
+ Requires.That(assignableType.IsAssignableFrom(derivedType), "assignableType", "Types are not related as required.");
// If this is the two types are equivalent...
if (derivedType.IsAssignableFrom(assignableType))
@@ -277,7 +275,6 @@ namespace DotNetOpenAuth.Messaging {
private static int CountInCommon(ICollection<string> collection1, ICollection<string> collection2, StringComparison comparison = StringComparison.Ordinal) {
Requires.NotNull(collection1, "collection1");
Requires.NotNull(collection2, "collection2");
- Contract.Ensures(Contract.Result<int>() >= 0 && Contract.Result<int>() <= Math.Min(collection1.Count, collection2.Count));
return collection1.Count(value1 => collection2.Any(value2 => string.Equals(value1, value2, comparison)));
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs b/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs
index 7ca5d45..413d0ad 100644
--- a/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs
@@ -7,10 +7,10 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using Reflection;
+ using Validation;
/// <summary>
/// A channel that uses the standard message factory.
@@ -27,16 +27,15 @@ namespace DotNetOpenAuth.Messaging {
private readonly ICollection<Version> versions;
/// <summary>
- /// Initializes a new instance of the <see cref="StandardMessageFactoryChannel"/> class.
+ /// Initializes a new instance of the <see cref="StandardMessageFactoryChannel" /> class.
/// </summary>
/// <param name="messageTypes">The message types that might be encountered.</param>
/// <param name="versions">All the possible message versions that might be encountered.</param>
- /// <param name="bindingElements">
- /// The binding elements to use in sending and receiving messages.
- /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages.
- /// </param>
- protected StandardMessageFactoryChannel(ICollection<Type> messageTypes, ICollection<Version> versions, params IChannelBindingElement[] bindingElements)
- : base(new StandardMessageFactory(), bindingElements) {
+ /// <param name="hostFactories">The host factories.</param>
+ /// <param name="bindingElements">The binding elements to use in sending and receiving messages.
+ /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages.</param>
+ protected StandardMessageFactoryChannel(ICollection<Type> messageTypes, ICollection<Version> versions, IHostFactories hostFactories, IChannelBindingElement[] bindingElements = null)
+ : base(new StandardMessageFactory(), bindingElements ?? new IChannelBindingElement[0], hostFactories) {
Requires.NotNull(messageTypes, "messageTypes");
Requires.NotNull(versions, "versions");
@@ -98,7 +97,6 @@ namespace DotNetOpenAuth.Messaging {
{
Requires.NotNull(messageTypes, "messageTypes");
Requires.NotNull(descriptionsCache, "descriptionsCache");
- Contract.Ensures(Contract.Result<IEnumerable<MessageDescription>>() != null);
// Get all the MessageDescription objects through the standard cache,
// so that perhaps it will be a quick lookup, or at least it will be
diff --git a/src/DotNetOpenAuth.Core/Messaging/StandardWebRequestHandler.cs b/src/DotNetOpenAuth.Core/Messaging/StandardWebRequestHandler.cs
deleted file mode 100644
index 65d4827..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/StandardWebRequestHandler.cs
+++ /dev/null
@@ -1,260 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="StandardWebRequestHandler.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Diagnostics;
- using System.Diagnostics.Contracts;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Reflection;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// The default handler for transmitting <see cref="HttpWebRequest"/> instances
- /// and returning the responses.
- /// </summary>
- public class StandardWebRequestHandler : IDirectWebRequestHandler {
- /// <summary>
- /// The set of options this web request handler supports.
- /// </summary>
- private const DirectWebRequestOptions SupportedOptions = DirectWebRequestOptions.AcceptAllHttpResponses;
-
- /// <summary>
- /// The value to use for the User-Agent HTTP header.
- /// </summary>
- private static string userAgentValue = Assembly.GetExecutingAssembly().GetName().Name + "/" + Util.AssemblyFileVersion;
-
- #region IWebRequestHandler Members
-
- /// <summary>
- /// Determines whether this instance can support the specified options.
- /// </summary>
- /// <param name="options">The set of options that might be given in a subsequent web request.</param>
- /// <returns>
- /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
- /// </returns>
- [Pure]
- public bool CanSupport(DirectWebRequestOptions options) {
- return (options & ~SupportedOptions) == 0;
- }
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <returns>
- /// The writer the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- public Stream GetRequestStream(HttpWebRequest request) {
- return this.GetRequestStream(request, DirectWebRequestOptions.None);
- }
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>
- /// The writer the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- public Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) {
- return GetRequestStreamCore(request);
- }
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <returns>
- /// An instance of <see cref="IncomingWebResponse"/> describing the response.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.</para>
- /// </remarks>
- public IncomingWebResponse GetResponse(HttpWebRequest request) {
- return this.GetResponse(request, DirectWebRequestOptions.None);
- }
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>
- /// An instance of <see cref="IncomingWebResponse"/> describing the response.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.</para>
- /// </remarks>
- public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
- // This request MAY have already been prepared by GetRequestStream, but
- // we have no guarantee, so do it just to be safe.
- PrepareRequest(request, false);
-
- try {
- Logger.Http.DebugFormat("HTTP {0} {1}", request.Method, request.RequestUri);
- HttpWebResponse response = (HttpWebResponse)request.GetResponse();
- return new NetworkDirectWebResponse(request.RequestUri, response);
- } catch (WebException ex) {
- HttpWebResponse response = (HttpWebResponse)ex.Response;
- if (response != null && response.StatusCode == HttpStatusCode.ExpectationFailed &&
- request.ServicePoint.Expect100Continue) {
- // Some OpenID servers doesn't understand the Expect header and send 417 error back.
- // If this server just failed from that, alter the ServicePoint for this server
- // so that we don't send that header again next time (whenever that is).
- // "Expect: 100-Continue" HTTP header. (see Google Code Issue 72)
- // We don't want to blindly set all ServicePoints to not use the Expect header
- // as that would be a security hole allowing any visitor to a web site change
- // the web site's global behavior when calling that host.
- Logger.Http.InfoFormat("HTTP POST to {0} resulted in 417 Expectation Failed. Changing ServicePoint to not use Expect: Continue next time.", request.RequestUri);
- request.ServicePoint.Expect100Continue = false; // TODO: investigate that CAS may throw here
-
- // An alternative to ServicePoint if we don't have permission to set that,
- // but we'd have to set it BEFORE each request.
- ////request.Expect = "";
- }
-
- if ((options & DirectWebRequestOptions.AcceptAllHttpResponses) != 0 && response != null &&
- response.StatusCode != HttpStatusCode.ExpectationFailed) {
- Logger.Http.InfoFormat("The HTTP error code {0} {1} is being accepted because the {2} flag is set.", (int)response.StatusCode, response.StatusCode, DirectWebRequestOptions.AcceptAllHttpResponses);
- return new NetworkDirectWebResponse(request.RequestUri, response);
- }
-
- if (response != null) {
- Logger.Http.ErrorFormat(
- "{0} returned {1} {2}: {3}",
- response.ResponseUri,
- (int)response.StatusCode,
- response.StatusCode,
- response.StatusDescription);
-
- if (Logger.Http.IsDebugEnabled) {
- using (var reader = new StreamReader(ex.Response.GetResponseStream())) {
- Logger.Http.DebugFormat(
- "WebException from {0}: {1}{2}", ex.Response.ResponseUri, Environment.NewLine, reader.ReadToEnd());
- }
- }
- } else {
- Logger.Http.ErrorFormat(
- "{0} connecting to {1}",
- ex.Status,
- request.RequestUri);
- }
-
- // Be sure to close the response stream to conserve resources and avoid
- // filling up all our incoming pipes and denying future requests.
- // If in the future, some callers actually want to read this response
- // we'll need to figure out how to reliably call Close on exception
- // responses at all callers.
- if (response != null) {
- response.Close();
- }
-
- throw ErrorUtilities.Wrap(ex, MessagingStrings.ErrorInRequestReplyMessage);
- }
- }
-
- #endregion
-
- /// <summary>
- /// Determines whether an exception was thrown because of the remote HTTP server returning HTTP 417 Expectation Failed.
- /// </summary>
- /// <param name="ex">The caught exception.</param>
- /// <returns>
- /// <c>true</c> if the failure was originally caused by a 417 Exceptation Failed error; otherwise, <c>false</c>.
- /// </returns>
- internal static bool IsExceptionFrom417ExpectationFailed(Exception ex) {
- while (ex != null) {
- WebException webEx = ex as WebException;
- if (webEx != null) {
- HttpWebResponse response = webEx.Response as HttpWebResponse;
- if (response != null) {
- if (response.StatusCode == HttpStatusCode.ExpectationFailed) {
- return true;
- }
- }
- }
-
- ex = ex.InnerException;
- }
-
- return false;
- }
-
- /// <summary>
- /// Initiates a POST request and prepares for sending data.
- /// </summary>
- /// <param name="request">The HTTP request with information about the remote party to contact.</param>
- /// <returns>
- /// The stream where the POST entity can be written.
- /// </returns>
- private static Stream GetRequestStreamCore(HttpWebRequest request) {
- PrepareRequest(request, true);
-
- try {
- return request.GetRequestStream();
- } catch (SocketException ex) {
- throw ErrorUtilities.Wrap(ex, MessagingStrings.WebRequestFailed, request.RequestUri);
- } catch (WebException ex) {
- throw ErrorUtilities.Wrap(ex, MessagingStrings.WebRequestFailed, request.RequestUri);
- }
- }
-
- /// <summary>
- /// Prepares an HTTP request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="preparingPost"><c>true</c> if this is a POST request whose headers have not yet been sent out; <c>false</c> otherwise.</param>
- private static void PrepareRequest(HttpWebRequest request, bool preparingPost) {
- Requires.NotNull(request, "request");
-
- // Be careful to not try to change the HTTP headers that have already gone out.
- if (preparingPost || request.Method == "GET") {
- // Set/override a few properties of the request to apply our policies for requests.
- if (Debugger.IsAttached) {
- // Since a debugger is attached, requests may be MUCH slower,
- // so give ourselves huge timeouts.
- request.ReadWriteTimeout = (int)TimeSpan.FromHours(1).TotalMilliseconds;
- request.Timeout = (int)TimeSpan.FromHours(1).TotalMilliseconds;
- }
-
- // Some sites, such as Technorati, return 403 Forbidden on identity
- // pages unless a User-Agent header is included.
- if (string.IsNullOrEmpty(request.UserAgent)) {
- request.UserAgent = userAgentValue;
- }
- }
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth.Core/Messaging/UntrustedWebRequestHandler.cs
deleted file mode 100644
index 25a7bbb..0000000
--- a/src/DotNetOpenAuth.Core/Messaging/UntrustedWebRequestHandler.cs
+++ /dev/null
@@ -1,476 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="UntrustedWebRequestHandler.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
- using System.Globalization;
- using System.IO;
- using System.Net;
- using System.Net.Cache;
- using System.Text.RegularExpressions;
- using DotNetOpenAuth.Configuration;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// A paranoid HTTP get/post request engine. It helps to protect against attacks from remote
- /// server leaving dangling connections, sending too much data, causing requests against
- /// internal servers, etc.
- /// </summary>
- /// <remarks>
- /// Protections include:
- /// * Conservative maximum time to receive the complete response.
- /// * Only HTTP and HTTPS schemes are permitted.
- /// * Internal IP address ranges are not permitted: 127.*.*.*, 1::*
- /// * Internal host names are not permitted (periods must be found in the host name)
- /// If a particular host would be permitted but is in the blacklist, it is not allowed.
- /// If a particular host would not be permitted but is in the whitelist, it is allowed.
- /// </remarks>
- public class UntrustedWebRequestHandler : IDirectWebRequestHandler {
- /// <summary>
- /// The set of URI schemes allowed in untrusted web requests.
- /// </summary>
- private ICollection<string> allowableSchemes = new List<string> { "http", "https" };
-
- /// <summary>
- /// The collection of blacklisted hosts.
- /// </summary>
- private ICollection<string> blacklistHosts = new List<string>(Configuration.BlacklistHosts.KeysAsStrings);
-
- /// <summary>
- /// The collection of regular expressions used to identify additional blacklisted hosts.
- /// </summary>
- private ICollection<Regex> blacklistHostsRegex = new List<Regex>(Configuration.BlacklistHostsRegex.KeysAsRegexs);
-
- /// <summary>
- /// The collection of whitelisted hosts.
- /// </summary>
- private ICollection<string> whitelistHosts = new List<string>(Configuration.WhitelistHosts.KeysAsStrings);
-
- /// <summary>
- /// The collection of regular expressions used to identify additional whitelisted hosts.
- /// </summary>
- private ICollection<Regex> whitelistHostsRegex = new List<Regex>(Configuration.WhitelistHostsRegex.KeysAsRegexs);
-
- /// <summary>
- /// The maximum redirections to follow in the course of a single request.
- /// </summary>
- [DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private int maximumRedirections = Configuration.MaximumRedirections;
-
- /// <summary>
- /// The maximum number of bytes to read from the response of an untrusted server.
- /// </summary>
- [DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private int maximumBytesToRead = Configuration.MaximumBytesToRead;
-
- /// <summary>
- /// The handler that will actually send the HTTP request and collect
- /// the response once the untrusted server gates have been satisfied.
- /// </summary>
- private IDirectWebRequestHandler chainedWebRequestHandler;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="UntrustedWebRequestHandler"/> class.
- /// </summary>
- public UntrustedWebRequestHandler()
- : this(new StandardWebRequestHandler()) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="UntrustedWebRequestHandler"/> class.
- /// </summary>
- /// <param name="chainedWebRequestHandler">The chained web request handler.</param>
- public UntrustedWebRequestHandler(IDirectWebRequestHandler chainedWebRequestHandler) {
- Requires.NotNull(chainedWebRequestHandler, "chainedWebRequestHandler");
-
- this.chainedWebRequestHandler = chainedWebRequestHandler;
- if (Debugger.IsAttached) {
- // Since a debugger is attached, requests may be MUCH slower,
- // so give ourselves huge timeouts.
- this.ReadWriteTimeout = TimeSpan.FromHours(1);
- this.Timeout = TimeSpan.FromHours(1);
- } else {
- this.ReadWriteTimeout = Configuration.ReadWriteTimeout;
- this.Timeout = Configuration.Timeout;
- }
- }
-
- /// <summary>
- /// Gets or sets the default maximum bytes to read in any given HTTP request.
- /// </summary>
- /// <value>Default is 1MB. Cannot be less than 2KB.</value>
- public int MaximumBytesToRead {
- get {
- return this.maximumBytesToRead;
- }
-
- set {
- Requires.InRange(value >= 2048, "value");
- this.maximumBytesToRead = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the total number of redirections to allow on any one request.
- /// Default is 10.
- /// </summary>
- public int MaximumRedirections {
- get {
- return this.maximumRedirections;
- }
-
- set {
- Requires.InRange(value >= 0, "value");
- this.maximumRedirections = value;
- }
- }
-
- /// <summary>
- /// Gets or sets the time allowed to wait for single read or write operation to complete.
- /// Default is 500 milliseconds.
- /// </summary>
- public TimeSpan ReadWriteTimeout { get; set; }
-
- /// <summary>
- /// Gets or sets the time allowed for an entire HTTP request.
- /// Default is 5 seconds.
- /// </summary>
- public TimeSpan Timeout { get; set; }
-
- /// <summary>
- /// Gets a collection of host name literals that should be allowed even if they don't
- /// pass standard security checks.
- /// </summary>
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Whitelist", Justification = "Spelling as intended.")]
- public ICollection<string> WhitelistHosts { get { return this.whitelistHosts; } }
-
- /// <summary>
- /// Gets a collection of host name regular expressions that indicate hosts that should
- /// be allowed even though they don't pass standard security checks.
- /// </summary>
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Whitelist", Justification = "Spelling as intended.")]
- public ICollection<Regex> WhitelistHostsRegex { get { return this.whitelistHostsRegex; } }
-
- /// <summary>
- /// Gets a collection of host name literals that should be rejected even if they
- /// pass standard security checks.
- /// </summary>
- public ICollection<string> BlacklistHosts { get { return this.blacklistHosts; } }
-
- /// <summary>
- /// Gets a collection of host name regular expressions that indicate hosts that should
- /// be rejected even if they pass standard security checks.
- /// </summary>
- public ICollection<Regex> BlacklistHostsRegex { get { return this.blacklistHostsRegex; } }
-
- /// <summary>
- /// Gets the configuration for this class that is specified in the host's .config file.
- /// </summary>
- private static UntrustedWebRequestElement Configuration {
- get { return DotNetOpenAuthSection.Messaging.UntrustedWebRequest; }
- }
-
- #region IDirectWebRequestHandler Members
-
- /// <summary>
- /// Determines whether this instance can support the specified options.
- /// </summary>
- /// <param name="options">The set of options that might be given in a subsequent web request.</param>
- /// <returns>
- /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
- /// </returns>
- [Pure]
- public bool CanSupport(DirectWebRequestOptions options) {
- // We support whatever our chained handler supports, plus RequireSsl.
- return this.chainedWebRequestHandler.CanSupport(options & ~DirectWebRequestOptions.RequireSsl);
- }
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>
- /// The writer the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- public Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) {
- this.EnsureAllowableRequestUri(request.RequestUri, (options & DirectWebRequestOptions.RequireSsl) != 0);
-
- this.PrepareRequest(request, true);
-
- // Submit the request and get the request stream back.
- return this.chainedWebRequestHandler.GetRequestStream(request, options & ~DirectWebRequestOptions.RequireSsl);
- }
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>
- /// An instance of <see cref="CachedDirectWebResponse"/> describing the response.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.</para>
- /// </remarks>
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")]
- public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
- // This request MAY have already been prepared by GetRequestStream, but
- // we have no guarantee, so do it just to be safe.
- this.PrepareRequest(request, false);
-
- // Since we may require SSL for every redirect, we handle each redirect manually
- // in order to detect and fail if any redirect sends us to an HTTP url.
- // We COULD allow automatic redirect in the cases where HTTPS is not required,
- // but our mock request infrastructure can't do redirects on its own either.
- Uri originalRequestUri = request.RequestUri;
- int i;
- for (i = 0; i < this.MaximumRedirections; i++) {
- this.EnsureAllowableRequestUri(request.RequestUri, (options & DirectWebRequestOptions.RequireSsl) != 0);
- CachedDirectWebResponse response = this.chainedWebRequestHandler.GetResponse(request, options & ~DirectWebRequestOptions.RequireSsl).GetSnapshot(this.MaximumBytesToRead);
- if (response.Status == HttpStatusCode.MovedPermanently ||
- response.Status == HttpStatusCode.Redirect ||
- response.Status == HttpStatusCode.RedirectMethod ||
- response.Status == HttpStatusCode.RedirectKeepVerb) {
- // We have no copy of the post entity stream to repeat on our manually
- // cloned HttpWebRequest, so we have to bail.
- ErrorUtilities.VerifyProtocol(request.Method != "POST", MessagingStrings.UntrustedRedirectsOnPOSTNotSupported);
- Uri redirectUri = new Uri(response.FinalUri, response.Headers[HttpResponseHeader.Location]);
- request = request.Clone(redirectUri);
- } else {
- if (response.FinalUri != request.RequestUri) {
- // Since we don't automatically follow redirects, there's only one scenario where this
- // can happen: when the server sends a (non-redirecting) Content-Location header in the response.
- // It's imperative that we do not trust that header though, so coerce the FinalUri to be
- // what we just requested.
- Logger.Http.WarnFormat("The response from {0} included an HTTP header indicating it's the same as {1}, but it's not a redirect so we won't trust that.", request.RequestUri, response.FinalUri);
- response.FinalUri = request.RequestUri;
- }
-
- return response;
- }
- }
-
- throw ErrorUtilities.ThrowProtocol(MessagingStrings.TooManyRedirects, originalRequestUri);
- }
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <returns>
- /// The writer the caller should write out the entity data to.
- /// </returns>
- Stream IDirectWebRequestHandler.GetRequestStream(HttpWebRequest request) {
- return this.GetRequestStream(request, DirectWebRequestOptions.None);
- }
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <returns>
- /// An instance of <see cref="IncomingWebResponse"/> describing the response.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.</para>
- /// </remarks>
- IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) {
- return this.GetResponse(request, DirectWebRequestOptions.None);
- }
-
- #endregion
-
- /// <summary>
- /// Determines whether an IP address is the IPv6 equivalent of "localhost/127.0.0.1".
- /// </summary>
- /// <param name="ip">The ip address to check.</param>
- /// <returns>
- /// <c>true</c> if this is a loopback IP address; <c>false</c> otherwise.
- /// </returns>
- private static bool IsIPv6Loopback(IPAddress ip) {
- Requires.NotNull(ip, "ip");
- byte[] addressBytes = ip.GetAddressBytes();
- for (int i = 0; i < addressBytes.Length - 1; i++) {
- if (addressBytes[i] != 0) {
- return false;
- }
- }
- if (addressBytes[addressBytes.Length - 1] != 1) {
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// Determines whether the given host name is in a host list or host name regex list.
- /// </summary>
- /// <param name="host">The host name.</param>
- /// <param name="stringList">The list of host names.</param>
- /// <param name="regexList">The list of regex patterns of host names.</param>
- /// <returns>
- /// <c>true</c> if the specified host falls within at least one of the given lists; otherwise, <c>false</c>.
- /// </returns>
- private static bool IsHostInList(string host, ICollection<string> stringList, ICollection<Regex> regexList) {
- Requires.NotNullOrEmpty(host, "host");
- Requires.NotNull(stringList, "stringList");
- Requires.NotNull(regexList, "regexList");
- foreach (string testHost in stringList) {
- if (string.Equals(host, testHost, StringComparison.OrdinalIgnoreCase)) {
- return true;
- }
- }
- foreach (Regex regex in regexList) {
- if (regex.IsMatch(host)) {
- return true;
- }
- }
- return false;
- }
-
- /// <summary>
- /// Determines whether a given host is whitelisted.
- /// </summary>
- /// <param name="host">The host name to test.</param>
- /// <returns>
- /// <c>true</c> if the host is whitelisted; otherwise, <c>false</c>.
- /// </returns>
- private bool IsHostWhitelisted(string host) {
- return IsHostInList(host, this.WhitelistHosts, this.WhitelistHostsRegex);
- }
-
- /// <summary>
- /// Determines whether a given host is blacklisted.
- /// </summary>
- /// <param name="host">The host name to test.</param>
- /// <returns>
- /// <c>true</c> if the host is blacklisted; otherwise, <c>false</c>.
- /// </returns>
- private bool IsHostBlacklisted(string host) {
- return IsHostInList(host, this.BlacklistHosts, this.BlacklistHostsRegex);
- }
-
- /// <summary>
- /// Verify that the request qualifies under our security policies
- /// </summary>
- /// <param name="requestUri">The request URI.</param>
- /// <param name="requireSsl">If set to <c>true</c>, only web requests that can be made entirely over SSL will succeed.</param>
- /// <exception cref="ProtocolException">Thrown when the URI is disallowed for security reasons.</exception>
- private void EnsureAllowableRequestUri(Uri requestUri, bool requireSsl) {
- ErrorUtilities.VerifyProtocol(this.IsUriAllowable(requestUri), MessagingStrings.UnsafeWebRequestDetected, requestUri);
- ErrorUtilities.VerifyProtocol(!requireSsl || string.Equals(requestUri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase), MessagingStrings.InsecureWebRequestWithSslRequired, requestUri);
- }
-
- /// <summary>
- /// Determines whether a URI is allowed based on scheme and host name.
- /// No requireSSL check is done here
- /// </summary>
- /// <param name="uri">The URI to test for whether it should be allowed.</param>
- /// <returns>
- /// <c>true</c> if [is URI allowable] [the specified URI]; otherwise, <c>false</c>.
- /// </returns>
- private bool IsUriAllowable(Uri uri) {
- Requires.NotNull(uri, "uri");
- if (!this.allowableSchemes.Contains(uri.Scheme)) {
- Logger.Http.WarnFormat("Rejecting URL {0} because it uses a disallowed scheme.", uri);
- return false;
- }
-
- // Allow for whitelist or blacklist to override our detection.
- Func<string, bool> failsUnlessWhitelisted = (string reason) => {
- if (IsHostWhitelisted(uri.DnsSafeHost)) {
- return true;
- }
- Logger.Http.WarnFormat("Rejecting URL {0} because {1}.", uri, reason);
- return false;
- };
-
- // Try to interpret the hostname as an IP address so we can test for internal
- // IP address ranges. Note that IP addresses can appear in many forms
- // (e.g. http://127.0.0.1, http://2130706433, http://0x0100007f, http://::1
- // So we convert them to a canonical IPAddress instance, and test for all
- // non-routable IP ranges: 10.*.*.*, 127.*.*.*, ::1
- // Note that Uri.IsLoopback is very unreliable, not catching many of these variants.
- IPAddress hostIPAddress;
- if (IPAddress.TryParse(uri.DnsSafeHost, out hostIPAddress)) {
- byte[] addressBytes = hostIPAddress.GetAddressBytes();
-
- // The host is actually an IP address.
- switch (hostIPAddress.AddressFamily) {
- case System.Net.Sockets.AddressFamily.InterNetwork:
- if (addressBytes[0] == 127 || addressBytes[0] == 10) {
- return failsUnlessWhitelisted("it is a loopback address.");
- }
- break;
- case System.Net.Sockets.AddressFamily.InterNetworkV6:
- if (IsIPv6Loopback(hostIPAddress)) {
- return failsUnlessWhitelisted("it is a loopback address.");
- }
- break;
- default:
- return failsUnlessWhitelisted("it does not use an IPv4 or IPv6 address.");
- }
- } else {
- // The host is given by name. We require names to contain periods to
- // help make sure it's not an internal address.
- if (!uri.Host.Contains(".")) {
- return failsUnlessWhitelisted("it does not contain a period in the host name.");
- }
- }
- if (this.IsHostBlacklisted(uri.DnsSafeHost)) {
- Logger.Http.WarnFormat("Rejected URL {0} because it is blacklisted.", uri);
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// Prepares the request by setting timeout and redirect policies.
- /// </summary>
- /// <param name="request">The request to prepare.</param>
- /// <param name="preparingPost"><c>true</c> if this is a POST request whose headers have not yet been sent out; <c>false</c> otherwise.</param>
- private void PrepareRequest(HttpWebRequest request, bool preparingPost) {
- Requires.NotNull(request, "request");
-
- // Be careful to not try to change the HTTP headers that have already gone out.
- if (preparingPost || request.Method == "GET") {
- // Set/override a few properties of the request to apply our policies for untrusted requests.
- request.ReadWriteTimeout = (int)this.ReadWriteTimeout.TotalMilliseconds;
- request.Timeout = (int)this.Timeout.TotalMilliseconds;
- request.KeepAlive = false;
- }
-
- // If SSL is required throughout, we cannot allow auto redirects because
- // it may include a pass through an unprotected HTTP request.
- // We have to follow redirects manually.
- // It also allows us to ignore HttpWebResponse.FinalUri since that can be affected by
- // the Content-Location header and open security holes.
- request.AllowAutoRedirect = false;
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs
index 242175e..1d7c424 100644
--- a/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs
@@ -7,7 +7,6 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
@@ -15,6 +14,7 @@ namespace DotNetOpenAuth.Messaging {
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.Messaging.Reflection;
+ using Validation;
/// <summary>
/// A serializer for <see cref="DataBag"/>-derived types
@@ -46,7 +46,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
protected internal UriStyleMessageFormatter(ICryptoKeyStore cryptoKeyStore = null, string bucket = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? minimumAge = null, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
: base(cryptoKeyStore, bucket, signed, encrypted, compressed, minimumAge, maximumAge, decodeOnceOnly) {
- Requires.True((cryptoKeyStore != null && !string.IsNullOrEmpty(bucket)) || (!signed && !encrypted), null);
+ Requires.That((cryptoKeyStore != null && !string.IsNullOrEmpty(bucket)) || (!signed && !encrypted), null, "Signing or encryption requires a cryptoKeyStore and bucket.");
}
/// <summary>
diff --git a/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs
index 21cbb94..9a98c9b 100644
--- a/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs
+++ b/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs
@@ -7,7 +7,6 @@
// We DON'T put an AssemblyVersionAttribute in here because it is generated in the build.
using System;
-using System.Diagnostics.Contracts;
using System.Net;
using System.Reflection;
using System.Resources;
@@ -31,8 +30,6 @@ using System.Web.UI;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7d73990c-47c0-4256-9f20-a893add9e289")]
-[assembly: ContractVerification(true)]
-
#if StrongNameSigned
// See comment at top of this file. We need this so that strong-naming doesn't
// keep this assembly from being useful to shared host (medium trust) web sites.
@@ -65,6 +62,7 @@ using System.Web.UI;
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
#else
[assembly: InternalsVisibleTo("DotNetOpenAuth.Test")]
+[assembly: InternalsVisibleTo("DotNetOpenAuth.Core.UI")]
[assembly: InternalsVisibleTo("DotNetOpenAuth.InfoCard")]
[assembly: InternalsVisibleTo("DotNetOpenAuth.InfoCard.UI")]
[assembly: InternalsVisibleTo("DotNetOpenAuth.OpenId")]
@@ -84,6 +82,7 @@ using System.Web.UI;
[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ClientAuthorization")]
[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer")]
[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client")]
+[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client.UI")]
[assembly: InternalsVisibleTo("DotNetOpenAuth.AspNet.Test")]
[assembly: InternalsVisibleTo("DotNetOpenAuth.AspNet")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
diff --git a/src/DotNetOpenAuth.Core/Reporting.cs b/src/DotNetOpenAuth.Core/Reporting.cs
index 80a3374..432a833 100644
--- a/src/DotNetOpenAuth.Core/Reporting.cs
+++ b/src/DotNetOpenAuth.Core/Reporting.cs
@@ -9,20 +9,23 @@ namespace DotNetOpenAuth {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading;
+ using System.Threading.Tasks;
using System.Web;
using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
+ using Validation;
/// <summary>
/// The statistical reporting mechanism used so this library's project authors
@@ -71,11 +74,6 @@ namespace DotNetOpenAuth {
private static Uri wellKnownPostLocation = new Uri("https://reports.dotnetopenauth.net/ReportingPost.ashx");
/// <summary>
- /// The outgoing HTTP request handler to use for publishing reports.
- /// </summary>
- private static IDirectWebRequestHandler webRequestHandler;
-
- /// <summary>
/// A few HTTP request hosts and paths we've seen.
/// </summary>
private static PersistentHashSet observedRequests;
@@ -170,7 +168,7 @@ namespace DotNetOpenAuth {
/// <param name="category">The category within the event. Null and empty strings are allowed, but considered the same.</param>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "PersistentCounter instances are stored in a table for later use.")]
internal static void RecordEventOccurrence(string eventName, string category) {
- Contract.Requires(!string.IsNullOrEmpty(eventName));
+ Requires.NotNullOrEmpty(eventName, "eventName");
// In release builds, just quietly return.
if (string.IsNullOrEmpty(eventName)) {
@@ -196,7 +194,7 @@ namespace DotNetOpenAuth {
/// <param name="eventNameByObjectType">The object whose type name is the event name to record.</param>
/// <param name="category">The category within the event. Null and empty strings are allowed, but considered the same.</param>
internal static void RecordEventOccurrence(object eventNameByObjectType, string category) {
- Contract.Requires(eventNameByObjectType != null);
+ Requires.NotNull(eventNameByObjectType, "eventNameByObjectType");
// In release builds, just quietly return.
if (eventNameByObjectType == null) {
@@ -213,7 +211,7 @@ namespace DotNetOpenAuth {
/// </summary>
/// <param name="feature">The feature.</param>
internal static void RecordFeatureUse(string feature) {
- Contract.Requires(!string.IsNullOrEmpty(feature));
+ Requires.NotNullOrEmpty(feature, "feature");
// In release builds, just quietly return.
if (string.IsNullOrEmpty(feature)) {
@@ -231,7 +229,7 @@ namespace DotNetOpenAuth {
/// </summary>
/// <param name="value">The object whose type is the feature to set as used.</param>
internal static void RecordFeatureUse(object value) {
- Contract.Requires(value != null);
+ Requires.NotNull(value, "value");
// In release builds, just quietly return.
if (value == null) {
@@ -250,7 +248,7 @@ namespace DotNetOpenAuth {
/// <param name="value">The object whose type is the feature to set as used.</param>
/// <param name="dependency1">Some dependency used by <paramref name="value"/>.</param>
internal static void RecordFeatureAndDependencyUse(object value, object dependency1) {
- Contract.Requires(value != null);
+ Requires.NotNull(value, "value");
// In release builds, just quietly return.
if (value == null) {
@@ -274,7 +272,7 @@ namespace DotNetOpenAuth {
/// <param name="dependency1">Some dependency used by <paramref name="value"/>.</param>
/// <param name="dependency2">Some dependency used by <paramref name="value"/>.</param>
internal static void RecordFeatureAndDependencyUse(object value, object dependency1, object dependency2) {
- Contract.Requires(value != null);
+ Requires.NotNull(value, "value");
// In release builds, just quietly return.
if (value == null) {
@@ -298,7 +296,7 @@ namespace DotNetOpenAuth {
/// </summary>
/// <param name="request">The request.</param>
internal static void RecordRequestStatistics(HttpRequestBase request) {
- Contract.Requires(request != null);
+ Requires.NotNull(request, "request");
// In release builds, just quietly return.
if (request == null) {
@@ -330,7 +328,7 @@ namespace DotNetOpenAuth {
lock (publishingConsiderationLock) {
if (DateTime.Now - lastPublished > Configuration.MinimumReportingInterval) {
lastPublished = DateTime.Now;
- SendStatsAsync();
+ var fireAndForget = SendStatsAsync();
}
}
}
@@ -346,7 +344,6 @@ namespace DotNetOpenAuth {
file = GetIsolatedStorage();
reportOriginIdentity = GetOrCreateOriginIdentity();
- webRequestHandler = new StandardWebRequestHandler();
observations.Add(observedRequests = new PersistentHashSet(file, "requests.txt", 3));
observations.Add(observedCultures = new PersistentHashSet(file, "cultures.txt", 20));
observations.Add(observedFeatures = new PersistentHashSet(file, "features.txt", int.MaxValue));
@@ -371,6 +368,18 @@ namespace DotNetOpenAuth {
}
/// <summary>
+ /// Creates an HTTP client that can be used for outbound HTTP requests.
+ /// </summary>
+ /// <returns>The HTTP client to use.</returns>
+ private static HttpClient CreateHttpClient() {
+ var channel = new HttpClientHandler();
+ channel.AllowAutoRedirect = false;
+ var webRequestHandler = new HttpClient(channel);
+ webRequestHandler.DefaultRequestHeaders.UserAgent.Add(Util.LibraryVersionHeader);
+ return webRequestHandler;
+ }
+
+ /// <summary>
/// Assembles a report for submission.
/// </summary>
/// <returns>A stream that contains the report.</returns>
@@ -426,30 +435,24 @@ namespace DotNetOpenAuth {
/// Sends the usage reports to the library authors.
/// </summary>
/// <returns>A value indicating whether submitting the report was successful.</returns>
- private static bool SendStats() {
+ private static async Task<bool> SendStatsAsync() {
try {
- var request = (HttpWebRequest)WebRequest.Create(wellKnownPostLocation);
- request.UserAgent = Util.LibraryVersion;
- request.AllowAutoRedirect = false;
- request.Method = "POST";
- request.ContentType = "text/dnoa-report1";
Stream report = GetReport();
- request.ContentLength = report.Length;
- using (var requestStream = webRequestHandler.GetRequestStream(request)) {
- report.CopyTo(requestStream);
- }
-
- using (var response = webRequestHandler.GetResponse(request)) {
- Logger.Library.Info("Statistical report submitted successfully.");
-
- // The response stream may contain a message for the webmaster.
- // Since as part of the report we submit the library version number,
- // the report receiving service may have alerts such as:
- // "You're using an obsolete version with exploitable security vulnerabilities."
- using (var responseReader = response.GetResponseReader()) {
- string line = responseReader.ReadLine();
- if (line != null) {
- DemuxLogMessage(line);
+ var content = new StreamContent(report);
+ content.Headers.ContentType = new MediaTypeHeaderValue("text/dnoa-report1");
+ using (var webRequestHandler = CreateHttpClient()) {
+ using (var response = await webRequestHandler.PostAsync(wellKnownPostLocation, content)) {
+ Logger.Library.Info("Statistical report submitted successfully.");
+
+ // The response stream may contain a message for the webmaster.
+ // Since as part of the report we submit the library version number,
+ // the report receiving service may have alerts such as:
+ // "You're using an obsolete version with exploitable security vulnerabilities."
+ using (var responseReader = new StreamReader(await response.Content.ReadAsStreamAsync())) {
+ string line = await responseReader.ReadLineAsync();
+ if (line != null) {
+ DemuxLogMessage(line);
+ }
}
}
}
@@ -508,34 +511,10 @@ namespace DotNetOpenAuth {
}
/// <summary>
- /// Sends the stats report asynchronously, and careful to not throw any unhandled exceptions.
- /// </summary>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Unhandled exceptions MUST NOT be thrown from here.")]
- private static void SendStatsAsync() {
- // Do it on a background thread since it could take a while and we
- // don't want to slow down this request we're borrowing.
- ThreadPool.QueueUserWorkItem(state => {
- try {
- SendStats();
- } catch (Exception ex) {
- // Something bad and unexpected happened. Just deactivate to avoid more trouble.
- try {
- broken = true;
- Logger.Library.Error("Error while trying to submit statistical report.", ex);
- } catch (Exception) {
- // swallow exceptions to prevent a crash.
- }
- }
- });
- }
-
- /// <summary>
/// Gets the isolated storage to use for reporting.
/// </summary>
/// <returns>An isolated storage location appropriate for our host.</returns>
private static IsolatedStorageFile GetIsolatedStorage() {
- Contract.Ensures(Contract.Result<IsolatedStorageFile>() != null);
-
IsolatedStorageFile result = null;
// We'll try for whatever storage location we can get,
@@ -567,8 +546,7 @@ namespace DotNetOpenAuth {
/// </remarks>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")]
private static Guid GetOrCreateOriginIdentity() {
- Requires.ValidState(file != null);
- Contract.Ensures(Contract.Result<Guid>() != Guid.Empty);
+ RequiresEx.ValidState(file != null, "file not set.");
Guid identityGuid = Guid.Empty;
const int GuidLength = 16;
diff --git a/src/DotNetOpenAuth.Core/Requires.cs b/src/DotNetOpenAuth.Core/Requires.cs
deleted file mode 100644
index 7d4d5be..0000000
--- a/src/DotNetOpenAuth.Core/Requires.cs
+++ /dev/null
@@ -1,255 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="Requires.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.Contracts;
- using System.Globalization;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// Argument validation checks that throw some kind of ArgumentException when they fail (unless otherwise noted).
- /// </summary>
- internal static class Requires {
- /// <summary>
- /// Validates that a given parameter is not null.
- /// </summary>
- /// <typeparam name="T">The type of the parameter</typeparam>
- /// <param name="value">The value.</param>
- /// <param name="parameterName">Name of the parameter.</param>
- /// <returns>The tested value, guaranteed to not be null.</returns>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static T NotNull<T>(T value, string parameterName) where T : class {
- if (value == null) {
- throw new ArgumentNullException(parameterName);
- }
-
- Contract.EndContractBlock();
- return value;
- }
-
- /// <summary>
- /// Validates that a parameter is not null or empty.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <param name="parameterName">Name of the parameter.</param>
- /// <returns>The validated value.</returns>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static string NotNullOrEmpty(string value, string parameterName) {
- NotNull(value, parameterName);
- True(value.Length > 0, parameterName, Strings.EmptyStringNotAllowed);
- Contract.Ensures(Contract.Result<string>() == value);
- Contract.EndContractBlock();
- return value;
- }
-
- /// <summary>
- /// Validates that an array is not null or empty.
- /// </summary>
- /// <typeparam name="T">The type of the elements in the sequence.</typeparam>
- /// <param name="value">The value.</param>
- /// <param name="parameterName">Name of the parameter.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void NotNullOrEmpty<T>(IEnumerable<T> value, string parameterName) {
- NotNull(value, parameterName);
- True(value.Any(), parameterName, Strings.InvalidArgument);
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Validates that an argument is either null or is a sequence with no null elements.
- /// </summary>
- /// <typeparam name="T">The type of elements in the sequence.</typeparam>
- /// <param name="sequence">The sequence.</param>
- /// <param name="parameterName">Name of the parameter.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void NullOrWithNoNullElements<T>(IEnumerable<T> sequence, string parameterName) where T : class {
- if (sequence != null) {
- if (sequence.Any(e => e == null)) {
- throw new ArgumentException(MessagingStrings.SequenceContainsNullElement, parameterName);
- }
- }
- }
-
- /// <summary>
- /// Validates some expression describing the acceptable range for an argument evaluates to true.
- /// </summary>
- /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="ArgumentOutOfRangeException"/>.</param>
- /// <param name="parameterName">Name of the parameter.</param>
- /// <param name="message">The message to include with the exception.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void InRange(bool condition, string parameterName, string message = null) {
- if (!condition) {
- throw new ArgumentOutOfRangeException(parameterName, message);
- }
-
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Validates some expression describing the acceptable condition for an argument evaluates to true.
- /// </summary>
- /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="ArgumentException"/>.</param>
- /// <param name="parameterName">Name of the parameter.</param>
- /// <param name="message">The message to include with the exception.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void True(bool condition, string parameterName = null, string message = null) {
- if (!condition) {
- throw new ArgumentException(message ?? Strings.InvalidArgument, parameterName);
- }
-
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Validates some expression describing the acceptable condition for an argument evaluates to true.
- /// </summary>
- /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="ArgumentException"/>.</param>
- /// <param name="parameterName">Name of the parameter.</param>
- /// <param name="unformattedMessage">The unformatted message.</param>
- /// <param name="args">Formatting arguments.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void True(bool condition, string parameterName, string unformattedMessage, params object[] args) {
- if (!condition) {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args), parameterName);
- }
-
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Validates some expression describing the acceptable condition for an argument evaluates to true.
- /// </summary>
- /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="InvalidOperationException"/>.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void ValidState(bool condition) {
- if (!condition) {
- throw new InvalidOperationException();
- }
-
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Validates some expression describing the acceptable condition for an argument evaluates to true.
- /// </summary>
- /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="InvalidOperationException"/>.</param>
- /// <param name="message">The message to include with the exception.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void ValidState(bool condition, string message) {
- if (!condition) {
- throw new InvalidOperationException(message);
- }
-
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Validates some expression describing the acceptable condition for an argument evaluates to true.
- /// </summary>
- /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="InvalidOperationException"/>.</param>
- /// <param name="unformattedMessage">The unformatted message.</param>
- /// <param name="args">Formatting arguments.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void ValidState(bool condition, string unformattedMessage, params object[] args) {
- if (!condition) {
- throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args));
- }
-
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Validates that some argument describes a type that is or derives from a required type.
- /// </summary>
- /// <typeparam name="T">The type that the argument must be or derive from.</typeparam>
- /// <param name="type">The type given in the argument.</param>
- /// <param name="parameterName">Name of the parameter.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void NotNullSubtype<T>(Type type, string parameterName) {
- NotNull(type, parameterName);
- True(typeof(T).IsAssignableFrom(type), parameterName, MessagingStrings.UnexpectedType, typeof(T).FullName, type.FullName);
-
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Validates some expression describing the acceptable condition for an argument evaluates to true.
- /// </summary>
- /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="FormatException"/>.</param>
- /// <param name="message">The message.</param>
-#if !CLR4
- [ContractArgumentValidator]
-#endif
- [Pure, DebuggerStepThrough]
- internal static void Format(bool condition, string message) {
- if (!condition) {
- throw new FormatException(message);
- }
-
- Contract.EndContractBlock();
- }
-
- /// <summary>
- /// Throws an <see cref="NotSupportedException"/> if a condition does not evaluate to <c>true</c>.
- /// </summary>
- /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="NotSupportedException"/>.</param>
- /// <param name="message">The message.</param>
- [Pure, DebuggerStepThrough]
- internal static void Support(bool condition, string message) {
- if (!condition) {
- throw new NotSupportedException(message);
- }
- }
-
- /// <summary>
- /// Throws an <see cref="ArgumentException"/>
- /// </summary>
- /// <param name="parameterName">Name of the parameter.</param>
- /// <param name="message">The message.</param>
- [Pure, DebuggerStepThrough]
- internal static void Fail(string parameterName, string message) {
- throw new ArgumentException(message, parameterName);
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Core/RequiresEx.cs b/src/DotNetOpenAuth.Core/RequiresEx.cs
new file mode 100644
index 0000000..1a077c4
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/RequiresEx.cs
@@ -0,0 +1,94 @@
+//-----------------------------------------------------------------------
+// <copyright file="RequiresEx.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using Validation;
+
+ /// <summary>
+ /// Argument validation checks that throw some kind of ArgumentException when they fail (unless otherwise noted).
+ /// </summary>
+ internal static class RequiresEx {
+ /// <summary>
+ /// Validates some expression describing the acceptable condition for an argument evaluates to true.
+ /// </summary>
+ /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="InvalidOperationException"/>.</param>
+ [Pure, DebuggerStepThrough]
+ internal static void ValidState(bool condition) {
+ if (!condition) {
+ throw new InvalidOperationException();
+ }
+ }
+
+ /// <summary>
+ /// Validates some expression describing the acceptable condition for an argument evaluates to true.
+ /// </summary>
+ /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="InvalidOperationException"/>.</param>
+ /// <param name="message">The message to include with the exception.</param>
+ [Pure, DebuggerStepThrough]
+ internal static void ValidState(bool condition, string message) {
+ if (!condition) {
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ /// <summary>
+ /// Validates some expression describing the acceptable condition for an argument evaluates to true.
+ /// </summary>
+ /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="InvalidOperationException"/>.</param>
+ /// <param name="unformattedMessage">The unformatted message.</param>
+ /// <param name="args">Formatting arguments.</param>
+ [Pure, DebuggerStepThrough]
+ internal static void ValidState(bool condition, string unformattedMessage, params object[] args) {
+ if (!condition) {
+ throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args));
+ }
+ }
+
+ /// <summary>
+ /// Validates that some argument describes a type that is or derives from a required type.
+ /// </summary>
+ /// <typeparam name="T">The type that the argument must be or derive from.</typeparam>
+ /// <param name="type">The type given in the argument.</param>
+ /// <param name="parameterName">Name of the parameter.</param>
+ [Pure, DebuggerStepThrough]
+ internal static void NotNullSubtype<T>(Type type, string parameterName) {
+ Requires.NotNull(type, parameterName);
+ Requires.That(typeof(T).IsAssignableFrom(type), parameterName, MessagingStrings.UnexpectedType, typeof(T).FullName, type.FullName);
+ }
+
+ /// <summary>
+ /// Validates some expression describing the acceptable condition for an argument evaluates to true.
+ /// </summary>
+ /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="FormatException"/>.</param>
+ /// <param name="message">The message.</param>
+ [Pure, DebuggerStepThrough]
+ internal static void Format(bool condition, string message) {
+ if (!condition) {
+ throw new FormatException(message);
+ }
+ }
+
+ /// <summary>
+ /// Throws an <see cref="NotSupportedException"/> if a condition does not evaluate to <c>true</c>.
+ /// </summary>
+ /// <param name="condition">The expression that must evaluate to true to avoid an <see cref="NotSupportedException"/>.</param>
+ /// <param name="message">The message.</param>
+ [Pure, DebuggerStepThrough]
+ internal static void Support(bool condition, string message) {
+ if (!condition) {
+ throw new NotSupportedException(message);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Strings.Designer.cs b/src/DotNetOpenAuth.Core/Strings.Designer.cs
index b0e66d2..b96f68a 100644
--- a/src/DotNetOpenAuth.Core/Strings.Designer.cs
+++ b/src/DotNetOpenAuth.Core/Strings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.17622
+// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -97,6 +97,24 @@ namespace DotNetOpenAuth {
}
/// <summary>
+ /// Looks up a localized string similar to The provided data could not be decrypted. If the current application is deployed in a web farm configuration, ensure that the &apos;decryptionKey&apos; and &apos;validationKey&apos; attributes are explicitly specified in the &lt;machineKey&gt; configuration section..
+ /// </summary>
+ internal static string Generic_CryptoFailure {
+ get {
+ return ResourceManager.GetString("Generic_CryptoFailure", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The HostFactories property must be set first..
+ /// </summary>
+ internal static string HostFactoriesRequired {
+ get {
+ return ResourceManager.GetString("HostFactoriesRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The argument has an unexpected value..
/// </summary>
internal static string InvalidArgument {
diff --git a/src/DotNetOpenAuth.Core/Strings.resx b/src/DotNetOpenAuth.Core/Strings.resx
index f4d61d1..1c2aee2 100644
--- a/src/DotNetOpenAuth.Core/Strings.resx
+++ b/src/DotNetOpenAuth.Core/Strings.resx
@@ -141,4 +141,10 @@
<data name="ResponseBodyNotSupported" xml:space="preserve">
<value>This object contains a response body, which is not supported.</value>
</data>
+ <data name="HostFactoriesRequired" xml:space="preserve">
+ <value>The HostFactories property must be set first.</value>
+ </data>
+ <data name="Generic_CryptoFailure" xml:space="preserve">
+ <value>The provided data could not be decrypted. If the current application is deployed in a web farm configuration, ensure that the 'decryptionKey' and 'validationKey' attributes are explicitly specified in the &lt;machineKey&gt; configuration section.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.Core/UriUtil.cs b/src/DotNetOpenAuth.Core/UriUtil.cs
index c52e1bb..25b92a2 100644
--- a/src/DotNetOpenAuth.Core/UriUtil.cs
+++ b/src/DotNetOpenAuth.Core/UriUtil.cs
@@ -8,17 +8,16 @@ namespace DotNetOpenAuth {
using System;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics.Contracts;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using DotNetOpenAuth.Messaging;
+ using Validation;
/// <summary>
/// Utility methods for working with URIs.
/// </summary>
- [ContractVerification(true)]
internal static class UriUtil {
/// <summary>
/// Tests a URI for the presence of an OAuth payload.
@@ -28,15 +27,14 @@ namespace DotNetOpenAuth {
/// <returns>
/// True if the URI contains an OAuth message.
/// </returns>
- [ContractVerification(false)] // bugs/limitations in CC static analysis
- internal static bool QueryStringContainPrefixedParameters(this Uri uri, string prefix) {
+ internal static bool QueryStringContainPrefixedParameters(this Uri uri, string prefix) {
Requires.NotNullOrEmpty(prefix, "prefix");
if (uri == null) {
return false;
}
NameValueCollection nvc = HttpUtility.ParseQueryString(uri.Query);
- Contract.Assume(nvc != null); // BCL
+ Assumes.True(nvc != null); // BCL
return nvc.Keys.OfType<string>().Any(key => key.StartsWith(prefix, StringComparison.Ordinal));
}
@@ -60,7 +58,6 @@ namespace DotNetOpenAuth {
/// <returns>The string version of the Uri.</returns>
internal static string ToStringWithImpliedPorts(this UriBuilder builder) {
Requires.NotNull(builder, "builder");
- Contract.Ensures(Contract.Result<string>() != null);
// We only check for implied ports on HTTP and HTTPS schemes since those
// are the only ones supported by OpenID anyway.
@@ -73,7 +70,7 @@ namespace DotNetOpenAuth {
// we're removing only the port (and not something in the query string that
// looks like a port.
string result = Regex.Replace(url, @"^(https?://[^:]+):\d+", m => m.Groups[1].Value, RegexOptions.IgnoreCase);
- Contract.Assume(result != null); // Regex.Replace never returns null
+ Assumes.True(result != null); // Regex.Replace never returns null
return result;
} else {
// The port must be explicitly given anyway.
@@ -95,12 +92,12 @@ namespace DotNetOpenAuth {
}
if (page != null && !designMode) {
- Contract.Assume(page.Request != null);
+ Assumes.True(page.Request != null);
// Validate new value by trying to construct a Realm object based on it.
string relativeUrl = page.ResolveUrl(value);
- Contract.Assume(page.Request.Url != null);
- Contract.Assume(relativeUrl != null);
+ Assumes.True(page.Request.Url != null);
+ Assumes.True(relativeUrl != null);
new Uri(page.Request.Url, relativeUrl); // throws an exception on failure.
} else {
// We can't fully test it, but it should start with either ~/ or a protocol.
diff --git a/src/DotNetOpenAuth.Core/Util.cs b/src/DotNetOpenAuth.Core/Util.cs
index 26b7b45..00d033f 100644
--- a/src/DotNetOpenAuth.Core/Util.cs
+++ b/src/DotNetOpenAuth.Core/Util.cs
@@ -6,22 +6,23 @@
namespace DotNetOpenAuth {
using System;
using System.Collections.Generic;
- using System.Diagnostics.Contracts;
using System.Globalization;
+ using System.Linq;
using System.Net;
+ using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
+ using System.Threading.Tasks;
using System.Web;
using System.Web.UI;
-
using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Reflection;
+ using Validation;
/// <summary>
/// A grab-bag utility class.
/// </summary>
- [ContractVerification(true)]
internal static class Util {
/// <summary>
/// The base namespace for this library from which all other namespaces derive.
@@ -29,24 +30,44 @@ namespace DotNetOpenAuth {
internal const string DefaultNamespace = "DotNetOpenAuth";
/// <summary>
+ /// A lazily-assembled string that describes the version of the library.
+ /// </summary>
+ private static readonly Lazy<string> libraryVersionLazy = new Lazy<string>(delegate {
+ var assembly = Assembly.GetExecutingAssembly();
+ string assemblyFullName = assembly.FullName;
+ bool official = assemblyFullName.Contains("PublicKeyToken=2780ccd10d57b246");
+ assemblyFullName = assemblyFullName.Replace(assembly.GetName().Version.ToString(), AssemblyFileVersion);
+
+ // We use InvariantCulture since this is used for logging.
+ return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", assemblyFullName, official ? "official" : "private");
+ });
+
+ /// <summary>
+ /// A lazily-assembled string that describes the version of the library.
+ /// </summary>
+ private static readonly Lazy<ProductInfoHeaderValue> libraryVersionHeaderLazy = new Lazy<ProductInfoHeaderValue>(delegate {
+ var assemblyName = Assembly.GetExecutingAssembly().GetName();
+ return new ProductInfoHeaderValue(assemblyName.Name, AssemblyFileVersion);
+ });
+
+ /// <summary>
/// The web.config file-specified provider of web resource URLs.
/// </summary>
- private static IEmbeddedResourceRetrieval embeddedResourceRetrieval = MessagingElement.Configuration.EmbeddedResourceRetrievalProvider.CreateInstance(null, false);
+ private static IEmbeddedResourceRetrieval embeddedResourceRetrieval = MessagingElement.Configuration.EmbeddedResourceRetrievalProvider.CreateInstance(null, false, null);
/// <summary>
/// Gets a human-readable description of the library name and version, including
/// whether the build is an official or private one.
/// </summary>
internal static string LibraryVersion {
- get {
- var assembly = Assembly.GetExecutingAssembly();
- string assemblyFullName = assembly.FullName;
- bool official = assemblyFullName.Contains("PublicKeyToken=2780ccd10d57b246");
- assemblyFullName = assemblyFullName.Replace(assembly.GetName().Version.ToString(), AssemblyFileVersion);
+ get { return libraryVersionLazy.Value; }
+ }
- // We use InvariantCulture since this is used for logging.
- return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", assemblyFullName, official ? "official" : "private");
- }
+ /// <summary>
+ /// Gets an HTTP header that can be included in outbound requests.
+ /// </summary>
+ internal static ProductInfoHeaderValue LibraryVersionHeader {
+ get { return libraryVersionHeaderLazy.Value; }
}
/// <summary>
@@ -103,8 +124,7 @@ namespace DotNetOpenAuth {
return new DelayedToString<IEnumerable<KeyValuePair<K, V>>>(
pairs,
p => {
- ////Contract.Requires(pairs != null); // CC: anonymous method can't handle it
- ErrorUtilities.VerifyArgumentNotNull(pairs, "pairs");
+ Requires.NotNull(pairs, "pairs");
var dictionary = pairs as IDictionary<K, V>;
var messageDictionary = pairs as MessageDictionary;
StringBuilder sb = new StringBuilder(dictionary != null ? dictionary.Count * 40 : 200);
@@ -139,7 +159,6 @@ namespace DotNetOpenAuth {
/// <param name="list">The list of elements.</param>
/// <param name="multiLineElements">if set to <c>true</c>, special formatting will be applied to the output to make it clear where one element ends and the next begins.</param>
/// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
- [ContractVerification(false)]
internal static object ToStringDeferred<T>(this IEnumerable<T> list, bool multiLineElements) {
return new DelayedToString<IEnumerable<T>>(
list,
@@ -148,7 +167,7 @@ namespace DotNetOpenAuth {
ErrorUtilities.VerifyArgumentNotNull(l, "l");
string newLine = Environment.NewLine;
- ////Contract.Assume(newLine != null && newLine.Length > 0);
+ ////Assumes.True(newLine != null && newLine.Length > 0);
StringBuilder sb = new StringBuilder();
if (multiLineElements) {
sb.AppendLine("[{");
@@ -215,6 +234,22 @@ namespace DotNetOpenAuth {
}
/// <summary>
+ /// Creates a dictionary of a sequence of elements and the result of an asynchronous transform,
+ /// allowing the async work to proceed concurrently.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the source.</typeparam>
+ /// <typeparam name="TResult">The type of the result.</typeparam>
+ /// <param name="source">The source.</param>
+ /// <param name="transform">The transform.</param>
+ /// <returns>A dictionary populated with the results of the transforms.</returns>
+ internal static async Task<Dictionary<TSource, TResult>> ToDictionaryAsync<TSource, TResult>(
+ this IEnumerable<TSource> source, Func<TSource, Task<TResult>> transform) {
+ var taskResults = source.ToDictionary(s => s, transform);
+ await Task.WhenAll(taskResults.Values);
+ return taskResults.ToDictionary(p => p.Key, p => p.Value.Result);
+ }
+
+ /// <summary>
/// Manages an individual deferred ToString call.
/// </summary>
/// <typeparam name="T">The type of object to be serialized as a string.</typeparam>
diff --git a/src/DotNetOpenAuth.Core/packages.config b/src/DotNetOpenAuth.Core/packages.config
new file mode 100644
index 0000000..ba1d0bf
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/packages.config
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="log4net" version="2.0.0" targetFramework="net45" />
+ <package id="Microsoft.AspNet.Mvc" version="4.0.20710.0" targetFramework="net45" />
+ <package id="Microsoft.AspNet.Razor" version="2.0.20715.0" targetFramework="net45" />
+ <package id="Microsoft.AspNet.WebPages" version="2.0.20710.0" targetFramework="net45" />
+ <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
+ <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
+ <package id="NLog" version="2.0.0.2000" targetFramework="net40" />
+ <package id="Validation" version="2.0.2.13022" targetFramework="net45" />
+</packages> \ No newline at end of file