Fix losing UTC DateTimeKind on ISO8601 UTC values (#2317) (#2357)

This commit is contained in:
Andrew Stanton-Nurse 2018-05-29 11:27:29 -07:00 committed by GitHub
parent a79c7da4d2
commit 3fa10f92ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 3 deletions

View File

@ -153,6 +153,29 @@ namespace Microsoft.AspNetCore.Internal
return true;
}
public static bool ReadForType(JsonTextReader reader, Type type)
{
// Explicity read values as dates from JSON with reader.
// We do this because otherwise dates are read as strings
// and the JsonSerializer will use a conversion method that won't
// preserve UTC in DateTime.Kind for UTC ISO8601 dates
if (type == typeof(DateTime) || type == typeof(DateTime?))
{
reader.ReadAsDateTime();
}
else if (type == typeof(DateTimeOffset) || type == typeof(DateTimeOffset?))
{
reader.ReadAsDateTimeOffset();
}
else
{
reader.Read();
}
// TokenType will be None if there is no more content
return reader.TokenType != JsonToken.None;
}
private class JsonArrayPool<T> : IArrayPool<T>
{
private readonly ArrayPool<T> _inner;

View File

@ -168,12 +168,12 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
error = JsonUtils.ReadAsString(reader, ErrorPropertyName);
break;
case ResultPropertyName:
JsonUtils.CheckRead(reader);
hasResult = true;
if (string.IsNullOrEmpty(invocationId))
{
JsonUtils.CheckRead(reader);
// If we don't have an invocation id then we need to store it as a JToken so we can parse it later
resultToken = JToken.Load(reader);
}
@ -181,6 +181,12 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
{
// If we have an invocation id already we can parse the end result
var returnType = binder.GetReturnType(invocationId);
if (!JsonUtils.ReadForType(reader, returnType))
{
throw new JsonReaderException("Unexpected end when reading JSON");
}
result = PayloadSerializer.Deserialize(reader, returnType);
}
break;
@ -599,13 +605,25 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
return new InvocationMessage(invocationId, target, arguments);
}
private bool ReadArgumentAsType(JsonTextReader reader, IReadOnlyList<Type> paramTypes, int paramIndex)
{
if (paramIndex < paramTypes.Count)
{
var paramType = paramTypes[paramIndex];
return JsonUtils.ReadForType(reader, paramType);
}
return reader.Read();
}
private object[] BindArguments(JsonTextReader reader, IReadOnlyList<Type> paramTypes)
{
object[] arguments = null;
var paramIndex = 0;
var argumentsCount = 0;
while (reader.Read())
while (ReadArgumentAsType(reader, paramTypes, paramIndex))
{
if (reader.TokenType == JsonToken.EndArray)
{

View File

@ -260,6 +260,37 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
Assert.Equal(expectedMessage, bindingFailure.BindingFailure.SourceException.Message);
}
[Theory]
[InlineData("{'type':1,'invocationId':'42','target':'foo','arguments':['2007-03-01T13:00:00Z']}")]
[InlineData("{'type':1,'invocationId':'42','arguments':['2007-03-01T13:00:00Z'],'target':'foo'}")]
public void DateTimeArgumentPreservesUtcKind(string input)
{
var binder = new TestBinder(new[] { typeof(DateTime) });
var protocol = new JsonHubProtocol();
var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(Frame(input)));
protocol.TryParseMessage(ref data, binder, out var message);
var invocationMessage = Assert.IsType<InvocationMessage>(message);
Assert.Single(invocationMessage.Arguments);
var dt = Assert.IsType<DateTime>(invocationMessage.Arguments[0]);
Assert.Equal(DateTimeKind.Utc, dt.Kind);
}
[Theory]
[InlineData("{'type':3,'invocationId':'42','target':'foo','arguments':[],'result':'2007-03-01T13:00:00Z'}")]
[InlineData("{'type':3,'target':'foo','arguments':[],'result':'2007-03-01T13:00:00Z','invocationId':'42'}")]
public void DateTimeReturnValuePreservesUtcKind(string input)
{
var binder = new TestBinder(typeof(DateTime));
var protocol = new JsonHubProtocol();
var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(Frame(input)));
protocol.TryParseMessage(ref data, binder, out var message);
var invocationMessage = Assert.IsType<CompletionMessage>(message);
var dt = Assert.IsType<DateTime>(invocationMessage.Result);
Assert.Equal(DateTimeKind.Utc, dt.Kind);
}
private static string Frame(string input)
{
var data = Encoding.UTF8.GetBytes(input);