diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2008-09-02 06:34:19 -0700 |
---|---|---|
committer | Andrew <andrewarnott@gmail.com> | 2008-09-02 06:34:19 -0700 |
commit | 938fa2719d3336d22326028bebd2adb74e0bee9d (patch) | |
tree | 456f1ca7cb3ce2de8a2413e1da7cdd7dcd832217 /src | |
parent | cded2f8669b219fff104260eb681c67bf7ea1c10 (diff) | |
download | DotNetOpenAuth-938fa2719d3336d22326028bebd2adb74e0bee9d.zip DotNetOpenAuth-938fa2719d3336d22326028bebd2adb74e0bee9d.tar.gz DotNetOpenAuth-938fa2719d3336d22326028bebd2adb74e0bee9d.tar.bz2 |
Lots of design work on the channel stack.
Diffstat (limited to 'src')
-rw-r--r-- | src/DotNetOAuth.Test/Messaging/MessageSerializerTest.cs | 26 | ||||
-rw-r--r-- | src/DotNetOAuth/DotNetOAuth.csproj | 19 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/Channel.cs | 121 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/DirectMessageChannel.cs | 21 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/IDirectedProtocolMessage.cs (renamed from src/DotNetOAuth/Messaging/IProtocolMessageRequest.cs) | 7 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/IProtocolMessage.cs | 1 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs | 4 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/MessageSerializer.cs | 167 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs | 90 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/MessagingStrings.resx | 129 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/MessagingUtilities.cs | 29 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/ProtocolMessageSerializer.cs | 111 | ||||
-rw-r--r-- | src/DotNetOAuth/Messaging/Response.cs (renamed from src/DotNetOAuth/Messaging/ProtocolMessageResponse.cs) | 4 | ||||
-rw-r--r-- | src/Settings.StyleCop | 5 |
14 files changed, 577 insertions, 157 deletions
diff --git a/src/DotNetOAuth.Test/Messaging/MessageSerializerTest.cs b/src/DotNetOAuth.Test/Messaging/MessageSerializerTest.cs index 3577c0b..b59e48f 100644 --- a/src/DotNetOAuth.Test/Messaging/MessageSerializerTest.cs +++ b/src/DotNetOAuth.Test/Messaging/MessageSerializerTest.cs @@ -17,25 +17,25 @@ namespace DotNetOAuth.Test.Messaging { public class MessageSerializerTest : TestBase {
[TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void SerializeNull() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
serializer.Serialize(null);
}
[TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void SerializeNullFields() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
serializer.Serialize(null, new Mocks.TestMessage());
}
[TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void SerializeNullMessage() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
serializer.Serialize(new Dictionary<string, string>(), null);
}
[TestMethod, ExpectedException(typeof(ProtocolException))]
public void SerializeInvalidMessage() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
Mocks.TestMessage message = new DotNetOAuth.Test.Mocks.TestMessage();
message.EmptyMember = "invalidvalue";
@@ -44,7 +44,7 @@ namespace DotNetOAuth.Test.Messaging { [TestMethod()]
public void SerializeTest() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
var message = new Mocks.TestMessage { Age = 15, Name = "Andrew", Location = new Uri("http://localhost") };
IDictionary<string, string> actual = serializer.Serialize(message);
Assert.AreEqual(3, actual.Count);
@@ -62,7 +62,7 @@ namespace DotNetOAuth.Test.Messaging { [TestMethod]
public void SerializeToExistingDictionary() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
var message = new Mocks.TestMessage { Age = 15, Name = "Andrew" };
var fields = new Dictionary<string, string>();
fields["someExtraField"] = "someValue";
@@ -75,19 +75,19 @@ namespace DotNetOAuth.Test.Messaging { [TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void DeserializeNull() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
serializer.Deserialize(null);
}
[TestMethod()]
public void DeserializeSimple() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
// We deliberately do this OUT of alphabetical order (caps would go first),
// since DataContractSerializer demands things to be IN alphabetical order.
fields["age"] = "15";
fields["Name"] = "Andrew";
- var actual = serializer.Deserialize(fields);
+ var actual = (Mocks.TestMessage)serializer.Deserialize(fields);
Assert.AreEqual(15, actual.Age);
Assert.AreEqual("Andrew", actual.Name);
Assert.IsNull(actual.EmptyMember);
@@ -95,14 +95,14 @@ namespace DotNetOAuth.Test.Messaging { [TestMethod]
public void DeserializeWithExtraFields() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
fields["age"] = "15";
fields["Name"] = "Andrew";
// Add some field that is not recognized by the class. This simulates a querystring with
// more parameters than are actually interesting to the protocol message.
fields["someExtraField"] = "asdf";
- var actual = serializer.Deserialize(fields);
+ var actual = (Mocks.TestMessage)serializer.Deserialize(fields);
Assert.AreEqual(15, actual.Age);
Assert.AreEqual("Andrew", actual.Name);
Assert.IsNull(actual.EmptyMember);
@@ -110,14 +110,14 @@ namespace DotNetOAuth.Test.Messaging { [TestMethod, ExpectedException(typeof(ProtocolException))]
public void DeserializeEmpty() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
var fields = new Dictionary<string, string>(StringComparer.Ordinal);
serializer.Deserialize(fields);
}
[TestMethod, ExpectedException(typeof(ProtocolException))]
public void DeserializeInvalidMessage() {
- var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
// Set an disallowed value.
fields["age"] = "-1";
diff --git a/src/DotNetOAuth/DotNetOAuth.csproj b/src/DotNetOAuth/DotNetOAuth.csproj index 618b0af..77fdd50 100644 --- a/src/DotNetOAuth/DotNetOAuth.csproj +++ b/src/DotNetOAuth/DotNetOAuth.csproj @@ -23,7 +23,7 @@ <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<DocumentationFile>..\..\bin\debug\DotNetOAuth.xml</DocumentationFile>
- <RunCodeAnalysis>true</RunCodeAnalysis>
+ <RunCodeAnalysis>false</RunCodeAnalysis>
<CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
@@ -69,10 +69,15 @@ <Compile Include="Consumer.cs" />
<Compile Include="Messaging\DictionaryXmlReader.cs" />
<Compile Include="Messaging\DictionaryXmlWriter.cs" />
- <Compile Include="Messaging\DirectMessageChannel.cs" />
- <Compile Include="Messaging\IProtocolMessageRequest.cs" />
+ <Compile Include="Messaging\Channel.cs" />
+ <Compile Include="Messaging\IDirectedProtocolMessage.cs" />
+ <Compile Include="Messaging\MessagingStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>MessagingStrings.resx</DependentUpon>
+ </Compile>
<Compile Include="Messaging\MessagingUtilities.cs" />
- <Compile Include="Messaging\ProtocolMessageResponse.cs" />
+ <Compile Include="Messaging\Response.cs" />
<Compile Include="Messaging\IndirectMessageEncoder.cs" />
<Compile Include="Messaging\IProtocolMessage.cs" />
<Compile Include="Logger.cs" />
@@ -83,7 +88,7 @@ <Compile Include="Messaging\MessageScheme.cs" />
<Compile Include="Messaging\MessageTransport.cs" />
<Compile Include="ProtocolException.cs" />
- <Compile Include="Messaging\ProtocolMessageSerializer.cs" />
+ <Compile Include="Messaging\MessageSerializer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Util.cs" />
<Compile Include="Protocol.cs" />
@@ -99,6 +104,10 @@ <None Include="ClassDiagram.cd" />
</ItemGroup>
<ItemGroup>
+ <EmbeddedResource Include="Messaging\MessagingStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>MessagingStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
<EmbeddedResource Include="Strings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
diff --git a/src/DotNetOAuth/Messaging/Channel.cs b/src/DotNetOAuth/Messaging/Channel.cs new file mode 100644 index 0000000..c50a90f --- /dev/null +++ b/src/DotNetOAuth/Messaging/Channel.cs @@ -0,0 +1,121 @@ +//-----------------------------------------------------------------------
+// <copyright file="Channel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Text;
+
+ /// <summary>
+ /// Manages sending direct messages to a remote party and receiving responses.
+ /// </summary>
+ internal class Channel {
+ /// <summary>
+ /// Gets or sets the message that came in as a request, if any.
+ /// </summary>
+ /// <remarks>
+ /// This message is used to help determine how to transmit the response.
+ /// </remarks>
+ internal IProtocolMessage RequestInProcess { get; set; }
+
+ /// <summary>
+ /// Gets or sets the HTTP response to send as a reply to the current incoming HTTP request.
+ /// </summary>
+ internal Response QueuedIndirectOrResponseMessage { get; set; }
+
+ /// <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>
+ internal IProtocolMessage Request(IDirectedProtocolMessage request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
+ MessageScheme transmissionMethod = MessageScheme.AuthorizationHeaderRequest;
+ switch (transmissionMethod) {
+ case MessageScheme.AuthorizationHeaderRequest:
+ throw new NotImplementedException();
+ break;
+ case MessageScheme.PostRequest:
+ throw new NotImplementedException();
+ break;
+ case MessageScheme.GetRequest:
+ throw new NotImplementedException();
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ }
+
+ /// <summary>
+ /// Queues 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>
+ internal void Send(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+ if (this.QueuedIndirectOrResponseMessage != null) {
+ throw new InvalidOperationException(MessagingStrings.QueuedMessageResponseAlreadyExists);
+ }
+
+ var directedMessage = message as IDirectedProtocolMessage;
+ if (directedMessage == null) {
+ // This is a response to a direct message.
+ this.SendDirectMessageResponse(message);
+ } else {
+ if (directedMessage.Recipient != null) {
+ // This is an indirect message request or reply.
+ this.SendIndirectMessage(directedMessage);
+ } else {
+ if (message is ProtocolException) {
+ if (this.RequestInProcess is IDirectedProtocolMessage) {
+ this.ReportErrorAsDirectResponse(directedMessage);
+ } else {
+ this.ReportErrorToUser(directedMessage);
+ }
+ }
+ }
+ }
+ }
+
+ private void SendIndirectMessage(IDirectedProtocolMessage directedMessage) {
+ 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>
+ /// <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;
+ }
+
+ private void ReportErrorToUser(IDirectedProtocolMessage directedMessage) {
+ throw new NotImplementedException();
+ }
+
+ private void ReportErrorAsDirectResponse(IDirectedProtocolMessage directedMessage) {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/DirectMessageChannel.cs b/src/DotNetOAuth/Messaging/DirectMessageChannel.cs deleted file mode 100644 index ee65c1f..0000000 --- a/src/DotNetOAuth/Messaging/DirectMessageChannel.cs +++ /dev/null @@ -1,21 +0,0 @@ -//-----------------------------------------------------------------------
-// <copyright file="DirectMessageChannel.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOAuth.Messaging {
- /// <summary>
- /// Manages sending direct messages to a remote party and receiving responses.
- /// </summary>
- internal class DirectMessageChannel {
- /// <summary>
- /// Sends a direct message and returns the response.
- /// </summary>
- /// <param name="message">The message to send.</param>
- /// <returns>The message response.</returns>
- public IProtocolMessage Send(IProtocolMessage message) {
- throw new System.NotImplementedException();
- }
- }
-}
diff --git a/src/DotNetOAuth/Messaging/IProtocolMessageRequest.cs b/src/DotNetOAuth/Messaging/IDirectedProtocolMessage.cs index c70f076..82ee08e 100644 --- a/src/DotNetOAuth/Messaging/IProtocolMessageRequest.cs +++ b/src/DotNetOAuth/Messaging/IDirectedProtocolMessage.cs @@ -1,5 +1,5 @@ //-----------------------------------------------------------------------
-// <copyright file="IProtocolMessageRequest.cs" company="Andrew Arnott">
+// <copyright file="IDirectedProtocolMessage.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
@@ -8,9 +8,10 @@ namespace DotNetOAuth.Messaging { using System;
/// <summary>
- /// Implemented by messages that are sent as requests.
+ /// Implemented by messages that have explicit recipients
+ /// (direct requests and all indirect messages).
/// </summary>
- internal interface IProtocolMessageRequest : IProtocolMessage {
+ internal interface IDirectedProtocolMessage : IProtocolMessage {
/// <summary>
/// Gets or sets the URL of the intended receiver of this message.
/// </summary>
diff --git a/src/DotNetOAuth/Messaging/IProtocolMessage.cs b/src/DotNetOAuth/Messaging/IProtocolMessage.cs index 76079c0..3d9f82e 100644 --- a/src/DotNetOAuth/Messaging/IProtocolMessage.cs +++ b/src/DotNetOAuth/Messaging/IProtocolMessage.cs @@ -22,6 +22,7 @@ namespace DotNetOAuth.Messaging { /// <summary>
/// Gets whether this is a direct or indirect message.
/// </summary>
+ [Obsolete("Are we using this anywhere?")]
MessageTransport Transport { get; }
/// <summary>
diff --git a/src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs b/src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs index d17fb2a..3cfc64e 100644 --- a/src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs +++ b/src/DotNetOAuth/Messaging/IndirectMessageEncoder.cs @@ -10,11 +10,11 @@ namespace DotNetOAuth.Messaging { /// </summary>
internal class IndirectMessageEncoder {
/// <summary>
- /// Prepares a protocol message for sending
+ /// Prepares a message for sending
/// </summary>
/// <param name="message">The indirect message to send.</param>
/// <returns>The encoded message to send to the user agent.</returns>
- internal ProtocolMessageResponse Encode(IProtocolMessage message) {
+ internal Response Encode(IProtocolMessage message) {
throw new System.NotImplementedException();
}
}
diff --git a/src/DotNetOAuth/Messaging/MessageSerializer.cs b/src/DotNetOAuth/Messaging/MessageSerializer.cs new file mode 100644 index 0000000..ac40232 --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessageSerializer.cs @@ -0,0 +1,167 @@ +//-----------------------------------------------------------------------
+// <copyright file="MessageSerializer.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Runtime.Serialization;
+ using System.Xml;
+ using System.Xml.Linq;
+
+ /// <summary>
+ /// Serializes/deserializes OAuth messages for/from transit.
+ /// </summary>
+ internal class MessageSerializer {
+ /// <summary>
+ /// The serializer that will be used as a reflection engine to extract
+ /// the OAuth message properties out of their containing <see cref="IProtocolMessage"/>
+ /// objects.
+ /// </summary>
+ private readonly DataContractSerializer serializer;
+
+ /// <summary>
+ /// The specific <see cref="IProtocolMessage"/>-derived type
+ /// that will be serialized and deserialized using this class.
+ /// </summary>
+ private readonly Type messageType;
+
+ /// <summary>
+ /// An AppDomain-wide cache of shared serializers for optimization purposes.
+ /// </summary>
+ private static Dictionary<Type, MessageSerializer> prebuiltSerializers = new Dictionary<Type, MessageSerializer>();
+
+ /// <summary>
+ /// Backing field for the <see cref="RootElement"/> property
+ /// </summary>
+ private XName rootElement;
+
+ /// <summary>
+ /// Initializes a new instance of the MessageSerializer class.
+ /// </summary>
+ /// <param name="messageType">The specific <see cref="IProtocolMessage"/>-derived type
+ /// that will be serialized and deserialized using this class.</param>
+ private MessageSerializer(Type messageType) {
+ if (messageType == null) {
+ throw new ArgumentNullException("messageType");
+ }
+ if (!typeof(IProtocolMessage).IsAssignableFrom(messageType)) {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedType,
+ typeof(IProtocolMessage).FullName,
+ messageType.FullName),
+ "messageType");
+ }
+
+ this.messageType = messageType;
+ this.serializer = new DataContractSerializer(
+ messageType, this.RootElement.LocalName, this.RootElement.NamespaceName);
+ }
+
+ /// <summary>
+ /// Gets the XML element that is used to surround all the XML values from the dictionary.
+ /// </summary>
+ private XName RootElement {
+ get {
+ if (this.rootElement == null) {
+ DataContractAttribute attribute = this.messageType.GetCustomAttributes(typeof(DataContractAttribute), false).OfType<DataContractAttribute>().Single();
+ this.rootElement = XName.Get("root", attribute.Namespace);
+ }
+
+ return this.rootElement;
+ }
+ }
+
+ /// <summary>
+ /// Returns a message serializer from a reusable collection of serializers.
+ /// </summary>
+ /// <param name="messageType">The type of message that will be serialized/deserialized.</param>
+ /// <returns>A previously created serializer if one exists, or a newly created one.</returns>
+ internal static MessageSerializer Get(Type messageType) {
+ if (messageType == null) {
+ throw new ArgumentNullException("messageType");
+ }
+
+ // We do this as efficiently as possible by first trying to fetch the
+ // serializer out of the dictionary without taking a lock.
+ MessageSerializer serializer;
+ if (prebuiltSerializers.TryGetValue(messageType, out serializer)) {
+ return serializer;
+ }
+
+ // Since it wasn't there, we'll be trying to write to the dictionary so
+ // we take a lock and try reading again first, then creating the serializer
+ // and storing it when we're sure it absolutely necessary.
+ lock (prebuiltSerializers) {
+ if (prebuiltSerializers.TryGetValue(messageType, out serializer)) {
+ return serializer;
+ }
+ serializer = new MessageSerializer(messageType);
+ prebuiltSerializers.Add(messageType, serializer);
+ }
+ return serializer;
+ }
+
+ /// <summary>
+ /// Reads the data from a message instance and returns a series of name=value pairs for the fields that must be included in the message.
+ /// </summary>
+ /// <param name="message">The message to be serialized.</param>
+ /// <returns>The dictionary of values to send for the message.</returns>
+ internal IDictionary<string, string> Serialize(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ this.Serialize(fields, message);
+ return fields;
+ }
+
+ /// <summary>
+ /// Saves the [DataMember] properties of a message to an existing dictionary.
+ /// </summary>
+ /// <param name="fields">The dictionary to save values to.</param>
+ /// <param name="message">The message to pull values from.</param>
+ internal void Serialize(IDictionary<string, string> fields, IProtocolMessage message) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ message.EnsureValidMessage();
+ using (XmlWriter writer = DictionaryXmlWriter.Create(fields)) {
+ this.serializer.WriteObjectContent(writer, message);
+ }
+ }
+
+ /// <summary>
+ /// Reads name=value pairs into an OAuth message.
+ /// </summary>
+ /// <param name="fields">The name=value pairs that were read in from the transport.</param>
+ /// <returns>The instantiated and initialized <see cref="IProtocolMessage"/> instance.</returns>
+ internal IProtocolMessage Deserialize(IDictionary<string, string> fields) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ var reader = DictionaryXmlReader.Create(this.RootElement, fields);
+ IProtocolMessage result;
+ try {
+ result = (IProtocolMessage)this.serializer.ReadObject(reader, false);
+ } catch (SerializationException ex) {
+ // Missing required fields is one cause of this exception.
+ throw new ProtocolException(Strings.InvalidIncomingMessage, ex);
+ }
+ result.EnsureValidMessage();
+ return result;
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs new file mode 100644 index 0000000..30321ca --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3053
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class MessagingStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal MessagingStrings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOAuth.Messaging.MessagingStrings", typeof(MessagingStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An instance of type {0} was expected, but received unexpected derived type {1}..
+ /// </summary>
+ internal static string DerivedTypeNotExpected {
+ get {
+ return ResourceManager.GetString("DerivedTypeNotExpected", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A message response is already queued for sending in the response stream..
+ /// </summary>
+ internal static string QueuedMessageResponseAlreadyExists {
+ get {
+ return ResourceManager.GetString("QueuedMessageResponseAlreadyExists", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type {0} or a derived type was expected, but {1} was given..
+ /// </summary>
+ internal static string UnexpectedType {
+ get {
+ return ResourceManager.GetString("UnexpectedType", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.resx b/src/DotNetOAuth/Messaging/MessagingStrings.resx new file mode 100644 index 0000000..c8ff072 --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessagingStrings.resx @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="DerivedTypeNotExpected" xml:space="preserve">
+ <value>An instance of type {0} was expected, but received unexpected derived type {1}.</value>
+ </data>
+ <data name="QueuedMessageResponseAlreadyExists" xml:space="preserve">
+ <value>A message response is already queued for sending in the response stream.</value>
+ </data>
+ <data name="UnexpectedType" xml:space="preserve">
+ <value>The type {0} or a derived type was expected, but {1} was given.</value>
+ </data>
+</root>
\ No newline at end of file diff --git a/src/DotNetOAuth/Messaging/MessagingUtilities.cs b/src/DotNetOAuth/Messaging/MessagingUtilities.cs index 7544fa4..c90a5fe 100644 --- a/src/DotNetOAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOAuth/Messaging/MessagingUtilities.cs @@ -6,7 +6,9 @@ namespace DotNetOAuth.Messaging {
using System;
+ using System.Collections.Generic;
using System.Net;
+ using System.Text;
using System.Web;
/// <summary>
@@ -40,5 +42,32 @@ namespace DotNetOAuth.Messaging { }
}
}
+
+ /// <summary>
+ /// Concatenates a list of name-value pairs as key=value&key=value,
+ /// taking care to properly encode each key and value for URL
+ /// transmission. No ? is prefixed to the string.
+ /// </summary>
+ /// <param name="args">The dictionary of key/values to read from.</param>
+ /// <returns>The formulated querystring style string.</returns>
+ internal static string CreateQueryString(IDictionary<string, string> args) {
+ if (args == null) {
+ throw new ArgumentNullException("args");
+ }
+ if (args.Count == 0) {
+ return string.Empty;
+ }
+ StringBuilder sb = new StringBuilder(args.Count * 10);
+
+ foreach (var p in args) {
+ sb.Append(HttpUtility.UrlEncode(p.Key));
+ sb.Append('=');
+ sb.Append(HttpUtility.UrlEncode(p.Value));
+ sb.Append('&');
+ }
+ sb.Length--; // remove trailing &
+
+ return sb.ToString();
+ }
}
}
diff --git a/src/DotNetOAuth/Messaging/ProtocolMessageSerializer.cs b/src/DotNetOAuth/Messaging/ProtocolMessageSerializer.cs deleted file mode 100644 index 0b28d49..0000000 --- a/src/DotNetOAuth/Messaging/ProtocolMessageSerializer.cs +++ /dev/null @@ -1,111 +0,0 @@ -//-----------------------------------------------------------------------
-// <copyright file="ProtocolMessageSerializer.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.Serialization;
- using System.Xml;
- using System.Xml.Linq;
-
- /// <summary>
- /// Serializes/deserializes OAuth messages for/from transit.
- /// </summary>
- /// <typeparam name="T">The specific <see cref="IProtocolMessage"/>-derived type
- /// that will be serialized and deserialized using this class.</typeparam>
- internal class ProtocolMessageSerializer<T> where T : IProtocolMessage {
- /// <summary>
- /// Backing field for the <see cref="RootElement"/> property
- /// </summary>
- private XName rootElement;
-
- /// <summary>
- /// The serializer that will be used as a reflection engine to extract
- /// the OAuth message properties out of their containing <see cref="IProtocolMessage"/>
- /// objects.
- /// </summary>
- private DataContractSerializer serializer;
-
- /// <summary>
- /// Initializes a new instance of the ProtocolMessageSerializer class.
- /// </summary>
- internal ProtocolMessageSerializer() {
- this.serializer = new DataContractSerializer(
- typeof(T), this.RootElement.LocalName, this.RootElement.NamespaceName);
- }
-
- /// <summary>
- /// Gets the XML element that is used to surround all the XML values from the dictionary.
- /// </summary>
- private XName RootElement {
- get {
- if (this.rootElement == null) {
- DataContractAttribute attribute = typeof(T).GetCustomAttributes(typeof(DataContractAttribute), false).OfType<DataContractAttribute>().Single();
- this.rootElement = XName.Get("root", attribute.Namespace);
- }
-
- return this.rootElement;
- }
- }
-
- /// <summary>
- /// Reads the data from a message instance and returns a series of name=value pairs for the fields that must be included in the message.
- /// </summary>
- /// <param name="message">The message to be serialized.</param>
- /// <returns>The dictionary of values to send for the message.</returns>
- internal IDictionary<string, string> Serialize(T message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- var fields = new Dictionary<string, string>(StringComparer.Ordinal);
- this.Serialize(fields, message);
- return fields;
- }
-
- /// <summary>
- /// Saves the [DataMember] properties of a message to an existing dictionary.
- /// </summary>
- /// <param name="fields">The dictionary to save values to.</param>
- /// <param name="message">The message to pull values from.</param>
- internal void Serialize(IDictionary<string, string> fields, T message) {
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- message.EnsureValidMessage();
- using (XmlWriter writer = DictionaryXmlWriter.Create(fields)) {
- this.serializer.WriteObjectContent(writer, message);
- }
- }
-
- /// <summary>
- /// Reads name=value pairs into an OAuth message.
- /// </summary>
- /// <param name="fields">The name=value pairs that were read in from the transport.</param>
- /// <returns>The instantiated and initialized <see cref="IProtocolMessage"/> instance.</returns>
- internal T Deserialize(IDictionary<string, string> fields) {
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
-
- var reader = DictionaryXmlReader.Create(this.RootElement, fields);
- T result;
- try {
- result = (T)this.serializer.ReadObject(reader, false);
- } catch (SerializationException ex) {
- // Missing required fields is one cause of this exception.
- throw new ProtocolException(Strings.InvalidIncomingMessage, ex);
- }
- result.EnsureValidMessage();
- return result;
- }
- }
-}
diff --git a/src/DotNetOAuth/Messaging/ProtocolMessageResponse.cs b/src/DotNetOAuth/Messaging/Response.cs index 1f2d081..68950ff 100644 --- a/src/DotNetOAuth/Messaging/ProtocolMessageResponse.cs +++ b/src/DotNetOAuth/Messaging/Response.cs @@ -1,5 +1,5 @@ //-----------------------------------------------------------------------
-// <copyright file="ProtocolMessageResponse.cs" company="Andrew Arnott">
+// <copyright file="Response.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
@@ -23,7 +23,7 @@ namespace DotNetOAuth.Messaging { /// can be canceled by calling <see cref="HttpResponse.End"/> after this message
/// is sent on the response stream.</para>
/// </remarks>
- public class ProtocolMessageResponse {
+ public class Response {
/// <summary>
/// Gets the headers that must be included in the response to the user agent.
/// </summary>
diff --git a/src/Settings.StyleCop b/src/Settings.StyleCop index 791e073..a00cce5 100644 --- a/src/Settings.StyleCop +++ b/src/Settings.StyleCop @@ -37,6 +37,11 @@ <BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
+ <Rule Name="CodeMustNotContainMultipleStatementsOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
|