diff options
16 files changed, 232 insertions, 20 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs index 8a188ac..64b35cb 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs @@ -1,13 +1,45 @@ namespace DotNetOpenAuth.ApplicationBlock { using System; using System.Collections.Generic; + using System.Diagnostics; using System.IO; + using System.Net; using DotNetOpenAuth.Messaging; - internal static class Util { + public static class Util { + /// <summary> + /// Pseudo-random data generator. + /// </summary> internal static readonly Random NonCryptoRandomDataGenerator = new Random(); /// <summary> + /// Sets the channel's outgoing HTTP requests to use default network credentials. + /// </summary> + /// <param name="channel">The channel to modify.</param> + public static void UseDefaultNetworkCredentialsOnOutgoingHttpRequests(this Channel channel) + { + Debug.Assert(!(channel.WebRequestHandler is WrappingWebRequestHandler), "Wrapping an already wrapped web request handler. This is legal, but highly suspect of a bug as you don't want to wrap the same channel repeatedly to apply the same effect."); + AddOutgoingHttpRequestTransform(channel, http => http.Credentials = CredentialCache.DefaultNetworkCredentials); + } + + /// <summary> + /// Adds some action to any outgoing HTTP request on this channel. + /// </summary> + /// <param name="channel">The channel's whose outgoing HTTP requests should be modified.</param> + /// <param name="action">The action to perform on outgoing HTTP requests.</param> + internal static void AddOutgoingHttpRequestTransform(this Channel channel, Action<HttpWebRequest> action) { + if (channel == null) { + throw new ArgumentNullException("channel"); + } + + if (action == null) { + throw new ArgumentNullException("action"); + } + + channel.WebRequestHandler = new WrappingWebRequestHandler(channel.WebRequestHandler, action); + } + + /// <summary> /// Enumerates through the individual set bits in a flag enum. /// </summary> /// <param name="flags">The flags enum value.</param> @@ -85,5 +117,154 @@ return totalCopiedBytes; } + + /// <summary> + /// Wraps some instance of a web request handler in order to perform some extra operation on all + /// outgoing HTTP requests. + /// </summary> + private class WrappingWebRequestHandler : IDirectWebRequestHandler + { + /// <summary> + /// The handler being wrapped. + /// </summary> + private readonly IDirectWebRequestHandler wrappedHandler; + + /// <summary> + /// The action to perform on outgoing HTTP requests. + /// </summary> + private readonly Action<HttpWebRequest> action; + + /// <summary> + /// Initializes a new instance of the <see cref="WrappingWebRequestHandler"/> class. + /// </summary> + /// <param name="wrappedHandler">The HTTP handler to wrap.</param> + /// <param name="action">The action to perform on outgoing HTTP requests.</param> + internal WrappingWebRequestHandler(IDirectWebRequestHandler wrappedHandler, Action<HttpWebRequest> action) + { + if (wrappedHandler == null) { + throw new ArgumentNullException("wrappedHandler"); + } + + if (action == null) { + throw new ArgumentNullException("action"); + } + + this.wrappedHandler = wrappedHandler; + this.action = action; + } + + #region Implementation of 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> + public bool CanSupport(DirectWebRequestOptions options) + { + return this.wrappedHandler.CanSupport(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> + public Stream GetRequestStream(HttpWebRequest request) + { + this.action(request); + return wrappedHandler.GetRequestStream(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> + public Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) + { + this.action(request); + return wrappedHandler.GetRequestStream(request, 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> + public IncomingWebResponse GetResponse(HttpWebRequest request) + { + // If the request has an entity, the action would have already been processed in GetRequestStream. + if (request.Method == "GET") + { + this.action(request); + } + + return wrappedHandler.GetResponse(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> + public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) + { + // If the request has an entity, the action would have already been processed in GetRequestStream. + if (request.Method == "GET") { + this.action(request); + } + + return wrappedHandler.GetResponse(request, options); + } + + #endregion + } } } diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs index 19e6a82..9deaecd 100644 --- a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs +++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs @@ -28,7 +28,12 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection { [TestMethod] public void OptionalNullableStruct() { - this.ParameterizedMessageTypeTest(typeof(MessageWithNullableOptionalStruct)); + var message = new MessageWithNullableOptionalStruct(); + var part = this.ParameterizedMessageTypeTest(message.GetType()); + + Assert.IsNull(part.GetValue(message)); + part.SetValue(message, "3"); + Assert.AreEqual("3", part.GetValue(message)); } [TestMethod] diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs index 6980761..c438f46 100644 --- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs +++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth.Test.Mocks { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; + using DotNetOpenAuth.Test.OAuth; internal class InMemoryTokenManager : IConsumerTokenManager, IServiceProviderTokenManager { private KeyedCollectionDelegate<string, ConsumerInfo> consumers = new KeyedCollectionDelegate<string, ConsumerInfo>(c => c.Key); diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs index fcdb5e8..6477510 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/HmacSha1SigningBindingElementTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements { +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using DotNetOpenAuth.Test.Mocks; diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index 856f164..e215bc1 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements { +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using System; using System.Collections.Generic; using System.Collections.Specialized; @@ -356,6 +356,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { { "Name", "Andrew" }, { "Location", "http://hostb/pathB" }, { "Timestamp", XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc) }, + { "realm" , "someValue" }, }; IProtocolMessage requestMessage = this.channel.ReadFromRequest(CreateHttpRequestInfo(scheme, fields)); Assert.IsNotNull(requestMessage); @@ -364,6 +365,12 @@ namespace DotNetOpenAuth.Test.ChannelElements { Assert.AreEqual(15, testMessage.Age); Assert.AreEqual("Andrew", testMessage.Name); Assert.AreEqual("http://hostb/pathB", testMessage.Location.AbsoluteUri); + if (scheme == HttpDeliveryMethods.AuthorizationHeaderRequest) { + // The realm value should be ignored in the authorization header + Assert.IsFalse(((IMessage)testMessage).ExtraData.ContainsKey("realm")); + } else { + Assert.AreEqual("someValue", ((IMessage)testMessage).ExtraData["realm"]); + } } } } diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs index 627db8f..80a1c01 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements { +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs index 6e566c8..49549f5 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements { +namespace DotNetOpenAuth.Test.OAuth.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuth; diff --git a/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs b/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs index 625f416..89105ef 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ConsumerDescription.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test { +namespace DotNetOpenAuth.Test.OAuth { /// <summary> /// Information necessary to initialize a <see cref="Consumer"/>, /// and to tell a <see cref="ServiceProvider"/> about it. diff --git a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs index ce548a9..c3ef6c2 100644 --- a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs +++ b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test { +namespace DotNetOpenAuth.Test.OAuth { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; diff --git a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs index ce8070b..e60a9e2 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test { +namespace DotNetOpenAuth.Test.OAuth { using DotNetOpenAuth.OAuth; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs index 760a9e9..3430103 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderDescriptionTests.cs @@ -4,7 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test { +namespace DotNetOpenAuth.Test.OAuth { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; diff --git a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs index 380e2d5..0f88247 100644 --- a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs +++ b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs @@ -78,7 +78,7 @@ namespace DotNetOpenAuth.Messaging { /// <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, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> IncomingWebResponse GetResponse(HttpWebRequest request); @@ -94,7 +94,7 @@ namespace DotNetOpenAuth.Messaging { /// <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, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options); } diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index a8f04ec..e54e137 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -110,9 +110,25 @@ namespace DotNetOpenAuth.Messaging.Reflection { if (attribute.Encoder == null) { if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) { - this.converter = new ValueMapping( - obj => obj != null ? obj.ToString() : null, - str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null); + if (this.memberDeclaredType.IsGenericType && + this.memberDeclaredType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + // It's a nullable type. Try again to look up an appropriate converter for the underlying type. + Type underlyingType = Nullable.GetUnderlyingType(this.memberDeclaredType); + ValueMapping underlyingMapping; + if (converters.TryGetValue(underlyingType, out underlyingMapping)) { + this.converter = new ValueMapping( + underlyingMapping.ValueToString, + str => str != null ? underlyingMapping.StringToValue(str) : null); + } else { + this.converter = new ValueMapping( + obj => obj != null ? obj.ToString() : null, + str => str != null ? Convert.ChangeType(str, underlyingType, CultureInfo.InvariantCulture) : null); + } + } else { + this.converter = new ValueMapping( + obj => obj != null ? obj.ToString() : null, + str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null); + } } } else { this.converter = new ValueMapping(GetEncoder(attribute.Encoder)); @@ -235,7 +251,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { } /// <summary> - /// Adds a pair of type conversion functions to the static converstion map. + /// Adds a pair of type conversion functions to the static conversion map. /// </summary> /// <typeparam name="T">The custom type to convert to and from strings.</typeparam> /// <param name="toString">The function to convert the custom type to a string.</param> diff --git a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs index cc991cd..0ed0b5c 100644 --- a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs +++ b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs @@ -95,7 +95,7 @@ namespace DotNetOpenAuth.Messaging { /// <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, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> public IncomingWebResponse GetResponse(HttpWebRequest request) { return this.GetResponse(request, DirectWebRequestOptions.None); @@ -115,7 +115,7 @@ namespace DotNetOpenAuth.Messaging { /// <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, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) { ErrorUtilities.VerifyArgumentNotNull(request, "request"); diff --git a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs index 1656155..f68ac73 100644 --- a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs +++ b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs @@ -230,7 +230,7 @@ namespace DotNetOpenAuth.Messaging { /// <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, shoud be Closed before throwing.</para> + /// 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) { @@ -299,7 +299,7 @@ namespace DotNetOpenAuth.Messaging { /// <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, shoud be Closed before throwing.</para> + /// value, if set, should be Closed before throwing.</para> /// </remarks> IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) { return this.GetResponse(request, DirectWebRequestOptions.None); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index d325825..a4be672 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -139,6 +139,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { } } } + + fields.Remove("realm"); // ignore the realm parameter, since we don't use it, and it must be omitted from signature base string. } // Scrape the entity |