From b44e9c6a24528c5ebec95da60295b4b74f83f8ec Mon Sep 17 00:00:00 2001 From: Kahbazi Date: Fri, 27 Sep 2019 01:39:42 +0330 Subject: [PATCH] Fix null reference exception for Streaming null object (#14004) --- .../Core/src/Internal/DefaultHubDispatcher.cs | 3 +- .../HubConnectionHandlerTestUtils/Hubs.cs | 30 ++++++++++ .../SignalR/test/HubConnectionHandlerTests.cs | 58 +++++++++++++++++++ .../test/Internal/TypedClientBuilderTests.cs | 4 +- 4 files changed, 92 insertions(+), 3 deletions(-) diff --git a/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs index 71ea5a687e..bc0f8c9fc5 100644 --- a/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs +++ b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs @@ -265,7 +265,8 @@ namespace Microsoft.AspNetCore.SignalR.Internal for (var parameterPointer = 0; parameterPointer < arguments.Length; parameterPointer++) { if (hubMethodInvocationMessage.Arguments.Length > hubInvocationArgumentPointer && - descriptor.OriginalParameterTypes[parameterPointer].IsAssignableFrom(hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer].GetType())) + (hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer] == null || + descriptor.OriginalParameterTypes[parameterPointer].IsAssignableFrom(hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer].GetType()))) { // The types match so it isn't a synthetic argument, just copy it into the arguments array arguments[parameterPointer] = hubMethodInvocationMessage.Arguments[hubInvocationArgumentPointer]; diff --git a/src/SignalR/server/SignalR/test/HubConnectionHandlerTestUtils/Hubs.cs b/src/SignalR/server/SignalR/test/HubConnectionHandlerTestUtils/Hubs.cs index 450697b318..3cae11661a 100644 --- a/src/SignalR/server/SignalR/test/HubConnectionHandlerTestUtils/Hubs.cs +++ b/src/SignalR/server/SignalR/test/HubConnectionHandlerTestUtils/Hubs.cs @@ -933,6 +933,36 @@ namespace Microsoft.AspNetCore.SignalR.Tests return channel.Reader; } + public ChannelReader CancelableStreamNullableParameter(int x, string y, CancellationToken token) + { + var channel = Channel.CreateBounded(10); + + Task.Run(async () => + { + _tcsService.StartedMethod.SetResult(x); + await token.WaitForCancellationAsync(); + channel.Writer.TryComplete(); + _tcsService.EndMethod.SetResult(y); + }); + + return channel.Reader; + } + + public ChannelReader StreamNullableParameter(int x, int? input) + { + var channel = Channel.CreateBounded(10); + + Task.Run(() => + { + _tcsService.StartedMethod.SetResult(x); + channel.Writer.TryComplete(); + _tcsService.EndMethod.SetResult(input); + return Task.CompletedTask; + }); + + return channel.Reader; + } + public ChannelReader CancelableStreamMiddleParameter(int ignore, CancellationToken token, int ignore2) { var channel = Channel.CreateBounded(10); diff --git a/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs b/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs index 9a45f00e68..c88f593db2 100644 --- a/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs +++ b/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs @@ -3591,6 +3591,64 @@ namespace Microsoft.AspNetCore.SignalR.Tests } } + [Fact] + public async Task StreamHubMethodCanAcceptNullableParameter() + { + using (StartVerifiableLog()) + { + var tcsService = new TcsService(); + var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(builder => + { + builder.AddSingleton(tcsService); + }, LoggerFactory); + var connectionHandler = serviceProvider.GetService>(); + + using (var client = new TestClient()) + { + var connectionHandlerTask = await client.ConnectAsync(connectionHandler).OrTimeout(); + + var streamInvocationId = await client.SendStreamInvocationAsync(nameof(LongRunningHub.StreamNullableParameter), 5, null).OrTimeout(); + // Wait for the stream method to start + var firstArgument = await tcsService.StartedMethod.Task.OrTimeout(); + Assert.Equal(5, firstArgument); + + var secondArgument = await tcsService.EndMethod.Task.OrTimeout(); + Assert.Null(secondArgument); + } + } + } + + + [Fact] + public async Task StreamHubMethodCanAcceptNullableParameterWithCancellationToken() + { + using (StartVerifiableLog()) + { + var tcsService = new TcsService(); + var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(builder => + { + builder.AddSingleton(tcsService); + }, LoggerFactory); + var connectionHandler = serviceProvider.GetService>(); + + using (var client = new TestClient()) + { + var connectionHandlerTask = await client.ConnectAsync(connectionHandler).OrTimeout(); + + var streamInvocationId = await client.SendStreamInvocationAsync(nameof(LongRunningHub.CancelableStreamNullableParameter), 5, null).OrTimeout(); + // Wait for the stream method to start + var firstArgument = await tcsService.StartedMethod.Task.OrTimeout(); + Assert.Equal(5, firstArgument); + + // Cancel the stream which should trigger the CancellationToken in the hub method + await client.SendHubMessageAsync(new CancelInvocationMessage(streamInvocationId)).OrTimeout(); + + var secondArgument = await tcsService.EndMethod.Task.OrTimeout(); + Assert.Null(secondArgument); + } + } + } + [Fact] public async Task InvokeHubMethodCannotAcceptCancellationTokenAsArgument() { diff --git a/src/SignalR/server/SignalR/test/Internal/TypedClientBuilderTests.cs b/src/SignalR/server/SignalR/test/Internal/TypedClientBuilderTests.cs index 9f412af8f4..f88820cb78 100644 --- a/src/SignalR/server/SignalR/test/Internal/TypedClientBuilderTests.cs +++ b/src/SignalR/server/SignalR/test/Internal/TypedClientBuilderTests.cs @@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests.Internal send1 => { Assert.Equal("Method", send1.Method); - Assert.Equal(1, send1.Arguments.Length); + Assert.Single(send1.Arguments); Assert.Collection(send1.Arguments, arg1 => Assert.Equal("foo", arg1)); Assert.Equal(cts1.Token, send1.CancellationToken); @@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests.Internal send2 => { Assert.Equal("NoArgumentMethod", send2.Method); - Assert.Equal(0, send2.Arguments.Length); + Assert.Empty(send2.Arguments); Assert.Equal(cts2.Token, send2.CancellationToken); send2.Complete(); });