diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2008-09-02 08:23:29 -0700 |
---|---|---|
committer | Andrew <andrewarnott@gmail.com> | 2008-09-02 08:23:29 -0700 |
commit | 202448b1fd4d0c15d009b2c0a6713c8fc41f8854 (patch) | |
tree | bd320e8214ab81593c282e24fc8192abf019fdfc | |
parent | 5fc9c66e00bfe654450afd909aca8510e780fb1a (diff) | |
download | DotNetOpenAuth-202448b1fd4d0c15d009b2c0a6713c8fc41f8854.zip DotNetOpenAuth-202448b1fd4d0c15d009b2c0a6713c8fc41f8854.tar.gz DotNetOpenAuth-202448b1fd4d0c15d009b2c0a6713c8fc41f8854.tar.bz2 |
Refactored Channel class into two classes.
-rw-r--r-- | src/DotNetOAuth/DotNetOAuth.csproj | 1 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/Channel.cs | 149 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/IMessageTypeProvider.cs | 28 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/MessagingUtilities.cs | 23 | ||||
-rw-r--r-- | src/DotNetOAuth/OAuthChannel.cs | 171 | ||||
-rw-r--r-- | src/DotNetOAuth/Protocol.cs | 18 |
6 files changed, 287 insertions, 103 deletions
diff --git a/src/DotNetOAuth/DotNetOAuth.csproj b/src/DotNetOAuth/DotNetOAuth.csproj index ee0e7e3..b6624ca 100644 --- a/src/DotNetOAuth/DotNetOAuth.csproj +++ b/src/DotNetOAuth/DotNetOAuth.csproj @@ -78,6 +78,7 @@ <DependentUpon>MessagingStrings.resx</DependentUpon>
</Compile>
<Compile Include="Messaging\MessagingUtilities.cs" />
+ <Compile Include="OAuthChannel.cs" />
<Compile Include="Messaging\Response.cs" />
<Compile Include="Messaging\IndirectMessageEncoder.cs" />
<Compile Include="Messaging\IProtocolMessage.cs" />
diff --git a/src/DotNetOAuth/Messaging/Channel.cs b/src/DotNetOAuth/Messaging/Channel.cs index 246a14e..b9a7629 100644 --- a/src/DotNetOAuth/Messaging/Channel.cs +++ b/src/DotNetOAuth/Messaging/Channel.cs @@ -15,8 +15,17 @@ namespace DotNetOAuth.Messaging { /// <summary>
/// Manages sending direct messages to a remote party and receiving responses.
/// </summary>
- internal class Channel {
- IMessageTypeProvider messageTypeProvider;
+ internal abstract class Channel {
+ /// <summary>
+ /// A tool that can figure out what kind of message is being received
+ /// so it can be deserialized.
+ /// </summary>
+ private IMessageTypeProvider messageTypeProvider;
+
+ /// <summary>
+ /// Gets or sets the HTTP response to send as a reply to the current incoming HTTP request.
+ /// </summary>
+ private Response queuedIndirectOrResponseMessage;
/// <summary>
/// Initializes a new instance of the <see cref="Channel"/> class.
@@ -25,7 +34,7 @@ namespace DotNetOAuth.Messaging { /// A class prepared to analyze incoming messages and indicate what concrete
/// message types can deserialize from it.
/// </param>
- internal Channel(IMessageTypeProvider messageTypeProvider) {
+ protected Channel(IMessageTypeProvider messageTypeProvider) {
if (messageTypeProvider == null) {
throw new ArgumentNullException("messageTypeProvider");
}
@@ -42,55 +51,21 @@ namespace DotNetOAuth.Messaging { internal IProtocolMessage RequestInProcess { get; set; }
/// <summary>
- /// Gets or sets the HTTP response to send as a reply to the current incoming HTTP request.
+ /// Gets a tool that can figure out what kind of message is being received
+ /// so it can be deserialized.
/// </summary>
- internal Response QueuedIndirectOrResponseMessage { get; set; }
+ protected IMessageTypeProvider MessageTypeProvider {
+ get { return this.messageTypeProvider; }
+ }
/// <summary>
- /// Sends a direct message to a remote party and waits for the response.
+ /// Retrieves the stored response for sending and clears it from the channel.
/// </summary>
- /// <param name="request">The message to send.</param>
- /// <returns>The remote party's response.</returns>
- internal IProtocolMessage Request(IDirectedProtocolMessage request) {
- if (request == null) {
- throw new ArgumentNullException("request");
- }
-
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(request.Recipient);
-
- MessageScheme transmissionMethod = MessageScheme.AuthorizationHeaderRequest;
- switch (transmissionMethod) {
- case MessageScheme.AuthorizationHeaderRequest:
- this.InitializeRequestAsAuthHeader(httpRequest, request);
- break;
- case MessageScheme.PostRequest:
- this.InitializeRequestAsPost(httpRequest, request);
- break;
- case MessageScheme.GetRequest:
- this.InitializeRequestAsGet(httpRequest, request);
- break;
- default:
- throw new NotSupportedException();
- }
-
- // Submit the request and await the reply.
- Dictionary<string, string> responseFields;
- try {
- using (HttpWebResponse response = (HttpWebResponse)httpRequest.GetResponse()) {
- using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
- string queryString = reader.ReadToEnd();
- responseFields = HttpUtility.ParseQueryString(queryString).ToDictionary();
- }
- }
- } catch (WebException ex) {
- throw new ProtocolException(MessagingStrings.ErrorInRequestReplyMessage, ex);
- }
-
- Type messageType = this.messageTypeProvider.GetMessageType(responseFields);
- var responseSerialize = MessageSerializer.Get(messageType);
- var responseMessage = responseSerialize.Deserialize(responseFields);
-
- return responseMessage;
+ /// <returns>The response to send as the HTTP response.</returns>
+ internal Response DequeueIndirectOrResponseMessage() {
+ Response response = this.queuedIndirectOrResponseMessage;
+ this.queuedIndirectOrResponseMessage = null;
+ return response;
}
/// <summary>
@@ -102,9 +77,6 @@ namespace DotNetOAuth.Messaging { if (message == null) {
throw new ArgumentNullException("message");
}
- if (this.QueuedIndirectOrResponseMessage != null) {
- throw new InvalidOperationException(MessagingStrings.QueuedMessageResponseAlreadyExists);
- }
var directedMessage = message as IDirectedProtocolMessage;
if (directedMessage == null) {
@@ -115,32 +87,48 @@ namespace DotNetOAuth.Messaging { // This is an indirect message request or reply.
this.SendIndirectMessage(directedMessage);
} else {
- if (message is ProtocolException) {
+ ProtocolException exception = message as ProtocolException;
+ if (exception != null) {
if (this.RequestInProcess is IDirectedProtocolMessage) {
- this.ReportErrorAsDirectResponse(directedMessage);
+ this.ReportErrorAsDirectResponse(exception);
} else {
- this.ReportErrorToUser(directedMessage);
+ this.ReportErrorToUser(exception);
}
+ } else {
+ throw new InvalidOperationException();
}
}
}
}
- private void InitializeRequestAsAuthHeader(HttpWebRequest httpRequest, IDirectedProtocolMessage requestMessage) {
- throw new NotImplementedException();
- }
+ /// <summary>
+ /// Takes a message and temporarily stores it for sending as the hosting site's
+ /// HTTP response to the current request.
+ /// </summary>
+ /// <param name="response">The message to store for sending.</param>
+ protected void QueueIndirectOrResponseMessage(Response response) {
+ if (response == null) {
+ throw new ArgumentNullException("response");
+ }
+ if (this.queuedIndirectOrResponseMessage != null) {
+ throw new InvalidOperationException(MessagingStrings.QueuedMessageResponseAlreadyExists);
+ }
- private void InitializeRequestAsPost(HttpWebRequest httpRequest, IDirectedProtocolMessage requestMessage) {
- throw new NotImplementedException();
+ this.queuedIndirectOrResponseMessage = response;
}
- private void InitializeRequestAsGet(HttpWebRequest httpRequest, IDirectedProtocolMessage requestMessage) {
- throw new NotImplementedException();
- }
+ /// <summary>
+ /// Sends a direct message to a remote party and waits for the response.
+ /// </summary>
+ /// <param name="request">The message to send.</param>
+ /// <returns>The remote party's response.</returns>
+ protected abstract IProtocolMessage Request(IDirectedProtocolMessage request);
- private void SendIndirectMessage(IDirectedProtocolMessage directedMessage) {
- throw new NotImplementedException();
- }
+ /// <summary>
+ /// Queues an indirect message for transmittal via the user agent.
+ /// </summary>
+ /// <param name="message">The message to send.</param>
+ protected abstract void SendIndirectMessage(IDirectedProtocolMessage message);
/// <summary>
/// Queues a message for sending in the response stream where the fields
@@ -150,26 +138,19 @@ namespace DotNetOAuth.Messaging { /// <remarks>
/// This method implements spec V1.0 section 5.3.
/// </remarks>
- private void SendDirectMessageResponse(IProtocolMessage response) {
- MessageSerializer serializer = MessageSerializer.Get(response.GetType());
- var fields = serializer.Serialize(response);
- string responseBody = MessagingUtilities.CreateQueryString(fields);
-
- Response encodedResponse = new Response {
- Body = Encoding.UTF8.GetBytes(responseBody),
- OriginalMessage = response,
- Status = System.Net.HttpStatusCode.OK,
- Headers = new System.Net.WebHeaderCollection(),
- };
- this.QueuedIndirectOrResponseMessage = encodedResponse;
- }
+ protected abstract void SendDirectMessageResponse(IProtocolMessage response);
- private void ReportErrorToUser(IDirectedProtocolMessage directedMessage) {
- throw new NotImplementedException();
- }
+ /// <summary>
+ /// Reports an error to the user via the user agent.
+ /// </summary>
+ /// <param name="exception">The error information.</param>
+ protected abstract void ReportErrorToUser(ProtocolException exception);
- private void ReportErrorAsDirectResponse(IDirectedProtocolMessage directedMessage) {
- throw new NotImplementedException();
- }
+ /// <summary>
+ /// Sends an error result directly to the calling remote party according to the
+ /// rules of the protocol.
+ /// </summary>
+ /// <param name="exception">The error information.</param>
+ protected abstract void ReportErrorAsDirectResponse(ProtocolException exception);
}
}
diff --git a/src/DotNetOAuth/Messaging/IMessageTypeProvider.cs b/src/DotNetOAuth/Messaging/IMessageTypeProvider.cs index 4a2f7c7..c67c6b5 100644 --- a/src/DotNetOAuth/Messaging/IMessageTypeProvider.cs +++ b/src/DotNetOAuth/Messaging/IMessageTypeProvider.cs @@ -1,16 +1,32 @@ -using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+//-----------------------------------------------------------------------
+// <copyright file="IMessageTypeProvider.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+
internal interface IMessageTypeProvider {
/// <summary>
- /// Analyzes a message payload to discover what kind of message is embedded in it.
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it.
+ /// </summary>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>The <see cref="IProtocolMessage"/>-derived concrete class that
+ /// this message can deserialize to.</returns>
+ Type GetRequestMessageType(IDictionary<string, string> fields);
+
+ /// <summary>
+ /// Analyzes a response message payload to discover what kind of message is embedded in it.
/// </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>The <see cref="IProtocolMessage"/>-derived concrete class that
/// this message can deserialize to.</returns>
- Type GetMessageType(IDictionary<string, string> fields);
+ Type GetResponseMessageType(IProtocolMessage request, IDictionary<string, string> fields);
}
}
diff --git a/src/DotNetOAuth/Messaging/MessagingUtilities.cs b/src/DotNetOAuth/Messaging/MessagingUtilities.cs index 274aba8..c298b2f 100644 --- a/src/DotNetOAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOAuth/Messaging/MessagingUtilities.cs @@ -72,6 +72,29 @@ namespace DotNetOAuth.Messaging { }
/// <summary>
+ /// Adds a set of name-value pairs to the end of a given URL
+ /// as part of the querystring piece. Prefixes a ? or & before
+ /// first element as necessary.
+ /// </summary>
+ /// <param name="builder">The UriBuilder to add arguments to.</param>
+ /// <param name="args">
+ /// The arguments to add to the query.
+ /// If null, <paramref name="builder"/> is not changed.
+ /// </param>
+ internal static void AppendQueryArgs(UriBuilder builder, IDictionary<string, string> args) {
+ if (args != null && args.Count > 0) {
+ StringBuilder sb = new StringBuilder(50 + (args.Count * 10));
+ if (!string.IsNullOrEmpty(builder.Query)) {
+ sb.Append(builder.Query.Substring(1));
+ sb.Append('&');
+ }
+ sb.Append(CreateQueryString(args));
+
+ builder.Query = sb.ToString();
+ }
+ }
+
+ /// <summary>
/// Converts a <see cref="NameValueCollection"/> to an IDictionary<string, string>.
/// </summary>
/// <param name="nvc">The NameValueCollection to convert. May be null.</param>
diff --git a/src/DotNetOAuth/OAuthChannel.cs b/src/DotNetOAuth/OAuthChannel.cs new file mode 100644 index 0000000..ddfc56d --- /dev/null +++ b/src/DotNetOAuth/OAuthChannel.cs @@ -0,0 +1,171 @@ +//-----------------------------------------------------------------------
+// <copyright file="OAuthChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Net;
+ using System.Text;
+ using System.Web;
+ using DotNetOAuth.Messaging;
+
+ internal class OAuthChannel : Channel {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthChannel"/> class.
+ /// </summary>
+ /// <param name="messageTypeProvider">
+ /// A class prepared to analyze incoming messages and indicate what concrete
+ /// message types can deserialize from it.
+ /// </param>
+ internal OAuthChannel(IMessageTypeProvider messageTypeProvider)
+ : base(messageTypeProvider) {
+ }
+
+ protected override void SendDirectMessageResponse(IProtocolMessage response) {
+ MessageSerializer serializer = MessageSerializer.Get(response.GetType());
+ var fields = serializer.Serialize(response);
+ string responseBody = MessagingUtilities.CreateQueryString(fields);
+
+ Response encodedResponse = new Response {
+ Body = Encoding.UTF8.GetBytes(responseBody),
+ OriginalMessage = response,
+ Status = System.Net.HttpStatusCode.OK,
+ Headers = new System.Net.WebHeaderCollection(),
+ };
+ this.QueueIndirectOrResponseMessage(encodedResponse);
+ }
+
+ protected override IProtocolMessage Request(IDirectedProtocolMessage request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
+ HttpWebRequest httpRequest;
+
+ MessageScheme transmissionMethod = MessageScheme.AuthorizationHeaderRequest;
+ switch (transmissionMethod) {
+ case MessageScheme.AuthorizationHeaderRequest:
+ httpRequest = this.InitializeRequestAsAuthHeader(request);
+ break;
+ case MessageScheme.PostRequest:
+ httpRequest = this.InitializeRequestAsPost(request);
+ break;
+ case MessageScheme.GetRequest:
+ httpRequest = this.InitializeRequestAsGet(request);
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+
+ // Submit the request and await the reply.
+ Dictionary<string, string> responseFields;
+ try {
+ using (HttpWebResponse response = (HttpWebResponse)httpRequest.GetResponse()) {
+ using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
+ string queryString = reader.ReadToEnd();
+ responseFields = HttpUtility.ParseQueryString(queryString).ToDictionary();
+ }
+ }
+ } catch (WebException ex) {
+ throw new ProtocolException(MessagingStrings.ErrorInRequestReplyMessage, ex);
+ }
+
+ Type messageType = this.MessageTypeProvider.GetResponseMessageType(request, responseFields);
+ var responseSerialize = MessageSerializer.Get(messageType);
+ var responseMessage = responseSerialize.Deserialize(responseFields);
+
+ return responseMessage;
+ }
+
+ protected override void SendIndirectMessage(IDirectedProtocolMessage message) {
+ throw new NotImplementedException();
+ }
+
+ protected override void ReportErrorToUser(ProtocolException exception) {
+ throw new NotImplementedException();
+ }
+
+ protected override void ReportErrorAsDirectResponse(ProtocolException exception) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider via the Authorization header.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method implements OAuth 1.0 section 5.2, item #1 (described in section 5.4).
+ /// </remarks>
+ private HttpWebRequest InitializeRequestAsAuthHeader(IDirectedProtocolMessage requestMessage) {
+ var serializer = MessageSerializer.Get(requestMessage.GetType());
+ var fields = serializer.Serialize(requestMessage);
+
+ HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
+
+ StringBuilder authorization = new StringBuilder();
+ authorization.Append(requestMessage.Protocol.AuthorizationHeaderScheme);
+ authorization.Append(" ");
+ foreach (var pair in fields) {
+ string key = Uri.EscapeDataString(pair.Key);
+ string value = Uri.EscapeDataString(pair.Value);
+ authorization.Append(key);
+ authorization.Append("=\"");
+ authorization.Append(value);
+ authorization.Append("\",");
+ }
+ authorization.Length--; // remove trailing comma
+
+ httpRequest.Headers.Add(HttpRequestHeader.Authorization, authorization.ToString());
+
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider as the payload of a POST request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method implements OAuth 1.0 section 5.2, item #2.
+ /// </remarks>
+ private HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
+ var serializer = MessageSerializer.Get(requestMessage.GetType());
+ var fields = serializer.Serialize(requestMessage);
+
+ HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
+ httpRequest.Method = "POST";
+ httpRequest.ContentType = "application/x-www-form-urlencoded";
+ string requestBody = MessagingUtilities.CreateQueryString(fields);
+ httpRequest.ContentLength = requestBody.Length;
+ using (StreamWriter writer = new StreamWriter(httpRequest.GetRequestStream())) {
+ writer.Write(requestBody);
+ }
+
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider as the query string in a GET request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method implements OAuth 1.0 section 5.2, item #3.
+ /// </remarks>
+ private HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
+ var serializer = MessageSerializer.Get(requestMessage.GetType());
+ var fields = serializer.Serialize(requestMessage);
+
+ UriBuilder builder = new UriBuilder(requestMessage.Recipient);
+ MessagingUtilities.AppendQueryArgs(builder, fields);
+ HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
+
+ return httpRequest;
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Protocol.cs b/src/DotNetOAuth/Protocol.cs index 85bf12d..db26a9f 100644 --- a/src/DotNetOAuth/Protocol.cs +++ b/src/DotNetOAuth/Protocol.cs @@ -47,14 +47,9 @@ namespace DotNetOAuth { private string parameterPrefix = "oauth_";
/// <summary>
- /// Strings that identify the various message schemes.
+ /// The scheme to use in Authorization header message requests.
/// </summary>
- /// <remarks>
- /// These strings should be checked with case INsensitivity.
- /// </remarks>
- private Dictionary<MessageScheme, string> messageSchemes = new Dictionary<MessageScheme, string> {
- { MessageScheme.AuthorizationHeaderRequest, "OAuth" },
- };
+ private string authorizationHeaderScheme = "OAuth";
/// <summary>
/// Gets the namespace to use for this version of the protocol.
@@ -71,13 +66,10 @@ namespace DotNetOAuth { }
/// <summary>
- /// Gets the strings that identify the various message schemes.
+ /// Gets the scheme to use in Authorization header message requests.
/// </summary>
- /// <remarks>
- /// These strings should be checked with case INsensitivity.
- /// </remarks>
- internal IDictionary<MessageScheme, string> MessageSchemes {
- get { return this.messageSchemes; }
+ internal string AuthorizationHeaderScheme {
+ get { return this.authorizationHeaderScheme; }
}
}
}
|