Split MsgPack into separate assembly (#1274)

This commit is contained in:
Andrew Stanton-Nurse 2018-01-10 14:20:57 -08:00 committed by GitHub
parent 50fe88195e
commit 144984e599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 522 additions and 203 deletions

View File

@ -88,6 +88,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JwtClientSample", "samples\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Tests.Utils", "test\Microsoft.AspNetCore.SignalR.Tests.Utils\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj", "{0A0A6135-EA24-4307-95C2-CE1B7E164A5E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Protocols.MsgPack", "src\Microsoft.AspNetCore.SignalR.Protocols.MsgPack\Microsoft.AspNetCore.SignalR.Protocols.MsgPack.csproj", "{55DB4B6F-12E5-4A27-97F4-E97E135470FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.MsgPack", "src\Microsoft.AspNetCore.SignalR.MsgPack\Microsoft.AspNetCore.SignalR.MsgPack.csproj", "{FDDB4E1F-2FD2-49E8-B0FF-874B0931369A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Client.MsgPack", "src\Microsoft.AspNetCore.SignalR.Client.MsgPack\Microsoft.AspNetCore.SignalR.Client.MsgPack.csproj", "{4DBF918E-BD37-4309-B448-BA68C935944D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -206,6 +212,18 @@ Global
{0A0A6135-EA24-4307-95C2-CE1B7E164A5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A0A6135-EA24-4307-95C2-CE1B7E164A5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A0A6135-EA24-4307-95C2-CE1B7E164A5E}.Release|Any CPU.Build.0 = Release|Any CPU
{55DB4B6F-12E5-4A27-97F4-E97E135470FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{55DB4B6F-12E5-4A27-97F4-E97E135470FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55DB4B6F-12E5-4A27-97F4-E97E135470FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55DB4B6F-12E5-4A27-97F4-E97E135470FF}.Release|Any CPU.Build.0 = Release|Any CPU
{FDDB4E1F-2FD2-49E8-B0FF-874B0931369A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDDB4E1F-2FD2-49E8-B0FF-874B0931369A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDDB4E1F-2FD2-49E8-B0FF-874B0931369A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDDB4E1F-2FD2-49E8-B0FF-874B0931369A}.Release|Any CPU.Build.0 = Release|Any CPU
{4DBF918E-BD37-4309-B448-BA68C935944D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DBF918E-BD37-4309-B448-BA68C935944D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DBF918E-BD37-4309-B448-BA68C935944D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DBF918E-BD37-4309-B448-BA68C935944D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -239,6 +257,9 @@ Global
{6A7491D3-3C97-49BD-A71C-433AED657F30} = {C4BC9889-B49F-41B6-806B-F84941B2549B}
{1A953296-E869-4DE2-A693-FD5FCDE27057} = {C4BC9889-B49F-41B6-806B-F84941B2549B}
{0A0A6135-EA24-4307-95C2-CE1B7E164A5E} = {6A35B453-52EC-48AF-89CA-D4A69800F131}
{55DB4B6F-12E5-4A27-97F4-E97E135470FF} = {DA69F624-5398-4884-87E4-B816698CDE65}
{FDDB4E1F-2FD2-49E8-B0FF-874B0931369A} = {DA69F624-5398-4884-87E4-B816698CDE65}
{4DBF918E-BD37-4309-B448-BA68C935944D} = {DA69F624-5398-4884-87E4-B816698CDE65}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7945A4E4-ACDB-4F6E-95CA-6AC6E7C2CD59}

View File

@ -1,3 +1,6 @@
// 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;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.SignalR.Internal;

View File

@ -8,6 +8,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Core\Microsoft.AspNetCore.SignalR.Core.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Protocols.MsgPack\Microsoft.AspNetCore.SignalR.Protocols.MsgPack.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Sockets\Microsoft.AspNetCore.Sockets.csproj" />
<PackageReference Include="BenchmarkDotNet" Version="$(BenchmarkDotNetPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />

View File

@ -23,11 +23,13 @@ namespace Microsoft.AspNetCore.SignalR.Test.Server
public void ConfigureServices(IServiceCollection services)
{
services.AddSockets();
services.AddSignalR(options => {
// we are running the same tests with JSON and MsgPack protocols and having
// consistent casing makes it cleaner to verify results
options.JsonSerializerSettings.ContractResolver = new DefaultContractResolver();
});
services.AddSignalR()
.AddJsonProtocol(options =>
{
// we are running the same tests with JSON and MsgPack protocols and having
// consistent casing makes it cleaner to verify results
options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
});
services.AddAuthorization(options =>
{

View File

@ -1,16 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SocketsSample.Hubs
{
public class Person
{
public string Name { get; set; }
public long Age { get; set; }
}
}

View File

@ -5,6 +5,7 @@ using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Serialization;
using SocketsSample.EndPoints;
using SocketsSample.Hubs;

View File

@ -1,11 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.Logging;
using MsgPack.Serialization;
using Newtonsoft.Json;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.SignalR.Client
{
@ -22,29 +21,9 @@ namespace Microsoft.AspNetCore.SignalR.Client
return hubConnectionBuilder.WithHubProtocol(new JsonHubProtocol());
}
public static IHubConnectionBuilder WithJsonProtocol(this IHubConnectionBuilder hubConnectionBuilder, JsonSerializerSettings serializerSettings)
public static IHubConnectionBuilder WithJsonProtocol(this IHubConnectionBuilder hubConnectionBuilder, JsonHubProtocolOptions options)
{
return hubConnectionBuilder.WithHubProtocol(new JsonHubProtocol(JsonSerializer.Create(serializerSettings)));
}
public static IHubConnectionBuilder WithJsonProtocol(this IHubConnectionBuilder hubConnectionBuilder, JsonSerializer jsonSerializer)
{
return hubConnectionBuilder.WithHubProtocol(new JsonHubProtocol(jsonSerializer));
}
public static IHubConnectionBuilder WithMessagePackProtocol(this IHubConnectionBuilder hubConnectionBuilder)
{
return hubConnectionBuilder.WithHubProtocol(new MessagePackHubProtocol());
}
public static IHubConnectionBuilder WithMessagePackProtocol(this IHubConnectionBuilder hubConnectionBuilder, SerializationContext serializationContext)
{
if (serializationContext == null)
{
throw new ArgumentNullException(nameof(serializationContext));
}
return hubConnectionBuilder.WithHubProtocol(new MessagePackHubProtocol(serializationContext));
return hubConnectionBuilder.WithHubProtocol(new JsonHubProtocol(Options.Create(options)));
}
public static IHubConnectionBuilder WithLoggerFactory(this IHubConnectionBuilder hubConnectionBuilder, ILoggerFactory loggerFactory)

View File

@ -0,0 +1,19 @@
// 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 Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.SignalR.Client
{
public static class MessagePackProtocolHubConnectionBuilderExtensions
{
public static IHubConnectionBuilder WithMessagePackProtocol(this IHubConnectionBuilder builder) =>
WithMessagePackProtocol(builder, new MessagePackHubProtocolOptions());
public static IHubConnectionBuilder WithMessagePackProtocol(this IHubConnectionBuilder builder, MessagePackHubProtocolOptions options)
{
return builder.WithHubProtocol(new MessagePackHubProtocol(Options.Create(options)));
}
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Provides client-side support for the MsgPack protocol in ASP.NET Core SignalR</Description>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Client.Core\Microsoft.AspNetCore.SignalR.Client.Core.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Protocols.MsgPack\Microsoft.AspNetCore.SignalR.Protocols.MsgPack.csproj" />
</ItemGroup>
</Project>

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.ExceptionServices;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
@ -23,35 +24,21 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
private const string ArgumentsPropertyName = "arguments";
private const string PayloadPropertyName = "payload";
public static readonly string ProtocolName = "json";
// ONLY to be used for application payloads (args, return values, etc.)
private JsonSerializer _payloadSerializer;
public JsonSerializer PayloadSerializer { get; }
/// <summary>
/// Creates an instance of the <see cref="JsonHubProtocol"/> using the default <see cref="JsonSerializer"/>
/// to serialize application payloads (arguments, results, etc.). The serialization of the outer protocol can
/// NOT be changed using this serializer.
/// </summary>
public JsonHubProtocol()
: this(JsonSerializer.Create(CreateDefaultSerializerSettings()))
{ }
/// <summary>
/// Creates an instance of the <see cref="JsonHubProtocol"/> using the specified <see cref="JsonSerializer"/>
/// to serialize application payloads (arguments, results, etc.). The serialization of the outer protocol can
/// NOT be changed using this serializer.
/// </summary>
/// <param name="payloadSerializer">The <see cref="JsonSerializer"/> to use to serialize application payloads (arguments, results, etc.).</param>
public JsonHubProtocol(JsonSerializer payloadSerializer)
public JsonHubProtocol() : this(Options.Create(new JsonHubProtocolOptions()))
{
if (payloadSerializer == null)
{
throw new ArgumentNullException(nameof(payloadSerializer));
}
_payloadSerializer = payloadSerializer;
}
public string Name => "json";
public JsonHubProtocol(IOptions<JsonHubProtocolOptions> options)
{
PayloadSerializer = JsonSerializer.Create(options.Value.PayloadSerializerSettings);
}
public string Name => ProtocolName;
public ProtocolType Type => ProtocolType.Text;
@ -167,7 +154,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
else if (message.HasResult)
{
writer.WritePropertyName(ResultPropertyName);
_payloadSerializer.Serialize(writer, message.Result);
PayloadSerializer.Serialize(writer, message.Result);
}
writer.WriteEndObject();
}
@ -184,7 +171,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
writer.WriteStartObject();
WriteHubInvocationMessageCommon(message, writer, HubProtocolConstants.StreamItemMessageType);
writer.WritePropertyName(ItemPropertyName);
_payloadSerializer.Serialize(writer, message.Item);
PayloadSerializer.Serialize(writer, message.Item);
writer.WriteEndObject();
}
@ -218,7 +205,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
writer.WriteStartArray();
foreach (var argument in arguments)
{
_payloadSerializer.Serialize(writer, argument);
PayloadSerializer.Serialize(writer, argument);
}
writer.WriteEndArray();
}
@ -299,7 +286,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
for (var i = 0; i < paramTypes.Length; i++)
{
var paramType = paramTypes[i];
arguments[i] = args[i].ToObject(paramType, _payloadSerializer);
arguments[i] = args[i].ToObject(paramType, PayloadSerializer);
}
return arguments;
@ -316,7 +303,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
var result = JsonUtils.GetRequiredProperty<JToken>(json, ItemPropertyName);
var returnType = binder.GetReturnType(invocationId);
return new StreamItemMessage(invocationId, result?.ToObject(returnType, _payloadSerializer));
return new StreamItemMessage(invocationId, result?.ToObject(returnType, PayloadSerializer));
}
private CompletionMessage BindCompletionMessage(JObject json, IInvocationBinder binder)
@ -336,7 +323,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
}
var returnType = binder.GetReturnType(invocationId);
var payload = resultProp.Value?.ToObject(returnType, _payloadSerializer);
var payload = resultProp.Value?.ToObject(returnType, PayloadSerializer);
return new CompletionMessage(invocationId, error, result: payload, hasResult: true);
}
@ -346,7 +333,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
return new CancelInvocationMessage(invocationId);
}
public static JsonSerializerSettings CreateDefaultSerializerSettings()
internal static JsonSerializerSettings CreateDefaultSerializerSettings()
{
return new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
}

View File

@ -0,0 +1,13 @@
// 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 Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.SignalR
{
public class JsonHubProtocolOptions
{
public JsonSerializerSettings PayloadSerializerSettings { get; set; } = JsonHubProtocol.CreateDefaultSerializerSettings();
}
}

View File

@ -9,11 +9,11 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
<PackageReference Include="MsgPack.Cli" Version="$(MsgPackCliPackageVersion)" />
<PackageReference Include="System.Buffers.Primitives" Version="$(SystemBuffersPrimitivesPackageVersion)" />
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
<PackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -12,7 +12,6 @@ using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.SignalR.Core;
using Microsoft.AspNetCore.SignalR.Core.Internal;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Encoders;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;

View File

@ -12,7 +12,6 @@ using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR.Core;
using Microsoft.AspNetCore.SignalR.Core.Internal;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.AspNetCore.Sockets;

View File

@ -2,9 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using MsgPack.Serialization;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.SignalR
{
@ -16,8 +13,6 @@ namespace Microsoft.AspNetCore.SignalR
/// </summary>
public static readonly TimeSpan DefaultKeepAliveInterval = TimeSpan.FromSeconds(15);
public JsonSerializerSettings JsonSerializerSettings { get; set; } = JsonHubProtocol.CreateDefaultSerializerSettings();
public SerializationContext MessagePackSerializationContext { get; set; } = MessagePackHubProtocol.CreateDefaultSerializationContext();
public TimeSpan NegotiateTimeout { get; set; } = TimeSpan.FromSeconds(5);
/// <summary>

View File

@ -1,8 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
@ -11,23 +15,37 @@ namespace Microsoft.AspNetCore.SignalR.Internal
public class DefaultHubProtocolResolver : IHubProtocolResolver
{
private readonly IOptions<HubOptions> _options;
private readonly ILogger<DefaultHubProtocolResolver> _logger;
private readonly Dictionary<string, IHubProtocol> _availableProtocols;
public DefaultHubProtocolResolver(IOptions<HubOptions> options)
public DefaultHubProtocolResolver(IOptions<HubOptions> options, IEnumerable<IHubProtocol> availableProtocols, ILogger<DefaultHubProtocolResolver> logger)
{
_options = options;
_options = options ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? NullLogger<DefaultHubProtocolResolver>.Instance;
_availableProtocols = new Dictionary<string, IHubProtocol>(StringComparer.OrdinalIgnoreCase);
foreach(var protocol in availableProtocols)
{
if(_availableProtocols.ContainsKey(protocol.Name))
{
throw new InvalidOperationException($"Multiple Hub Protocols with the name '{protocol.Name}' were registered.");
}
_logger.RegisteredSignalRProtocol(protocol.Name, protocol.GetType());
_availableProtocols.Add(protocol.Name, protocol);
}
}
public IHubProtocol GetProtocol(string protocolName, HubConnectionContext connection)
{
switch (protocolName?.ToLowerInvariant())
protocolName = protocolName ?? throw new ArgumentNullException(nameof(protocolName));
if (_availableProtocols.TryGetValue(protocolName, out var protocol))
{
case "json":
return new JsonHubProtocol(JsonSerializer.Create(_options.Value.JsonSerializerSettings));
case "messagepack":
return new MessagePackHubProtocol(_options.Value.MessagePackSerializationContext);
default:
throw new NotSupportedException($"The protocol '{protocolName ?? "(null)"}' is not supported.");
_logger.FoundImplementationForProtocol(protocolName);
return protocol;
}
throw new NotSupportedException($"The protocol '{protocolName ?? "(null)"}' is not supported.");
}
}
}

View File

@ -5,7 +5,7 @@ using System;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.SignalR.Core.Internal
namespace Microsoft.AspNetCore.SignalR.Internal
{
internal static class SignalRCoreLoggerExtensions
{
@ -77,6 +77,13 @@ namespace Microsoft.AspNetCore.SignalR.Core.Internal
private static readonly Action<ILogger, Exception> _transportBufferFull =
LoggerMessage.Define(LogLevel.Debug, new EventId(4, nameof(TransportBufferFull)), "Unable to send Ping message to client, the transport buffer is full.");
// Category: DefaultHubProtocolResolver
private static readonly Action<ILogger, string, Type, Exception> _registeredSignalRProtocol =
LoggerMessage.Define<string, Type>(LogLevel.Debug, new EventId(0, nameof(RegisteredSignalRProtocol)), "Registered SignalR Protocol: {ProtocolName}, implemented by {ImplementationType}.");
private static readonly Action<ILogger, string, Exception> _foundImplementationForProtocol =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(1, nameof(FoundImplementationForProtocol)), "Found protocol implementation for requested protocol: {ProtocolName}.");
public static void UsingHubProtocol(this ILogger logger, string hubProtocol)
{
_usingHubProtocol(logger, hubProtocol, null);
@ -186,5 +193,15 @@ namespace Microsoft.AspNetCore.SignalR.Core.Internal
{
_transportBufferFull(logger, null);
}
public static void RegisteredSignalRProtocol(this ILogger logger, string protocolName, Type implementationType)
{
_registeredSignalRProtocol(logger, protocolName, implementationType, null);
}
public static void FoundImplementationForProtocol(this ILogger logger, string protocolName)
{
_foundImplementationForProtocol(logger, protocolName, null);
}
}
}

View File

@ -0,0 +1,37 @@
using System;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
public static class JsonProtocolDependencyInjectionExtensions
{
/// <summary>
/// Enables the JSON protocol for SignalR.
/// </summary>
/// <remarks>
/// This has no effect if the JSON protocol has already been enabled.
/// </remarks>
/// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add JSON protocol support to.</param>
/// <returns>The value of <paramref name="builder"/></returns>
public static ISignalRBuilder AddJsonProtocol(this ISignalRBuilder builder) => AddJsonProtocol(builder, _ => { });
/// <summary>
/// Enables the JSON protocol for SignalR and allows options for the JSON protocol to be configured.
/// </summary>
/// <remarks>
/// Any options configured here will be applied, even if the JSON protocol has already been registered with the server.
/// </remarks>
/// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add JSON protocol support to.</param>
/// <param name="configure">A delegate that can be used to configure the <see cref="JsonHubProtocolOptions"/></param>
/// <returns>The value of <paramref name="builder"/></returns>
public static ISignalRBuilder AddJsonProtocol(this ISignalRBuilder builder, Action<JsonHubProtocolOptions> configure)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, JsonHubProtocol>());
builder.Services.Configure(configure);
return builder;
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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 Microsoft.AspNetCore.SignalR;

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Provides server-side support for the MsgPack protocol in ASP.NET Core SignalR</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Core\Microsoft.AspNetCore.SignalR.Core.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Protocols.MsgPack\Microsoft.AspNetCore.SignalR.Protocols.MsgPack.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,37 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
public static class MsgPackProtocolDependencyInjectionExtensions
{
/// <summary>
/// Enables the MsgPack protocol for SignalR.
/// </summary>
/// <remarks>
/// This has no effect if the MsgPack protocol has already been enabled.
/// </remarks>
/// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add MsgPack protocol support to.</param>
/// <returns>The value of <paramref name="builder"/></returns>
public static ISignalRBuilder AddMessagePackProtocol(this ISignalRBuilder builder) => AddMessagePackProtocol(builder, _ => { });
/// <summary>
/// Enables the MsgPack protocol for SignalR and allows options for the MsgPack protocol to be configured.
/// </summary>
/// <remarks>
/// Any options configured here will be applied, even if the MsgPack protocol has already been registered with the server.
/// </remarks>
/// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add MsgPack protocol support to.</param>
/// <param name="configure">A delegate that can be used to configure the <see cref="MessagePackHubProtocolOptions"/></param>
/// <returns>The value of <paramref name="builder"/></returns>
public static ISignalRBuilder AddMessagePackProtocol(this ISignalRBuilder builder, Action<MessagePackHubProtocolOptions> configure)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, MessagePackHubProtocol>());
builder.Services.Configure(configure);
return builder;
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.ExceptionServices;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.Extensions.Options;
using MsgPack;
using MsgPack.Serialization;
@ -17,19 +18,21 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
private const int VoidResult = 2;
private const int NonVoidResult = 3;
private readonly SerializationContext _serializationContext;
public static readonly string ProtocolName = "messagepack";
public string Name => "messagepack";
public SerializationContext SerializationContext { get; }
public string Name => ProtocolName;
public ProtocolType Type => ProtocolType.Binary;
public MessagePackHubProtocol()
: this(CreateDefaultSerializationContext())
: this(Options.Create(new MessagePackHubProtocolOptions()))
{ }
public MessagePackHubProtocol(SerializationContext serializationContext)
public MessagePackHubProtocol(IOptions<MessagePackHubProtocolOptions> options)
{
_serializationContext = serializationContext;
SerializationContext = options.Value.SerializationContext;
}
public bool TryParseMessages(ReadOnlySpan<byte> input, IInvocationBinder binder, out IList<HubMessage> messages)
@ -236,7 +239,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
packer.PackString(invocationMessage.InvocationId);
}
packer.PackString(invocationMessage.Target);
packer.PackObject(invocationMessage.Arguments, _serializationContext);
packer.PackObject(invocationMessage.Arguments, SerializationContext);
}
private void WriteStreamInvocationMessage(StreamInvocationMessage streamInvocationMessage, Packer packer)
@ -245,7 +248,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
packer.Pack(HubProtocolConstants.StreamInvocationMessageType);
packer.PackString(streamInvocationMessage.InvocationId);
packer.PackString(streamInvocationMessage.Target);
packer.PackObject(streamInvocationMessage.Arguments, _serializationContext);
packer.PackObject(streamInvocationMessage.Arguments, SerializationContext);
}
private void WriteStreamingItemMessage(StreamItemMessage streamItemMessage, Packer packer)
@ -253,7 +256,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
packer.PackArrayHeader(3);
packer.Pack(HubProtocolConstants.StreamItemMessageType);
packer.PackString(streamItemMessage.InvocationId);
packer.PackObject(streamItemMessage.Item, _serializationContext);
packer.PackObject(streamItemMessage.Item, SerializationContext);
}
private void WriteCompletionMessage(CompletionMessage completionMessage, Packer packer)
@ -274,7 +277,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
packer.PackString(completionMessage.Error);
break;
case NonVoidResult:
packer.PackObject(completionMessage.Result, _serializationContext);
packer.PackObject(completionMessage.Result, SerializationContext);
break;
}
}
@ -395,7 +398,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
throw new FormatException($"Deserializing object of the `{type.Name}` type for '{field}' failed.", msgPackException);
}
public static SerializationContext CreateDefaultSerializationContext()
internal static SerializationContext CreateDefaultSerializationContext()
{
// serializes objects (here: arguments and results) as maps so that property names are preserved
var serializationContext = new SerializationContext { SerializationMethod = SerializationMethod.Map };

View File

@ -0,0 +1,13 @@
// 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 Microsoft.AspNetCore.SignalR.Internal.Protocol;
using MsgPack.Serialization;
namespace Microsoft.AspNetCore.SignalR
{
public class MessagePackHubProtocolOptions
{
public SerializationContext SerializationContext { get; set; } = MessagePackHubProtocol.CreateDefaultSerializationContext();
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>
Implements the SignalR Hub Protocol over MsgPack. This package provides internal infrastructure.
To use MsgPack, use the Microsoft.AspNetCore.SignalR.Client.MsgPack (for the Client) or
Microsoft.AspNetCore.SignalR.MsgPack (for the Server) packages.
</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MsgPack.Cli" Version="$(MsgPackCliPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -17,7 +17,8 @@ namespace Microsoft.Extensions.DependencyInjection
{
services.Configure(configure);
services.AddSockets();
return services.AddSignalRCore();
return services.AddSignalRCore()
.AddJsonProtocol();
}
}
}

View File

@ -17,6 +17,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.MsgPack\Microsoft.AspNetCore.SignalR.MsgPack.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Protocols.MsgPack\Microsoft.AspNetCore.SignalR.Protocols.MsgPack.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR\Microsoft.AspNetCore.SignalR.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Client\Microsoft.AspNetCore.SignalR.Client.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Tests.Utils\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj" />

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -19,7 +19,8 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSignalR()
.AddMessagePackProtocol();
services.AddAuthorization(options =>
{
options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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.Net.Http;

View File

@ -1,7 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using MsgPack.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Xunit;
namespace Microsoft.AspNetCore.SignalR.Client.Tests
@ -30,5 +34,53 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
Assert.Equal("loggerFactory",
Assert.Throws<ArgumentNullException>(() => new HubConnectionBuilder().WithLoggerFactory(null)).ParamName);
}
[Fact]
public void WithJsonHubProtocolSetsHubProtocolToJsonWithDefaultOptions()
{
Assert.True(new HubConnectionBuilder().WithJsonProtocol().TryGetSetting<IHubProtocol>(HubConnectionBuilderDefaults.HubProtocolKey, out var hubProtocol));
var actualProtocol = Assert.IsType<JsonHubProtocol>(hubProtocol);
Assert.IsType<CamelCasePropertyNamesContractResolver>(actualProtocol.PayloadSerializer.ContractResolver);
}
[Fact]
public void WithJsonHubProtocolSetsHubProtocolToJsonWithProvidedOptions()
{
var expectedOptions = new JsonHubProtocolOptions()
{
PayloadSerializerSettings = new JsonSerializerSettings()
{
DateFormatString = "JUST A TEST"
}
};
Assert.True(new HubConnectionBuilder().WithJsonProtocol(expectedOptions).TryGetSetting<IHubProtocol>(HubConnectionBuilderDefaults.HubProtocolKey, out var hubProtocol));
var actualProtocol = Assert.IsType<JsonHubProtocol>(hubProtocol);
Assert.Equal("JUST A TEST", actualProtocol.PayloadSerializer.DateFormatString);
}
[Fact]
public void WithMessagePackHubProtocolSetsHubProtocolToMsgPackWithDefaultOptions()
{
Assert.True(new HubConnectionBuilder().WithMessagePackProtocol().TryGetSetting<IHubProtocol>(HubConnectionBuilderDefaults.HubProtocolKey, out var hubProtocol));
var actualProtocol = Assert.IsType<MessagePackHubProtocol>(hubProtocol);
Assert.Equal(SerializationMethod.Map, actualProtocol.SerializationContext.SerializationMethod);
}
[Fact]
public void WithMessagePackHubProtocolSetsHubProtocolToMsgPackWithProvidedOptions()
{
var expectedOptions = new MessagePackHubProtocolOptions()
{
SerializationContext = new SerializationContext()
{
SerializationMethod = SerializationMethod.Array
}
};
Assert.True(new HubConnectionBuilder().WithMessagePackProtocol(expectedOptions).TryGetSetting<IHubProtocol>(HubConnectionBuilderDefaults.HubProtocolKey, out var hubProtocol));
var actualProtocol = Assert.IsType<MessagePackHubProtocol>(hubProtocol);
Assert.Equal(SerializationMethod.Array, actualProtocol.SerializationContext.SerializationMethod);
}
}
}

View File

@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
public async Task ConnectionNotClosedOnCallbackArgumentCountMismatch()
{
var connection = new TestConnection();
var hubConnection = new HubConnection(connection, new JsonHubProtocol(new JsonSerializer()), new LoggerFactory());
var hubConnection = new HubConnection(connection, new JsonHubProtocol(), new LoggerFactory());
var receiveTcs = new TaskCompletionSource<int>();
try

View File

@ -141,7 +141,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
public async Task StreamCompletesWhenCompletionMessageIsReceived()
{
var connection = new TestConnection();
var hubConnection = new HubConnection(connection, new JsonHubProtocol(new JsonSerializer()), new LoggerFactory());
var hubConnection = new HubConnection(connection, new JsonHubProtocol(), new LoggerFactory());
try
{
await hubConnection.StartAsync();

View File

@ -11,6 +11,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Client.MsgPack\Microsoft.AspNetCore.SignalR.Client.MsgPack.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Client\Microsoft.AspNetCore.SignalR.Client.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Tests.Utils\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj" />
</ItemGroup>

View File

@ -7,6 +7,7 @@ using System.IO;
using System.Text;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Xunit;
@ -67,13 +68,16 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{
expectedOutput = Frame(expectedOutput);
var jsonSerializer = new JsonSerializer
var protocolOptions = new JsonHubProtocolOptions
{
NullValueHandling = nullValueHandling,
ContractResolver = camelCase ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver()
PayloadSerializerSettings = new JsonSerializerSettings()
{
NullValueHandling = nullValueHandling,
ContractResolver = camelCase ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver()
}
};
var protocol = new JsonHubProtocol(jsonSerializer);
var protocol = new JsonHubProtocol(Options.Create(protocolOptions));
using (var ms = new MemoryStream())
{
@ -90,14 +94,17 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{
input = Frame(input);
var jsonSerializer = new JsonSerializer
var protocolOptions = new JsonHubProtocolOptions
{
NullValueHandling = nullValueHandling,
ContractResolver = camelCase ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver()
PayloadSerializerSettings = new JsonSerializerSettings
{
NullValueHandling = nullValueHandling,
ContractResolver = camelCase ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver()
}
};
var binder = new TestBinder(expectedMessage);
var protocol = new JsonHubProtocol(jsonSerializer);
var protocol = new JsonHubProtocol(Options.Create(protocolOptions));
protocol.TryParseMessages(Encoding.UTF8.GetBytes(input), binder, out var messages);
Assert.Equal(expectedMessage, messages[0], TestHubMessageEqualityComparer.Instance);

View File

@ -12,6 +12,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Protocols.MsgPack\Microsoft.AspNetCore.SignalR.Protocols.MsgPack.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Tests.Utils\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj" />
</ItemGroup>

View File

@ -2,10 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.SignalR.Redis.Tests
{
@ -51,7 +53,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
return null;
}
public int Start(ILogger logger)
public void Start(ILogger logger)
{
logger.LogInformation("Starting docker container");
@ -59,23 +61,41 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
// use static name 'redisTestContainer' so if the container doesn't get removed we don't keep adding more
// use redis base docker image
// 10 second timeout to allow redis image to be downloaded
return RunProcess(_path, $"run --rm -p 6379:6379 --name {_dockerContainerName} -d redis", logger, TimeSpan.FromSeconds(10));
RunProcessAndThrowIfFailed(_path, $"run --rm -p 6379:6379 --name {_dockerContainerName} -d redis", logger, TimeSpan.FromSeconds(10));
}
public int Stop(ILogger logger)
public void Stop(ILogger logger)
{
logger.LogInformation("Stopping docker container");
return RunProcess(_path, $"stop {_dockerContainerName}", logger, TimeSpan.FromSeconds(5));
RunProcessAndThrowIfFailed(_path, $"stop {_dockerContainerName}", logger, TimeSpan.FromSeconds(5));
}
private static int RunProcess(string fileName, string arugments, ILogger logger, TimeSpan timeout)
public int RunCommand(string commandAndArguments, out string output) =>
RunCommand(commandAndArguments, NullLogger.Instance, out output);
public int RunCommand(string commandAndArguments, ILogger logger, out string output)
{
return RunProcess(_path, commandAndArguments, logger, TimeSpan.FromSeconds(5), out output);
}
private static void RunProcessAndThrowIfFailed(string fileName, string arguments, ILogger logger, TimeSpan timeout)
{
var exitCode = RunProcess(fileName, arguments, logger, timeout, out var output);
if(exitCode != 0)
{
throw new Exception($"Command '{fileName} {arguments}' failed with exit code '{exitCode}'. Output:{Environment.NewLine}{output}");
}
}
private static int RunProcess(string fileName, string arguments, ILogger logger, TimeSpan timeout, out string output)
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = fileName,
Arguments = arugments,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
@ -84,9 +104,18 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
};
var exitCode = 0;
var lines = new ConcurrentQueue<string>();
process.Exited += (_, __) => exitCode = process.ExitCode;
process.OutputDataReceived += (_, a) => LogIfNotNull(logger.LogInformation, "stdout: {0}", a.Data);
process.ErrorDataReceived += (_, a) => LogIfNotNull(logger.LogError, "stderr: {0}", a.Data);
process.OutputDataReceived += (_, a) =>
{
LogIfNotNull(logger.LogInformation, "stdout: {0}", a.Data);
lines.Enqueue(a.Data);
};
process.ErrorDataReceived += (_, a) =>
{
LogIfNotNull(logger.LogError, "stderr: {0}", a.Data);
lines.Enqueue(a.Data);
};
process.Start();
@ -95,6 +124,8 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
process.WaitForExit((int)timeout.TotalMilliseconds);
output = string.Join(Environment.NewLine, lines);
return exitCode;
}
@ -106,4 +137,4 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
}
}
}
}
}

