Skip to content
This repository was archived by the owner on Mar 20, 2019. It is now read-only.

Commit 01d8c73

Browse files
committed
Moved some JSON serialization logic to MessagingUtilities and added a unit test.
1 parent 9c732b8 commit 01d8c73

7 files changed

Lines changed: 124 additions & 18 deletions

File tree

src/DotNetOpenAuth.Core/Assumes.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ internal static void Fail(string message = null) {
5757
}
5858
}
5959

60+
/// <summary>
61+
/// Throws an internal error exception.
62+
/// </summary>
63+
/// <returns>Nothing. This method always throws.</returns>
64+
internal static Exception NotReachable() {
65+
throw new InternalErrorException();
66+
}
67+
6068
/// <summary>
6169
/// An internal error exception that should never be caught.
6270
/// </summary>

src/DotNetOpenAuth.Core/Messaging/Channel.cs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ public abstract class Channel : IDisposable {
3737
/// </summary>
3838
internal static readonly Encoding PostEntityEncoding = new UTF8Encoding(false);
3939

40+
/// <summary>
41+
/// A default set of XML dictionary reader quotas that are relatively safe from causing unbounded memory consumption.
42+
/// </summary>
43+
internal static readonly XmlDictionaryReaderQuotas DefaultUntrustedXmlDictionaryReaderQuotas = new XmlDictionaryReaderQuotas {
44+
MaxArrayLength = 1,
45+
MaxDepth = 2,
46+
MaxBytesPerRead = 8 * 1024,
47+
MaxStringContentLength = 16 * 1024,
48+
};
49+
4050
/// <summary>
4151
/// The content-type used on HTTP POST requests where the POST entity is a
4252
/// URL-encoded series of key=value pairs.
@@ -152,12 +162,7 @@ protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingEle
152162

153163
this.messageTypeProvider = messageTypeProvider;
154164
this.WebRequestHandler = new StandardWebRequestHandler();
155-
this.XmlDictionaryReaderQuotas = new XmlDictionaryReaderQuotas {
156-
MaxArrayLength = 1,
157-
MaxDepth = 2,
158-
MaxBytesPerRead = 8 * 1024,
159-
MaxStringContentLength = 16 * 1024,
160-
};
165+
this.XmlDictionaryReaderQuotas = DefaultUntrustedXmlDictionaryReaderQuotas;
161166

162167
this.outgoingBindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
163168
this.incomingBindingElements = new List<IChannelBindingElement>(this.outgoingBindingElements);
@@ -991,17 +996,7 @@ protected virtual HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage requ
991996
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")]
992997
protected virtual string SerializeAsJson(IMessage message) {
993998
Requires.NotNull(message, "message");
994-
995-
MessageDictionary messageDictionary = this.MessageDescriptions.GetAccessor(message);
996-
using (var memoryStream = new MemoryStream()) {
997-
using (var jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(memoryStream, Encoding.UTF8)) {
998-
MessageSerializer.Serialize(messageDictionary, jsonWriter);
999-
jsonWriter.Flush();
1000-
}
1001-
1002-
string json = Encoding.UTF8.GetString(memoryStream.ToArray());
1003-
return json;
1004-
}
999+
return MessagingUtilities.SerializeAsJson(message, this.MessageDescriptions);
10051000
}
10061001

10071002
/// <summary>

src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ internal static void DeserializeJsonAsFlatDictionary(IDictionary<string, string>
7474
}
7575

7676
/// <summary>
77-
/// Reads the data from a message instance and writes a XML/JSON encoding of it.
77+
/// Reads the data from a message instance and writes an XML/JSON encoding of it.
7878
/// </summary>
7979
/// <param name="messageDictionary">The message to be serialized.</param>
8080
/// <param name="writer">The writer to use for the serialized form.</param>

src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,6 +1671,68 @@ internal static string CreateJsonObject(IEnumerable<KeyValuePair<string, string>
16711671
return builder.ToString();
16721672
}
16731673

1674+
/// <summary>
1675+
/// Serializes the given message as a JSON string.
1676+
/// </summary>
1677+
/// <param name="message">The message to serialize.</param>
1678+
/// <param name="messageDescriptions">The cached message descriptions to use for reflection.</param>
1679+
/// <returns>A JSON string.</returns>
1680+
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "This Dispose is safe.")]
1681+
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")]
1682+
internal static string SerializeAsJson(IMessage message, MessageDescriptionCollection messageDescriptions) {
1683+
Requires.NotNull(message, "message");
1684+
Requires.NotNull(messageDescriptions, "messageDescriptions");
1685+
1686+
var encoding = Encoding.UTF8;
1687+
var bytes = SerializeAsJsonBytes(message, messageDescriptions, encoding);
1688+
string json = encoding.GetString(bytes);
1689+
return json;
1690+
}
1691+
1692+
/// <summary>
1693+
/// Serializes the given message as a JSON string.
1694+
/// </summary>
1695+
/// <param name="message">The message to serialize.</param>
1696+
/// <param name="messageDescriptions">The cached message descriptions to use for reflection.</param>
1697+
/// <param name="encoding">The encoding to use. Defaults to <see cref="Encoding.UTF8"/></param>
1698+
/// <returns>A JSON string.</returns>
1699+
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "This Dispose is safe.")]
1700+
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")]
1701+
internal static byte[] SerializeAsJsonBytes(IMessage message, MessageDescriptionCollection messageDescriptions, Encoding encoding = null) {
1702+
Requires.NotNull(message, "message");
1703+
Requires.NotNull(messageDescriptions, "messageDescriptions");
1704+
1705+
encoding = encoding ?? Encoding.UTF8;
1706+
MessageDictionary messageDictionary = messageDescriptions.GetAccessor(message);
1707+
using (var memoryStream = new MemoryStream()) {
1708+
using (var jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(memoryStream, encoding)) {
1709+
MessageSerializer.Serialize(messageDictionary, jsonWriter);
1710+
jsonWriter.Flush();
1711+
}
1712+
1713+
return memoryStream.ToArray();
1714+
}
1715+
}
1716+
1717+
/// <summary>
1718+
/// Deserializes a JSON object into a message.
1719+
/// </summary>
1720+
/// <param name="jsonBytes">The buffer containing the JSON string.</param>
1721+
/// <param name="receivingMessage">The message to deserialize the object into.</param>
1722+
/// <param name="messageDescriptions">The cache of message descriptions.</param>
1723+
/// <param name="encoding">The encoding that the JSON bytes are in.</param>
1724+
internal static void DeserializeFromJson(byte[] jsonBytes, IMessage receivingMessage, MessageDescriptionCollection messageDescriptions, Encoding encoding = null) {
1725+
Requires.NotNull(jsonBytes, "jsonBytes");
1726+
Requires.NotNull(receivingMessage, "receivingMessage");
1727+
Requires.NotNull(messageDescriptions, "messageDescriptions");
1728+
1729+
encoding = encoding ?? Encoding.UTF8;
1730+
MessageDictionary messageDictionary = messageDescriptions.GetAccessor(receivingMessage);
1731+
using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(jsonBytes, 0, jsonBytes.Length, encoding, Channel.DefaultUntrustedXmlDictionaryReaderQuotas, null)) {
1732+
MessageSerializer.Deserialize(messageDictionary, jsonReader);
1733+
}
1734+
}
1735+
16741736
/// <summary>
16751737
/// Prepares what SHOULD be simply a string value for safe injection into Javascript
16761738
/// by using appropriate character escaping.

