diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2013-03-26 11:19:06 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2013-03-26 11:19:06 -0700 |
commit | 3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb (patch) | |
tree | c15816c3d7f6e74334553f2ff98605ce1c22c538 /src/DotNetOpenAuth.OpenId/OpenId/ChannelElements | |
parent | 5e9014f36b2d53b8e419918675df636540ea24e2 (diff) | |
parent | e6f7409f4caceb7bc2a5b4ddbcb1a4097af340f2 (diff) | |
download | DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.zip DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.tar.gz DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.tar.bz2 |
Move to HttpClient throughout library.
Diffstat (limited to 'src/DotNetOpenAuth.OpenId/OpenId/ChannelElements')
7 files changed, 178 insertions, 77 deletions
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs index ff8a766..4c55360 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs @@ -6,6 +6,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Messages; @@ -17,6 +19,17 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// </summary> internal class BackwardCompatibilityBindingElement : 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?> NoneTask = + Task.FromResult<MessageProtections?>(MessageProtections.None); + + /// <summary> /// The "dnoa.op_endpoint" callback parameter that stores the Provider Endpoint URL /// to tack onto the return_to URI. /// </summary> @@ -51,6 +64,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -59,7 +73,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { SignedResponseRequest request = message as SignedResponseRequest; if (request != null && request.Version.Major < 2) { request.AddReturnToArguments(ProviderEndpointParameterName, request.Recipient.AbsoluteUri); @@ -69,10 +83,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { request.AddReturnToArguments(ClaimedIdentifierParameterName, authRequest.ClaimedIdentifier); } - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } /// <summary> @@ -80,6 +94,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -92,7 +107,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null && response.Version.Major < 2) { // GetReturnToArgument may return parameters that are not signed, @@ -118,10 +133,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } } - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ExtensionsBindingElement.cs index f24c8b4..727dad7 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ExtensionsBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ExtensionsBindingElement.cs @@ -10,6 +10,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Extensions; @@ -22,6 +24,17 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// </summary> internal class ExtensionsBindingElement : 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?> NoneTask = + Task.FromResult<MessageProtections?>(MessageProtections.None); + + /// <summary> /// False if unsigned extensions should be dropped. Must always be true on Providers, since RPs never sign extensions. /// </summary> private readonly bool receiveUnsignedExtensions; @@ -68,6 +81,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -77,7 +91,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "It doesn't look too bad to me. :)")] - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var extendableMessage = message as IProtocolMessageWithExtensions; if (extendableMessage != null) { Protocol protocol = Protocol.Lookup(message.Version); @@ -120,10 +134,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { // Add the extension parameters to the base message for transmission. baseMessageDictionary.AddExtraParameters(extensionManager.GetArgumentsToSend(includeOpenIdPrefix)); - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } /// <summary> @@ -131,6 +145,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -143,7 +158,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var extendableMessage = message as IProtocolMessageWithExtensions; if (extendableMessage != null) { // First add the extensions that are signed by the Provider. @@ -164,10 +179,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } } - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/KeyValueFormEncoding.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/KeyValueFormEncoding.cs index 6ad66c0..9c06e6b 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/KeyValueFormEncoding.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/KeyValueFormEncoding.cs @@ -11,6 +11,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Globalization; using System.IO; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using Validation; @@ -131,12 +133,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <param name="data">The stream of Key-Value Form encoded bytes.</param> /// <returns>The deserialized dictionary.</returns> /// <exception cref="FormatException">Thrown when the data is not in the expected format.</exception> - public IDictionary<string, string> GetDictionary(Stream data) { + public async Task<IDictionary<string, string>> GetDictionaryAsync(Stream data, CancellationToken cancellationToken) { using (StreamReader reader = new StreamReader(data, textEncoding)) { var dict = new Dictionary<string, string>(); int line_num = 0; string line; - while ((line = reader.ReadLine()) != null) { + while ((line = await reader.ReadLineAsync()) != null) { + cancellationToken.ThrowIfCancellationRequested(); line_num++; if (this.ConformanceLevel == KeyValueFormConformanceLevel.Loose) { line = line.Trim(); diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs index 5a6b8bb..89bcd77 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs @@ -7,12 +7,19 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Net; + using System.Net.Http; + using System.Net.Http.Headers; using System.Text; + using System.Threading; + using System.Threading.Tasks; + + using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId.Extensions; @@ -40,13 +47,14 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { private KeyValueFormEncoding keyValueForm = new KeyValueFormEncoding(); /// <summary> - /// Initializes a new instance of the <see cref="OpenIdChannel"/> class. + /// Initializes a new instance of the <see cref="OpenIdChannel" /> 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.</param> - protected OpenIdChannel(IMessageFactory messageTypeProvider, IChannelBindingElement[] bindingElements) - : base(messageTypeProvider, bindingElements) { + /// <param name="hostFactories">The host factories.</param> + protected OpenIdChannel(IMessageFactory messageTypeProvider, IChannelBindingElement[] bindingElements, IHostFactories hostFactories) + : base(messageTypeProvider, bindingElements, hostFactories ?? new DefaultOpenIdHostFactories()) { Requires.NotNull(messageTypeProvider, "messageTypeProvider"); // Customize the binding element order, since we play some tricks for higher @@ -68,33 +76,30 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } this.CustomizeBindingElementOrder(outgoingBindingElements, incomingBindingElements); - - // Change out the standard web request handler to reflect the standard - // OpenID pattern that outgoing web requests are to unknown and untrusted - // servers on the Internet. - this.WebRequestHandler = new UntrustedWebRequestHandler(); } /// <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, except for check_authentication messages. - /// This can be due to tampering, replay attack or expiration, among other things. - /// </exception> - protected override void ProcessIncomingMessage(IProtocolMessage 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, except for check_authentication messages. + /// This can be due to tampering, replay attack or expiration, among other things.</exception> + protected override async Task ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var checkAuthRequest = message as CheckAuthenticationRequest; if (checkAuthRequest != null) { IndirectSignedResponse originalResponse = new IndirectSignedResponse(checkAuthRequest, this); try { - base.ProcessIncomingMessage(originalResponse); + await base.ProcessIncomingMessageAsync(originalResponse, cancellationToken); checkAuthRequest.IsValid = true; } catch (ProtocolException) { checkAuthRequest.IsValid = false; } } else { - base.ProcessIncomingMessage(message); + await base.ProcessIncomingMessageAsync(message, cancellationToken); } // Convert an OpenID indirect error message, which we never expect @@ -120,7 +125,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <returns> /// The <see cref="HttpWebRequest"/> prepared to send the request. /// </returns> - protected override HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) { + protected override HttpRequestMessage CreateHttpRequest(IDirectedProtocolMessage request) { return this.InitializeRequestAsPost(request); } @@ -128,13 +133,16 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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> + /// <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 override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) { + protected override async Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken) { try { - return this.keyValueForm.GetDictionary(response.ResponseStream); + using (var responseStream = await response.Content.ReadAsStreamAsync()) { + return await this.keyValueForm.GetDictionaryAsync(responseStream, cancellationToken); + } } catch (FormatException ex) { throw ErrorUtilities.Wrap(ex, ex.Message); } @@ -145,7 +153,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// </summary> /// <param name="response">The HTTP direct response.</param> /// <param name="message">The newly instantiated message, prior to deserialization.</param> - protected override void OnReceivingDirectResponse(IncomingWebResponse response, IDirectResponseProtocolMessage message) { + protected override void OnReceivingDirectResponse(HttpResponseMessage response, IDirectResponseProtocolMessage message) { base.OnReceivingDirectResponse(response, message); // Verify that the expected HTTP status code was used for the message, @@ -155,10 +163,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { var httpDirectResponse = message as IHttpDirectResponse; if (httpDirectResponse != null) { ErrorUtilities.VerifyProtocol( - httpDirectResponse.HttpStatusCode == response.Status, + httpDirectResponse.HttpStatusCode == response.StatusCode, MessagingStrings.UnexpectedHttpStatusCode, (int)httpDirectResponse.HttpStatusCode, - (int)response.Status); + (int)response.StatusCode); } } } @@ -174,55 +182,81 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <remarks> /// This method implements spec V1.0 section 5.3. /// </remarks> - protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) { + protected override HttpResponseMessage PrepareDirectResponse(IProtocolMessage response) { var messageAccessor = this.MessageDescriptions.GetAccessor(response); var fields = messageAccessor.Serialize(); byte[] keyValueEncoding = KeyValueFormEncoding.GetBytes(fields); - OutgoingWebResponse preparedResponse = new OutgoingWebResponse(); + var preparedResponse = new HttpResponseMessage(); ApplyMessageTemplate(response, preparedResponse); - preparedResponse.Headers.Add(HttpResponseHeader.ContentType, KeyValueFormContentType); - preparedResponse.OriginalMessage = response; - preparedResponse.ResponseStream = new MemoryStream(keyValueEncoding); + var content = new StreamContent(new MemoryStream(keyValueEncoding)); + content.Headers.ContentType = new MediaTypeHeaderValue(KeyValueFormContentType); + preparedResponse.Content = content; IHttpDirectResponse httpMessage = response as IHttpDirectResponse; if (httpMessage != null) { - preparedResponse.Status = httpMessage.HttpStatusCode; + preparedResponse.StatusCode = httpMessage.HttpStatusCode; } return preparedResponse; } /// <summary> - /// Gets the direct response of a direct HTTP request. + /// Provides derived-types the opportunity to wrap an <see cref="HttpMessageHandler" /> with another one. + /// </summary> + /// <param name="innerHandler">The inner handler received from <see cref="IHostFactories" /></param> + /// <returns> + /// The handler to use in <see cref="HttpClient" /> instances. + /// </returns> + protected override HttpMessageHandler WrapMessageHandler(HttpMessageHandler innerHandler) { + return new ErrorFilteringMessageHandler(base.WrapMessageHandler(innerHandler)); + } + + /// <summary> + /// An HTTP handler that throws an exception if the response message's HTTP status code doesn't fall + /// within those allowed by the OpenID spec. /// </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 override IncomingWebResponse GetDirectResponse(HttpWebRequest webRequest) { - IncomingWebResponse response = this.WebRequestHandler.GetResponse(webRequest, DirectWebRequestOptions.AcceptAllHttpResponses); - - // Filter the responses to the allowable set of HTTP status codes. - if (response.Status != HttpStatusCode.OK && response.Status != HttpStatusCode.BadRequest) { - if (Logger.Channel.IsErrorEnabled) { - using (var reader = new StreamReader(response.ResponseStream)) { + private class ErrorFilteringMessageHandler : DelegatingHandler { + /// <summary> + /// Initializes a new instance of the <see cref="ErrorFilteringMessageHandler" /> class. + /// </summary> + /// <param name="innerHandler">The inner handler which is responsible for processing the HTTP response messages.</param> + internal ErrorFilteringMessageHandler(HttpMessageHandler innerHandler) + : base(innerHandler) { + } + + /// <summary> + /// Sends an HTTP request to the inner handler to send to the server as an asynchronous operation. + /// </summary> + /// <param name="request">The HTTP request message to send to the server.</param> + /// <param name="cancellationToken">A cancellation token to cancel operation.</param> + /// <returns> + /// Returns <see cref="T:System.Threading.Tasks.Task`1" />. The task object representing the asynchronous operation. + /// </returns> + protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { + var response = await base.SendAsync(request, cancellationToken); + + // Filter the responses to the allowable set of HTTP status codes. + if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.BadRequest) { + if (Logger.Channel.IsErrorEnabled) { + var content = await response.Content.ReadAsStringAsync(); Logger.Channel.ErrorFormat( "Unexpected HTTP status code {0} {1} received in direct response:{2}{3}", - (int)response.Status, - response.Status, + (int)response.StatusCode, + response.StatusCode, Environment.NewLine, - reader.ReadToEnd()); + content); } - } - // Call dispose before throwing since we're not including the response in the - // exception we're throwing. - response.Dispose(); + // Call dispose before throwing since we're not including the response in the + // exception we're throwing. + response.Dispose(); - ErrorUtilities.ThrowProtocol(OpenIdStrings.UnexpectedHttpStatusCode, (int)response.Status, response.Status); - } + ErrorUtilities.ThrowProtocol(OpenIdStrings.UnexpectedHttpStatusCode, (int)response.StatusCode, response.StatusCode); + } - return response; + return response; + } } } } diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs index 726c01f..2aad922 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs @@ -9,6 +9,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Cryptography; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; @@ -32,6 +34,17 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// </remarks> internal class ReturnToSignatureBindingElement : 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?> NoneTask = + Task.FromResult<MessageProtections?>(MessageProtections.None); + + /// <summary> /// The name of the callback parameter we'll tack onto the return_to value /// to store our signature on the return_to parameter. /// </summary> @@ -90,6 +103,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -98,7 +112,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { SignedResponseRequest request = message as SignedResponseRequest; if (request != null && request.ReturnTo != null && request.SignReturnTo) { var cryptoKeyPair = this.cryptoKeyStore.GetCurrentKey(SecretUri.AbsoluteUri, OpenIdElement.Configuration.MaxAuthenticationTime); @@ -107,10 +121,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { request.AddReturnToArguments(ReturnToSignatureParameterName, signature); // We return none because we are not signing the entire message (only a part). - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } /// <summary> @@ -118,6 +132,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -130,7 +145,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null) { @@ -150,11 +165,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { Logger.Bindings.WarnFormat("The return_to signature failed verification."); } - return MessageProtections.None; + return NoneTask; } } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SigningBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SigningBindingElement.cs index 584b0e9..8f602cf 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SigningBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SigningBindingElement.cs @@ -11,6 +11,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Globalization; using System.Linq; using System.Net.Security; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Loggers; using DotNetOpenAuth.Messaging; @@ -23,6 +25,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Signs and verifies authentication assertions. /// </summary> internal abstract class SigningBindingElement : 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); + #region IChannelBindingElement Properties /// <summary> @@ -53,12 +60,13 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. /// </returns> - public virtual MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { - return null; + public virtual Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { + return NullTask; } /// <summary> @@ -66,6 +74,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -74,7 +83,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Thrown when the binding element rules indicate that this message is invalid and should /// NOT be processed. /// </exception> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public async Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var signedMessage = message as ITamperResistantOpenIdMessage; if (signedMessage != null) { Logger.Bindings.DebugFormat("Verifying incoming {0} message signature of: {1}", message.GetType().Name, signedMessage.Signature); @@ -92,7 +101,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } else { ErrorUtilities.VerifyInternal(this.Channel != null, "Cannot verify private association signature because we don't have a channel."); - protectionsApplied = this.VerifySignatureByUnrecognizedHandle(message, signedMessage, protectionsApplied); + protectionsApplied = await this.VerifySignatureByUnrecognizedHandleAsync(message, signedMessage, protectionsApplied, cancellationToken); } return protectionsApplied; @@ -107,8 +116,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <param name="message">The message.</param> /// <param name="signedMessage">The signed message.</param> /// <param name="protectionsApplied">The protections applied.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The applied protections.</returns> - protected abstract MessageProtections VerifySignatureByUnrecognizedHandle(IProtocolMessage message, ITamperResistantOpenIdMessage signedMessage, MessageProtections protectionsApplied); + protected abstract Task<MessageProtections> VerifySignatureByUnrecognizedHandleAsync(IProtocolMessage message, ITamperResistantOpenIdMessage signedMessage, MessageProtections protectionsApplied, CancellationToken cancellationToken); #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SkipSecurityBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SkipSecurityBindingElement.cs index d162cf6..ad8d59e 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SkipSecurityBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SkipSecurityBindingElement.cs @@ -10,12 +10,19 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Diagnostics; using System.Linq; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; /// <summary> /// Spoofs security checks on incoming OpenID messages. /// </summary> internal class SkipSecurityBindingElement : 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); + #region IChannelBindingElement Members /// <summary> @@ -42,6 +49,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -50,7 +58,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { Debug.Fail("SkipSecurityBindingElement.ProcessOutgoingMessage should never be called."); return null; } @@ -60,6 +68,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// 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. @@ -72,14 +81,14 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var signedMessage = message as ITamperResistantOpenIdMessage; if (signedMessage != null) { Logger.Bindings.DebugFormat("Skipped security checks of incoming {0} message for preview purposes.", message.GetType().Name); - return this.Protection; + return Task.FromResult<MessageProtections?>(this.Protection); } - return null; + return NullTask; } #endregion |