diff options
Diffstat (limited to 'src')
9 files changed, 85 insertions, 31 deletions
diff --git a/src/DotNetOAuth.Test/Messaging/MessageSerializerTests.cs b/src/DotNetOAuth.Test/Messaging/MessageSerializerTests.cs index 2eb1dbf..25dd8e3 100644 --- a/src/DotNetOAuth.Test/Messaging/MessageSerializerTests.cs +++ b/src/DotNetOAuth.Test/Messaging/MessageSerializerTests.cs @@ -31,15 +31,6 @@ namespace DotNetOAuth.Test.Messaging { MessageSerializer.Get(null);
}
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void SerializeInvalidMessage() {
- var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
- Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
- Mocks.TestMessage message = new Mocks.TestMessage();
- message.EmptyMember = "invalidvalue";
- serializer.Serialize(message);
- }
-
[TestMethod()]
public void SerializeTest() {
var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
@@ -47,7 +38,7 @@ namespace DotNetOAuth.Test.Messaging { Age = 15,
Name = "Andrew",
Location = new Uri("http://localhost"),
- Timestamp = DateTime.Parse("1/1/1990"),
+ Timestamp = DateTime.SpecifyKind(DateTime.Parse("1/1/1990"), DateTimeKind.Utc),
};
IDictionary<string, string> actual = serializer.Serialize(message);
Assert.AreEqual(4, actual.Count);
@@ -60,7 +51,7 @@ namespace DotNetOAuth.Test.Messaging { Assert.AreEqual("15", actual["age"]);
Assert.AreEqual("Andrew", actual["Name"]);
Assert.AreEqual("http://localhost/", actual["Location"]);
- Assert.AreEqual("1990-01-01T00:00:00", actual["Timestamp"]);
+ Assert.AreEqual("1990-01-01T00:00:00Z", actual["Timestamp"]);
Assert.IsFalse(actual.ContainsKey("EmptyMember"));
}
@@ -131,13 +122,6 @@ namespace DotNetOAuth.Test.Messaging { }
[TestMethod, ExpectedException(typeof(ProtocolException))]
- public void DeserializeEmpty() {
- 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 = MessageSerializer.Get(typeof(Mocks.TestMessage));
Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
diff --git a/src/DotNetOAuth.Test/Messaging/Reflection/MessageDictionaryTest.cs b/src/DotNetOAuth.Test/Messaging/Reflection/MessageDictionaryTest.cs index f798bde..f6b1f38 100644 --- a/src/DotNetOAuth.Test/Messaging/Reflection/MessageDictionaryTest.cs +++ b/src/DotNetOAuth.Test/Messaging/Reflection/MessageDictionaryTest.cs @@ -12,6 +12,7 @@ namespace DotNetOAuth.Test.Messaging.Reflection { using DotNetOAuth.Messaging;
using DotNetOAuth.Messaging.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+ using System.Xml;
[TestClass]
public class MessageDictionaryTest : MessagingTestBase {
@@ -37,7 +38,7 @@ namespace DotNetOAuth.Test.Messaging.Reflection { IDictionary<string, string> target = new MessageDictionary(this.message);
Collection<string> expected = new Collection<string> {
this.message.Age.ToString(),
- this.message.Timestamp.ToString(),
+ XmlConvert.ToString(DateTime.SpecifyKind(this.message.Timestamp, DateTimeKind.Utc), XmlDateTimeSerializationMode.Utc),
};
CollectionAssert<string>.AreEquivalent(expected, target.Values);
@@ -49,7 +50,7 @@ namespace DotNetOAuth.Test.Messaging.Reflection { this.message.Age.ToString(),
this.message.Location.AbsoluteUri,
this.message.Name,
- this.message.Timestamp.ToString(),
+ XmlConvert.ToString(DateTime.SpecifyKind(this.message.Timestamp, DateTimeKind.Utc), XmlDateTimeSerializationMode.Utc),
"a",
};
CollectionAssert<string>.AreEquivalent(expected, target.Values);
@@ -80,7 +81,7 @@ namespace DotNetOAuth.Test.Messaging.Reflection { [TestMethod]
public void ItemTest() {
IDictionary<string, string> target = new MessageDictionary(this.message);
-
+
// Test setting of declared message properties.
this.message.Age = 15;
Assert.AreEqual("15", target["age"]);
@@ -194,7 +195,7 @@ namespace DotNetOAuth.Test.Messaging.Reflection { Assert.IsTrue(target.Remove("Name"));
Assert.IsNull(this.message.Name);
Assert.IsFalse(target.Remove("Name"));
-
+
Assert.IsFalse(target.Remove("extra"));
target["extra"] = "value";
Assert.IsTrue(target.Remove("extra"));
diff --git a/src/DotNetOAuth.Test/Messaging/Reflection/MessagePartTests.cs b/src/DotNetOAuth.Test/Messaging/Reflection/MessagePartTests.cs index 646599c..1d7e1b8 100644 --- a/src/DotNetOAuth.Test/Messaging/Reflection/MessagePartTests.cs +++ b/src/DotNetOAuth.Test/Messaging/Reflection/MessagePartTests.cs @@ -16,12 +16,29 @@ namespace DotNetOAuth.Test.Messaging.Reflection { [MessagePart(IsRequired = false)]
internal int optionalInt;
}
+ class MessageWithNullableOptionalStruct {
+ /// <summary>
+ /// Optional structs like int must be nullable for Optional to make sense.
+ /// </summary>
+ [MessagePart(IsRequired = false)]
+ internal int? optionalInt;
+ }
+
[TestMethod, ExpectedException(typeof(ArgumentException))]
public void OptionalNonNullableStruct() {
- FieldInfo field = typeof(MessageWithNonNullableOptionalStruct).GetField("optionalInt", BindingFlags.NonPublic | BindingFlags.Instance);
+ ParameterizedMessageTypeTest(typeof(MessageWithNonNullableOptionalStruct));
+ }
+
+ [TestMethod]
+ public void OptionalNullableStruct() {
+ ParameterizedMessageTypeTest(typeof(MessageWithNullableOptionalStruct));
+ }
+
+ private void ParameterizedMessageTypeTest(Type messageType) {
+ FieldInfo field = messageType.GetField("optionalInt", BindingFlags.NonPublic | BindingFlags.Instance);
MessagePartAttribute attribute = field.GetCustomAttributes(typeof(MessagePartAttribute), true).OfType<MessagePartAttribute>().Single();
- new MessagePart(field, attribute); // should recognize invalid optional non-nullable struct
+ new MessagePart(field, attribute);
}
}
}
diff --git a/src/DotNetOAuth.Test/Mocks/TestExpiringMessage.cs b/src/DotNetOAuth.Test/Mocks/TestExpiringMessage.cs index fbe0d9a..1b06969 100644 --- a/src/DotNetOAuth.Test/Mocks/TestExpiringMessage.cs +++ b/src/DotNetOAuth.Test/Mocks/TestExpiringMessage.cs @@ -25,7 +25,7 @@ namespace DotNetOAuth.Test.Mocks { #region IExpiringProtocolMessage Members
- [MessagePart(Name = "created_on")]
+ [MessagePart(Name = "created_on", IsRequired = true)]
DateTime IExpiringProtocolMessage.UtcCreationDate {
get { return this.utcCreationDate; }
set { this.utcCreationDate = value.ToUniversalTime(); }
diff --git a/src/DotNetOAuth.Test/Mocks/TestMessage.cs b/src/DotNetOAuth.Test/Mocks/TestMessage.cs index ceb6dbd..1bd94bf 100644 --- a/src/DotNetOAuth.Test/Mocks/TestMessage.cs +++ b/src/DotNetOAuth.Test/Mocks/TestMessage.cs @@ -32,7 +32,7 @@ namespace DotNetOAuth.Test.Mocks { public string EmptyMember { get; set; }
[MessagePart]
public Uri Location { get; set; }
- [MessagePart]
+ [MessagePart(IsRequired = true)]
public DateTime Timestamp { get; set; }
#region IProtocolMessage Members
diff --git a/src/DotNetOAuth/Messaging/Channel.cs b/src/DotNetOAuth/Messaging/Channel.cs index e3b7a73..fd7e676 100644 --- a/src/DotNetOAuth/Messaging/Channel.cs +++ b/src/DotNetOAuth/Messaging/Channel.cs @@ -15,6 +15,7 @@ namespace DotNetOAuth.Messaging { using System.Net;
using System.Text;
using System.Web;
+ using DotNetOAuth.Messaging.Reflection;
/// <summary>
/// Manages sending direct messages to a remote party and receiving responses.
@@ -525,10 +526,21 @@ namespace DotNetOAuth.Messaging { throw new UnprotectedMessageException(message, appliedProtection);
}
- // TODO: call MessagePart.IsValidValue()
-
-
+ EnsureValidMessageParts(message);
message.EnsureValidMessage();
}
+
+ private void EnsureValidMessageParts(IProtocolMessage message) {
+ Debug.Assert(message != null, "message == null");
+ // TODO: call MessagePart.IsValidValue()
+ MessageDescription description = new MessageDescription(message.GetType());
+ List<MessagePart> invalidParts = description.Mapping.Values.Where(part => !part.IsValidValue(message)).ToList();
+ if (invalidParts.Count > 0) {
+ throw new ProtocolException(string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.InvalidMessageParts,
+ string.Join(", ", invalidParts.Select(part => part.Name).ToArray())));
+ }
+ }
}
}
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs index 7852ea2..b8183c8 100644 --- a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs +++ b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs @@ -151,6 +151,15 @@ namespace DotNetOAuth.Messaging { }
/// <summary>
+ /// Looks up a localized string similar to Some part(s) of the message have invalid values: {0}.
+ /// </summary>
+ internal static string InvalidMessageParts {
+ get {
+ return ResourceManager.GetString("InvalidMessageParts", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to An item with the same key has already been added..
/// </summary>
internal static string KeyAlreadyExists {
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.resx b/src/DotNetOAuth/Messaging/MessagingStrings.resx index 121f9b8..b6b98d1 100644 --- a/src/DotNetOAuth/Messaging/MessagingStrings.resx +++ b/src/DotNetOAuth/Messaging/MessagingStrings.resx @@ -147,6 +147,9 @@ <data name="InsufficentMessageProtection" xml:space="preserve">
<value>The message required protections {0} but the channel could only apply {1}.</value>
</data>
+ <data name="InvalidMessageParts" xml:space="preserve">
+ <value>Some part(s) of the message have invalid values: {0}</value>
+ </data>
<data name="KeyAlreadyExists" xml:space="preserve">
<value>An item with the same key has already been added.</value>
</data>
diff --git a/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs index 2005370..b3c66d0 100644 --- a/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs @@ -9,6 +9,8 @@ namespace DotNetOAuth.Messaging.Reflection { using System.Collections.Generic;
using System.Net.Security;
using System.Reflection;
+ using System.Xml;
+ using System.Globalization;
internal class MessagePart {
private static readonly Dictionary<Type, ValueMapping> converters = new Dictionary<Type, ValueMapping>();
@@ -25,6 +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));
}
internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
@@ -53,6 +56,9 @@ namespace DotNetOAuth.Messaging.Reflection { obj => obj != null ? obj.ToString() : null,
str => str != null ? Convert.ChangeType(str, memberDeclaredType) : null);
}
+
+ // Validate a sane combination of settings
+ ValidateSettings();
}
internal string Name { get; set; }
@@ -109,12 +115,34 @@ namespace DotNetOAuth.Messaging.Reflection { }
}
- private static void Map<T>(Func<T, string> toString, Func<string, T> toValue) where T : class {
+ private static void Map<T>(Func<T, string> toString, Func<string, T> toValue) {
converters.Add(
typeof(T),
new ValueMapping(
obj => obj != null ? toString((T)obj) : null,
- str => str != null ? toValue(str) : null));
+ str => str != null ? toValue(str) : default(T)));
+ }
+
+ private void ValidateSettings() {
+ // An optional tag on a non-nullable value type is a contradiction.
+ if (!this.IsRequired && IsNonNullableValueType(this.memberDeclaredType)) {
+ MemberInfo member = (MemberInfo)this.field ?? this.property;
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
+ "Invalid combination: {0} on message type {1} is a non-nullable value type but is marked as optional.",
+ member.Name, member.DeclaringType));
+ }
+ }
+
+ private static bool IsNonNullableValueType(Type type) {
+ if (!type.IsValueType) {
+ return false;
+ }
+
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
+ return false;
+ }
+
+ return true;
}
}
}
|