src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@
234234
<Compile Include="Mocks\TestBaseMessage.cs" />
235235
<Compile Include="Mocks\TestDerivedMessage.cs" />
236236
<Compile Include="Mocks\TestDirectResponseMessageWithHttpStatus.cs" />
237+
<Compile Include="Mocks\TestMessageWithDate.cs" />
237238
<Compile Include="Mocks\TestReplayProtectedMessage.cs" />
238239
<Compile Include="Mocks\TestDirectedMessage.cs" />
239240
<Compile Include="Mocks\TestBadChannel.cs" />

src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.Test.Messaging {
1111
using System.Diagnostics;
1212
using System.IO;
1313
using System.Net;
14+
using System.Text;
1415
using System.Text.RegularExpressions;
1516
using System.Web;
1617
using DotNetOpenAuth.Messaging;
@@ -229,6 +230,27 @@ public void EncryptDecrypt() {
229230
Assert.AreEqual(PlainText, roundTripped);
230231
}
231232

233+
[Test]
234+
public void SerializeAsJsonTest() {
235+
var message = new TestMessageWithDate() {
236+
Age = 18,
237+
Timestamp = DateTime.Parse("4/28/2012"),
238+
Name = "Andrew",
239+
};
240+
string json = MessagingUtilities.SerializeAsJson(message, this.MessageDescriptions);
241+
Assert.That(json, Is.EqualTo("{\"ts\":\"2012-04-28T00:00:00Z\",\"age\":18,\"Name\":\"Andrew\"}"));
242+
}
243+
244+
[Test]
245+
public void DeserializeFromJson() {
246+
var message = new TestMessageWithDate();
247+
string json = "{\"ts\":\"2012-04-28T00:00:00Z\",\"age\":18,\"Name\":\"Andrew\"}";
248+
MessagingUtilities.DeserializeFromJson(Encoding.UTF8.GetBytes(json), message, this.MessageDescriptions);
249+
Assert.That(message.Age, Is.EqualTo(18));
250+
Assert.That(message.Timestamp, Is.EqualTo(DateTime.Parse("4/28/2012")));
251+
Assert.That(message.Name, Is.EqualTo("Andrew"));
252+
}
253+
232254
/// <summary>
233255
/// Verifies that the time-independent string equality check works accurately.
234256
/// </summary>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="TestMessageWithDate.cs" company="Andrew Arnott">
3+
// Copyright (c) Andrew Arnott. All rights reserved.
4+
// </copyright>
5+
//-----------------------------------------------------------------------
6+
7+
namespace DotNetOpenAuth.Test.Mocks {
8+
using System;
9+
using System.Collections.Generic;
10+
using System.Linq;
11+
using System.Text;
12+
using DotNetOpenAuth.Messaging;
13+
14+
internal class TestMessageWithDate : TestBaseMessage {
15+
[MessagePart("ts", IsRequired = true)]
16+
internal DateTime Timestamp { get; set; }
17+
}
18+
}

0 commit comments

Comments
 (0)