View File

@ -11,6 +11,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.MsgPack\Microsoft.AspNetCore.SignalR.MsgPack.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Protocols.MsgPack\Microsoft.AspNetCore.SignalR.Protocols.MsgPack.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Redis\Microsoft.AspNetCore.SignalR.Redis.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Sockets\Microsoft.AspNetCore.Sockets.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR\Microsoft.AspNetCore.SignalR.csproj" />

View File

@ -9,7 +9,31 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class SkipIfDockerNotPresentAttribute : Attribute, ITestCondition
{
public bool IsMet => Docker.Default != null;
public string SkipReason => "Docker is not installed on the host machine.";
public bool IsMet => CheckDocker();
public string SkipReason { get; private set; } = "Docker is not available";
private bool CheckDocker()
{
if(Docker.Default != null)
{
// Docker is present, but is it working?
if (Docker.Default.RunCommand("ps", out var output) != 0)
{
SkipReason = $"Failed to invoke test command 'docker ps'. Output: {output}";
}
else
{
// We have a docker
return true;
}
}
else
{
SkipReason = "Docker is not installed on the host machine.";
}
// If we get here, we don't have a docker
return false;
}
}
}

View File

@ -12,6 +12,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR()
.AddMessagePackProtocol()
.AddRedis(options =>
{
// We start the servers before starting redis so we want to time them out ASAP

View File

@ -42,6 +42,30 @@ namespace System.Threading.Tasks
return await task;
}
public static async Task OrThrowIfOtherFails(this Task task, Task otherTask)
{
var completed = await Task.WhenAny(task, otherTask);
if(completed == otherTask && otherTask.IsFaulted)
{
// Manifest the exception
otherTask.GetAwaiter().GetResult();
throw new Exception("Unreachable code");
}
else
{
// Await the task we were asked to await. Either it's finished, or the otherTask finished successfully, and it's not our job to check that
await task;
}
}
public static async Task<T> OrThrowIfOtherFails<T>(this Task<T> task, Task otherTask)
{
await OrThrowIfOtherFails((Task)task, otherTask);
// If we get here, 'task' is finished and succeeded.
return task.GetAwaiter().GetResult();
}
private static string GetMessage(string memberName, string filePath, int? lineNumber)
{
if (!string.IsNullOrEmpty(memberName))

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -54,8 +54,10 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils
{
var services = new ServiceCollection();
services.AddOptions()
.AddLogging()
.AddSignalR();
.AddLogging();
services.AddSignalR()
.AddMessagePackProtocol();
addServices?.Invoke(services);

View File

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils;
using Microsoft.AspNetCore.Sockets;
using Microsoft.AspNetCore.Sockets.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using MsgPack;
using MsgPack.Serialization;
@ -969,7 +970,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
Assert.Equal("Second and Third", invocation.Arguments[0]);
// Check that first client only got the completion message
var hubMessage= await firstClient.ReadAsync().OrTimeout();
var hubMessage = await firstClient.ReadAsync().OrTimeout();
Assert.IsType<CompletionMessage>(hubMessage);
Assert.Null(firstClient.TryRead());
@ -1338,7 +1339,8 @@ namespace Microsoft.AspNetCore.SignalR.Tests
var endPointLifetime = endPoint.OnConnectedAsync(client.Connection);
await client.Connected.OrTimeout();
// Wait for a connection, or for the endpoint to fail.
await client.Connected.OrThrowIfOtherFails(endPointLifetime).OrTimeout();
var messages = await client.StreamAsync(method, 4).OrTimeout();
@ -1470,13 +1472,15 @@ namespace Microsoft.AspNetCore.SignalR.Tests
{
var serviceProvider = HubEndPointTestUtils.CreateServiceProvider(services =>
{
services.AddSignalR(o =>
{
o.JsonSerializerSettings = new JsonSerializerSettings
services
.AddSignalR()
.AddJsonProtocol(o =>
{
ContractResolver = new DefaultContractResolver()
};
});
o.PayloadSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver()
};
});
});
var endPoint = serviceProvider.GetService<HubEndPoint<MethodHub>>();
@ -1532,20 +1536,19 @@ namespace Microsoft.AspNetCore.SignalR.Tests
[Fact]
public async Task HubOptionsCanUseCustomMessagePackSettings()
{
var serializationContext = MessagePackHubProtocol.CreateDefaultSerializationContext();
serializationContext.SerializationMethod = SerializationMethod.Array;
var serviceProvider = HubEndPointTestUtils.CreateServiceProvider(services =>
{
services.AddSignalR(options =>
{
options.MessagePackSerializationContext = serializationContext;
});
services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.SerializationContext.SerializationMethod = SerializationMethod.Array;
});
});
var endPoint = serviceProvider.GetService<HubEndPoint<MethodHub>>();
using (var client = new TestClient(synchronousCallbacks: false, protocol: new MessagePackHubProtocol(serializationContext)))
var msgPackOptions = serviceProvider.GetRequiredService<IOptions<MessagePackHubProtocolOptions>>();
using (var client = new TestClient(synchronousCallbacks: false, protocol: new MessagePackHubProtocol(msgPackOptions)))
{
var transportFeature = new Mock<IConnectionTransportFeature>();
transportFeature.SetupGet(f => f.TransportCapabilities).Returns(TransferMode.Binary);
@ -1633,7 +1636,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
var endPointLifetime = endPoint.OnConnectedAsync(client.Connection);
await client.Connected.OrTimeout();
await client.Connected.OrThrowIfOtherFails(endPointLifetime).OrTimeout();
await client.SendInvocationAsync(nameof(MethodHub.SendAnonymousObject)).OrTimeout();

View File

@ -3,40 +3,67 @@
using System;
using System.Collections.Generic;
using System.Threading.Channels;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.AspNetCore.Sockets;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNetCore.SignalR.Common.Protocol.Tests
{
public class DefaultHubProtocolResolverTests
{
private static readonly IList<IHubProtocol> AllProtocols = new List<IHubProtocol>()
{
new JsonHubProtocol(),
new MessagePackHubProtocol()
};
[Theory]
[MemberData(nameof(HubProtocols))]
public void DefaultHubProtocolResolverTestsCanCreateSupportedProtocols(IHubProtocol protocol)
{
var mockConnection = new Mock<HubConnectionContext>(new Mock<ConnectionContext>().Object, TimeSpan.FromSeconds(30), NullLoggerFactory.Instance);
var resolver = new DefaultHubProtocolResolver(Options.Create(new HubOptions()), AllProtocols, NullLogger<DefaultHubProtocolResolver>.Instance);
Assert.IsType(
protocol.GetType(),
new DefaultHubProtocolResolver(Options.Create(new HubOptions())).GetProtocol(protocol.Name, mockConnection.Object));
resolver.GetProtocol(protocol.Name, mockConnection.Object));
}
[Theory]
[InlineData(null)]
[InlineData("dummy")]
public void DefaultHubProtocolResolverThrowsForNotSupportedProtocol(string protocolName)
[Fact]
public void DefaultHubProtocolResolverThrowsForNullProtocol()
{
var mockConnection = new Mock<HubConnectionContext>(new Mock<ConnectionContext>().Object, TimeSpan.FromSeconds(30), NullLoggerFactory.Instance);
var exception = Assert.Throws<NotSupportedException>(
() => new DefaultHubProtocolResolver(Options.Create(new HubOptions())).GetProtocol(protocolName, mockConnection.Object));
var resolver = new DefaultHubProtocolResolver(Options.Create(new HubOptions()), AllProtocols, NullLogger<DefaultHubProtocolResolver>.Instance);
var exception = Assert.Throws<ArgumentNullException>(
() => resolver.GetProtocol(null, mockConnection.Object));
Assert.Equal($"The protocol '{protocolName ?? "(null)"}' is not supported.", exception.Message);
Assert.Equal("protocolName", exception.ParamName);
}
[Fact]
public void DefaultHubProtocolResolverThrowsForNotSupportedProtocol()
{
var mockConnection = new Mock<HubConnectionContext>(new Mock<ConnectionContext>().Object, TimeSpan.FromSeconds(30), NullLoggerFactory.Instance);
var resolver = new DefaultHubProtocolResolver(Options.Create(new HubOptions()), AllProtocols, NullLogger<DefaultHubProtocolResolver>.Instance);
var exception = Assert.Throws<NotSupportedException>(
() => resolver.GetProtocol("notARealProtocol", mockConnection.Object));
Assert.Equal("The protocol 'notARealProtocol' is not supported.", exception.Message);
}
[Fact]
public void RegisteringMultipleHubProtocolsFails()
{
var mockConnection = new Mock<HubConnectionContext>(new Mock<ConnectionContext>().Object, TimeSpan.FromSeconds(30), NullLoggerFactory.Instance);
var exception = Assert.Throws<InvalidOperationException>(() => new DefaultHubProtocolResolver(Options.Create(new HubOptions()), new[] {
new JsonHubProtocol(),
new JsonHubProtocol()
}, NullLogger<DefaultHubProtocolResolver>.Instance));
Assert.Equal($"Multiple Hub Protocols with the name '{JsonHubProtocol.ProtocolName}' were registered.", exception.Message);
}
public static IEnumerable<object[]> HubProtocols =>

View File

@ -22,6 +22,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.MsgPack\Microsoft.AspNetCore.SignalR.MsgPack.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR\Microsoft.AspNetCore.SignalR.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Client\Microsoft.AspNetCore.SignalR.Client.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.SignalR.Tests.Utils\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj" />

View File

@ -1,38 +0,0 @@
// 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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MsgPack.Serialization;
using Xunit;
namespace Microsoft.AspNetCore.SignalR.Tests
{
public class SignalRDependencyInjectionExtensionsTests
{
[Fact]
public void JSonSerializerSettingsShouldNotBeNullInOptions()
{
var services = new ServiceCollection();
services.AddOptions();
services.AddSignalR();
var serviceProvider = services.BuildServiceProvider();
var hubOptions = serviceProvider.GetService<IOptions<HubOptions>>();
Assert.NotNull(hubOptions.Value.JsonSerializerSettings);
}
[Fact]
public void MessagePackSerializationContextInOptionsIsSetAndHasDefaultSettings()
{
var services = new ServiceCollection();
services.AddOptions();
services.AddSignalR();
var serviceProvider = services.BuildServiceProvider();
var hubOptions = serviceProvider.GetService<IOptions<HubOptions>>();
var serializationContext = hubOptions.Value.MessagePackSerializationContext;
Assert.NotNull(serializationContext);
Assert.Equal(SerializationMethod.Map, serializationContext.SerializationMethod);
Assert.True(serializationContext.CompatibilityOptions.AllowAsymmetricSerializer);
}
}
}

View File

@ -1,4 +1,4 @@
{
"longRunningTestSeconds": 5,
"longRunningTestSeconds": 30,
"diagnosticMessages": true
}