Swtich to MessagePack-CSharp (#1879)

This commit is contained in:
BrennanConroy 2018-04-09 17:04:32 -07:00 committed by GitHub
parent ecd665c471
commit 6d050140e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 540 additions and 425 deletions

View File

@ -57,7 +57,7 @@
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26403-06</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MoqPackageVersion>4.7.49</MoqPackageVersion>
<MsgPackCliPackageVersion>1.0.0-rc</MsgPackCliPackageVersion>
<MessagePackPackageVersion>1.7.3.4</MessagePackPackageVersion>
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
<StackExchangeRedisStrongNamePackageVersion>1.2.4</StackExchangeRedisStrongNamePackageVersion>
<SystemBuffersPackageVersion>4.5.0-preview2-26403-05</SystemBuffersPackageVersion>

View File

@ -11,5 +11,6 @@ namespace FunctionalTests
public int[] IntArray { get; set; }
public byte[] ByteArray { get; set; }
public Guid GUID { get; set; }
public DateTime DateTime { get;set; }
}
}

View File

@ -70,5 +70,17 @@ namespace FunctionalTests
{
return complexObject;
}
public ComplexObject SendComplexObject()
{
return new ComplexObject
{
ByteArray = new byte[] { 0x1, 0x2, 0x3 },
DateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc),
GUID = new Guid("00010203-0405-0607-0706-050403020100"),
IntArray = new int[] { 1, 2, 3 },
String = "hello world",
};
}
}
}

View File

@ -379,9 +379,10 @@ describe("hubConnection", () => {
ByteArray: protocol.name === "json"
? "aGVsbG8="
: new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]),
GUID: protocol.name === "json"
? "00010203-0405-0607-0706-050403020100"
: new Uint8Array([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00]),
DateTime: protocol.name === "json"
? "2002-04-01T10:20:15Z"
: new Date(Date.UTC(2002, 3, 1, 10, 20, 15)), // Apr 1, 2002, 10:20:15am UTC
GUID: "00010203-0405-0607-0706-050403020100",
IntArray: [0x01, 0x02, 0x03, 0xff],
String: "Hello, World!",
};
@ -395,9 +396,50 @@ describe("hubConnection", () => {
// msgpack creates a Buffer for byte arrays and jasmine fails to compare a Buffer
// and a Uint8Array even though Buffer instances are also Uint8Array instances
value.ByteArray = new Uint8Array(value.ByteArray);
}
expect(value).toEqual(complexObject);
})
.then(() => {
hubConnection.stop();
})
.catch((e) => {
fail(e);
done();
});
});
// GUIDs are serialized as Buffer as well.
value.GUID = new Uint8Array(value.GUID);
it("can receive different types", (done) => {
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
...commonOptions,
protocol,
transport: transportType,
});
hubConnection.onclose((error) => {
expect(error).toBe(undefined);
done();
});
const complexObject = {
ByteArray: protocol.name === "json"
? "AQID"
: new Uint8Array([0x1, 0x2, 0x3]),
DateTime: protocol.name === "json"
? "2000-01-01T00:00:00Z"
: new Date(Date.UTC(2000, 0, 1)),
GUID: "00010203-0405-0607-0706-050403020100",
IntArray: [0x01, 0x02, 0x03],
String: "hello world",
};
hubConnection.start()
.then(() => {
return hubConnection.invoke("SendComplexObject");
})
.then((value) => {
if (protocol.name === "messagepack") {
// msgpack creates a Buffer for byte arrays and jasmine fails to compare a Buffer
// and a Uint8Array even though Buffer instances are also Uint8Array instances
value.ByteArray = new Uint8Array(value.ByteArray);
}
expect(value).toEqual(complexObject);
})

View File

@ -35,11 +35,12 @@
"dev": true
},
"bl": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
"integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": {
"readable-stream": "2.3.3"
"readable-stream": "2.3.6",
"safe-buffer": "5.1.1"
}
},
"buffer": {
@ -74,32 +75,32 @@
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"msgpack5": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.0.1.tgz",
"integrity": "sha512-Wx+c6YEE5UMrVrDQuFSIseprjgfeAvMYD35klSKm1xNCvtFI5KICfRmwKMc0Wmu7BknXUA3DUhFWugyCwTsRsg==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.0.2.tgz",
"integrity": "sha512-rEIx0/KFtWGtqlF5D/NIMzOHDhm7AhIFzHR3/PLqMrXXbMKoSitDE/IDuTactlTjxEc0ScmHx/5qoH015uL7xA==",
"requires": {
"bl": "1.2.1",
"bl": "1.2.2",
"inherits": "2.0.3",
"readable-stream": "2.3.3",
"readable-stream": "2.3.6",
"safe-buffer": "5.1.1"
}
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"process-nextick-args": "2.0.0",
"safe-buffer": "5.1.1",
"string_decoder": "1.0.3",
"string_decoder": "1.1.1",
"util-deprecate": "1.0.2"
}
},
@ -109,9 +110,9 @@
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "5.1.1"
}

View File

@ -43,7 +43,7 @@
"@aspnet/signalr": "^1.0.0-preview3"
},
"dependencies": {
"msgpack5": "^4.0.1"
"msgpack5": "^4.0.2"
},
"devDependencies": {
"@types/msgpack5": "^3.4.1",

View File

@ -5,7 +5,6 @@ using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using MsgPack.Serialization;
using SignalRSamples.ConnectionHandlers;
using SignalRSamples.Hubs;
@ -24,10 +23,7 @@ namespace SignalRSamples
// Faster pings for testing
options.KeepAliveInterval = TimeSpan.FromSeconds(5);
})
.AddMessagePackProtocol(options =>
{
options.SerializationContext.DictionarySerlaizationOptions.KeyTransformer = DictionaryKeyTransformers.LowerCamel;
});
.AddMessagePackProtocol();
//.AddRedis();
services.AddCors(o =>

View File

@ -8,12 +8,12 @@ using System.Diagnostics;
using System.IO;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using MessagePack;
using MessagePack.Formatters;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.Extensions.Options;
using MsgPack;
using MsgPack.Serialization;
namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
{
@ -23,11 +23,11 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
private const int VoidResult = 2;
private const int NonVoidResult = 3;
private IFormatterResolver _resolver;
public static readonly string ProtocolName = "messagepack";
public static readonly int ProtocolVersion = 1;
public SerializationContext SerializationContext { get; }
public string Name => ProtocolName;
public int Version => ProtocolVersion;
@ -40,7 +40,32 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
public MessagePackHubProtocol(IOptions<MessagePackHubProtocolOptions> options)
{
SerializationContext = options.Value.SerializationContext;
var msgPackOptions = options.Value;
SetupResolver(msgPackOptions);
}
private void SetupResolver(MessagePackHubProtocolOptions options)
{
// if counts don't match then we know users customized resolvers so we set up the options
// with the provided resolvers
if (options.FormatterResolvers.Count != SignalRResolver.Resolvers.Count)
{
_resolver = new CombinedResolvers(options.FormatterResolvers);
return;
}
for (var i = 0; i < options.FormatterResolvers.Count; i++)
{
// check if the user customized the resolvers
if (options.FormatterResolvers[i] != SignalRResolver.Resolvers[i])
{
_resolver = new CombinedResolvers(options.FormatterResolvers);
return;
}
}
// Use optimized cached resolver if the default is chosen
_resolver = SignalRResolver.Instance;
}
public bool IsVersionSupported(int version)
@ -58,9 +83,8 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
var arraySegment = GetArraySegment(payload);
message = ParseMessage(arraySegment.Array, arraySegment.Offset, binder);
return message != null;
message = ParseMessage(arraySegment.Array, arraySegment.Offset, binder, _resolver);
return true;
}
private static ArraySegment<byte> GetArraySegment(in ReadOnlySequence<byte> input)
@ -77,41 +101,39 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
return new ArraySegment<byte>(input.ToArray());
}
private static HubMessage ParseMessage(byte[] input, int startOffset, IInvocationBinder binder)
private static HubMessage ParseMessage(byte[] input, int startOffset, IInvocationBinder binder, IFormatterResolver resolver)
{
using (var unpacker = Unpacker.Create(input, startOffset))
_ = MessagePackBinary.ReadArrayHeader(input, startOffset, out var readSize);
startOffset += readSize;
var messageType = ReadInt32(input, ref startOffset, "messageType");
switch (messageType)
{
_ = ReadArrayLength(unpacker, "elementCount");
var messageType = ReadInt32(unpacker, "messageType");
switch (messageType)
{
case HubProtocolConstants.InvocationMessageType:
return CreateInvocationMessage(unpacker, binder);
case HubProtocolConstants.StreamInvocationMessageType:
return CreateStreamInvocationMessage(unpacker, binder);
case HubProtocolConstants.StreamItemMessageType:
return CreateStreamItemMessage(unpacker, binder);
case HubProtocolConstants.CompletionMessageType:
return CreateCompletionMessage(unpacker, binder);
case HubProtocolConstants.CancelInvocationMessageType:
return CreateCancelInvocationMessage(unpacker);
case HubProtocolConstants.PingMessageType:
return PingMessage.Instance;
case HubProtocolConstants.CloseMessageType:
return CreateCloseMessage(unpacker);
default:
// Future protocol changes can add message types, old clients can ignore them
return null;
}
case HubProtocolConstants.InvocationMessageType:
return CreateInvocationMessage(input, ref startOffset, binder, resolver);
case HubProtocolConstants.StreamInvocationMessageType:
return CreateStreamInvocationMessage(input, ref startOffset, binder, resolver);
case HubProtocolConstants.StreamItemMessageType:
return CreateStreamItemMessage(input, ref startOffset, binder, resolver);
case HubProtocolConstants.CompletionMessageType:
return CreateCompletionMessage(input, ref startOffset, binder, resolver);
case HubProtocolConstants.CancelInvocationMessageType:
return CreateCancelInvocationMessage(input, ref startOffset);
case HubProtocolConstants.PingMessageType:
return PingMessage.Instance;
case HubProtocolConstants.CloseMessageType:
return CreateCloseMessage(input, ref startOffset);
default:
// Future protocol changes can add message types, old clients can ignore them
return null;
}
}
private static InvocationMessage CreateInvocationMessage(Unpacker unpacker, IInvocationBinder binder)
private static InvocationMessage CreateInvocationMessage(byte[] input, ref int offset, IInvocationBinder binder, IFormatterResolver resolver)
{
var headers = ReadHeaders(unpacker);
var invocationId = ReadInvocationId(unpacker);
var headers = ReadHeaders(input, ref offset);
var invocationId = ReadInvocationId(input, ref offset);
// For MsgPack, we represent an empty invocation ID as an empty string,
// so we need to normalize that to "null", which is what indicates a non-blocking invocation.
@ -120,12 +142,12 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
invocationId = null;
}
var target = ReadString(unpacker, "target");
var target = ReadString(input, ref offset, "target");
var parameterTypes = binder.GetParameterTypes(target);
try
{
var arguments = BindArguments(unpacker, parameterTypes);
var arguments = BindArguments(input, ref offset, parameterTypes, resolver);
return ApplyHeaders(headers, new InvocationMessage(invocationId, target, argumentBindingException: null, arguments: arguments));
}
catch (Exception ex)
@ -134,16 +156,16 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
}
}
private static StreamInvocationMessage CreateStreamInvocationMessage(Unpacker unpacker, IInvocationBinder binder)
private static StreamInvocationMessage CreateStreamInvocationMessage(byte[] input, ref int offset, IInvocationBinder binder, IFormatterResolver resolver)
{
var headers = ReadHeaders(unpacker);
var invocationId = ReadInvocationId(unpacker);
var target = ReadString(unpacker, "target");
var headers = ReadHeaders(input, ref offset);
var invocationId = ReadInvocationId(input, ref offset);
var target = ReadString(input, ref offset, "target");
var parameterTypes = binder.GetParameterTypes(target);
try
{
var arguments = BindArguments(unpacker, parameterTypes);
var arguments = BindArguments(input, ref offset, parameterTypes, resolver);
return ApplyHeaders(headers, new StreamInvocationMessage(invocationId, target, argumentBindingException: null, arguments: arguments));
}
catch (Exception ex)
@ -152,20 +174,20 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
}
}
private static StreamItemMessage CreateStreamItemMessage(Unpacker unpacker, IInvocationBinder binder)
private static StreamItemMessage CreateStreamItemMessage(byte[] input, ref int offset, IInvocationBinder binder, IFormatterResolver resolver)
{
var headers = ReadHeaders(unpacker);
var invocationId = ReadInvocationId(unpacker);
var headers = ReadHeaders(input, ref offset);
var invocationId = ReadInvocationId(input, ref offset);
var itemType = binder.GetReturnType(invocationId);
var value = DeserializeObject(unpacker, itemType, "item");
var value = DeserializeObject(input, ref offset, itemType, "item", resolver);
return ApplyHeaders(headers, new StreamItemMessage(invocationId, value));
}
private static CompletionMessage CreateCompletionMessage(Unpacker unpacker, IInvocationBinder binder)
private static CompletionMessage CreateCompletionMessage(byte[] input, ref int offset, IInvocationBinder binder, IFormatterResolver resolver)
{
var headers = ReadHeaders(unpacker);
var invocationId = ReadInvocationId(unpacker);
var resultKind = ReadInt32(unpacker, "resultKind");
var headers = ReadHeaders(input, ref offset);
var invocationId = ReadInvocationId(input, ref offset);
var resultKind = ReadInt32(input, ref offset, "resultKind");
string error = null;
object result = null;
@ -174,11 +196,11 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
switch (resultKind)
{
case ErrorResult:
error = ReadString(unpacker, "error");
error = ReadString(input, ref offset, "error");
break;
case NonVoidResult:
var itemType = binder.GetReturnType(invocationId);
result = DeserializeObject(unpacker, itemType, "argument");
result = DeserializeObject(input, ref offset, itemType, "argument", resolver);
hasResult = true;
break;
case VoidResult:
@ -191,22 +213,22 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
return ApplyHeaders(headers, new CompletionMessage(invocationId, error, result, hasResult));
}
private static CancelInvocationMessage CreateCancelInvocationMessage(Unpacker unpacker)
private static CancelInvocationMessage CreateCancelInvocationMessage(byte[] input, ref int offset)
{
var headers = ReadHeaders(unpacker);
var invocationId = ReadInvocationId(unpacker);
var headers = ReadHeaders(input, ref offset);
var invocationId = ReadInvocationId(input, ref offset);
return ApplyHeaders(headers, new CancelInvocationMessage(invocationId));
}
private static CloseMessage CreateCloseMessage(Unpacker unpacker)
private static CloseMessage CreateCloseMessage(byte[] input, ref int offset)
{
var error = ReadString(unpacker, "error");
var error = ReadString(input, ref offset, "error");
return new CloseMessage(error);
}
private static Dictionary<string, string> ReadHeaders(Unpacker unpacker)
private static Dictionary<string, string> ReadHeaders(byte[] input, ref int offset)
{
var headerCount = ReadMapLength(unpacker, "headers");
var headerCount = ReadMapLength(input, ref offset, "headers");
if (headerCount > 0)
{
// If headerCount is larger than int.MaxValue, things are going to go horribly wrong anyway :)
@ -214,8 +236,8 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
for (var i = 0; i < headerCount; i++)
{
var key = ReadString(unpacker, $"headers[{i}].Key");
var value = ReadString(unpacker, $"headers[{i}].Value");
var key = ReadString(input, ref offset, $"headers[{i}].Key");
var value = ReadString(input, ref offset, $"headers[{i}].Value");
headers[key] = value;
}
return headers;
@ -226,9 +248,9 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
}
}
private static object[] BindArguments(Unpacker unpacker, IReadOnlyList<Type> parameterTypes)
private static object[] BindArguments(byte[] input, ref int offset, IReadOnlyList<Type> parameterTypes, IFormatterResolver resolver)
{
var argumentCount = ReadArrayLength(unpacker, "arguments");
var argumentCount = ReadArrayLength(input, ref offset, "arguments");
if (parameterTypes.Count != argumentCount)
{
@ -241,7 +263,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
var arguments = new object[argumentCount];
for (var i = 0; i < argumentCount; i++)
{
arguments[i] = DeserializeObject(unpacker, parameterTypes[i], "argument");
arguments[i] = DeserializeObject(input, ref offset, parameterTypes[i], "argument", resolver);
}
return arguments;
@ -281,11 +303,8 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
}
}
private void WriteMessageCore(HubMessage message, Stream output)
private void WriteMessageCore(HubMessage message, Stream packer)
{
// PackerCompatibilityOptions.None prevents from serializing byte[] as strings
// and allows extended objects
var packer = Packer.Create(output, PackerCompatibilityOptions.None);
switch (message)
{
case InvocationMessage invocationMessage:
@ -314,135 +333,147 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
}
}
private void WriteInvocationMessage(InvocationMessage message, Packer packer)
private void WriteInvocationMessage(InvocationMessage message, Stream packer)
{
packer.PackArrayHeader(5);
packer.Pack(HubProtocolConstants.InvocationMessageType);
MessagePackBinary.WriteArrayHeader(packer, 5);
MessagePackBinary.WriteInt32(packer, HubProtocolConstants.InvocationMessageType);
PackHeaders(packer, message.Headers);
if (string.IsNullOrEmpty(message.InvocationId))
{
packer.PackNull();
MessagePackBinary.WriteNil(packer);
}
else
{
packer.PackString(message.InvocationId);
MessagePackBinary.WriteString(packer, message.InvocationId);
}
packer.PackString(message.Target);
packer.PackArrayHeader(message.Arguments.Length);
MessagePackBinary.WriteString(packer, message.Target);
MessagePackBinary.WriteArrayHeader(packer, message.Arguments.Length);
foreach (var arg in message.Arguments)
{
packer.PackObject(arg, SerializationContext);
WriteArgument(arg, packer);
}
}
private void WriteStreamInvocationMessage(StreamInvocationMessage message, Packer packer)
private void WriteStreamInvocationMessage(StreamInvocationMessage message, Stream packer)
{
packer.PackArrayHeader(5);
packer.Pack(HubProtocolConstants.StreamInvocationMessageType);
MessagePackBinary.WriteArrayHeader(packer, 5);
MessagePackBinary.WriteInt16(packer, HubProtocolConstants.StreamInvocationMessageType);
PackHeaders(packer, message.Headers);
packer.PackString(message.InvocationId);
packer.PackString(message.Target);
packer.PackArrayHeader(message.Arguments.Length);
MessagePackBinary.WriteString(packer, message.InvocationId);
MessagePackBinary.WriteString(packer, message.Target);
MessagePackBinary.WriteArrayHeader(packer, message.Arguments.Length);
foreach (var arg in message.Arguments)
{
packer.PackObject(arg, SerializationContext);
WriteArgument(arg, packer);
}
}
private void WriteStreamingItemMessage(StreamItemMessage message, Packer packer)
private void WriteStreamingItemMessage(StreamItemMessage message, Stream packer)
{
packer.PackArrayHeader(4);
packer.Pack(HubProtocolConstants.StreamItemMessageType);
MessagePackBinary.WriteArrayHeader(packer, 4);
MessagePackBinary.WriteInt16(packer, HubProtocolConstants.StreamItemMessageType);
PackHeaders(packer, message.Headers);
packer.PackString(message.InvocationId);
packer.PackObject(message.Item, SerializationContext);
MessagePackBinary.WriteString(packer, message.InvocationId);
WriteArgument(message.Item, packer);
}
private void WriteCompletionMessage(CompletionMessage message, Packer packer)
private void WriteArgument(object argument, Stream stream)
{
if (argument == null)
{
MessagePackBinary.WriteNil(stream);
}
else
{
MessagePackSerializer.NonGeneric.Serialize(argument.GetType(), stream, argument, _resolver);
}
}
private void WriteCompletionMessage(CompletionMessage message, Stream packer)
{
var resultKind =
message.Error != null ? ErrorResult :
message.HasResult ? NonVoidResult :
VoidResult;
packer.PackArrayHeader(4 + (resultKind != VoidResult ? 1 : 0));
packer.Pack(HubProtocolConstants.CompletionMessageType);
MessagePackBinary.WriteArrayHeader(packer, 4 + (resultKind != VoidResult ? 1 : 0));
MessagePackBinary.WriteInt32(packer, HubProtocolConstants.CompletionMessageType);
PackHeaders(packer, message.Headers);
packer.PackString(message.InvocationId);
packer.Pack(resultKind);
MessagePackBinary.WriteString(packer, message.InvocationId);
MessagePackBinary.WriteInt32(packer, resultKind);
switch (resultKind)
{
case ErrorResult:
packer.PackString(message.Error);
MessagePackBinary.WriteString(packer, message.Error);
break;
case NonVoidResult:
packer.PackObject(message.Result, SerializationContext);
WriteArgument(message.Result, packer);
break;
}
}
private void WriteCancelInvocationMessage(CancelInvocationMessage message, Packer packer)
private void WriteCancelInvocationMessage(CancelInvocationMessage message, Stream packer)
{
packer.PackArrayHeader(3);
packer.Pack(HubProtocolConstants.CancelInvocationMessageType);
MessagePackBinary.WriteArrayHeader(packer, 3);
MessagePackBinary.WriteInt16(packer, HubProtocolConstants.CancelInvocationMessageType);
PackHeaders(packer, message.Headers);
packer.PackString(message.InvocationId);
MessagePackBinary.WriteString(packer, message.InvocationId);
}
private void WriteCloseMessage(CloseMessage message, Packer packer)
private void WriteCloseMessage(CloseMessage message, Stream packer)
{
packer.PackArrayHeader(2);
packer.Pack(HubProtocolConstants.CloseMessageType);
MessagePackBinary.WriteArrayHeader(packer, 2);
MessagePackBinary.WriteInt16(packer, HubProtocolConstants.CloseMessageType);
if (string.IsNullOrEmpty(message.Error))
{
packer.PackNull();
MessagePackBinary.WriteNil(packer);
}
else
{
packer.PackString(message.Error);
MessagePackBinary.WriteString(packer, message.Error);
}
}
private void WritePingMessage(PingMessage pingMessage, Packer packer)
private void WritePingMessage(PingMessage pingMessage, Stream packer)
{
packer.PackArrayHeader(1);
packer.Pack(HubProtocolConstants.PingMessageType);
MessagePackBinary.WriteArrayHeader(packer, 1);
MessagePackBinary.WriteInt32(packer, HubProtocolConstants.PingMessageType);
}
private void PackHeaders(Packer packer, IDictionary<string, string> headers)
private void PackHeaders(Stream packer, IDictionary<string, string> headers)
{
if (headers != null)
{
packer.PackMapHeader(headers.Count);
MessagePackBinary.WriteMapHeader(packer, headers.Count);
if (headers.Count > 0)
{
foreach (var header in headers)
{
packer.PackString(header.Key);
packer.PackString(header.Value);
MessagePackBinary.WriteString(packer, header.Key);
MessagePackBinary.WriteString(packer, header.Value);
}
}
}
else
{
packer.PackMapHeader(0);
MessagePackBinary.WriteMapHeader(packer, 0);
}
}
private static string ReadInvocationId(Unpacker unpacker)
private static string ReadInvocationId(byte[] input, ref int offset)
{
return ReadString(unpacker, "invocationId");
return ReadString(input, ref offset, "invocationId");
}
private static int ReadInt32(Unpacker unpacker, string field)
private static int ReadInt32(byte[] input, ref int offset, string field)
{
Exception msgPackException = null;
try
{
if (unpacker.ReadInt32(out var value))
{
return value;
}
var readInt = MessagePackBinary.ReadInt32(input, offset, out var readSize);
offset += readSize;
return readInt;
}
catch (Exception e)
{
@ -452,22 +483,14 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
throw new InvalidDataException($"Reading '{field}' as Int32 failed.", msgPackException);
}
private static string ReadString(Unpacker unpacker, string field)
private static string ReadString(byte[] input, ref int offset, string field)
{
Exception msgPackException = null;
try
{
if (unpacker.Read())
{
if (unpacker.LastReadData.IsNil)
{
return null;
}
else
{
return unpacker.LastReadData.AsString();
}
}
var readString = MessagePackBinary.ReadString(input, offset, out var readSize);
offset += readSize;
return readString;
}
catch (Exception e)
{
@ -477,15 +500,14 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
throw new InvalidDataException($"Reading '{field}' as String failed.", msgPackException);
}
private static bool ReadBoolean(Unpacker unpacker, string field)
private static bool ReadBoolean(byte[] input, ref int offset, string field)
{
Exception msgPackException = null;
try
{
if (unpacker.ReadBoolean(out var value))
{
return value;
}
var readBool = MessagePackBinary.ReadBoolean(input, offset, out var readSize);
offset += readSize;
return readBool;
}
catch (Exception e)
{
@ -495,15 +517,14 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
throw new InvalidDataException($"Reading '{field}' as Boolean failed.", msgPackException);
}
private static long ReadMapLength(Unpacker unpacker, string field)
private static long ReadMapLength(byte[] input, ref int offset, string field)
{
Exception msgPackException = null;
try
{
if (unpacker.ReadMapLength(out var value))
{
return value;
}
var readMap = MessagePackBinary.ReadMapHeader(input, offset, out var readSize);
offset += readSize;
return readMap;
}
catch (Exception e)
{
@ -513,15 +534,14 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
throw new InvalidDataException($"Reading map length for '{field}' failed.", msgPackException);
}
private static long ReadArrayLength(Unpacker unpacker, string field)
private static long ReadArrayLength(byte[] input, ref int offset, string field)
{
Exception msgPackException = null;
try
{
if (unpacker.ReadArrayLength(out var value))
{
return value;
}
var readArray = MessagePackBinary.ReadArrayHeader(input, offset, out var readSize);
offset += readSize;
return readArray;
}
catch (Exception e)
{
@ -531,16 +551,14 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
throw new InvalidDataException($"Reading array length for '{field}' failed.", msgPackException);
}
private static object DeserializeObject(Unpacker unpacker, Type type, string field)
private static object DeserializeObject(byte[] input, ref int offset, Type type, string field, IFormatterResolver resolver)
{
Exception msgPackException = null;
try
{
if (unpacker.Read())
{
var serializer = MessagePackSerializer.Get(type);
return serializer.UnpackFrom(unpacker);
}
var obj = MessagePackSerializer.NonGeneric.Deserialize(type, new ArraySegment<byte>(input, offset, input.Length - offset), resolver);
offset += MessagePackBinary.ReadNextBlock(input, offset);
return obj;
}
catch (Exception ex)
{
@ -550,14 +568,68 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
throw new InvalidDataException($"Deserializing object of the `{type.Name}` type for '{field}' failed.", msgPackException);
}
internal static SerializationContext CreateDefaultSerializationContext()
internal static List<IFormatterResolver> CreateDefaultFormatterResolvers()
{
// serializes objects (here: arguments and results) as maps so that property names are preserved
var serializationContext = new SerializationContext { SerializationMethod = SerializationMethod.Map };
// Copy to allow users to add/remove resolvers without changing the static SignalRResolver list
return new List<IFormatterResolver>(SignalRResolver.Resolvers);
}
// allows for serializing objects that cannot be deserialized due to the lack of the default ctor etc.
serializationContext.CompatibilityOptions.AllowAsymmetricSerializer = true;
return serializationContext;
internal class SignalRResolver : IFormatterResolver
{
public static readonly IFormatterResolver Instance = new SignalRResolver();
public static readonly IList<IFormatterResolver> Resolvers = new[]
{
MessagePack.Resolvers.DynamicEnumAsStringResolver.Instance,
MessagePack.Resolvers.ContractlessStandardResolver.Instance,
};
public IMessagePackFormatter<T> GetFormatter<T>()
{
return Cache<T>.Formatter;
}
private static class Cache<T>
{
public static readonly IMessagePackFormatter<T> Formatter;
static Cache()
{
foreach (var resolver in Resolvers)
{
Formatter = resolver.GetFormatter<T>();
if (Formatter != null)
{
return;
}
}
}
}
}
// Support for users making their own Formatter lists
internal class CombinedResolvers : IFormatterResolver
{
private readonly IList<IFormatterResolver> _resolvers;
public CombinedResolvers(IList<IFormatterResolver> resolvers)
{
_resolvers = resolvers;
}
public IMessagePackFormatter<T> GetFormatter<T>()
{
foreach (var resolver in _resolvers)
{
var formatter = resolver.GetFormatter<T>();
if (formatter != null)
{
return formatter;
}
}
return null;
}
}
}
}

View File

@ -1,13 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using MessagePack;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using MsgPack.Serialization;
namespace Microsoft.AspNetCore.SignalR
{
public class MessagePackHubProtocolOptions
{
public SerializationContext SerializationContext { get; set; } = MessagePackHubProtocol.CreateDefaultSerializationContext();
public IList<IFormatterResolver> FormatterResolvers { get; set; } = MessagePackHubProtocol.CreateDefaultFormatterResolvers();
}
}

View File

@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="MsgPack.Cli" Version="$(MsgPackCliPackageVersion)" />
<PackageReference Include="MessagePack" Version="$(MessagePackPackageVersion)" />
</ItemGroup>
<ItemGroup>

View File

@ -7,7 +7,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.DependencyInjection;
using MsgPack.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Xunit;
@ -78,27 +77,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
}
[Fact]
public void AddMessagePackProtocolSetsHubProtocolToMsgPackWithDefaultOptions()
public void AddMessagePackProtocolSetsHubProtocolToMsgPack()
{
var serviceProvider = new HubConnectionBuilder().AddMessagePackProtocol().Services.BuildServiceProvider();
var actualProtocol = Assert.IsType<MessagePackHubProtocol>(serviceProvider.GetService<IHubProtocol>());
Assert.Equal(SerializationMethod.Map, actualProtocol.SerializationContext.SerializationMethod);
}
[Fact]
public void AddMessagePackProtocolSetsHubProtocolToMsgPackWithProvidedOptions()
{
var serviceProvider = new HubConnectionBuilder().AddMessagePackProtocol(options =>
{
options.SerializationContext = new SerializationContext
{
SerializationMethod = SerializationMethod.Array
};
}).Services.BuildServiceProvider();
var actualProtocol = Assert.IsType<MessagePackHubProtocol>(serviceProvider.GetService<IHubProtocol>());
Assert.Equal(SerializationMethod.Array, actualProtocol.SerializationContext.SerializationMethod);
Assert.IsType<MessagePackHubProtocol>(serviceProvider.GetService<IHubProtocol>());
}
}
}

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public int IntProp { get; set; } = 42;
public DateTime DateTimeProp { get; set; } = new DateTime(2017, 4, 11);
public DateTime DateTimeProp { get; set; } = new DateTime(2017, 4, 11, 0, 0, 0, DateTimeKind.Utc);
public object NullProp { get; set; } = null;

View File

@ -37,10 +37,10 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", null, 1, "Foo", 2.0f), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
new JsonProtocolTestData("InvocationMessage_HasBoolArgument", new InvocationMessage(null, "Target", null, true), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[true]}"),
new JsonProtocolTestData("InvocationMessage_HasNullArgument", new InvocationMessage(null, "Target", null, new object[] { null }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[null]}"),
new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNoCamelCase", new InvocationMessage(null, "Target", null, new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"ByteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnore", new InvocationMessage(null, "Target", null, new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new InvocationMessage(null, "Target", null, new CustomObject()), false, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueInclude", new InvocationMessage(null, "Target", null, new CustomObject()), true, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNoCamelCase", new InvocationMessage(null, "Target", null, new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnore", new InvocationMessage(null, "Target", null, new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new InvocationMessage(null, "Target", null, new CustomObject()), false, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueInclude", new InvocationMessage(null, "Target", null, new CustomObject()), true, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("InvocationMessage_HasHeaders", AddHeaders(TestHeaders, new InvocationMessage("123", "Target", null, 1, "Foo", 2.0f)), true, NullValueHandling.Ignore, "{\"type\":1," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", null, "2016-05-10T13:51:20+12:34"), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"),
new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgument", new InvocationMessage("Method", null, DateTimeOffset.Parse("2016-05-10T13:51:20+12:34")), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"),
@ -50,22 +50,22 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
new JsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":2.0}"),
new JsonProtocolTestData("StreamItemMessage_HasBoolItem", new StreamItemMessage("123", true), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":true}"),
new JsonProtocolTestData("StreamItemMessage_HasNullItem", new StreamItemMessage("123", null), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":null}"),
new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"ByteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnore", new StreamItemMessage("123", new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnoreAndNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, NullValueHandling.Include, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueInclude", new StreamItemMessage("123", new CustomObject()), true, NullValueHandling.Include, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), true, NullValueHandling.Include, "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnore", new StreamItemMessage("123", new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnoreAndNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, NullValueHandling.Include, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueInclude", new StreamItemMessage("123", new CustomObject()), true, NullValueHandling.Include, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), true, NullValueHandling.Include, "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasIntergerResult", CompletionMessage.WithResult("123", 1), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":1}"),
new JsonProtocolTestData("CompletionMessage_HasStringResult", CompletionMessage.WithResult("123", "Foo"), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":\"Foo\"}"),
new JsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":2.0}"),
new JsonProtocolTestData("CompletionMessage_HasBoolResult", CompletionMessage.WithResult("123", true), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":true}"),
new JsonProtocolTestData("CompletionMessage_HasNullResult", CompletionMessage.WithResult("123", null), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":null}"),
new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"ByteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIgnore", CompletionMessage.WithResult("123", new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIncludeAndNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, NullValueHandling.Include, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueInclude", CompletionMessage.WithResult("123", new CustomObject()), true, NullValueHandling.Include, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasTestHeadersAndCustomItemResult", AddHeaders(TestHeaders, CompletionMessage.WithResult("123", new CustomObject())), true, NullValueHandling.Include, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIgnore", CompletionMessage.WithResult("123", new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIncludeAndNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, NullValueHandling.Include, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueInclude", CompletionMessage.WithResult("123", new CustomObject()), true, NullValueHandling.Include, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasTestHeadersAndCustomItemResult", AddHeaders(TestHeaders, CompletionMessage.WithResult("123", new CustomObject())), true, NullValueHandling.Include, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new JsonProtocolTestData("CompletionMessage_HasError", CompletionMessage.WithError("123", "Whoops!"), false, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"error\":\"Whoops!\"}"),
new JsonProtocolTestData("CompletionMessage_HasErrorAndHeaders", AddHeaders(TestHeaders, CompletionMessage.WithError("123", "Whoops!")), false, NullValueHandling.Ignore, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"error\":\"Whoops!\"}"),
new JsonProtocolTestData("CompletionMessage_HasErrorAndCamelCase", CompletionMessage.Empty("123"), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\"}"),
@ -75,11 +75,11 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", null, 1, "Foo", 2.0f), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasBoolArgument", new StreamInvocationMessage("123", "Target", null, true), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[true]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasNullArgument", new StreamInvocationMessage("123", "Target", null, new object[] { null }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[null]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNoCamelCase", new StreamInvocationMessage("123", "Target", null, new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"ByteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnore", new StreamInvocationMessage("123", "Target", null, new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new StreamInvocationMessage("123", "Target", null, new CustomObject()), false, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueInclude", new StreamInvocationMessage("123", "Target", null, new CustomObject()), true, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", null, new CustomObject())), true, NullValueHandling.Include, "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNoCamelCase", new StreamInvocationMessage("123", "Target", null, new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnore", new StreamInvocationMessage("123", "Target", null, new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new StreamInvocationMessage("123", "Target", null, new CustomObject()), false, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueInclude", new StreamInvocationMessage("123", "Target", null, new CustomObject()), true, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", null, new CustomObject())), true, NullValueHandling.Include, "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new JsonProtocolTestData("CancelInvocationMessage_HasInvocationId", new CancelInvocationMessage("123"), true, NullValueHandling.Ignore, "{\"type\":5,\"invocationId\":\"123\"}"),
new JsonProtocolTestData("CancelInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new CancelInvocationMessage("123")), true, NullValueHandling.Ignore, "{\"type\":5," + SerializedHeaders + ",\"invocationId\":\"123\"}"),

View File

@ -1,15 +0,0 @@
using System;
using System.Linq;
using MsgPack;
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{
public static class MessagePackHelpers
{
public static MessagePackObject Array(params MessagePackObject[] items) =>
new MessagePackObject(items);
public static MessagePackObject Map(params (MessagePackObject Key, MessagePackObject Value)[] items) =>
new MessagePackObject(new MessagePackObjectDictionary(items.ToDictionary(i => i.Key, i => i.Value)));
}
}

View File

@ -4,19 +4,17 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using MsgPack;
using Xunit;
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{
using static HubMessageHelpers;
using static MessagePackHelpers;
public class MessagePackHubProtocolTests
{
@ -27,36 +25,22 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{ "ValueWithNewLines", "Also\nWorks\r\nFine" },
};
private static readonly MessagePackObject TestHeadersSerialized = Map(
("Foo", "Bar"),
("KeyWith\nNew\r\nLines", "Still Works"),
("ValueWithNewLines", "Also\nWorks\r\nFine"));
private static readonly MessagePackHubProtocol _hubProtocol
= new MessagePackHubProtocol();
private static readonly MessagePackObject CustomObjectSerialized = Map(
("ByteArrProp", new MessagePackObject(new byte[] { 1, 2, 3 }, isBinary: true)),
("DateTimeProp", new MessagePackObject(Timestamp.FromDateTime(new DateTime(2017, 4, 11)))),
("DoubleProp", 6.2831853071),
("IntProp", 42),
("NullProp", MessagePackObject.Nil),
("StringProp", "SignalR!"));
public enum TestEnum
{
Zero = 0,
One
}
// Test Data for Parse/WriteMessages:
// * Name: A string name that is used when reporting the test (it's the ToString value for ProtocolTestData)
// * Message: The HubMessage that is either expected (in Parse) or used as input (in Write)
// * Encoded: Raw MessagePackObject values (using the MessagePackHelpers static "Arr" and "Map" helpers) describing the message
// * Binary: Base64-encoded binary "baseline" to sanity-check MsgPack-Cli behavior
// * Binary: Base64-encoded binary "baseline" to sanity-check MessagePack-CSharp behavior
//
// The Encoded value is used as input to "Parse" and as the expected output that is verified in "Write". So if our encoding changes,
// those values will change and the Assert will give you a useful error telling you how the MsgPack structure itself changed (rather than just
// a bunch of random bytes). However, we want to be sure MsgPack-Cli doesn't change behavior, so we also verify that the binary encoding
// matches our expectation by comparing against a base64-string.
//
// If you change MsgPack encoding, you should update the 'encoded' values for these items, and then re-run the test. You'll get a failure which will
// provide a new Base64 binary string to replace in the 'binary' value. Use a tool like https://sugendran.github.io/msgpack-visualizer/ to verify
// that the MsgPack is correct and then just replace the Base64 value.
// When changing the tests/message pack parsing if you get test failures look at the base64 encoding and
// use a tool like https://sugendran.github.io/msgpack-visualizer/ to verify that the MsgPack is correct and then just replace the Base64 value.
public static IEnumerable<object[]> TestDataNames
{
@ -75,202 +59,180 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
new ProtocolTestData(
name: "InvocationWithNoHeadersAndNoArgs",
message: new InvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null),
encoded: Array(HubProtocolConstants.InvocationMessageType, Map(), "xyz", "method", Array()),
binary: "lQGAo3h5eqZtZXRob2SQ"),
new ProtocolTestData(
name: "InvocationWithNoHeadersNoIdAndNoArgs",
message: new InvocationMessage(target: "method", argumentBindingException: null),
encoded: Array(HubProtocolConstants.InvocationMessageType, Map(), MessagePackObject.Nil, "method", Array()),
binary: "lQGAwKZtZXRob2SQ"),
new ProtocolTestData(
name: "InvocationWithNoHeadersNoIdAndSingleNullArg",
message: new InvocationMessage(target: "method", argumentBindingException: null, new object[] { null }),
encoded: Array(HubProtocolConstants.InvocationMessageType, Map(), MessagePackObject.Nil, "method", Array(MessagePackObject.Nil)),
binary: "lQGAwKZtZXRob2SRwA=="),
new ProtocolTestData(
name: "InvocationWithNoHeadersNoIdAndSingleIntArg",
message: new InvocationMessage(target: "method", argumentBindingException: null, 42),
encoded: Array(HubProtocolConstants.InvocationMessageType, Map(), MessagePackObject.Nil, "method", Array(42)),
binary: "lQGAwKZtZXRob2SRKg=="),
new ProtocolTestData(
name: "InvocationWithNoHeadersNoIdIntAndStringArgs",
message: new InvocationMessage(target: "method", argumentBindingException: null, 42, "string"),
encoded: Array(HubProtocolConstants.InvocationMessageType, Map(), MessagePackObject.Nil, "method", Array(42, "string")),
binary: "lQGAwKZtZXRob2SSKqZzdHJpbmc="),
new ProtocolTestData(
name: "InvocationWithNoHeadersNoIdIntAndEnumArgs",
message: new InvocationMessage(target: "method", argumentBindingException: null, 42, TestEnum.One),
binary: "lQGAwKZtZXRob2SSKqNPbmU="),
new ProtocolTestData(
name: "InvocationWithNoHeadersNoIdAndCustomObjectArg",
message: new InvocationMessage(target: "method", argumentBindingException: null, 42, "string", new CustomObject()),
encoded: Array(HubProtocolConstants.InvocationMessageType, Map(), MessagePackObject.Nil, "method", Array(42, "string", CustomObjectSerialized)),
binary: "lQGAwKZtZXRob2STKqZzdHJpbmeGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiE="),
binary: "lQGAwKZtZXRob2STKqZzdHJpbmeGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="),
new ProtocolTestData(
name: "InvocationWithNoHeadersNoIdAndArrayOfCustomObjectArgs",
message: new InvocationMessage(target: "method", argumentBindingException: null, new[] { new CustomObject(), new CustomObject() }),
encoded: Array(HubProtocolConstants.InvocationMessageType, Map(), MessagePackObject.Nil, "method", Array(CustomObjectSerialized, CustomObjectSerialized)),
binary: "lQGAwKZtZXRob2SShqtCeXRlQXJyUHJvcMQDAQIDrERhdGVUaW1lUHJvcNb/WOwcgKpEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqqE51bGxQcm9wwKpTdHJpbmdQcm9wqFNpZ25hbFIhhqtCeXRlQXJyUHJvcMQDAQIDrERhdGVUaW1lUHJvcNb/WOwcgKpEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqqE51bGxQcm9wwKpTdHJpbmdQcm9wqFNpZ25hbFIh"),
binary: "lQGAwKZtZXRob2SShqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQIDhqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQID"),
new ProtocolTestData(
name: "InvocationWithHeadersNoIdAndArrayOfCustomObjectArgs",
message: AddHeaders(TestHeaders, new InvocationMessage(target: "method", argumentBindingException: null, new[] { new CustomObject(), new CustomObject() })),
encoded: Array(HubProtocolConstants.InvocationMessageType, TestHeadersSerialized, MessagePackObject.Nil, "method", Array(CustomObjectSerialized, CustomObjectSerialized)),
binary: "lQGDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmXApm1ldGhvZJKGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiGGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiE="),
binary: "lQGDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmXApm1ldGhvZJKGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgOGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="),
// StreamItem Messages
new ProtocolTestData(
name: "StreamItemWithNoHeadersAndNullItem",
message: new StreamItemMessage(invocationId: "xyz", item: null),
encoded: Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz", MessagePackObject.Nil),
binary: "lAKAo3h5esA="),
new ProtocolTestData(
name: "StreamItemWithNoHeadersAndIntItem",
message: new StreamItemMessage(invocationId: "xyz", item: 42),
encoded: Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz", 42),
binary: "lAKAo3h5eio="),
new ProtocolTestData(
name: "StreamItemWithNoHeadersAndFloatItem",
message: new StreamItemMessage(invocationId: "xyz", item: 42.0f),
encoded: Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz", 42.0f),
binary: "lAKAo3h5espCKAAA"),
new ProtocolTestData(
name: "StreamItemWithNoHeadersAndStringItem",
message: new StreamItemMessage(invocationId: "xyz", item: "string"),
encoded: Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz", "string"),
binary: "lAKAo3h5eqZzdHJpbmc="),
new ProtocolTestData(
name: "StreamItemWithNoHeadersAndBoolItem",
message: new StreamItemMessage(invocationId: "xyz", item: true),
encoded: Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz", true),
binary: "lAKAo3h5esM="),
new ProtocolTestData(
name: "StreamItemWithNoHeadersAndEnumItem",
message: new StreamItemMessage(invocationId: "xyz", item: TestEnum.One),
binary: "lAKAo3h5eqNPbmU="),
new ProtocolTestData(
name: "StreamItemWithNoHeadersAndCustomObjectItem",
message: new StreamItemMessage(invocationId: "xyz", item: new CustomObject()),
encoded: Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz", CustomObjectSerialized),
binary: "lAKAo3h5eoarQnl0ZUFyclByb3DEAwECA6xEYXRlVGltZVByb3DW/1jsHICqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqhOdWxsUHJvcMCqU3RyaW5nUHJvcKhTaWduYWxSIQ=="),
binary: "lAKAo3h5eoaqU3RyaW5nUHJvcKhTaWduYWxSIapEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqrERhdGVUaW1lUHJvcNb/WOwcgKhOdWxsUHJvcMCrQnl0ZUFyclByb3DEAwECAw=="),
new ProtocolTestData(
name: "StreamItemWithNoHeadersAndCustomObjectArrayItem",
message: new StreamItemMessage(invocationId: "xyz", item: new[] { new CustomObject(), new CustomObject() }),
encoded: Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz", Array(CustomObjectSerialized, CustomObjectSerialized)),
binary: "lAKAo3h5epKGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiGGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiE="),
binary: "lAKAo3h5epKGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgOGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="),
new ProtocolTestData(
name: "StreamItemWithHeadersAndCustomObjectArrayItem",
message: AddHeaders(TestHeaders, new StreamItemMessage(invocationId: "xyz", item: new[] { new CustomObject(), new CustomObject() })),
encoded: Array(HubProtocolConstants.StreamItemMessageType, TestHeadersSerialized, "xyz", Array(CustomObjectSerialized, CustomObjectSerialized)),
binary: "lAKDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6koarQnl0ZUFyclByb3DEAwECA6xEYXRlVGltZVByb3DW/1jsHICqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqhOdWxsUHJvcMCqU3RyaW5nUHJvcKhTaWduYWxSIYarQnl0ZUFyclByb3DEAwECA6xEYXRlVGltZVByb3DW/1jsHICqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqhOdWxsUHJvcMCqU3RyaW5nUHJvcKhTaWduYWxSIQ=="),
binary: "lAKDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6koaqU3RyaW5nUHJvcKhTaWduYWxSIapEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqrERhdGVUaW1lUHJvcNb/WOwcgKhOdWxsUHJvcMCrQnl0ZUFyclByb3DEAwECA4aqU3RyaW5nUHJvcKhTaWduYWxSIapEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqrERhdGVUaW1lUHJvcNb/WOwcgKhOdWxsUHJvcMCrQnl0ZUFyclByb3DEAwECAw=="),
// Completion Messages
new ProtocolTestData(
name: "CompletionWithNoHeadersAndError",
message: CompletionMessage.WithError(invocationId: "xyz", error: "Error not found!"),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 1, "Error not found!"),
binary: "lQOAo3h5egGwRXJyb3Igbm90IGZvdW5kIQ=="),
new ProtocolTestData(
name: "CompletionWithHeadersAndError",
message: AddHeaders(TestHeaders, CompletionMessage.WithError(invocationId: "xyz", error: "Error not found!")),
encoded: Array(HubProtocolConstants.CompletionMessageType, TestHeadersSerialized, "xyz", 1, "Error not found!"),
binary: "lQODo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6AbBFcnJvciBub3QgZm91bmQh"),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndNoResult",
message: CompletionMessage.Empty(invocationId: "xyz"),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 2),
binary: "lAOAo3h5egI="),
new ProtocolTestData(
name: "CompletionWithHeadersAndNoResult",
message: AddHeaders(TestHeaders, CompletionMessage.Empty(invocationId: "xyz")),
encoded: Array(HubProtocolConstants.CompletionMessageType, TestHeadersSerialized, "xyz", 2),
binary: "lAODo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6Ag=="),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndNullResult",
message: CompletionMessage.WithResult(invocationId: "xyz", payload: null),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3, MessagePackObject.Nil),
binary: "lQOAo3h5egPA"),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndIntResult",
message: CompletionMessage.WithResult(invocationId: "xyz", payload: 42),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3, 42),
binary: "lQOAo3h5egMq"),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndEnumResult",
message: CompletionMessage.WithResult(invocationId: "xyz", payload: TestEnum.One),
binary: "lQOAo3h5egOjT25l"),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndFloatResult",
message: CompletionMessage.WithResult(invocationId: "xyz", payload: 42.0f),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3, 42.0f),
binary: "lQOAo3h5egPKQigAAA=="),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndStringResult",
message: CompletionMessage.WithResult(invocationId: "xyz", payload: "string"),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3, "string"),
binary: "lQOAo3h5egOmc3RyaW5n"),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndBooleanResult",
message: CompletionMessage.WithResult(invocationId: "xyz", payload: true),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3, true),
binary: "lQOAo3h5egPD"),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndCustomObjectResult",
message: CompletionMessage.WithResult(invocationId: "xyz", payload: new CustomObject()),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3, CustomObjectSerialized),
binary: "lQOAo3h5egOGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiE="),
binary: "lQOAo3h5egOGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="),
new ProtocolTestData(
name: "CompletionWithNoHeadersAndCustomObjectArrayResult",
message: CompletionMessage.WithResult(invocationId: "xyz", payload: new[] { new CustomObject(), new CustomObject() }),
encoded: Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3, Array(CustomObjectSerialized, CustomObjectSerialized)),
binary: "lQOAo3h5egOShqtCeXRlQXJyUHJvcMQDAQIDrERhdGVUaW1lUHJvcNb/WOwcgKpEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqqE51bGxQcm9wwKpTdHJpbmdQcm9wqFNpZ25hbFIhhqtCeXRlQXJyUHJvcMQDAQIDrERhdGVUaW1lUHJvcNb/WOwcgKpEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqqE51bGxQcm9wwKpTdHJpbmdQcm9wqFNpZ25hbFIh"),
binary: "lQOAo3h5egOShqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQIDhqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQID"),
new ProtocolTestData(
name: "CompletionWithHeadersAndCustomObjectArrayResult",
message: AddHeaders(TestHeaders, CompletionMessage.WithResult(invocationId: "xyz", payload: new[] { new CustomObject(), new CustomObject() })),
encoded: Array(HubProtocolConstants.CompletionMessageType, TestHeadersSerialized, "xyz", 3, Array(CustomObjectSerialized, CustomObjectSerialized)),
binary: "lQODo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6A5KGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiGGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiE="),
binary: "lQODo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6A5KGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgOGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="),
// StreamInvocation Messages
new ProtocolTestData(
name: "StreamInvocationWithNoHeadersAndNoArgs",
message: new StreamInvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null),
encoded: Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "xyz", "method", Array()),
binary: "lQSAo3h5eqZtZXRob2SQ"),
new ProtocolTestData(
name: "StreamInvocationWithNoHeadersAndNullArg",
message: new StreamInvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null, new object[] { null }),
encoded: Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "xyz", "method", Array(MessagePackObject.Nil)),
binary: "lQSAo3h5eqZtZXRob2SRwA=="),
new ProtocolTestData(
name: "StreamInvocationWithNoHeadersAndIntArg",
message: new StreamInvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null, 42),
encoded: Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "xyz", "method", Array(42)),
binary: "lQSAo3h5eqZtZXRob2SRKg=="),
new ProtocolTestData(
name: "StreamInvocationWithNoHeadersAndEnumArg",
message: new StreamInvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null, TestEnum.One),
binary: "lQSAo3h5eqZtZXRob2SRo09uZQ=="),
new ProtocolTestData(
name: "StreamInvocationWithNoHeadersAndIntAndStringArgs",
message: new StreamInvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null, 42, "string"),
encoded: Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "xyz", "method", Array(42, "string")),
binary: "lQSAo3h5eqZtZXRob2SSKqZzdHJpbmc="),
new ProtocolTestData(
name: "StreamInvocationWithNoHeadersAndIntStringAndCustomObjectArgs",
message: new StreamInvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null, 42, "string", new CustomObject()),
encoded: Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "xyz", "method", Array(42, "string", CustomObjectSerialized)),
binary: "lQSAo3h5eqZtZXRob2STKqZzdHJpbmeGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiE="),
binary: "lQSAo3h5eqZtZXRob2STKqZzdHJpbmeGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="),
new ProtocolTestData(
name: "StreamInvocationWithNoHeadersAndCustomObjectArrayArg",
message: new StreamInvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null, new[] { new CustomObject(), new CustomObject() }),
encoded: Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "xyz", "method", Array(CustomObjectSerialized, CustomObjectSerialized)),
binary: "lQSAo3h5eqZtZXRob2SShqtCeXRlQXJyUHJvcMQDAQIDrERhdGVUaW1lUHJvcNb/WOwcgKpEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqqE51bGxQcm9wwKpTdHJpbmdQcm9wqFNpZ25hbFIhhqtCeXRlQXJyUHJvcMQDAQIDrERhdGVUaW1lUHJvcNb/WOwcgKpEb3VibGVQcm9wy0AZIftUQs8Sp0ludFByb3AqqE51bGxQcm9wwKpTdHJpbmdQcm9wqFNpZ25hbFIh"),
binary: "lQSAo3h5eqZtZXRob2SShqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQIDhqpTdHJpbmdQcm9wqFNpZ25hbFIhqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqsRGF0ZVRpbWVQcm9w1v9Y7ByAqE51bGxQcm9wwKtCeXRlQXJyUHJvcMQDAQID"),
new ProtocolTestData(
name: "StreamInvocationWithHeadersAndCustomObjectArrayArg",
message: AddHeaders(TestHeaders, new StreamInvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null, new[] { new CustomObject(), new CustomObject() })),
encoded: Array(HubProtocolConstants.StreamInvocationMessageType, TestHeadersSerialized, "xyz", "method", Array(CustomObjectSerialized, CustomObjectSerialized)),
binary: "lQSDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6pm1ldGhvZJKGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiGGq0J5dGVBcnJQcm9wxAMBAgOsRGF0ZVRpbWVQcm9w1v9Y7ByAqkRvdWJsZVByb3DLQBkh+1RCzxKnSW50UHJvcCqoTnVsbFByb3DAqlN0cmluZ1Byb3CoU2lnbmFsUiE="),
binary: "lQSDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6pm1ldGhvZJKGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgOGqlN0cmluZ1Byb3CoU2lnbmFsUiGqRG91YmxlUHJvcMtAGSH7VELPEqdJbnRQcm9wKqxEYXRlVGltZVByb3DW/1jsHICoTnVsbFByb3DAq0J5dGVBcnJQcm9wxAMBAgM="),
// CancelInvocation Messages
new ProtocolTestData(
name: "CancelInvocationWithNoHeaders",
message: new CancelInvocationMessage(invocationId: "xyz"),
encoded: Array(HubProtocolConstants.CancelInvocationMessageType, Map(), "xyz"),
binary: "kwWAo3h5eg=="),
new ProtocolTestData(
name: "CancelInvocationWithHeaders",
message: AddHeaders(TestHeaders, new CancelInvocationMessage(invocationId: "xyz")),
encoded: Array(HubProtocolConstants.CancelInvocationMessageType, TestHeadersSerialized, "xyz"),
binary: "kwWDo0Zvb6NCYXKyS2V5V2l0aApOZXcNCkxpbmVzq1N0aWxsIFdvcmtzsVZhbHVlV2l0aE5ld0xpbmVzsEFsc28KV29ya3MNCkZpbmWjeHl6"),
// Ping Messages
new ProtocolTestData(
name: "Ping",
message: PingMessage.Instance,
encoded: Array(HubProtocolConstants.PingMessageType),
binary: "kQY="),
}.ToDictionary(t => t.Name);
@ -282,14 +244,11 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
// Verify that the input binary string decodes to the expected MsgPack primitives
var bytes = Convert.FromBase64String(testData.Binary);
var obj = Unpack(bytes);
Assert.Equal(testData.Encoded, obj);
// Parse the input fully now.
bytes = Frame(bytes);
var protocol = new MessagePackHubProtocol();
var data = new ReadOnlySequence<byte>(bytes);
Assert.True(protocol.TryParseMessage(ref data, new TestBinder(testData.Message), out var message));
Assert.True(_hubProtocol.TryParseMessage(ref data, new TestBinder(testData.Message), out var message));
Assert.NotNull(message);
Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
@ -299,19 +258,14 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public void ParseMessageWithExtraData()
{
var expectedMessage = new InvocationMessage(invocationId: "xyz", target: "method", argumentBindingException: null);
var encodedObj = Array(HubProtocolConstants.InvocationMessageType, Map(), "xyz", "method", Array(), "ex");
var binary = "lgGAo3h5eqZtZXRob2SQomV4";
// Verify that the input binary string decodes to the expected MsgPack primitives
var bytes = Convert.FromBase64String(binary);
var obj = Unpack(bytes);
Assert.Equal(encodedObj, obj);
var bytes = new byte[] { Array(6), 1, 0x80, String(3), (byte)'x', (byte)'y', (byte)'z', String(6), (byte)'m', (byte)'e', (byte)'t', (byte)'h', (byte)'o', (byte)'d', Array(0), String(2), (byte)'e', (byte)'x' };
// Parse the input fully now.
bytes = Frame(bytes);
var protocol = new MessagePackHubProtocol();
var data = new ReadOnlySequence<byte>(bytes);
Assert.True(protocol.TryParseMessage(ref data, new TestBinder(expectedMessage), out var message));
Assert.True(_hubProtocol.TryParseMessage(ref data, new TestBinder(expectedMessage), out var message));
Assert.NotNull(message);
Assert.Equal(expectedMessage, message, TestHubMessageEqualityComparer.Instance);
@ -324,7 +278,6 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
var testData = TestData[testDataName];
var bytes = Write(testData.Message);
AssertMessages(testData.Encoded, bytes);
// Unframe the message to check the binary encoding
var byteSpan = new ReadOnlySequence<byte>(bytes);
@ -335,45 +288,94 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
Assert.True(string.Equals(actual, testData.Binary, StringComparison.Ordinal), $"Binary encoding changed from{Environment.NewLine} [{testData.Binary}]{Environment.NewLine} to{Environment.NewLine} [{actual}]{Environment.NewLine}Please verify the MsgPack output and update the baseline");
}
[Fact]
public void WriteAndParseDateTimeConvertsToUTC()
{
var dateTime = new DateTime(2018, 4, 9);
var writer = MemoryBufferWriter.Get();
try
{
_hubProtocol.WriteMessage(CompletionMessage.WithResult("xyz", dateTime), writer);
var bytes = new ReadOnlySequence<byte>(writer.ToArray());
_hubProtocol.TryParseMessage(ref bytes, new TestBinder(typeof(DateTime)), out var hubMessage);
var completionMessage = Assert.IsType<CompletionMessage>(hubMessage);
var resultDateTime = (DateTime)completionMessage.Result;
// The messagepack Timestamp format specifies that time is stored as seconds since 1970-01-01 UTC
// so the library has no choice but to store the time as UTC
// https://github.com/msgpack/msgpack/blob/master/spec.md#timestamp-extension-type
Assert.Equal(dateTime.ToUniversalTime(), resultDateTime);
}
finally
{
MemoryBufferWriter.Return(writer);
}
}
[Fact]
public void WriteAndParseDateTimeOffset()
{
var dateTimeOffset = new DateTimeOffset(new DateTime(2018, 4, 9), TimeSpan.FromHours(10));
var writer = MemoryBufferWriter.Get();
try
{
_hubProtocol.WriteMessage(CompletionMessage.WithResult("xyz", dateTimeOffset), writer);
var bytes = new ReadOnlySequence<byte>(writer.ToArray());
_hubProtocol.TryParseMessage(ref bytes, new TestBinder(typeof(DateTimeOffset)), out var hubMessage);
var completionMessage = Assert.IsType<CompletionMessage>(hubMessage);
var resultDateTimeOffset = (DateTimeOffset)completionMessage.Result;
Assert.Equal(dateTimeOffset, resultDateTimeOffset);
}
finally
{
MemoryBufferWriter.Return(writer);
}
}
public static IDictionary<string, InvalidMessageData> InvalidPayloads => new[]
{
// Message Type
new InvalidMessageData("MessageTypeString", Array("foo"), "Reading 'messageType' as Int32 failed."),
new InvalidMessageData("MessageTypeString", new byte[] { 0x91, 0xa3, (byte)'f', (byte)'o', (byte)'o' }, "Reading 'messageType' as Int32 failed."),
// Headers
new InvalidMessageData("HeadersNotAMap", Array(HubProtocolConstants.InvocationMessageType, "foo"), "Reading map length for 'headers' failed."),
new InvalidMessageData("HeaderKeyInt", Array(HubProtocolConstants.InvocationMessageType, Map((42, "foo"))), "Reading 'headers[0].Key' as String failed."),
new InvalidMessageData("HeaderValueInt", Array(HubProtocolConstants.InvocationMessageType, Map(("foo", 42))), "Reading 'headers[0].Value' as String failed."),
new InvalidMessageData("HeaderKeyArray", Array(HubProtocolConstants.InvocationMessageType, Map(("biz", "boz"), (Array(), "foo"))), "Reading 'headers[1].Key' as String failed."),
new InvalidMessageData("HeaderValueArray", Array(HubProtocolConstants.InvocationMessageType, Map(("biz", "boz"), ("foo", Array()))), "Reading 'headers[1].Value' as String failed."),
new InvalidMessageData("HeadersNotAMap", new byte[] { 0x92, 1, 0xa3, (byte)'f', (byte)'o', (byte)'o' }, "Reading map length for 'headers' failed."),
new InvalidMessageData("HeaderKeyInt", new byte[] { 0x92, 1, 0x82, 0x2a, 0xa3, (byte)'f', (byte)'o', (byte)'o' }, "Reading 'headers[0].Key' as String failed."),
new InvalidMessageData("HeaderValueInt", new byte[] { 0x92, 1, 0x82, 0xa3, (byte)'f', (byte)'o', (byte)'o', 42 }, "Reading 'headers[0].Value' as String failed."),
new InvalidMessageData("HeaderKeyArray", new byte[] { 0x92, 1, 0x84, 0xa3, (byte)'f', (byte)'o', (byte)'o', 0xa3, (byte)'f', (byte)'o', (byte)'o', 0x90, 0xa3, (byte)'f', (byte)'o', (byte)'o' }, "Reading 'headers[1].Key' as String failed."),
new InvalidMessageData("HeaderValueArray", new byte[] { 0x92, 1, 0x84, 0xa3, (byte)'f', (byte)'o', (byte)'o', 0xa3, (byte)'f', (byte)'o', (byte)'o', 0xa3, (byte)'f', (byte)'o', (byte)'o', 0x90 }, "Reading 'headers[1].Value' as String failed."),
// InvocationMessage
new InvalidMessageData("InvocationMissingId", Array(HubProtocolConstants.InvocationMessageType, Map()), "Reading 'invocationId' as String failed."),
new InvalidMessageData("InvocationIdBoolean", Array(HubProtocolConstants.InvocationMessageType, Map(), false), "Reading 'invocationId' as String failed."),
new InvalidMessageData("InvocationTargetMissing", Array(HubProtocolConstants.InvocationMessageType, Map(), "abc"), "Reading 'target' as String failed."),
new InvalidMessageData("InvocationTargetInt", Array(HubProtocolConstants.InvocationMessageType, Map(), "abc", 42), "Reading 'target' as String failed."),
new InvalidMessageData("InvocationMissingId", new byte[] { 0x92, 1, 0x80 }, "Reading 'invocationId' as String failed."),
new InvalidMessageData("InvocationIdBoolean", new byte[] { 0x91, 1, 0x80, 0xc2 }, "Reading 'invocationId' as String failed."),
new InvalidMessageData("InvocationTargetMissing", new byte[] { 0x93, 1, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c' }, "Reading 'target' as String failed."),
new InvalidMessageData("InvocationTargetInt", new byte[] { 0x94, 1, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 42 }, "Reading 'target' as String failed."),
// StreamInvocationMessage
new InvalidMessageData("StreamInvocationMissingId", Array(HubProtocolConstants.StreamInvocationMessageType, Map()), "Reading 'invocationId' as String failed."),
new InvalidMessageData("StreamInvocationIdBoolean", Array(HubProtocolConstants.StreamInvocationMessageType, Map(), false), "Reading 'invocationId' as String failed."),
new InvalidMessageData("StreamInvocationTargetMissing", Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "abc"), "Reading 'target' as String failed."),
new InvalidMessageData("StreamInvocationTargetInt", Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "abc", 42), "Reading 'target' as String failed."),
new InvalidMessageData("StreamInvocationMissingId", new byte[] { 0x92, 4, 0x80 }, "Reading 'invocationId' as String failed."),
new InvalidMessageData("StreamInvocationIdBoolean", new byte[] { 0x93, 4, 0x80, 0xc2 }, "Reading 'invocationId' as String failed."),
new InvalidMessageData("StreamInvocationTargetMissing", new byte[] { 0x93, 4, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c' }, "Reading 'target' as String failed."),
new InvalidMessageData("StreamInvocationTargetInt", new byte[] { 0x94, 4, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 42 }, "Reading 'target' as String failed."),
// StreamItemMessage
new InvalidMessageData("StreamItemMissingId", Array(HubProtocolConstants.StreamItemMessageType, Map()), "Reading 'invocationId' as String failed."),
new InvalidMessageData("StreamItemInvocationIdBoolean", Array(HubProtocolConstants.StreamItemMessageType, Map(), false), "Reading 'invocationId' as String failed."),
new InvalidMessageData("StreamItemMissing", Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz"), "Deserializing object of the `String` type for 'item' failed."),
new InvalidMessageData("StreamItemTypeMismatch", Array(HubProtocolConstants.StreamItemMessageType, Map(), "xyz", 42), "Deserializing object of the `String` type for 'item' failed."),
new InvalidMessageData("StreamItemMissingId", new byte[] { 0x92, 2, 0x80 }, "Reading 'invocationId' as String failed."),
new InvalidMessageData("StreamItemInvocationIdBoolean", new byte[] { 0x93, 2, 0x80, 0xc2 }, "Reading 'invocationId' as String failed."),
new InvalidMessageData("StreamItemMissing", new byte[] { 0x93, 2, 0x80, 0xa3, (byte)'x', (byte)'y', (byte)'z' }, "Deserializing object of the `String` type for 'item' failed."),
new InvalidMessageData("StreamItemTypeMismatch", new byte[] { 0x94, 2, 0x80, 0xa3, (byte)'x', (byte)'y', (byte)'z', 42 }, "Deserializing object of the `String` type for 'item' failed."),
// CompletionMessage
new InvalidMessageData("CompletionMissingId", Array(HubProtocolConstants.CompletionMessageType, Map()), "Reading 'invocationId' as String failed."),
new InvalidMessageData("CompletionIdBoolean", Array(HubProtocolConstants.CompletionMessageType, Map(), false), "Reading 'invocationId' as String failed."),
new InvalidMessageData("CompletionResultKindString", Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", "abc"), "Reading 'resultKind' as Int32 failed."),
new InvalidMessageData("CompletionResultKindOutOfRange", Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 42), "Invalid invocation result kind."),
new InvalidMessageData("CompletionErrorMissing", Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 1), "Reading 'error' as String failed."),
new InvalidMessageData("CompletionErrorInt", Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 1, 42), "Reading 'error' as String failed."),
new InvalidMessageData("CompletionResultMissing", Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3), "Deserializing object of the `String` type for 'argument' failed."),
new InvalidMessageData("CompletionResultTypeMismatch", Array(HubProtocolConstants.CompletionMessageType, Map(), "xyz", 3, 42), "Deserializing object of the `String` type for 'argument' failed."),
new InvalidMessageData("CompletionMissingId", new byte[] { 0x92, 3, 0x80 }, "Reading 'invocationId' as String failed."),
new InvalidMessageData("CompletionIdBoolean", new byte[] { 0x93, 3, 0x80, 0xc2 }, "Reading 'invocationId' as String failed."),
new InvalidMessageData("CompletionResultKindString", new byte[] { 0x94, 3, 0x80, 0xa3, (byte)'x', (byte)'y', (byte)'z', 0xa3, (byte)'x', (byte)'y', (byte)'z' }, "Reading 'resultKind' as Int32 failed."),
new InvalidMessageData("CompletionResultKindOutOfRange", new byte[] { 0x94, 3, 0x80, 0xa3, (byte)'x', (byte)'y', (byte)'z', 42 }, "Invalid invocation result kind."),
new InvalidMessageData("CompletionErrorMissing", new byte[] { 0x94, 3, 0x80, 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x01 }, "Reading 'error' as String failed."),
new InvalidMessageData("CompletionErrorInt", new byte[] { 0x95, 3, 0x80, 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x01, 42 }, "Reading 'error' as String failed."),
new InvalidMessageData("CompletionResultMissing", new byte[] { 0x94, 3, 0x80, 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x03 }, "Deserializing object of the `String` type for 'argument' failed."),
new InvalidMessageData("CompletionResultTypeMismatch", new byte[] { 0x95, 3, 0x80, 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x03, 42 }, "Deserializing object of the `String` type for 'argument' failed."),
}.ToDictionary(t => t.Name);
public static IEnumerable<object[]> InvalidPayloadNames => InvalidPayloads.Keys.Select(name => new object[] { name });
@ -384,7 +386,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{
var testData = InvalidPayloads[invalidPayloadName];
var buffer = Frame(Pack(testData.Encoded));
var buffer = Frame(testData.Encoded);
var binder = new TestBinder(new[] { typeof(string) }, typeof(string));
var data = new ReadOnlySequence<byte>(buffer);
var exception = Assert.Throws<InvalidDataException>(() => _hubProtocol.TryParseMessage(ref data, binder, out _));
@ -395,18 +397,18 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public static IDictionary<string, InvalidMessageData> ArgumentBindingErrors => new[]
{
// InvocationMessage
new InvalidMessageData("InvocationArgumentArrayMissing", Array(HubProtocolConstants.InvocationMessageType, Map(), "abc", "xyz"), "Reading array length for 'arguments' failed."),
new InvalidMessageData("InvocationArgumentArrayNotAnArray", Array(HubProtocolConstants.InvocationMessageType, Map(), "abc", "xyz", 42), "Reading array length for 'arguments' failed."),
new InvalidMessageData("InvocationArgumentArraySizeMismatchEmpty", Array(HubProtocolConstants.InvocationMessageType, Map(), "abc", "xyz", Array()), "Invocation provides 0 argument(s) but target expects 1."),
new InvalidMessageData("InvocationArgumentArraySizeMismatchTooLarge", Array(HubProtocolConstants.InvocationMessageType, Map(), "abc", "xyz", Array("a", "b")), "Invocation provides 2 argument(s) but target expects 1."),
new InvalidMessageData("InvocationArgumentTypeMismatch", Array(HubProtocolConstants.InvocationMessageType, Map(), "abc", "xyz", Array(42)), "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked."),
new InvalidMessageData("InvocationArgumentArrayMissing", new byte[] { 0x94, 1, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z' }, "Reading array length for 'arguments' failed."),
new InvalidMessageData("InvocationArgumentArrayNotAnArray", new byte[] { 0x95, 1, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z', 42 }, "Reading array length for 'arguments' failed."),
new InvalidMessageData("InvocationArgumentArraySizeMismatchEmpty", new byte[] { 0x95, 1, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x90 }, "Invocation provides 0 argument(s) but target expects 1."),
new InvalidMessageData("InvocationArgumentArraySizeMismatchTooLarge", new byte[] { 0x95, 1, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x92, 0xa1, (byte)'a', 0xa1, (byte)'b' }, "Invocation provides 2 argument(s) but target expects 1."),
new InvalidMessageData("InvocationArgumentTypeMismatch", new byte[] { 0x95, 1, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x91, 42 }, "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked."),
// StreamInvocationMessage
new InvalidMessageData("StreamInvocationArgumentArrayMissing", Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "abc", "xyz"), "Reading array length for 'arguments' failed."), // array is missing
new InvalidMessageData("StreamInvocationArgumentArrayNotAnArray", Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "abc", "xyz", 42), "Reading array length for 'arguments' failed."), // arguments isn't an array
new InvalidMessageData("StreamInvocationArgumentArraySizeMismatchEmpty", Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "abc", "xyz", Array()), "Invocation provides 0 argument(s) but target expects 1."), // array is missing elements
new InvalidMessageData("StreamInvocationArgumentArraySizeMismatchTooLarge", Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "abc", "xyz", Array("a", "b")), "Invocation provides 2 argument(s) but target expects 1."), // argument count does not match binder argument count
new InvalidMessageData("StreamInvocationArgumentTypeMismatch", Array(HubProtocolConstants.StreamInvocationMessageType, Map(), "abc", "xyz", Array(42)), "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked."), // argument type mismatch
new InvalidMessageData("StreamInvocationArgumentArrayMissing", new byte[] { 0x94, 4, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z' }, "Reading array length for 'arguments' failed."), // array is missing
new InvalidMessageData("StreamInvocationArgumentArrayNotAnArray", new byte[] { 0x95, 4, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z', 42 }, "Reading array length for 'arguments' failed."), // arguments isn't an array
new InvalidMessageData("StreamInvocationArgumentArraySizeMismatchEmpty", new byte[] { 0x95, 4, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x90 }, "Invocation provides 0 argument(s) but target expects 1."), // array is missing elements
new InvalidMessageData("StreamInvocationArgumentArraySizeMismatchTooLarge", new byte[] { 0x95, 4, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x92, 0xa1, (byte)'a', 0xa1, (byte)'b' }, "Invocation provides 2 argument(s) but target expects 1."), // argument count does not match binder argument count
new InvalidMessageData("StreamInvocationArgumentTypeMismatch", new byte[] { 0x95, 4, 0x80, 0xa3, (byte)'a', (byte)'b', (byte)'c', 0xa3, (byte)'x', (byte)'y', (byte)'z', 0x91, 42 }, "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked."), // argument type mismatch
}.ToDictionary(t => t.Name);
public static IEnumerable<object[]> ArgumentBindingErrorNames => ArgumentBindingErrors.Keys.Select(name => new object[] { name });
@ -417,7 +419,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{
var testData = ArgumentBindingErrors[argumentBindingErrorName];
var buffer = Frame(Pack(testData.Encoded));
var buffer = Frame(testData.Encoded);
var binder = new TestBinder(new[] { typeof(string) }, typeof(string));
var data = new ReadOnlySequence<byte>(buffer);
_hubProtocol.TryParseMessage(ref data, binder, out var message);
@ -440,15 +442,28 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public void SerializerCanSerializeTypesWithNoDefaultCtor()
{
var result = Write(CompletionMessage.WithResult("0", new List<int> { 42 }.AsReadOnly()));
AssertMessages(Array(HubProtocolConstants.CompletionMessageType, Map(), "0", 3, Array(42)), result);
AssertMessages(new byte[] { Array(5), 3, 0x80, String(1), (byte)'0', 0x03, Array(1), 42 }, result);
}
private static void AssertMessages(MessagePackObject expectedOutput, byte[] bytes)
private byte Array(int size)
{
Debug.Assert(size < 16, "Test code doesn't support array sizes greater than 15");
return (byte)(0x90 | size);
}
private byte String(int size)
{
Debug.Assert(size < 16, "Test code doesn't support string sizes greater than 15");
return (byte)(0xa0 | size);
}
private static void AssertMessages(byte[] expectedOutput, ReadOnlyMemory<byte> bytes)
{
var data = new ReadOnlySequence<byte>(bytes);
Assert.True(BinaryMessageParser.TryParseMessage(ref data, out var message));
var obj = Unpack(message.ToArray());
Assert.Equal(expectedOutput, obj);
Assert.Equal(expectedOutput, message.ToArray());
}
private static byte[] Frame(byte[] input)
@ -466,44 +481,12 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
}
}
private static MessagePackObject Unpack(byte[] input)
{
using (var stream = new MemoryStream(input))
{
using (var unpacker = Unpacker.Create(stream))
{
Assert.True(unpacker.ReadObject(out var obj));
return obj;
}
}
}
private static byte[] Pack(MessagePackObject input)
{
var options = new PackingOptions()
{
StringEncoding = Encoding.UTF8
};
using (var stream = new MemoryStream())
{
using (var packer = Packer.Create(stream))
{
input.PackToMessage(packer, options);
packer.Flush();
}
stream.Flush();
return stream.ToArray();
}
}
private static byte[] Write(HubMessage message)
{
var protocol = new MessagePackHubProtocol();
var writer = MemoryBufferWriter.Get();
try
{
protocol.WriteMessage(message, writer);
_hubProtocol.WriteMessage(message, writer);
return writer.ToArray();
}
finally
@ -515,10 +498,10 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public class InvalidMessageData
{
public string Name { get; private set; }
public MessagePackObject Encoded { get; private set; }
public byte[] Encoded { get; private set; }
public string ErrorMessage { get; private set; }
public InvalidMessageData(string name, MessagePackObject encoded, string errorMessage)
public InvalidMessageData(string name, byte[] encoded, string errorMessage)
{
Name = name;
Encoded = encoded;
@ -532,14 +515,12 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{
public string Name { get; }
public string Binary { get; }
public MessagePackObject Encoded { get; }
public HubMessage Message { get; }
public ProtocolTestData(string name, HubMessage message, MessagePackObject encoded, string binary)
public ProtocolTestData(string name, HubMessage message, string binary)
{
Name = name;
Message = message;
Encoded = encoded;
Binary = binary;
}

View File

@ -11,7 +11,6 @@ using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using MsgPack.Serialization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Xunit;
@ -506,7 +505,6 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
var server = new TestRedisServer();
var messagePackOptions = new MessagePackHubProtocolOptions();
messagePackOptions.SerializationContext.DictionarySerlaizationOptions.KeyTransformer = DictionaryKeyTransformers.LowerCamel;
var jsonOptions = new JsonHubProtocolOptions();
jsonOptions.PayloadSerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

View File

@ -8,6 +8,8 @@ using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using MessagePack;
using MessagePack.Formatters;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Connections;
@ -16,8 +18,6 @@ using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using MsgPack;
using MsgPack.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
@ -1679,7 +1679,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.SerializationContext.SerializationMethod = SerializationMethod.Array;
options.FormatterResolvers.Insert(0, new CustomFormatter());
});
});
@ -1697,10 +1697,9 @@ namespace Microsoft.AspNetCore.SignalR.Tests
var message = Assert.IsType<InvocationMessage>(await client.ReadAsync().OrTimeout());
var msgPackObject = Assert.IsType<MessagePackObject>(message.Arguments[0]);
// Custom serialization - object was serialized as an array and not a map
Assert.True(msgPackObject.IsArray);
Assert.Equal(new[] { "test", "param" }, ((MessagePackObject[])msgPackObject.ToObject()).Select(o => o.AsString()));
var result = message.Arguments[0] as Dictionary<object, object>;
Assert.Equal("formattedString", result["Message"]);
Assert.Equal("formattedString", result["paramName"]);
client.Dispose();
@ -1708,6 +1707,50 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
private class CustomFormatter : IFormatterResolver
{
public IMessagePackFormatter<T> GetFormatter<T>()
{
if (typeof(T) == typeof(string))
{
return new StringFormatter<T>();
}
return null;
}
private class StringFormatter<T> : IMessagePackFormatter<T>
{
public T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize)
{
// this method isn't used in our tests
readSize = 0;
return default;
}
public int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver)
{
// string of size 15
bytes[offset] = 0xAF;
bytes[offset + 1] = (byte)'f';
bytes[offset + 2] = (byte)'o';
bytes[offset + 3] = (byte)'r';
bytes[offset + 4] = (byte)'m';
bytes[offset + 5] = (byte)'a';
bytes[offset + 6] = (byte)'t';
bytes[offset + 7] = (byte)'t';
bytes[offset + 8] = (byte)'e';
bytes[offset + 9] = (byte)'d';
bytes[offset + 10] = (byte)'S';
bytes[offset + 11] = (byte)'t';
bytes[offset + 12] = (byte)'r';
bytes[offset + 13] = (byte)'i';
bytes[offset + 14] = (byte)'n';
bytes[offset + 15] = (byte)'g';
return 16;
}
}
}
[Fact]
public async Task CanGetHttpContextFromHubConnectionContext()
{