summaryrefslogtreecommitdiffstats
path: root/src/DotNetOAuth/Messaging
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOAuth/Messaging')
-rw-r--r--src/DotNetOAuth/Messaging/Bindings/EnsureCompleteMessageBindingElement.cs33
-rw-r--r--src/DotNetOAuth/Messaging/Channel.cs7
-rw-r--r--src/DotNetOAuth/Messaging/MessageSerializer.cs10
-rw-r--r--src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs9
-rw-r--r--src/DotNetOAuth/Messaging/MessagingStrings.resx3
-rw-r--r--src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs38
-rw-r--r--src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs2
-rw-r--r--src/DotNetOAuth/Messaging/Reflection/MessagePart.cs2
8 files changed, 97 insertions, 7 deletions
diff --git a/src/DotNetOAuth/Messaging/Bindings/EnsureCompleteMessageBindingElement.cs b/src/DotNetOAuth/Messaging/Bindings/EnsureCompleteMessageBindingElement.cs
new file mode 100644
index 0000000..dc00c12
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/Bindings/EnsureCompleteMessageBindingElement.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using DotNetOAuth.Messaging.Reflection;
+
+namespace DotNetOAuth.Messaging.Bindings {
+ internal class EnsureCompleteMessageBindingElement : IChannelBindingElement {
+ #region IChannelBindingElement Members
+
+ public MessageProtection Protection {
+ get { return MessageProtection.None; }
+ }
+
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ // Before we're sending the message, make sure all the required parts are present.
+ MessageDictionary dictionary = new MessageDictionary(message);
+ MessageDescription.Get(message.GetType()).EnsureRequiredMessagePartsArePresent(dictionary.Keys);
+ return true;
+ }
+
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ // Once the message is deserialized, it is too late to use the MessageDictionary
+ // to see if all the message parts were included in the original serialized message
+ // because non-nullable value types will already have default values by now.
+ // The code for verifying complete incoming messages is included in
+ // MessageSerializer.Deserialize.
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/Channel.cs b/src/DotNetOAuth/Messaging/Channel.cs
index fd7e676..be3cdf2 100644
--- a/src/DotNetOAuth/Messaging/Channel.cs
+++ b/src/DotNetOAuth/Messaging/Channel.cs
@@ -16,6 +16,7 @@ namespace DotNetOAuth.Messaging {
using System.Text;
using System.Web;
using DotNetOAuth.Messaging.Reflection;
+ using DotNetOAuth.Messaging.Bindings;
/// <summary>
/// Manages sending direct messages to a remote party and receiving responses.
@@ -83,6 +84,9 @@ namespace DotNetOAuth.Messaging {
this.messageTypeProvider = messageTypeProvider;
this.bindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
+
+ // Add a complete message check as a last outgoing step.
+ this.bindingElements.Add(new EnsureCompleteMessageBindingElement());
}
/// <summary>
@@ -532,8 +536,7 @@ namespace DotNetOAuth.Messaging {
private void EnsureValidMessageParts(IProtocolMessage message) {
Debug.Assert(message != null, "message == null");
- // TODO: call MessagePart.IsValidValue()
- MessageDescription description = new MessageDescription(message.GetType());
+ MessageDescription description = MessageDescription.Get(message.GetType());
List<MessagePart> invalidParts = description.Mapping.Values.Where(part => !part.IsValidValue(message)).ToList();
if (invalidParts.Count > 0) {
throw new ProtocolException(string.Format(
diff --git a/src/DotNetOAuth/Messaging/MessageSerializer.cs b/src/DotNetOAuth/Messaging/MessageSerializer.cs
index 4a06076..6edb0b6 100644
--- a/src/DotNetOAuth/Messaging/MessageSerializer.cs
+++ b/src/DotNetOAuth/Messaging/MessageSerializer.cs
@@ -13,6 +13,7 @@ namespace DotNetOAuth.Messaging {
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Linq;
+ using DotNetOAuth.Messaging.Reflection;
/// <summary>
/// Serializes/deserializes OAuth messages for/from transit.
@@ -68,7 +69,9 @@ namespace DotNetOAuth.Messaging {
throw new ArgumentNullException("message");
}
- return new Reflection.MessageDictionary(message);
+ var result = new Reflection.MessageDictionary(message);
+
+ return result;
}
/// <summary>
@@ -81,6 +84,9 @@ namespace DotNetOAuth.Messaging {
throw new ArgumentNullException("fields");
}
+ // Before we deserialize the message, make sure all the required parts are present.
+ MessageDescription.Get(this.messageType).EnsureRequiredMessagePartsArePresent(fields.Keys);
+
IProtocolMessage result ;
try {
result = (IProtocolMessage)Activator.CreateInstance(this.messageType, true);
@@ -88,7 +94,7 @@ namespace DotNetOAuth.Messaging {
throw new ProtocolException("Failed to instantiate type " + this.messageType.FullName, ex);
}
foreach (var pair in fields) {
- IDictionary<string, string> dictionary = new Reflection.MessageDictionary(result);
+ IDictionary<string, string> dictionary = new MessageDictionary(result);
dictionary.Add(pair);
}
result.EnsureValidMessage();
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
index b8183c8..9386f53 100644
--- a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
@@ -196,6 +196,15 @@ namespace DotNetOAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to The following required parameters were missing from the {0} message: {1}.
+ /// </summary>
+ internal static string RequiredParametersMissing {
+ get {
+ return ResourceManager.GetString("RequiredParametersMissing", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The binding element offering the {0} protection requires other protection that is not provided..
/// </summary>
internal static string RequiredProtectionMissing {
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.resx b/src/DotNetOAuth/Messaging/MessagingStrings.resx
index b6b98d1..bdbd212 100644
--- a/src/DotNetOAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOAuth/Messaging/MessagingStrings.resx
@@ -162,6 +162,9 @@
<data name="ReplayProtectionNotSupported" xml:space="preserve">
<value>This channel does not support replay protection.</value>
</data>
+ <data name="RequiredParametersMissing" xml:space="preserve">
+ <value>The following required parameters were missing from the {0} message: {1}</value>
+ </data>
<data name="RequiredProtectionMissing" xml:space="preserve">
<value>The binding element offering the {0} protection requires other protection that is not provided.</value>
</data>
diff --git a/src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs
index ef73a31..6b898bb 100644
--- a/src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs
@@ -9,12 +9,14 @@ namespace DotNetOAuth.Messaging.Reflection {
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+ using System.Globalization;
internal class MessageDescription {
+ private static Dictionary<Type, MessageDescription> reflectedMessageTypes = new Dictionary<Type,MessageDescription>();
private Type messageType;
private Dictionary<string, MessagePart> mapping;
- internal MessageDescription(Type messageType) {
+ private MessageDescription(Type messageType) {
if (messageType == null) {
throw new ArgumentNullException("messageType");
}
@@ -27,6 +29,23 @@ namespace DotNetOAuth.Messaging.Reflection {
this.ReflectMessageType();
}
+ internal static MessageDescription Get(Type messageType) {
+ if (messageType == null) {
+ throw new ArgumentNullException("messageType");
+ }
+
+ MessageDescription result;
+ if (!reflectedMessageTypes.TryGetValue(messageType, out result)) {
+ lock (reflectedMessageTypes) {
+ if (!reflectedMessageTypes.TryGetValue(messageType, out result)) {
+ reflectedMessageTypes[messageType] = result = new MessageDescription(messageType);
+ }
+ }
+ }
+
+ return result;
+ }
+
internal Type MessageType {
get { return this.messageType; }
}
@@ -52,5 +71,22 @@ namespace DotNetOAuth.Messaging.Reflection {
currentType = currentType.BaseType;
} while (currentType != null);
}
+
+ /// <summary>
+ /// Verifies that a given set of keys include all the required parameters
+ /// for this message type or throws an exception.
+ /// </summary>
+ /// <exception cref="ProtocolException">Thrown when required parts of a message are not in <paramref name="keys"/></exception>
+ internal void EnsureRequiredMessagePartsArePresent(IEnumerable<string> keys) {
+ var missingKeys = (from part in Mapping.Values
+ where part.IsRequired && !keys.Contains(part.Name)
+ select part.Name).ToArray();
+ if (missingKeys.Length > 0) {
+ throw new ProtocolException(string.Format(CultureInfo.CurrentCulture,
+ MessagingStrings.RequiredParametersMissing,
+ this.messageType.FullName,
+ string.Join(", ", missingKeys)));
+ }
+ }
}
}
diff --git a/src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs
index 9490894..196af54 100644
--- a/src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs
@@ -26,7 +26,7 @@ namespace DotNetOAuth.Messaging.Reflection {
}
this.message = message;
- this.description = new MessageDescription(message.GetType());
+ this.description = MessageDescription.Get(message.GetType());
}
#region IDictionary<string,string> Members
diff --git a/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs
index b3c66d0..4d2ebe6 100644
--- a/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs
@@ -27,7 +27,7 @@ namespace DotNetOAuth.Messaging.Reflection {
static MessagePart() {
Map<Uri>(uri => uri.AbsoluteUri, str => new Uri(str));
- Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => DateTime.Parse(str));
+ Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
}
internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {