This commit is contained in:
James Newton-King 2018-04-17 20:39:19 +12:00
commit 8985fee4b5
No known key found for this signature in database
GPG Key ID: 0A66B2F456BF5526
108 changed files with 1307 additions and 440 deletions

View File

@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
var hubConnection = new HubConnectionContext(connection, Timeout.InfiniteTimeSpan, NullLoggerFactory.Instance); var hubConnection = new HubConnectionContext(connection, Timeout.InfiniteTimeSpan, NullLoggerFactory.Instance);
hubConnection.Protocol = protocol; hubConnection.Protocol = protocol;
_hubLifetimeManager.OnConnectedAsync(hubConnection).GetAwaiter().GetResult(); _hubLifetimeManager.OnConnectedAsync(hubConnection).GetAwaiter().GetResult();
_hubLifetimeManager.AddGroupAsync(connection.ConnectionId, TestGroupName).GetAwaiter().GetResult(); _hubLifetimeManager.AddToGroupAsync(connection.ConnectionId, TestGroupName).GetAwaiter().GetResult();
_ = ConsumeAsync(connection.Application); _ = ConsumeAsync(connection.Application);
} }

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks namespace Microsoft.AspNetCore.SignalR.Microbenchmarks

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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
hubConnectionContext.Protocol = jsonHubProtocol; hubConnectionContext.Protocol = jsonHubProtocol;
_hubLifetimeManager.OnConnectedAsync(hubConnectionContext).GetAwaiter().GetResult(); _hubLifetimeManager.OnConnectedAsync(hubConnectionContext).GetAwaiter().GetResult();
_hubLifetimeManager.AddGroupAsync(connectionId, groupName); _hubLifetimeManager.AddToGroupAsync(connectionId, groupName);
} }
} }

View File

@ -76,6 +76,6 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
public class TestConnectionInherentKeepAliveFeature : IConnectionInherentKeepAliveFeature public class TestConnectionInherentKeepAliveFeature : IConnectionInherentKeepAliveFeature
{ {
public TimeSpan KeepAliveInterval { get; } = TimeSpan.Zero; public bool HasInherentKeepAlive { get; } = true;
} }
} }

View File

@ -1,9 +1,11 @@
// 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;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{ {

View File

@ -5,6 +5,11 @@
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Include="..\..\src\Common\BinaryMessageFormatter.cs" Link="BinaryMessageFormatter.cs" />
<Compile Include="..\..\src\Common\BinaryMessageParser.cs" Link="BinaryMessageParser.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Http.Connections.Client\Microsoft.AspNetCore.Http.Connections.Client.csproj" /> <ProjectReference Include="..\..\src\Microsoft.AspNetCore.Http.Connections.Client\Microsoft.AspNetCore.Http.Connections.Client.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Http.Connections\Microsoft.AspNetCore.Http.Connections.csproj" /> <ProjectReference Include="..\..\src\Microsoft.AspNetCore.Http.Connections\Microsoft.AspNetCore.Http.Connections.csproj" />

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http.Connections.Internal; using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks namespace Microsoft.AspNetCore.SignalR.Microbenchmarks

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
private RedisHubLifetimeManager<TestHub> _manager2; private RedisHubLifetimeManager<TestHub> _manager2;
private TestClient[] _clients; private TestClient[] _clients;
private object[] _args; private object[] _args;
private readonly List<string> _excludedIds = new List<string>(); private readonly List<string> _excludedConnectionIds = new List<string>();
private readonly List<string> _sendIds = new List<string>(); private readonly List<string> _sendIds = new List<string>();
private readonly List<string> _groups = new List<string>(); private readonly List<string> _groups = new List<string>();
private readonly List<string> _users = new List<string>(); private readonly List<string> _users = new List<string>();
@ -42,18 +42,18 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
var protocols = GenerateProtocols(ProtocolCount).ToArray(); var protocols = GenerateProtocols(ProtocolCount).ToArray();
var options = Options.Create(new RedisOptions() var options = Options.Create(new RedisOptions()
{ {
Factory = _ => Task.FromResult<IConnectionMultiplexer>(new TestConnectionMultiplexer(server)) ConnectionFactory = _ => Task.FromResult<IConnectionMultiplexer>(new TestConnectionMultiplexer(server))
}); });
var resolver = new DefaultHubProtocolResolver(protocols, NullLogger<DefaultHubProtocolResolver>.Instance); var resolver = new DefaultHubProtocolResolver(protocols, NullLogger<DefaultHubProtocolResolver>.Instance);
_manager1 = new RedisHubLifetimeManager<TestHub>(logger, options, resolver); _manager1 = new RedisHubLifetimeManager<TestHub>(logger, options, resolver);
_manager2 = new RedisHubLifetimeManager<TestHub>(logger, options, resolver); _manager2 = new RedisHubLifetimeManager<TestHub>(logger, options, resolver);
async Task ConnectClient(TestClient client, IHubProtocol protocol, string userId, string group) async Task ConnectClient(TestClient client, IHubProtocol protocol, string userId, string groupName)
{ {
await _manager2.OnConnectedAsync(HubConnectionContextUtils.Create(client.Connection, protocol, userId)); await _manager2.OnConnectedAsync(HubConnectionContextUtils.Create(client.Connection, protocol, userId));
await _manager2.AddGroupAsync(client.Connection.ConnectionId, "Everyone"); await _manager2.AddToGroupAsync(client.Connection.ConnectionId, "Everyone");
await _manager2.AddGroupAsync(client.Connection.ConnectionId, group); await _manager2.AddToGroupAsync(client.Connection.ConnectionId, groupName);
} }
// Connect clients // Connect clients
@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{ {
group = "Evens"; group = "Evens";
user = "EvenUser"; user = "EvenUser";
_excludedIds.Add(_clients[i].Connection.ConnectionId); _excludedConnectionIds.Add(_clients[i].Connection.ConnectionId);
} }
else else
{ {
@ -144,13 +144,13 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
[Benchmark] [Benchmark]
public async Task SendAllExcept() public async Task SendAllExcept()
{ {
await _manager1.SendAllExceptAsync("Test", _args, _excludedIds); await _manager1.SendAllExceptAsync("Test", _args, _excludedConnectionIds);
} }
[Benchmark] [Benchmark]
public async Task SendGroupExcept() public async Task SendGroupExcept()
{ {
await _manager1.SendGroupExceptAsync("Everyone", "Test", _args, _excludedIds); await _manager1.SendGroupExceptAsync("Everyone", "Test", _args, _excludedConnectionIds);
} }
[Benchmark] [Benchmark]

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 System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
@ -14,8 +17,8 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
private RedisGroupCommand _groupCommand; private RedisGroupCommand _groupCommand;
private object[] _args; private object[] _args;
private string _methodName; private string _methodName;
private IReadOnlyList<string> _excludedIdsSmall; private IReadOnlyList<string> _excludedConnectionIdsSmall;
private IReadOnlyList<string> _excludedIdsLarge; private IReadOnlyList<string> _excludedConnectionIdsLarge;
private byte[] _writtenAck; private byte[] _writtenAck;
private byte[] _writtenGroupCommand; private byte[] _writtenGroupCommand;
private byte[] _writtenInvocationNoExclusions; private byte[] _writtenInvocationNoExclusions;
@ -36,14 +39,14 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
_args = Array.Empty<object>(); _args = Array.Empty<object>();
_methodName = "Method"; _methodName = "Method";
_excludedIdsSmall = GenerateIds(2); _excludedConnectionIdsSmall = GenerateIds(2);
_excludedIdsLarge = GenerateIds(20); _excludedConnectionIdsLarge = GenerateIds(20);
_writtenAck = _protocol.WriteAck(42); _writtenAck = _protocol.WriteAck(42);
_writtenGroupCommand = _protocol.WriteGroupCommand(_groupCommand); _writtenGroupCommand = _protocol.WriteGroupCommand(_groupCommand);
_writtenInvocationNoExclusions = _protocol.WriteInvocation(_methodName, _args, null); _writtenInvocationNoExclusions = _protocol.WriteInvocation(_methodName, _args, null);
_writtenInvocationSmallExclusions = _protocol.WriteInvocation(_methodName, _args, _excludedIdsSmall); _writtenInvocationSmallExclusions = _protocol.WriteInvocation(_methodName, _args, _excludedConnectionIdsSmall);
_writtenInvocationLargeExclusions = _protocol.WriteInvocation(_methodName, _args, _excludedIdsLarge); _writtenInvocationLargeExclusions = _protocol.WriteInvocation(_methodName, _args, _excludedConnectionIdsLarge);
} }
[Benchmark] [Benchmark]
@ -67,13 +70,13 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
[Benchmark] [Benchmark]
public void WriteInvocationSmallExclusions() public void WriteInvocationSmallExclusions()
{ {
_protocol.WriteInvocation(_methodName, _args, _excludedIdsSmall); _protocol.WriteInvocation(_methodName, _args, _excludedConnectionIdsSmall);
} }
[Benchmark] [Benchmark]
public void WriteInvocationLargeExclusions() public void WriteInvocationLargeExclusions()
{ {
_protocol.WriteInvocation(_methodName, _args, _excludedIdsLarge); _protocol.WriteInvocation(_methodName, _args, _excludedConnectionIdsLarge);
} }
[Benchmark] [Benchmark]

View File

@ -1,13 +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 Microsoft.AspNetCore.Connections.Features;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks.Shared
{
public class TestConnectionInherentKeepAliveFeature : IConnectionInherentKeepAliveFeature
{
public TimeSpan KeepAliveInterval { get; } = TimeSpan.Zero;
}
}

View File

@ -1,76 +1,76 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Package Versions"> <PropertyGroup Label="Package Versions">
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion> <BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<GoogleProtobufPackageVersion>3.1.0</GoogleProtobufPackageVersion> <GoogleProtobufPackageVersion>3.1.0</GoogleProtobufPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview3-17002</InternalAspNetCoreSdkPackageVersion> <InternalAspNetCoreSdkPackageVersion>2.1.0-preview3-17018</InternalAspNetCoreSdkPackageVersion>
<MessagePackPackageVersion>1.7.3.4</MessagePackPackageVersion> <MessagePackPackageVersion>1.7.3.4</MessagePackPackageVersion>
<MicrosoftAspNetCoreAllPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreAllPackageVersion> <MicrosoftAspNetCoreAllPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreAllPackageVersion>
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion> <MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreAuthenticationCorePackageVersion> <MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
<MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion> <MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>
<MicrosoftAspNetCoreAuthorizationPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreAuthorizationPackageVersion> <MicrosoftAspNetCoreAuthorizationPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreAuthorizationPackageVersion>
<MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreAuthorizationPolicyPackageVersion> <MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion> <MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreConnectionsAbstractionsPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreConnectionsAbstractionsPackageVersion> <MicrosoftAspNetCoreConnectionsAbstractionsPackageVersion>2.1.0-a-preview3-inherent-keep-alive-bool-17670</MicrosoftAspNetCoreConnectionsAbstractionsPackageVersion>
<MicrosoftAspNetCoreCorsPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreCorsPackageVersion> <MicrosoftAspNetCoreCorsPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreCorsPackageVersion>
<MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion> <MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreDiagnosticsPackageVersion> <MicrosoftAspNetCoreDiagnosticsPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreDiagnosticsPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreHostingAbstractionsPackageVersion> <MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreHostingPackageVersion> <MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreHttpAbstractionsPackageVersion> <MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreHttpFeaturesPackageVersion> <MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreHttpPackageVersion> <MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion> <MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion>
<MicrosoftAspNetCoreMvcPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreMvcPackageVersion> <MicrosoftAspNetCoreMvcPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreMvcPackageVersion>
<MicrosoftAspNetCoreRoutingPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreRoutingPackageVersion> <MicrosoftAspNetCoreRoutingPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreRoutingPackageVersion>
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreServerIISIntegrationPackageVersion> <MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
<MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>0.5.0-preview2-32196</MicrosoftAspNetCoreServerIntegrationTestingPackageVersion> <MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>0.5.0-preview2-32233</MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreServerKestrelPackageVersion> <MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.0-a-preview3-inherent-keep-alive-bool-17670</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreStaticFilesPackageVersion> <MicrosoftAspNetCoreStaticFilesPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreStaticFilesPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreTestHostPackageVersion> <MicrosoftAspNetCoreTestHostPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreTestingPackageVersion> <MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebSocketsPackageVersion>2.1.0-preview3-32196</MicrosoftAspNetCoreWebSocketsPackageVersion> <MicrosoftAspNetCoreWebSocketsPackageVersion>2.1.0-preview3-32233</MicrosoftAspNetCoreWebSocketsPackageVersion>
<MicrosoftCSharpPackageVersion>4.5.0-preview2-26406-04</MicrosoftCSharpPackageVersion> <MicrosoftCSharpPackageVersion>4.5.0-preview3-26413-02</MicrosoftCSharpPackageVersion>
<MicrosoftEntityFrameworkCoreDesignPackageVersion>2.1.0-preview3-32196</MicrosoftEntityFrameworkCoreDesignPackageVersion> <MicrosoftEntityFrameworkCoreDesignPackageVersion>2.1.0-preview3-32233</MicrosoftEntityFrameworkCoreDesignPackageVersion>
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.0-preview3-32196</MicrosoftEntityFrameworkCoreSqlServerPackageVersion> <MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.1.0-preview3-32233</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
<MicrosoftEntityFrameworkCoreToolsPackageVersion>2.1.0-preview3-32196</MicrosoftEntityFrameworkCoreToolsPackageVersion> <MicrosoftEntityFrameworkCoreToolsPackageVersion>2.1.0-preview3-32233</MicrosoftEntityFrameworkCoreToolsPackageVersion>
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsBuffersTestingSourcesPackageVersion> <MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion> <MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion> <MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsConfigurationCommandLinePackageVersion> <MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion> <MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsConfigurationUserSecretsPackageVersion> <MicrosoftExtensionsConfigurationUserSecretsPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion> <MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsDependencyInjectionPackageVersion> <MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsLoggingAbstractionsPackageVersion> <MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConfigurationPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsLoggingConfigurationPackageVersion> <MicrosoftExtensionsLoggingConfigurationPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsLoggingConfigurationPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsLoggingConsolePackageVersion> <MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsLoggingDebugPackageVersion> <MicrosoftExtensionsLoggingDebugPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsLoggingPackageVersion> <MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsLoggingTestingPackageVersion> <MicrosoftExtensionsLoggingTestingPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion> <MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsOptionsPackageVersion> <MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsSecurityHelperSourcesPackageVersion> <MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsValueStopwatchSourcesPackageVersion> <MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersSourcesPackageVersion>2.1.0-preview3-32196</MicrosoftExtensionsWebEncodersSourcesPackageVersion> <MicrosoftExtensionsWebEncodersSourcesPackageVersion>2.1.0-preview3-32233</MicrosoftExtensionsWebEncodersSourcesPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion> <MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26406-04</MicrosoftNETCoreApp21PackageVersion> <MicrosoftNETCoreApp21PackageVersion>2.1.0-preview3-26413-05</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion> <MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MoqPackageVersion>4.7.49</MoqPackageVersion> <MoqPackageVersion>4.7.49</MoqPackageVersion>
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion> <NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
<StackExchangeRedisStrongNamePackageVersion>1.2.4</StackExchangeRedisStrongNamePackageVersion> <StackExchangeRedisStrongNamePackageVersion>1.2.4</StackExchangeRedisStrongNamePackageVersion>
<SystemBuffersPackageVersion>4.5.0-preview2-26406-04</SystemBuffersPackageVersion> <SystemBuffersPackageVersion>4.5.0-preview3-26413-02</SystemBuffersPackageVersion>
<SystemIOPipelinesPackageVersion>4.5.0-preview2-26406-04</SystemIOPipelinesPackageVersion> <SystemIOPipelinesPackageVersion>4.5.0-preview3-26413-02</SystemIOPipelinesPackageVersion>
<SystemMemoryPackageVersion>4.5.0-preview2-26406-04</SystemMemoryPackageVersion> <SystemMemoryPackageVersion>4.5.0-preview3-26413-02</SystemMemoryPackageVersion>
<SystemNumericsVectorsPackageVersion>4.5.0-preview2-26406-04</SystemNumericsVectorsPackageVersion> <SystemNumericsVectorsPackageVersion>4.5.0-preview3-26413-02</SystemNumericsVectorsPackageVersion>
<SystemReactiveLinqPackageVersion>3.1.1</SystemReactiveLinqPackageVersion> <SystemReactiveLinqPackageVersion>3.1.1</SystemReactiveLinqPackageVersion>
<SystemReflectionEmitPackageVersion>4.3.0</SystemReflectionEmitPackageVersion> <SystemReflectionEmitPackageVersion>4.3.0</SystemReflectionEmitPackageVersion>
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.5.0-preview2-26406-04</SystemRuntimeCompilerServicesUnsafePackageVersion> <SystemRuntimeCompilerServicesUnsafePackageVersion>4.5.0-preview3-26413-02</SystemRuntimeCompilerServicesUnsafePackageVersion>
<SystemThreadingChannelsPackageVersion>4.5.0-preview2-26406-04</SystemThreadingChannelsPackageVersion> <SystemThreadingChannelsPackageVersion>4.5.0-preview3-26413-02</SystemThreadingChannelsPackageVersion>
<SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview2-26406-04</SystemThreadingTasksExtensionsPackageVersion> <SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview3-26413-02</SystemThreadingTasksExtensionsPackageVersion>
<XunitPackageVersion>2.3.1</XunitPackageVersion> <XunitPackageVersion>2.3.1</XunitPackageVersion>
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion> <XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
</PropertyGroup> </PropertyGroup>

View File

@ -1,6 +1,6 @@
{ {
"name": "@aspnet/signalr-protocol-msgpack", "name": "@aspnet/signalr-protocol-msgpack",
"version": "1.0.0-preview3-t000", "version": "1.1.0-preview1-t000",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@aspnet/signalr-protocol-msgpack", "name": "@aspnet/signalr-protocol-msgpack",
"version": "1.0.0-preview3-t000", "version": "1.1.0-preview1-t000",
"description": "MsgPack Protocol support for ASP.NET Core SignalR", "description": "MsgPack Protocol support for ASP.NET Core SignalR",
"main": "./dist/cjs/index.js", "main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js", "module": "./dist/esm/index.js",

View File

@ -1,6 +1,6 @@
{ {
"name": "@aspnet/signalr", "name": "@aspnet/signalr",
"version": "1.0.0-preview3-t000", "version": "1.1.0-preview1-t000",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@aspnet/signalr", "name": "@aspnet/signalr",
"version": "1.0.0-preview3-t000", "version": "1.1.0-preview1-t000",
"description": "ASP.NET Core SignalR Client", "description": "ASP.NET Core SignalR Client",
"main": "./dist/cjs/index.js", "main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js", "module": "./dist/esm/index.js",

View File

@ -288,13 +288,73 @@ describe("HttpConnection", () => {
} }
}); });
it("sets inherentKeepAlive feature when using LongPolling", async (done) => { it("authorization header removed when token factory returns null and using LongPolling", async (done) => {
const availableTransport = { transport: "LongPolling", transferFormats: ["Text"] }; const availableTransport = { transport: "LongPolling", transferFormats: ["Text"] };
var httpClientGetCount = 0;
var accessTokenFactoryCount = 0;
const options: IHttpConnectionOptions = { const options: IHttpConnectionOptions = {
...commonOptions, ...commonOptions,
httpClient: new TestHttpClient() httpClient: new TestHttpClient()
.on("POST", (r) => ({ connectionId: "42", availableTransports: [availableTransport] })), .on("POST", (r) => ({ connectionId: "42", availableTransports: [availableTransport] }))
.on("GET", (r) => {
httpClientGetCount++;
const authorizationValue = r.headers["Authorization"];
if (httpClientGetCount == 1) {
if (authorizationValue) {
fail("First long poll request should have a authorization header.");
}
// First long polling request must succeed so start completes
return "";
} else {
// Check second long polling request has its header removed
if (authorizationValue) {
fail("Second long poll request should have no authorization header.");
}
throw new Error("fail");
}
}),
accessTokenFactory: () => {
accessTokenFactoryCount++;
if (accessTokenFactoryCount == 1) {
return "A token value";
} else {
// Return a null value after the first call to test the header being removed
return null;
}
},
} as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options);
try {
await connection.start(TransferFormat.Text);
expect(httpClientGetCount).toBeGreaterThanOrEqual(2);
expect(accessTokenFactoryCount).toBeGreaterThanOrEqual(2);
done();
} catch (e) {
fail(e);
done();
}
});
it("sets inherentKeepAlive feature when using LongPolling", async (done) => {
const availableTransport = { transport: "LongPolling", transferFormats: ["Text"] };
var httpClientGetCount = 0;
const options: IHttpConnectionOptions = {
...commonOptions,
httpClient: new TestHttpClient()
.on("POST", (r) => ({ connectionId: "42", availableTransports: [availableTransport] }))
.on("GET", (r) => {
httpClientGetCount++;
if (httpClientGetCount == 1) {
// First long polling request must succeed so start completes
return "";
} else {
throw new Error("fail");
}
}),
} as IHttpConnectionOptions; } as IHttpConnectionOptions;
const connection = new HttpConnection("http://tempuri.org", options); const connection = new HttpConnection("http://tempuri.org", options);

View File

@ -31,7 +31,7 @@ export class LongPollingTransport implements ITransport {
this.logMessageContent = logMessageContent; this.logMessageContent = logMessageContent;
} }
public connect(url: string, transferFormat: TransferFormat): Promise<void> { public async connect(url: string, transferFormat: TransferFormat): Promise<void> {
Arg.isRequired(url, "url"); Arg.isRequired(url, "url");
Arg.isRequired(transferFormat, "transferFormat"); Arg.isRequired(transferFormat, "transferFormat");
Arg.isIn(transferFormat, TransferFormat, "transferFormat"); Arg.isIn(transferFormat, TransferFormat, "transferFormat");
@ -45,13 +45,6 @@ export class LongPollingTransport implements ITransport {
throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported."); throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");
} }
this.poll(this.url, transferFormat);
return Promise.resolve();
}
private async poll(url: string, transferFormat: TransferFormat): Promise<void> {
this.running = true;
const pollOptions: HttpRequest = { const pollOptions: HttpRequest = {
abortSignal: this.pollAbort.signal, abortSignal: this.pollAbort.signal,
headers: {}, headers: {},
@ -62,15 +55,49 @@ export class LongPollingTransport implements ITransport {
pollOptions.responseType = "arraybuffer"; pollOptions.responseType = "arraybuffer";
} }
const token = await this.accessTokenFactory();
this.updateHeaderToken(pollOptions, token);
let closeError: Error; let closeError: Error;
// Make initial long polling request
// Server uses first long polling request to finish initializing connection and it returns without data
const pollUrl = `${url}&_=${Date.now()}`;
this.logger.log(LogLevel.Trace, `(LongPolling transport) polling: ${pollUrl}`);
const response = await this.httpClient.get(pollUrl, pollOptions);
if (response.statusCode !== 200) {
this.logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}`);
// Mark running as false so that the poll immediately ends and runs the close logic
closeError = new HttpError(response.statusText, response.statusCode);
this.running = false;
} else {
this.running = true;
}
this.poll(this.url, pollOptions, closeError);
return Promise.resolve();
}
private updateHeaderToken(request: HttpRequest, token: string) {
if (token) {
// tslint:disable-next-line:no-string-literal
request.headers["Authorization"] = `Bearer ${token}`;
return;
}
// tslint:disable-next-line:no-string-literal
if (request.headers["Authorization"]) {
// tslint:disable-next-line:no-string-literal
delete request.headers["Authorization"];
}
}
private async poll(url: string, pollOptions: HttpRequest, closeError: Error): Promise<void> {
try { try {
while (this.running) { while (this.running) {
// We have to get the access token on each poll, in case it changes // We have to get the access token on each poll, in case it changes
const token = await this.accessTokenFactory(); const token = await this.accessTokenFactory();
if (token) { this.updateHeaderToken(pollOptions, token);
// tslint:disable-next-line:no-string-literal
pollOptions.headers["Authorization"] = `Bearer ${token}`;
}
try { try {
const pollUrl = `${url}&_=${Date.now()}`; const pollUrl = `${url}&_=${Date.now()}`;
@ -142,14 +169,11 @@ export class LongPollingTransport implements ITransport {
this.running = false; this.running = false;
this.logger.log(LogLevel.Trace, `(LongPolling transport) sending DELETE request to ${this.url}.`); this.logger.log(LogLevel.Trace, `(LongPolling transport) sending DELETE request to ${this.url}.`);
const deleteOptions: HttpRequest = {}; const deleteOptions: HttpRequest = {
headers: {},
};
const token = await this.accessTokenFactory(); const token = await this.accessTokenFactory();
if (token) { this.updateHeaderToken(deleteOptions, token);
// tslint:disable-next-line:no-string-literal
deleteOptions.headers = {
["Authorization"]: `Bearer ${token}`,
};
}
const response = await this.httpClient.delete(this.url, deleteOptions); const response = await this.httpClient.delete(this.url, deleteOptions);
this.logger.log(LogLevel.Trace, "(LongPolling transport) DELETE request accepted."); this.logger.log(LogLevel.Trace, "(LongPolling transport) DELETE request accepted.");

View File

@ -1,2 +1,2 @@
version:2.1.0-preview3-17002 version:2.1.0-preview3-17018
commithash:b8e4e6ab104adc94c0719bb74229870e9b584a7f commithash:af264ca131f212b5ba8aafbc5110fc0fc510a2be

View File

@ -143,9 +143,9 @@ namespace ChatSample
return _wrappedHubLifetimeManager.SendAllAsync(methodName, args); return _wrappedHubLifetimeManager.SendAllAsync(methodName, args);
} }
public override Task SendAllExceptAsync(string methodName, object[] args, IReadOnlyList<string> excludedIds) public override Task SendAllExceptAsync(string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds)
{ {
return _wrappedHubLifetimeManager.SendAllExceptAsync(methodName, args, excludedIds); return _wrappedHubLifetimeManager.SendAllExceptAsync(methodName, args, excludedConnectionIds);
} }
public override Task SendConnectionAsync(string connectionId, string methodName, object[] args) public override Task SendConnectionAsync(string connectionId, string methodName, object[] args)
@ -173,19 +173,19 @@ namespace ChatSample
return _wrappedHubLifetimeManager.SendUserAsync(userId, methodName, args); return _wrappedHubLifetimeManager.SendUserAsync(userId, methodName, args);
} }
public override Task AddGroupAsync(string connectionId, string groupName) public override Task AddToGroupAsync(string connectionId, string groupName)
{ {
return _wrappedHubLifetimeManager.AddGroupAsync(connectionId, groupName); return _wrappedHubLifetimeManager.AddToGroupAsync(connectionId, groupName);
} }
public override Task RemoveGroupAsync(string connectionId, string groupName) public override Task RemoveFromGroupAsync(string connectionId, string groupName)
{ {
return _wrappedHubLifetimeManager.RemoveGroupAsync(connectionId, groupName); return _wrappedHubLifetimeManager.RemoveFromGroupAsync(connectionId, groupName);
} }
public override Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList<string> excludedIds) public override Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds)
{ {
return _wrappedHubLifetimeManager.SendGroupExceptAsync(groupName, methodName, args, excludedIds); return _wrappedHubLifetimeManager.SendGroupExceptAsync(groupName, methodName, args, excludedConnectionIds);
} }
public override Task SendUsersAsync(IReadOnlyList<string> userIds, string methodName, object[] args) public override Task SendUsersAsync(IReadOnlyList<string> userIds, string methodName, object[] args)

View File

@ -65,7 +65,7 @@ namespace ChatSample
{ {
// TODO: handle connection failures // TODO: handle connection failures
_redisConnection = await ConnectToRedis(_options, _logger); _redisConnection = await ConnectToRedis(_options, _logger);
_redisDatabase = _redisConnection.GetDatabase(_options.Options.DefaultDatabase.GetValueOrDefault()); _redisDatabase = _redisConnection.GetDatabase(_options.Configuration.DefaultDatabase.GetValueOrDefault());
// Register connection // Register connection
_redisDatabase.SetAdd(ServerIndexRedisKey, ServerId); _redisDatabase.SetAdd(ServerIndexRedisKey, ServerId);
@ -103,14 +103,14 @@ namespace ChatSample
private static async Task<IConnectionMultiplexer> ConnectToRedis(RedisOptions options, ILogger logger) private static async Task<IConnectionMultiplexer> ConnectToRedis(RedisOptions options, ILogger logger)
{ {
var loggerTextWriter = new LoggerTextWriter(logger); var loggerTextWriter = new LoggerTextWriter(logger);
if (options.Factory != null) if (options.ConnectionFactory != null)
{ {
return await options.Factory(loggerTextWriter); return await options.ConnectionFactory(loggerTextWriter);
} }
if (options.Options.EndPoints.Any()) if (options.Configuration.EndPoints.Any())
{ {
return await ConnectionMultiplexer.ConnectAsync(options.Options, loggerTextWriter); return await ConnectionMultiplexer.ConnectAsync(options.Configuration, loggerTextWriter);
} }
var configurationOptions = new ConfigurationOptions(); var configurationOptions = new ConfigurationOptions();

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Pipelines; using System.IO.Pipelines;
@ -40,7 +40,8 @@ namespace ClientSample
public override string ConnectionId { get; set; } = Guid.NewGuid().ToString(); public override string ConnectionId { get; set; } = Guid.NewGuid().ToString();
public override IDictionary<object, object> Items { get; set; } = new ConnectionItems(); public override IDictionary<object, object> Items { get; set; } = new ConnectionItems();
public TimeSpan KeepAliveInterval => Timeout.InfiniteTimeSpan; // We claim to have inherent keep-alive so the client doesn't kill the connection when it hasn't seen ping frames.
public bool HasInherentKeepAlive { get; } = true;
public Task DisposeAsync() public Task DisposeAsync()
{ {

View File

@ -46,7 +46,7 @@ namespace SignalRSamples.Hubs
public async Task JoinGroup(string groupName) public async Task JoinGroup(string groupName)
{ {
await Groups.AddAsync(Context.ConnectionId, groupName); await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} joined {groupName}"); await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} joined {groupName}");
} }
@ -55,7 +55,7 @@ namespace SignalRSamples.Hubs
{ {
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} left {groupName}"); await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} left {groupName}");
await Groups.RemoveAsync(Context.ConnectionId, groupName); await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
} }
public Task Echo(string message) public Task Echo(string message)

View File

@ -41,7 +41,7 @@ namespace SignalRSamples.Hubs
public async Task JoinGroup(string groupName) public async Task JoinGroup(string groupName)
{ {
await Groups.AddAsync(Context.ConnectionId, groupName); await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).Send($"{Context.ConnectionId} joined {groupName}"); await Clients.Group(groupName).Send($"{Context.ConnectionId} joined {groupName}");
} }
@ -50,7 +50,7 @@ namespace SignalRSamples.Hubs
{ {
await Clients.Group(groupName).Send($"{Context.ConnectionId} left {groupName}"); await Clients.Group(groupName).Send($"{Context.ConnectionId} left {groupName}");
await Groups.RemoveAsync(Context.ConnectionId, groupName); await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
} }
public Task Echo(string message) public Task Echo(string message)

View File

@ -41,7 +41,7 @@ namespace SignalRSamples.Hubs
public async Task JoinGroup(string groupName) public async Task JoinGroup(string groupName)
{ {
await Groups.AddAsync(Context.ConnectionId, groupName); await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).Send($"{Context.ConnectionId} joined {groupName}"); await Clients.Group(groupName).Send($"{Context.ConnectionId} joined {groupName}");
} }
@ -50,7 +50,7 @@ namespace SignalRSamples.Hubs
{ {
await Clients.Group(groupName).Send($"{Context.ConnectionId} left {groupName}"); await Clients.Group(groupName).Send($"{Context.ConnectionId} left {groupName}");
await Groups.RemoveAsync(Context.ConnectionId, groupName); await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
} }
public Task Echo(string message) public Task Echo(string message)

View File

@ -4,9 +4,9 @@
using System; using System;
using System.Buffers; using System.Buffers;
namespace Microsoft.AspNetCore.SignalR.Internal.Formatters namespace Microsoft.AspNetCore.Internal
{ {
public static class BinaryMessageFormatter internal static class BinaryMessageFormatter
{ {
public static void WriteLengthPrefix(long length, IBufferWriter<byte> output) public static void WriteLengthPrefix(long length, IBufferWriter<byte> output)
{ {

View File

@ -4,9 +4,9 @@
using System; using System;
using System.Buffers; using System.Buffers;
namespace Microsoft.AspNetCore.SignalR.Internal.Formatters namespace Microsoft.AspNetCore.Internal
{ {
public static class BinaryMessageParser internal static class BinaryMessageParser
{ {
private const int MaxLengthPrefixSize = 5; private const int MaxLengthPrefixSize = 5;

View File

@ -4,9 +4,9 @@
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
namespace Microsoft.AspNetCore.SignalR.Internal.Formatters namespace Microsoft.AspNetCore.Internal
{ {
public static class TextMessageFormatter internal static class TextMessageFormatter
{ {
// This record separator is supposed to be used only for JSON payloads where 0x1e character // This record separator is supposed to be used only for JSON payloads where 0x1e character
// will not occur (is not a valid character) and therefore it is safe to not escape it // will not occur (is not a valid character) and therefore it is safe to not escape it

View File

@ -4,9 +4,9 @@
using System; using System;
using System.Buffers; using System.Buffers;
namespace Microsoft.AspNetCore.SignalR.Internal.Formatters namespace Microsoft.AspNetCore.Internal
{ {
public static class TextMessageParser internal static class TextMessageParser
{ {
public static bool TryParseMessage(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> payload) public static bool TryParseMessage(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> payload)
{ {

View File

@ -12,15 +12,13 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http.Connections.Client.Internal; using Microsoft.AspNetCore.Http.Connections.Client.Internal;
using Microsoft.AspNetCore.Http.Connections.Features;
using Microsoft.AspNetCore.Http.Connections.Internal;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Http.Connections.Client namespace Microsoft.AspNetCore.Http.Connections.Client
{ {
public partial class HttpConnection : ConnectionContext public partial class HttpConnection : ConnectionContext, IConnectionInherentKeepAliveFeature
{ {
private static readonly TimeSpan HttpClientTimeout = TimeSpan.FromSeconds(120); private static readonly TimeSpan HttpClientTimeout = TimeSpan.FromSeconds(120);
#if !NETCOREAPP2_1 #if !NETCOREAPP2_1
@ -32,6 +30,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(1, 1);
private bool _started; private bool _started;
private bool _disposed; private bool _disposed;
private bool _hasInherentKeepAlive;
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private readonly HttpConnectionOptions _httpConnectionOptions; private readonly HttpConnectionOptions _httpConnectionOptions;
@ -60,6 +59,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
public override string ConnectionId { get; set; } public override string ConnectionId { get; set; }
public override IDictionary<object, object> Items { get; set; } = new ConnectionItems(); public override IDictionary<object, object> Items { get; set; } = new ConnectionItems();
bool IConnectionInherentKeepAliveFeature.HasInherentKeepAlive => _hasInherentKeepAlive;
public HttpConnection(Uri url) public HttpConnection(Uri url)
: this(url, HttpTransports.All) : this(url, HttpTransports.All)
{ } { }
@ -103,6 +104,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
_transportFactory = new DefaultTransportFactory(httpConnectionOptions.Transports, _loggerFactory, _httpClient, httpConnectionOptions); _transportFactory = new DefaultTransportFactory(httpConnectionOptions.Transports, _loggerFactory, _httpClient, httpConnectionOptions);
_logScope = new ConnectionLogScope(); _logScope = new ConnectionLogScope();
_scopeDisposable = _logger.BeginScope(_logScope); _scopeDisposable = _logger.BeginScope(_logScope);
Features.Set<IConnectionInherentKeepAliveFeature>(this);
} }
// Used by unit tests // Used by unit tests
@ -348,11 +351,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
throw; throw;
} }
if (transportType == HttpTransportType.LongPolling) // Disable keep alives for long polling
{ _hasInherentKeepAlive = transportType == HttpTransportType.LongPolling;
// Disable keep alives for long polling
Features.Set<IConnectionInherentKeepAliveFeature>(new ConnectionInherentKeepAliveFeature(_httpClient.Timeout));
}
// We successfully started, set the transport properties (we don't want to set these until the transport is definitely running). // We successfully started, set the transport properties (we don't want to set these until the transport is definitely running).
_transport = transport; _transport = transport;

View File

@ -8,7 +8,6 @@ using System.Net.Http;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Connections.Internal;
namespace Microsoft.AspNetCore.Http.Connections.Client namespace Microsoft.AspNetCore.Http.Connections.Client
{ {

View File

@ -5,7 +5,6 @@ using System;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.AspNetCore.Http.Connections.Client;
using Microsoft.AspNetCore.Http.Connections.Client.Internal; using Microsoft.AspNetCore.Http.Connections.Client.Internal;
using Microsoft.AspNetCore.Http.Connections.Internal;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Http.Connections.Client.Internal namespace Microsoft.AspNetCore.Http.Connections.Client.Internal

View File

@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
_logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<LongPollingTransport>(); _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<LongPollingTransport>();
} }
public Task StartAsync(Uri url, TransferFormat transferFormat) public async Task StartAsync(Uri url, TransferFormat transferFormat)
{ {
if (transferFormat != TransferFormat.Binary && transferFormat != TransferFormat.Text) if (transferFormat != TransferFormat.Binary && transferFormat != TransferFormat.Text)
{ {
@ -49,6 +49,14 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
Log.StartTransport(_logger, transferFormat); Log.StartTransport(_logger, transferFormat);
// Make initial long polling request
// Server uses first long polling request to finish initializing connection and it returns without data
var request = new HttpRequestMessage(HttpMethod.Get, url);
using (var response = await _httpClient.SendAsync(request))
{
response.EnsureSuccessStatusCode();
}
// Create the pipe pair (Application's writer is connected to Transport's reader, and vice versa) // Create the pipe pair (Application's writer is connected to Transport's reader, and vice versa)
var options = ClientPipeOptions.DefaultOptions; var options = ClientPipeOptions.DefaultOptions;
var pair = DuplexPipe.CreateConnectionPair(options, options); var pair = DuplexPipe.CreateConnectionPair(options, options);
@ -57,8 +65,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
_application = pair.Application; _application = pair.Application;
Running = ProcessAsync(url); Running = ProcessAsync(url);
return Task.CompletedTask;
} }
private async Task ProcessAsync(Uri url) private async Task ProcessAsync(Uri url)
@ -105,6 +111,12 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
{ {
Log.TransportStopping(_logger); Log.TransportStopping(_logger);
if (_application == null)
{
// We never started
return;
}
_application.Input.CancelPendingRead(); _application.Input.CancelPendingRead();
try try

View File

@ -11,7 +11,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Http.Connections.Client namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
{ {
internal static class SendUtils internal static class SendUtils
{ {

View File

@ -207,6 +207,12 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
{ {
Log.TransportStopping(_logger); Log.TransportStopping(_logger);
if (_application == null)
{
// We never started
return;
}
_transport.Output.Complete(); _transport.Output.Complete();
_transport.Input.Complete(); _transport.Input.Complete();

View File

@ -3,7 +3,7 @@
using System; using System;
namespace Microsoft.AspNetCore.Http.Connections.Client namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
{ {
internal static class Utils internal static class Utils
{ {

View File

@ -373,6 +373,12 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
{ {
Log.TransportStopping(_logger); Log.TransportStopping(_logger);
if (_application == null)
{
// We never started
return;
}
_transport.Output.Complete(); _transport.Output.Complete();
_transport.Input.Complete(); _transport.Input.Complete();

View File

@ -3,11 +3,11 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Microsoft.AspNetCore.Http.Connections.Internal namespace Microsoft.AspNetCore.Http.Connections
{ {
public class AvailableTransport public class AvailableTransport
{ {
public string Transport { get; set; } public string Transport { get; set; }
public List<string> TransferFormats { get; set; } public IList<string> TransferFormats { get; set; }
} }
} }

View File

@ -1,18 +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 Microsoft.AspNetCore.Connections.Features;
namespace Microsoft.AspNetCore.Http.Connections.Features
{
public class ConnectionInherentKeepAliveFeature : IConnectionInherentKeepAliveFeature
{
public TimeSpan KeepAliveInterval { get; }
public ConnectionInherentKeepAliveFeature(TimeSpan keepAliveInterval)
{
KeepAliveInterval = keepAliveInterval;
}
}
}

View File

@ -1,7 +1,7 @@
// 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Http.Connections.Internal namespace Microsoft.AspNetCore.Http.Connections
{ {
public static class HttpTransports public static class HttpTransports
{ {

View File

@ -8,7 +8,7 @@ using System.IO;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Http.Connections.Internal namespace Microsoft.AspNetCore.Http.Connections
{ {
public static class NegotiateProtocol public static class NegotiateProtocol
{ {

View File

@ -3,11 +3,11 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Microsoft.AspNetCore.Http.Connections.Internal namespace Microsoft.AspNetCore.Http.Connections
{ {
public class NegotiationResponse public class NegotiationResponse
{ {
public string ConnectionId { get; set; } public string ConnectionId { get; set; }
public List<AvailableTransport> AvailableTransports { get; set; } public IList<AvailableTransport> AvailableTransports { get; set; }
} }
} }

View File

@ -24,7 +24,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
IConnectionHeartbeatFeature, IConnectionHeartbeatFeature,
ITransferFormatFeature, ITransferFormatFeature,
IHttpContextFeature, IHttpContextFeature,
IHttpTransportFeature IHttpTransportFeature,
IConnectionInherentKeepAliveFeature
{ {
private readonly object _itemsLock = new object(); private readonly object _itemsLock = new object();
private readonly object _heartbeatLock = new object(); private readonly object _heartbeatLock = new object();
@ -65,6 +66,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
Features.Set<ITransferFormatFeature>(this); Features.Set<ITransferFormatFeature>(this);
Features.Set<IHttpContextFeature>(this); Features.Set<IHttpContextFeature>(this);
Features.Set<IHttpTransportFeature>(this); Features.Set<IHttpTransportFeature>(this);
Features.Set<IConnectionInherentKeepAliveFeature>(this);
} }
public HttpConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application, ILogger logger = null) public HttpConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application, ILogger logger = null)
@ -97,6 +99,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
public ClaimsPrincipal User { get; set; } public ClaimsPrincipal User { get; set; }
public bool HasInherentKeepAlive { get; set; }
public override IDictionary<object, object> Items public override IDictionary<object, object> Items
{ {
get get

View File

@ -210,7 +210,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
using (connection.Cancellation) using (connection.Cancellation)
{ {
// Cancel the previous request // Cancel the previous request
connection.Cancellation.Cancel(); connection.Cancellation?.Cancel();
// Wait for the previous request to drain // Wait for the previous request to drain
await connection.TransportTask; await connection.TransportTask;
@ -228,29 +228,38 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
Log.EstablishedConnection(_logger); Log.EstablishedConnection(_logger);
connection.ApplicationTask = ExecuteApplication(connectionDelegate, connection); connection.ApplicationTask = ExecuteApplication(connectionDelegate, connection);
context.Response.ContentType = "application/octet-stream";
// This request has no content
context.Response.ContentLength = 0;
// On the first poll, we flush the response immediately to mark the poll as "initialized" so future
// requests can be made safely
connection.TransportTask = context.Response.Body.FlushAsync();
} }
else else
{ {
Log.ResumingConnection(_logger); Log.ResumingConnection(_logger);
// REVIEW: Performance of this isn't great as this does a bunch of per request allocations
connection.Cancellation = new CancellationTokenSource();
var timeoutSource = new CancellationTokenSource();
var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(connection.Cancellation.Token, context.RequestAborted, timeoutSource.Token);
// Dispose these tokens when the request is over
context.Response.RegisterForDispose(timeoutSource);
context.Response.RegisterForDispose(tokenSource);
var longPolling = new LongPollingTransport(timeoutSource.Token, connection.Application.Input, _loggerFactory);
// Start the transport
connection.TransportTask = longPolling.ProcessRequestAsync(context, tokenSource.Token);
// Start the timeout after we return from creating the transport task
timeoutSource.CancelAfter(options.LongPolling.PollTimeout);
} }
// REVIEW: Performance of this isn't great as this does a bunch of per request allocations
connection.Cancellation = new CancellationTokenSource();
var timeoutSource = new CancellationTokenSource();
var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(connection.Cancellation.Token, context.RequestAborted, timeoutSource.Token);
// Dispose these tokens when the request is over
context.Response.RegisterForDispose(timeoutSource);
context.Response.RegisterForDispose(tokenSource);
var longPolling = new LongPollingTransport(timeoutSource.Token, connection.Application.Input, _loggerFactory);
// Start the transport
connection.TransportTask = longPolling.ProcessRequestAsync(context, tokenSource.Token);
// Start the timeout after we return from creating the transport task
timeoutSource.CancelAfter(options.LongPolling.PollTimeout);
} }
finally finally
{ {
@ -302,7 +311,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
connection.Status = HttpConnectionStatus.Inactive; connection.Status = HttpConnectionStatus.Inactive;
// Dispose the cancellation token // Dispose the cancellation token
connection.Cancellation.Dispose(); connection.Cancellation?.Dispose();
connection.Cancellation = null; connection.Cancellation = null;
} }
@ -366,10 +375,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
private async Task ExecuteApplication(ConnectionDelegate connectionDelegate, HttpConnectionContext connection) private async Task ExecuteApplication(ConnectionDelegate connectionDelegate, HttpConnectionContext connection)
{ {
// Verify some initialization invariants // Verify some initialization invariants
// We want to be positive that the IConnectionInherentKeepAliveFeature is initialized before invoking the application, if the long polling transport is in use.
Debug.Assert(connection.TransportType != HttpTransportType.None, "Transport has not been initialized yet"); Debug.Assert(connection.TransportType != HttpTransportType.None, "Transport has not been initialized yet");
Debug.Assert(connection.TransportType != HttpTransportType.LongPolling ||
connection.Features.Get<IConnectionInherentKeepAliveFeature>() != null, "Long-polling transport is in use but IConnectionInherentKeepAliveFeature as not configured");
// Jump onto the thread pool thread so blocking user code doesn't block the setup of the // Jump onto the thread pool thread so blocking user code doesn't block the setup of the
// connection and transport // connection and transport
@ -555,7 +561,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// Configure transport-specific features. // Configure transport-specific features.
if (transportType == HttpTransportType.LongPolling) if (transportType == HttpTransportType.LongPolling)
{ {
connection.Features.Set<IConnectionInherentKeepAliveFeature>(new ConnectionInherentKeepAliveFeature(options.LongPolling.PollTimeout)); connection.HasInherentKeepAlive = true;
// For long polling, the requests come and go but the connection is still alive. // For long polling, the requests come and go but the connection is still alive.
// To make the IHttpContextFeature work well, we make a copy of the relevant properties // To make the IHttpContextFeature work well, we make a copy of the relevant properties

View File

@ -13,6 +13,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Client.Internal;
using Microsoft.AspNetCore.SignalR.Internal; using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -719,7 +720,9 @@ namespace Microsoft.AspNetCore.SignalR.Client
{ {
// Check if we need keep-alive // Check if we need keep-alive
Timer timeoutTimer = null; Timer timeoutTimer = null;
if (connectionState.Connection.Features.Get<IConnectionInherentKeepAliveFeature>() == null)
// We use '!== true' because it could be null, which we treat as false.
if (connectionState.Connection.Features.Get<IConnectionInherentKeepAliveFeature>()?.HasInherentKeepAlive != true)
{ {
Log.StartingServerTimeoutTimer(_logger, ServerTimeout); Log.StartingServerTimeoutTimer(_logger, ServerTimeout);
timeoutTimer = new Timer( timeoutTimer = new Timer(

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.SignalR.Client namespace Microsoft.AspNetCore.SignalR.Client.Internal
{ {
internal abstract class InvocationRequest : IDisposable internal abstract class InvocationRequest : IDisposable
{ {

View File

@ -2,8 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Runtime.Serialization;
namespace Microsoft.AspNetCore.SignalR.Client namespace Microsoft.AspNetCore.SignalR
{ {
[Serializable] [Serializable]
public class HubException : Exception public class HubException : Exception
@ -19,5 +20,9 @@ namespace Microsoft.AspNetCore.SignalR.Client
public HubException(string message, Exception innerException) : base(message, innerException) public HubException(string message, Exception innerException) : base(message, innerException)
{ {
} }
public HubException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
} }
} }

View File

@ -10,6 +10,8 @@
<ItemGroup> <ItemGroup>
<Compile Include="..\Common\JsonUtils.cs" Link="Internal\JsonUtils.cs" /> <Compile Include="..\Common\JsonUtils.cs" Link="Internal\JsonUtils.cs" />
<Compile Include="..\Common\MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" /> <Compile Include="..\Common\MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" />
<Compile Include="..\Common\TextMessageFormatter.cs" Link="Internal\TextMessageFormatter.cs" />
<Compile Include="..\Common\TextMessageParser.cs" Link="Internal\TextMessageParser.cs" />
<Compile Include="..\Common\Utf8BufferTextReader.cs" Link="Internal\Utf8BufferTextReader.cs" /> <Compile Include="..\Common\Utf8BufferTextReader.cs" Link="Internal\Utf8BufferTextReader.cs" />
<Compile Include="..\Common\Utf8BufferTextWriter.cs" Link="Internal\Utf8BufferTextWriter.cs" /> <Compile Include="..\Common\Utf8BufferTextWriter.cs" Link="Internal\Utf8BufferTextWriter.cs" />
</ItemGroup> </ItemGroup>

View File

@ -6,7 +6,6 @@ using System.Buffers;
using System.IO; using System.IO;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal; using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Microsoft.AspNetCore.SignalR.Protocol namespace Microsoft.AspNetCore.SignalR.Protocol

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace Microsoft.AspNetCore.SignalR namespace Microsoft.AspNetCore.SignalR
{ {
public static class IClientProxyExtensions public static class ClientProxyExtensions
{ {
/// <summary> /// <summary>
/// Invokes a method on the connection(s) represented by the <see cref="IClientProxy"/> instance. /// Invokes a method on the connection(s) represented by the <see cref="IClientProxy"/> instance.

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.SignalR
_logger = logger; _logger = logger;
} }
public override Task AddGroupAsync(string connectionId, string groupName) public override Task AddToGroupAsync(string connectionId, string groupName)
{ {
if (connectionId == null) if (connectionId == null)
{ {
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.SignalR
return Task.CompletedTask; return Task.CompletedTask;
} }
public override Task RemoveGroupAsync(string connectionId, string groupName) public override Task RemoveFromGroupAsync(string connectionId, string groupName)
{ {
if (connectionId == null) if (connectionId == null)
{ {
@ -219,7 +219,7 @@ namespace Microsoft.AspNetCore.SignalR
return Task.CompletedTask; return Task.CompletedTask;
} }
public override Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList<string> excludedIds) public override Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds)
{ {
if (groupName == null) if (groupName == null)
{ {
@ -232,7 +232,7 @@ namespace Microsoft.AspNetCore.SignalR
List<Task> tasks = null; List<Task> tasks = null;
SerializedHubMessage message = null; SerializedHubMessage message = null;
SendToGroupConnections(methodName, args, group, connection => !excludedIds.Contains(connection.ConnectionId), ref tasks, ref message); SendToGroupConnections(methodName, args, group, connection => !excludedConnectionIds.Contains(connection.ConnectionId), ref tasks, ref message);
if (tasks != null) if (tasks != null)
{ {
@ -271,9 +271,9 @@ namespace Microsoft.AspNetCore.SignalR
return Task.CompletedTask; return Task.CompletedTask;
} }
public override Task SendAllExceptAsync(string methodName, object[] args, IReadOnlyList<string> excludedIds) public override Task SendAllExceptAsync(string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds)
{ {
return SendToAllConnections(methodName, args, connection => !excludedIds.Contains(connection.ConnectionId)); return SendToAllConnections(methodName, args, connection => !excludedConnectionIds.Contains(connection.ConnectionId));
} }
public override Task SendConnectionsAsync(IReadOnlyList<string> connectionIds, string methodName, object[] args) public override Task SendConnectionsAsync(IReadOnlyList<string> connectionIds, string methodName, object[] args)

View File

@ -16,13 +16,13 @@ namespace Microsoft.AspNetCore.SignalR
} }
public dynamic All => new DynamicClientProxy(_clients.All); public dynamic All => new DynamicClientProxy(_clients.All);
public dynamic AllExcept(IReadOnlyList<string> excludedIds) => new DynamicClientProxy(_clients.AllExcept(excludedIds)); public dynamic AllExcept(IReadOnlyList<string> excludedConnectionIds) => new DynamicClientProxy(_clients.AllExcept(excludedConnectionIds));
public dynamic Caller => new DynamicClientProxy(_clients.Caller); public dynamic Caller => new DynamicClientProxy(_clients.Caller);
public dynamic Client(string connectionId) => new DynamicClientProxy(_clients.Client(connectionId)); public dynamic Client(string connectionId) => new DynamicClientProxy(_clients.Client(connectionId));
public dynamic Clients(IReadOnlyList<string> connectionIds) => new DynamicClientProxy(_clients.Clients(connectionIds)); public dynamic Clients(IReadOnlyList<string> connectionIds) => new DynamicClientProxy(_clients.Clients(connectionIds));
public dynamic Group(string groupName) => new DynamicClientProxy(_clients.Group(groupName)); public dynamic Group(string groupName) => new DynamicClientProxy(_clients.Group(groupName));
public dynamic Groups(IReadOnlyList<string> groupNames) => new DynamicClientProxy(_clients.Groups(groupNames)); public dynamic Groups(IReadOnlyList<string> groupNames) => new DynamicClientProxy(_clients.Groups(groupNames));
public dynamic GroupExcept(string groupName, IReadOnlyList<string> excludedIds) => new DynamicClientProxy(_clients.GroupExcept(groupName, excludedIds)); public dynamic GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds) => new DynamicClientProxy(_clients.GroupExcept(groupName, excludedConnectionIds));
public dynamic OthersInGroup(string groupName) => new DynamicClientProxy(_clients.OthersInGroup(groupName)); public dynamic OthersInGroup(string groupName) => new DynamicClientProxy(_clients.OthersInGroup(groupName));
public dynamic Others => new DynamicClientProxy(_clients.Others); public dynamic Others => new DynamicClientProxy(_clients.Others);
public dynamic User(string userId) => new DynamicClientProxy(_clients.User(userId)); public dynamic User(string userId) => new DynamicClientProxy(_clients.User(userId));

View File

@ -52,7 +52,8 @@ namespace Microsoft.AspNetCore.SignalR
public virtual IDictionary<object, object> Items => _connectionContext.Items; public virtual IDictionary<object, object> Items => _connectionContext.Items;
public virtual PipeReader Input => _connectionContext.Transport.Input; // Used by HubConnectionHandler
internal PipeReader Input => _connectionContext.Transport.Input;
public string UserIdentifier { get; set; } public string UserIdentifier { get; set; }
@ -338,7 +339,8 @@ namespace Microsoft.AspNetCore.SignalR
UserIdentifier = userIdProvider.GetUserId(this); UserIdentifier = userIdProvider.GetUserId(this);
if (Features.Get<IConnectionInherentKeepAliveFeature>() == null) // != true needed because it could be null (which we treat as false)
if (Features.Get<IConnectionInherentKeepAliveFeature>()?.HasInherentKeepAlive != true)
{ {
// Only register KeepAlive after protocol handshake otherwise KeepAliveTick could try to write without having a ProtocolReaderWriter // Only register KeepAlive after protocol handshake otherwise KeepAliveTick could try to write without having a ProtocolReaderWriter
Features.Get<IConnectionHeartbeatFeature>()?.OnHeartbeat(state => ((HubConnectionContext)state).KeepAliveTick(), this); Features.Get<IConnectionHeartbeatFeature>()?.OnHeartbeat(state => ((HubConnectionContext)state).KeepAliveTick(), this);

View File

@ -169,16 +169,18 @@ namespace Microsoft.AspNetCore.SignalR
try try
{ {
var input = connection.Input;
var protocol = connection.Protocol;
while (true) while (true)
{ {
var result = await connection.Input.ReadAsync(connection.ConnectionAborted); var result = await input.ReadAsync(connection.ConnectionAborted);
var buffer = result.Buffer; var buffer = result.Buffer;
try try
{ {
if (!buffer.IsEmpty) if (!buffer.IsEmpty)
{ {
while (connection.Protocol.TryParseMessage(ref buffer, _dispatcher, out var message)) while (protocol.TryParseMessage(ref buffer, _dispatcher, out var message))
{ {
// Don't wait on the result of execution, continue processing other // Don't wait on the result of execution, continue processing other
// incoming messages on this connection. // incoming messages on this connection.
@ -195,7 +197,7 @@ namespace Microsoft.AspNetCore.SignalR
// The buffer was sliced up to where it was consumed, so we can just advance to the start. // The buffer was sliced up to where it was consumed, so we can just advance to the start.
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data // We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
// before yielding the read again. // before yielding the read again.
connection.Input.AdvanceTo(buffer.Start, buffer.End); input.AdvanceTo(buffer.Start, buffer.End);
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.SignalR
public abstract Task SendAllAsync(string methodName, object[] args); public abstract Task SendAllAsync(string methodName, object[] args);
public abstract Task SendAllExceptAsync(string methodName, object[] args, IReadOnlyList<string> excludedIds); public abstract Task SendAllExceptAsync(string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds);
public abstract Task SendConnectionAsync(string connectionId, string methodName, object[] args); public abstract Task SendConnectionAsync(string connectionId, string methodName, object[] args);
@ -24,15 +24,15 @@ namespace Microsoft.AspNetCore.SignalR
public abstract Task SendGroupsAsync(IReadOnlyList<string> groupNames, string methodName, object[] args); public abstract Task SendGroupsAsync(IReadOnlyList<string> groupNames, string methodName, object[] args);
public abstract Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList<string> excludedIds); public abstract Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds);
public abstract Task SendUserAsync(string userId, string methodName, object[] args); public abstract Task SendUserAsync(string userId, string methodName, object[] args);
public abstract Task SendUsersAsync(IReadOnlyList<string> userIds, string methodName, object[] args); public abstract Task SendUsersAsync(IReadOnlyList<string> userIds, string methodName, object[] args);
public abstract Task AddGroupAsync(string connectionId, string groupName); public abstract Task AddToGroupAsync(string connectionId, string groupName);
public abstract Task RemoveGroupAsync(string connectionId, string groupName); public abstract Task RemoveFromGroupAsync(string connectionId, string groupName);
} }
} }

View File

@ -1,6 +1,8 @@
// 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.SignalR.Internal;
namespace Microsoft.AspNetCore.SignalR namespace Microsoft.AspNetCore.SignalR
{ {
public class Hub<T> : Hub where T : class public class Hub<T> : Hub where T : class

View File

@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.SignalR
{ {
public interface IGroupManager public interface IGroupManager
{ {
Task AddAsync(string connectionId, string groupName); Task AddToGroupAsync(string connectionId, string groupName);
Task RemoveAsync(string connectionId, string groupName); Task RemoveFromGroupAsync(string connectionId, string groupName);
} }
} }

View File

@ -0,0 +1,558 @@
// 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;
namespace Microsoft.AspNetCore.SignalR
{
public static class IHubClientsExtensions
{
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <returns></returns>
public static T AllExcept<T>(this IHubClients<T> hubClients, string excludedConnectionId1)
{
return hubClients.AllExcept(new [] { excludedConnectionId1 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <returns></returns>
public static T AllExcept<T>(this IHubClients<T> hubClients, string excludedConnectionId1, string excludedConnectionId2)
{
return hubClients.AllExcept(new [] { excludedConnectionId1, excludedConnectionId2 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <returns></returns>
public static T AllExcept<T>(this IHubClients<T> hubClients, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3)
{
return hubClients.AllExcept(new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <returns></returns>
public static T AllExcept<T>(this IHubClients<T> hubClients, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4)
{
return hubClients.AllExcept(new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <param name="excludedConnectionId5">The fifth connection to exclude.</param>
/// <returns></returns>
public static T AllExcept<T>(this IHubClients<T> hubClients, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4, string excludedConnectionId5)
{
return hubClients.AllExcept(new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4, excludedConnectionId5 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <param name="excludedConnectionId5">The fifth connection to exclude.</param>
/// <param name="excludedConnectionId6">The sixth connection to exclude.</param>
/// <returns></returns>
public static T AllExcept<T>(this IHubClients<T> hubClients, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4, string excludedConnectionId5, string excludedConnectionId6)
{
return hubClients.AllExcept(new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4, excludedConnectionId5, excludedConnectionId6 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <param name="excludedConnectionId5">The fifth connection to exclude.</param>
/// <param name="excludedConnectionId6">The sixth connection to exclude.</param>
/// <param name="excludedConnectionId7">The seventh connection to exclude.</param>
/// <returns></returns>
public static T AllExcept<T>(this IHubClients<T> hubClients, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4, string excludedConnectionId5, string excludedConnectionId6, string excludedConnectionId7)
{
return hubClients.AllExcept(new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4, excludedConnectionId5, excludedConnectionId6, excludedConnectionId7 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <param name="excludedConnectionId5">The fifth connection to exclude.</param>
/// <param name="excludedConnectionId6">The sixth connection to exclude.</param>
/// <param name="excludedConnectionId7">The seventh connection to exclude.</param>
/// <param name="excludedConnectionId8">The eighth connection to exclude.</param>
/// <returns></returns>
public static T AllExcept<T>(this IHubClients<T> hubClients, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4, string excludedConnectionId5, string excludedConnectionId6, string excludedConnectionId7, string excludedConnectionId8)
{
return hubClients.AllExcept(new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4, excludedConnectionId5, excludedConnectionId6, excludedConnectionId7, excludedConnectionId8 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="connection1">The first connection to include.</param>
/// <returns></returns>
public static T Clients<T>(this IHubClients<T> hubClients, string connection1)
{
return hubClients.Clients(new [] { connection1 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="connection1">The first connection to include.</param>
/// <param name="connection2">The second connection to include.</param>
/// <returns></returns>
public static T Clients<T>(this IHubClients<T> hubClients, string connection1, string connection2)
{
return hubClients.Clients(new [] { connection1, connection2 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="connection1">The first connection to include.</param>
/// <param name="connection2">The second connection to include.</param>
/// <param name="connection3">The third connection to include.</param>
/// <returns></returns>
public static T Clients<T>(this IHubClients<T> hubClients, string connection1, string connection2, string connection3)
{
return hubClients.Clients(new [] { connection1, connection2, connection3 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="connection1">The first connection to include.</param>
/// <param name="connection2">The second connection to include.</param>
/// <param name="connection3">The third connection to include.</param>
/// <param name="connection4">The fourth connection to include.</param>
/// <returns></returns>
public static T Clients<T>(this IHubClients<T> hubClients, string connection1, string connection2, string connection3, string connection4)
{
return hubClients.Clients(new [] { connection1, connection2, connection3, connection4 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="connection1">The first connection to include.</param>
/// <param name="connection2">The second connection to include.</param>
/// <param name="connection3">The third connection to include.</param>
/// <param name="connection4">The fourth connection to include.</param>
/// <param name="connection5">The fifth connection to include.</param>
/// <returns></returns>
public static T Clients<T>(this IHubClients<T> hubClients, string connection1, string connection2, string connection3, string connection4, string connection5)
{
return hubClients.Clients(new [] { connection1, connection2, connection3, connection4, connection5 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="connection1">The first connection to include.</param>
/// <param name="connection2">The second connection to include.</param>
/// <param name="connection3">The third connection to include.</param>
/// <param name="connection4">The fourth connection to include.</param>
/// <param name="connection5">The fifth connection to include.</param>
/// <param name="connection6">The sixth connection to include.</param>
/// <returns></returns>
public static T Clients<T>(this IHubClients<T> hubClients, string connection1, string connection2, string connection3, string connection4, string connection5, string connection6)
{
return hubClients.Clients(new [] { connection1, connection2, connection3, connection4, connection5, connection6 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="connection1">The first connection to include.</param>
/// <param name="connection2">The second connection to include.</param>
/// <param name="connection3">The third connection to include.</param>
/// <param name="connection4">The fourth connection to include.</param>
/// <param name="connection5">The fifth connection to include.</param>
/// <param name="connection6">The sixth connection to include.</param>
/// <param name="connection7">The seventh connection to include.</param>
/// <returns></returns>
public static T Clients<T>(this IHubClients<T> hubClients, string connection1, string connection2, string connection3, string connection4, string connection5, string connection6, string connection7)
{
return hubClients.Clients(new [] { connection1, connection2, connection3, connection4, connection5, connection6, connection7 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="connection1">The first connection to include.</param>
/// <param name="connection2">The second connection to include.</param>
/// <param name="connection3">The third connection to include.</param>
/// <param name="connection4">The fourth connection to include.</param>
/// <param name="connection5">The fifth connection to include.</param>
/// <param name="connection6">The sixth connection to include.</param>
/// <param name="connection7">The seventh connection to include.</param>
/// <param name="connection8">The eighth connection to include.</param>
/// <returns></returns>
public static T Clients<T>(this IHubClients<T> hubClients, string connection1, string connection2, string connection3, string connection4, string connection5, string connection6, string connection7, string connection8)
{
return hubClients.Clients(new [] { connection1, connection2, connection3, connection4, connection5, connection6, connection7, connection8 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="group1">The first group to include.</param>
/// <returns></returns>
public static T Groups<T>(this IHubClients<T> hubClients, string group1)
{
return hubClients.Groups(new [] { group1 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="group1">The first group to include.</param>
/// <param name="group2">The second group to include.</param>
/// <returns></returns>
public static T Groups<T>(this IHubClients<T> hubClients, string group1, string group2)
{
return hubClients.Groups(new [] { group1, group2 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="group1">The first group to include.</param>
/// <param name="group2">The second group to include.</param>
/// <param name="group3">The third group to include.</param>
/// <returns></returns>
public static T Groups<T>(this IHubClients<T> hubClients, string group1, string group2, string group3)
{
return hubClients.Groups(new [] { group1, group2, group3 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="group1">The first group to include.</param>
/// <param name="group2">The second group to include.</param>
/// <param name="group3">The third group to include.</param>
/// <param name="group4">The fourth group to include.</param>
/// <returns></returns>
public static T Groups<T>(this IHubClients<T> hubClients, string group1, string group2, string group3, string group4)
{
return hubClients.Groups(new [] { group1, group2, group3, group4 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="group1">The first group to include.</param>
/// <param name="group2">The second group to include.</param>
/// <param name="group3">The third group to include.</param>
/// <param name="group4">The fourth group to include.</param>
/// <param name="group5">The fifth group to include.</param>
/// <returns></returns>
public static T Groups<T>(this IHubClients<T> hubClients, string group1, string group2, string group3, string group4, string group5)
{
return hubClients.Groups(new [] { group1, group2, group3, group4, group5 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="group1">The first group to include.</param>
/// <param name="group2">The second group to include.</param>
/// <param name="group3">The third group to include.</param>
/// <param name="group4">The fourth group to include.</param>
/// <param name="group5">The fifth group to include.</param>
/// <param name="group6">The sixth group to include.</param>
/// <returns></returns>
public static T Groups<T>(this IHubClients<T> hubClients, string group1, string group2, string group3, string group4, string group5, string group6)
{
return hubClients.Groups(new [] { group1, group2, group3, group4, group5, group6 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="group1">The first group to include.</param>
/// <param name="group2">The second group to include.</param>
/// <param name="group3">The third group to include.</param>
/// <param name="group4">The fourth group to include.</param>
/// <param name="group5">The fifth group to include.</param>
/// <param name="group6">The sixth group to include.</param>
/// <param name="group7">The seventh group to include.</param>
/// <returns></returns>
public static T Groups<T>(this IHubClients<T> hubClients, string group1, string group2, string group3, string group4, string group5, string group6, string group7)
{
return hubClients.Groups(new [] { group1, group2, group3, group4, group5, group6, group7 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="group1">The first group to include.</param>
/// <param name="group2">The second group to include.</param>
/// <param name="group3">The third group to include.</param>
/// <param name="group4">The fourth group to include.</param>
/// <param name="group5">The fifth group to include.</param>
/// <param name="group6">The sixth group to include.</param>
/// <param name="group7">The seventh group to include.</param>
/// <param name="group8">The eighth group to include.</param>
/// <returns></returns>
public static T Groups<T>(this IHubClients<T> hubClients, string group1, string group2, string group3, string group4, string group5, string group6, string group7, string group8)
{
return hubClients.Groups(new [] { group1, group2, group3, group4, group5, group6, group7, group8 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="groupName"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <returns></returns>
public static T GroupExcept<T>(this IHubClients<T> hubClients, string groupName, string excludedConnectionId1)
{
return hubClients.GroupExcept(groupName, new [] { excludedConnectionId1 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="groupName"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <returns></returns>
public static T GroupExcept<T>(this IHubClients<T> hubClients, string groupName, string excludedConnectionId1, string excludedConnectionId2)
{
return hubClients.GroupExcept(groupName, new [] { excludedConnectionId1, excludedConnectionId2 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="groupName"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <returns></returns>
public static T GroupExcept<T>(this IHubClients<T> hubClients, string groupName, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3)
{
return hubClients.GroupExcept(groupName, new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="groupName"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <returns></returns>
public static T GroupExcept<T>(this IHubClients<T> hubClients, string groupName, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4)
{
return hubClients.GroupExcept(groupName, new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="groupName"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <param name="excludedConnectionId5">The fifth connection to exclude.</param>
/// <returns></returns>
public static T GroupExcept<T>(this IHubClients<T> hubClients, string groupName, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4, string excludedConnectionId5)
{
return hubClients.GroupExcept(groupName, new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4, excludedConnectionId5 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="groupName"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <param name="excludedConnectionId5">The fifth connection to exclude.</param>
/// <param name="excludedConnectionId6">The sixth connection to exclude.</param>
/// <returns></returns>
public static T GroupExcept<T>(this IHubClients<T> hubClients, string groupName, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4, string excludedConnectionId5, string excludedConnectionId6)
{
return hubClients.GroupExcept(groupName, new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4, excludedConnectionId5, excludedConnectionId6 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="groupName"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <param name="excludedConnectionId5">The fifth connection to exclude.</param>
/// <param name="excludedConnectionId6">The sixth connection to exclude.</param>
/// <param name="excludedConnectionId7">The seventh connection to exclude.</param>
/// <returns></returns>
public static T GroupExcept<T>(this IHubClients<T> hubClients, string groupName, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4, string excludedConnectionId5, string excludedConnectionId6, string excludedConnectionId7)
{
return hubClients.GroupExcept(groupName, new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4, excludedConnectionId5, excludedConnectionId6, excludedConnectionId7 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="groupName"></param>
/// <param name="excludedConnectionId1">The first connection to exclude.</param>
/// <param name="excludedConnectionId2">The second connection to exclude.</param>
/// <param name="excludedConnectionId3">The third connection to exclude.</param>
/// <param name="excludedConnectionId4">The fourth connection to exclude.</param>
/// <param name="excludedConnectionId5">The fifth connection to exclude.</param>
/// <param name="excludedConnectionId6">The sixth connection to exclude.</param>
/// <param name="excludedConnectionId7">The seventh connection to exclude.</param>
/// <param name="excludedConnectionId8">The eighth connection to exclude.</param>
/// <returns></returns>
public static T GroupExcept<T>(this IHubClients<T> hubClients, string groupName, string excludedConnectionId1, string excludedConnectionId2, string excludedConnectionId3, string excludedConnectionId4, string excludedConnectionId5, string excludedConnectionId6, string excludedConnectionId7, string excludedConnectionId8)
{
return hubClients.GroupExcept(groupName, new [] { excludedConnectionId1, excludedConnectionId2, excludedConnectionId3, excludedConnectionId4, excludedConnectionId5, excludedConnectionId6, excludedConnectionId7, excludedConnectionId8 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="user1">The first user to include.</param>
/// <returns></returns>
public static T Users<T>(this IHubClients<T> hubClients, string user1)
{
return hubClients.Users(new [] { user1 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="user1">The first user to include.</param>
/// <param name="user2">The second user to include.</param>
/// <returns></returns>
public static T Users<T>(this IHubClients<T> hubClients, string user1, string user2)
{
return hubClients.Users(new [] { user1, user2 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="user1">The first user to include.</param>
/// <param name="user2">The second user to include.</param>
/// <param name="user3">The third user to include.</param>
/// <returns></returns>
public static T Users<T>(this IHubClients<T> hubClients, string user1, string user2, string user3)
{
return hubClients.Users(new [] { user1, user2, user3 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="user1">The first user to include.</param>
/// <param name="user2">The second user to include.</param>
/// <param name="user3">The third user to include.</param>
/// <param name="user4">The fourth user to include.</param>
/// <returns></returns>
public static T Users<T>(this IHubClients<T> hubClients, string user1, string user2, string user3, string user4)
{
return hubClients.Users(new [] { user1, user2, user3, user4 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="user1">The first user to include.</param>
/// <param name="user2">The second user to include.</param>
/// <param name="user3">The third user to include.</param>
/// <param name="user4">The fourth user to include.</param>
/// <param name="user5">The fifth user to include.</param>
/// <returns></returns>
public static T Users<T>(this IHubClients<T> hubClients, string user1, string user2, string user3, string user4, string user5)
{
return hubClients.Users(new [] { user1, user2, user3, user4, user5 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="user1">The first user to include.</param>
/// <param name="user2">The second user to include.</param>
/// <param name="user3">The third user to include.</param>
/// <param name="user4">The fourth user to include.</param>
/// <param name="user5">The fifth user to include.</param>
/// <param name="user6">The sixth user to include.</param>
/// <returns></returns>
public static T Users<T>(this IHubClients<T> hubClients, string user1, string user2, string user3, string user4, string user5, string user6)
{
return hubClients.Users(new [] { user1, user2, user3, user4, user5, user6 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="user1">The first user to include.</param>
/// <param name="user2">The second user to include.</param>
/// <param name="user3">The third user to include.</param>
/// <param name="user4">The fourth user to include.</param>
/// <param name="user5">The fifth user to include.</param>
/// <param name="user6">The sixth user to include.</param>
/// <param name="user7">The seventh user to include.</param>
/// <returns></returns>
public static T Users<T>(this IHubClients<T> hubClients, string user1, string user2, string user3, string user4, string user5, string user6, string user7)
{
return hubClients.Users(new [] { user1, user2, user3, user4, user5, user6, user7 });
}
/// <summary>
/// </summary>
/// <param name="hubClients"></param>
/// <param name="user1">The first user to include.</param>
/// <param name="user2">The second user to include.</param>
/// <param name="user3">The third user to include.</param>
/// <param name="user4">The fourth user to include.</param>
/// <param name="user5">The fifth user to include.</param>
/// <param name="user6">The sixth user to include.</param>
/// <param name="user7">The seventh user to include.</param>
/// <param name="user8">The eighth user to include.</param>
/// <returns></returns>
public static T Users<T>(this IHubClients<T> hubClients, string user1, string user2, string user3, string user4, string user5, string user6, string user7, string user8)
{
return hubClients.Users(new [] { user1, user2, user3, user4, user5, user6, user7, user8 });
}
}
}

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.SignalR
{ {
T All { get; } T All { get; }
T AllExcept(IReadOnlyList<string> excludedIds); T AllExcept(IReadOnlyList<string> excludedConnectionIds);
T Client(string connectionId); T Client(string connectionId);
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.SignalR
T Groups(IReadOnlyList<string> groupNames); T Groups(IReadOnlyList<string> groupNames);
T GroupExcept(string groupName, IReadOnlyList<string> excludeIds); T GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds);
T User(string userId); T User(string userId);

View File

@ -5,7 +5,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.SignalR namespace Microsoft.AspNetCore.SignalR.Internal
{ {
public class DefaultHubActivator<THub> : IHubActivator<THub> where THub: Hub public class DefaultHubActivator<THub> : IHubActivator<THub> where THub: Hub
{ {
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.SignalR
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
public THub Create() public virtual THub Create()
{ {
Debug.Assert(!_created.HasValue, "hub activators must not be reused."); Debug.Assert(!_created.HasValue, "hub activators must not be reused.");
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.SignalR
return hub; return hub;
} }
public void Release(THub hub) public virtual void Release(THub hub)
{ {
if (hub == null) if (hub == null)
{ {

View File

@ -8,7 +8,7 @@ using System.Security.Claims;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.SignalR namespace Microsoft.AspNetCore.SignalR.Internal
{ {
public class DefaultHubCallerContext : HubCallerContext public class DefaultHubCallerContext : HubCallerContext
{ {

View File

@ -24,9 +24,9 @@ namespace Microsoft.AspNetCore.SignalR.Internal
public IClientProxy All => _hubClients.All; public IClientProxy All => _hubClients.All;
public IClientProxy AllExcept(IReadOnlyList<string> excludedIds) public IClientProxy AllExcept(IReadOnlyList<string> excludedConnectionIds)
{ {
return _hubClients.AllExcept(excludedIds); return _hubClients.AllExcept(excludedConnectionIds);
} }
public IClientProxy Client(string connectionId) public IClientProxy Client(string connectionId)
@ -49,9 +49,9 @@ namespace Microsoft.AspNetCore.SignalR.Internal
return _hubClients.GroupExcept(groupName, _currentConnectionId); return _hubClients.GroupExcept(groupName, _currentConnectionId);
} }
public IClientProxy GroupExcept(string groupName, IReadOnlyList<string> excludeIds) public IClientProxy GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds)
{ {
return _hubClients.GroupExcept(groupName, excludeIds); return _hubClients.GroupExcept(groupName, excludedConnectionIds);
} }
public IClientProxy User(string userId) public IClientProxy User(string userId)

View File

@ -17,9 +17,9 @@ namespace Microsoft.AspNetCore.SignalR.Internal
public IClientProxy All { get; } public IClientProxy All { get; }
public IClientProxy AllExcept(IReadOnlyList<string> excludedIds) public IClientProxy AllExcept(IReadOnlyList<string> excludedConnectionIds)
{ {
return new AllClientsExceptProxy<THub>(_lifetimeManager, excludedIds); return new AllClientsExceptProxy<THub>(_lifetimeManager, excludedConnectionIds);
} }
public IClientProxy Client(string connectionId) public IClientProxy Client(string connectionId)
@ -32,9 +32,9 @@ namespace Microsoft.AspNetCore.SignalR.Internal
return new GroupProxy<THub>(_lifetimeManager, groupName); return new GroupProxy<THub>(_lifetimeManager, groupName);
} }
public IClientProxy GroupExcept(string groupName, IReadOnlyList<string> excludeIds) public IClientProxy GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds)
{ {
return new GroupExceptProxy<THub>(_lifetimeManager, groupName, excludeIds); return new GroupExceptProxy<THub>(_lifetimeManager, groupName, excludedConnectionIds);
} }
public IClientProxy Clients(IReadOnlyList<string> connectionIds) public IClientProxy Clients(IReadOnlyList<string> connectionIds)

View File

@ -17,9 +17,9 @@ namespace Microsoft.AspNetCore.SignalR.Internal
public T All { get; } public T All { get; }
public T AllExcept(IReadOnlyList<string> excludedIds) public T AllExcept(IReadOnlyList<string> excludedConnectionIds)
{ {
return TypedClientBuilder<T>.Build(new AllClientsExceptProxy<THub>(_lifetimeManager, excludedIds)); return TypedClientBuilder<T>.Build(new AllClientsExceptProxy<THub>(_lifetimeManager, excludedConnectionIds));
} }
public virtual T Client(string connectionId) public virtual T Client(string connectionId)
@ -37,9 +37,9 @@ namespace Microsoft.AspNetCore.SignalR.Internal
return TypedClientBuilder<T>.Build(new GroupProxy<THub>(_lifetimeManager, groupName)); return TypedClientBuilder<T>.Build(new GroupProxy<THub>(_lifetimeManager, groupName));
} }
public T GroupExcept(string groupName, IReadOnlyList<string> excludeIds) public T GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds)
{ {
return TypedClientBuilder<T>.Build(new GroupExceptProxy<THub>(_lifetimeManager, groupName, excludeIds)); return TypedClientBuilder<T>.Build(new GroupExceptProxy<THub>(_lifetimeManager, groupName, excludedConnectionIds));
} }
public T Groups(IReadOnlyList<string> groupNames) public T Groups(IReadOnlyList<string> groupNames)

View File

@ -78,18 +78,18 @@ namespace Microsoft.AspNetCore.SignalR.Internal
{ {
private readonly string _groupName; private readonly string _groupName;
private readonly HubLifetimeManager<THub> _lifetimeManager; private readonly HubLifetimeManager<THub> _lifetimeManager;
private readonly IReadOnlyList<string> _excludedIds; private readonly IReadOnlyList<string> _excludedConnectionIds;
public GroupExceptProxy(HubLifetimeManager<THub> lifetimeManager, string groupName, IReadOnlyList<string> excludedIds) public GroupExceptProxy(HubLifetimeManager<THub> lifetimeManager, string groupName, IReadOnlyList<string> excludedConnectionIds)
{ {
_lifetimeManager = lifetimeManager; _lifetimeManager = lifetimeManager;
_groupName = groupName; _groupName = groupName;
_excludedIds = excludedIds; _excludedConnectionIds = excludedConnectionIds;
} }
public Task SendCoreAsync(string method, object[] args) public Task SendCoreAsync(string method, object[] args)
{ {
return _lifetimeManager.SendGroupExceptAsync(_groupName, method, args, _excludedIds); return _lifetimeManager.SendGroupExceptAsync(_groupName, method, args, _excludedConnectionIds);
} }
} }
@ -111,17 +111,17 @@ namespace Microsoft.AspNetCore.SignalR.Internal
internal class AllClientsExceptProxy<THub> : IClientProxy where THub : Hub internal class AllClientsExceptProxy<THub> : IClientProxy where THub : Hub
{ {
private readonly HubLifetimeManager<THub> _lifetimeManager; private readonly HubLifetimeManager<THub> _lifetimeManager;
private readonly IReadOnlyList<string> _excludedIds; private readonly IReadOnlyList<string> _excludedConnectionIds;
public AllClientsExceptProxy(HubLifetimeManager<THub> lifetimeManager, IReadOnlyList<string> excludedIds) public AllClientsExceptProxy(HubLifetimeManager<THub> lifetimeManager, IReadOnlyList<string> excludedConnectionIds)
{ {
_lifetimeManager = lifetimeManager; _lifetimeManager = lifetimeManager;
_excludedIds = excludedIds; _excludedConnectionIds = excludedConnectionIds;
} }
public Task SendCoreAsync(string method, object[] args) public Task SendCoreAsync(string method, object[] args)
{ {
return _lifetimeManager.SendAllExceptAsync(method, args, _excludedIds); return _lifetimeManager.SendAllExceptAsync(method, args, _excludedConnectionIds);
} }
} }
@ -168,14 +168,14 @@ namespace Microsoft.AspNetCore.SignalR.Internal
_lifetimeManager = lifetimeManager; _lifetimeManager = lifetimeManager;
} }
public Task AddAsync(string connectionId, string groupName) public Task AddToGroupAsync(string connectionId, string groupName)
{ {
return _lifetimeManager.AddGroupAsync(connectionId, groupName); return _lifetimeManager.AddToGroupAsync(connectionId, groupName);
} }
public Task RemoveAsync(string connectionId, string groupName) public Task RemoveFromGroupAsync(string connectionId, string groupName)
{ {
return _lifetimeManager.RemoveGroupAsync(connectionId, groupName); return _lifetimeManager.RemoveFromGroupAsync(connectionId, groupName);
} }
} }
} }

View File

@ -1,7 +1,7 @@
// 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.AspNetCore.SignalR.Internal
{ {
internal class SignalRMarkerService internal class SignalRMarkerService
{ {

View File

@ -9,7 +9,7 @@ using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Microsoft.AspNetCore.SignalR namespace Microsoft.AspNetCore.SignalR.Internal
{ {
internal static class TypedClientBuilder<T> internal static class TypedClientBuilder<T>
{ {

View File

@ -3,7 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Microsoft.AspNetCore.SignalR namespace Microsoft.AspNetCore.SignalR.Internal
{ {
internal class TypedHubClients<T> : IHubCallerClients<T> internal class TypedHubClients<T> : IHubCallerClients<T>
{ {
@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.SignalR
public T Others => TypedClientBuilder<T>.Build(_hubClients.Others); public T Others => TypedClientBuilder<T>.Build(_hubClients.Others);
public T AllExcept(IReadOnlyList<string> excludedIds) => TypedClientBuilder<T>.Build(_hubClients.AllExcept(excludedIds)); public T AllExcept(IReadOnlyList<string> excludedConnectionIds) => TypedClientBuilder<T>.Build(_hubClients.AllExcept(excludedConnectionIds));
public T Client(string connectionId) public T Client(string connectionId)
{ {
@ -32,9 +32,9 @@ namespace Microsoft.AspNetCore.SignalR
return TypedClientBuilder<T>.Build(_hubClients.Group(groupName)); return TypedClientBuilder<T>.Build(_hubClients.Group(groupName));
} }
public T GroupExcept(string groupName, IReadOnlyList<string> excludeIds) public T GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds)
{ {
return TypedClientBuilder<T>.Build(_hubClients.GroupExcept(groupName, excludeIds)); return TypedClientBuilder<T>.Build(_hubClients.GroupExcept(groupName, excludedConnectionIds));
} }
public T Clients(IReadOnlyList<string> connectionIds) public T Clients(IReadOnlyList<string> connectionIds)

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
namespace Microsoft.AspNetCore.SignalR.Internal namespace Microsoft.AspNetCore.SignalR
{ {
/// <summary> /// <summary>
/// This class is designed to support the framework. The API is subject to breaking changes. /// This class is designed to support the framework. The API is subject to breaking changes.

View File

@ -3,7 +3,7 @@
using System; using System;
namespace Microsoft.AspNetCore.SignalR.Internal namespace Microsoft.AspNetCore.SignalR
{ {
public readonly struct SerializedMessage public readonly struct SerializedMessage
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.SignalR namespace Microsoft.AspNetCore.SignalR

View File

@ -9,6 +9,8 @@
<ItemGroup> <ItemGroup>
<Compile Include="..\Common\JsonUtils.cs" Link="Internal\JsonUtils.cs" /> <Compile Include="..\Common\JsonUtils.cs" Link="Internal\JsonUtils.cs" />
<Compile Include="..\Common\TextMessageFormatter.cs" Link="TextMessageFormatter.cs" />
<Compile Include="..\Common\TextMessageParser.cs" Link="TextMessageParser.cs" />
<Compile Include="..\Common\Utf8BufferTextReader.cs" Link="Utf8BufferTextReader.cs" /> <Compile Include="..\Common\Utf8BufferTextReader.cs" Link="Utf8BufferTextReader.cs" />
<Compile Include="..\Common\Utf8BufferTextWriter.cs" Link="Utf8BufferTextWriter.cs" /> <Compile Include="..\Common\Utf8BufferTextWriter.cs" Link="Utf8BufferTextWriter.cs" />
</ItemGroup> </ItemGroup>

View File

@ -9,7 +9,6 @@ using System.Runtime.ExceptionServices;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal; using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;

View File

@ -8,6 +8,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Common\BinaryMessageFormatter.cs" Link="BinaryMessageFormatter.cs" />
<Compile Include="..\Common\BinaryMessageParser.cs" Link="BinaryMessageParser.cs" />
<Compile Include="..\Common\MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" /> <Compile Include="..\Common\MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" />
</ItemGroup> </ItemGroup>

View File

@ -12,7 +12,6 @@ using MessagePack;
using MessagePack.Formatters; using MessagePack.Formatters;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.SignalR.Protocol namespace Microsoft.AspNetCore.SignalR.Protocol

View File

@ -10,24 +10,24 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Internal
/// Gets a list of connections that should be excluded from this invocation. /// Gets a list of connections that should be excluded from this invocation.
/// May be null to indicate that no connections are to be excluded. /// May be null to indicate that no connections are to be excluded.
/// </summary> /// </summary>
public IReadOnlyList<string> ExcludedIds { get; } public IReadOnlyList<string> ExcludedConnectionIds { get; }
/// <summary> /// <summary>
/// Gets the message serialization cache containing serialized payloads for the message. /// Gets the message serialization cache containing serialized payloads for the message.
/// </summary> /// </summary>
public SerializedHubMessage Message { get; } public SerializedHubMessage Message { get; }
public RedisInvocation(SerializedHubMessage message, IReadOnlyList<string> excludedIds) public RedisInvocation(SerializedHubMessage message, IReadOnlyList<string> excludedConnectionIds)
{ {
Message = message; Message = message;
ExcludedIds = excludedIds; ExcludedConnectionIds = excludedConnectionIds;
} }
public static RedisInvocation Create(string target, object[] arguments, IReadOnlyList<string> excludedIds = null) public static RedisInvocation Create(string target, object[] arguments, IReadOnlyList<string> excludedConnectionIds = null)
{ {
return new RedisInvocation( return new RedisInvocation(
new SerializedHubMessage(new InvocationMessage(target, null, arguments)), new SerializedHubMessage(new InvocationMessage(target, null, arguments)),
excludedIds); excludedConnectionIds);
} }
} }
} }

View File

@ -6,7 +6,7 @@ using System.Linq;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using StackExchange.Redis; using StackExchange.Redis;
namespace Microsoft.AspNetCore.SignalR.Redis namespace Microsoft.AspNetCore.SignalR.Redis.Internal
{ {
// We don't want to use our nested static class here because RedisHubLifetimeManager is generic. // We don't want to use our nested static class here because RedisHubLifetimeManager is generic.
// We'd end up creating separate instances of all the LoggerMessage.Define values for each Hub. // We'd end up creating separate instances of all the LoggerMessage.Define values for each Hub.

View File

@ -10,7 +10,6 @@ using MessagePack;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal; using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
using StackExchange.Redis;
namespace Microsoft.AspNetCore.SignalR.Redis.Internal namespace Microsoft.AspNetCore.SignalR.Redis.Internal
{ {
@ -35,9 +34,9 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Internal
// * A 7-bit variable length integer encodes the length in bytes, followed by the encoded string in UTF-8. // * A 7-bit variable length integer encodes the length in bytes, followed by the encoded string in UTF-8.
public byte[] WriteInvocation(string methodName, object[] args) => public byte[] WriteInvocation(string methodName, object[] args) =>
WriteInvocation(methodName, args, excludedIds: null); WriteInvocation(methodName, args, excludedConnectionIds: null);
public byte[] WriteInvocation(string methodName, object[] args, IReadOnlyList<string> excludedIds) public byte[] WriteInvocation(string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds)
{ {
// Written as a MessagePack 'arr' containing at least these items: // Written as a MessagePack 'arr' containing at least these items:
// * A MessagePack 'arr' of 'str's representing the excluded ids // * A MessagePack 'arr' of 'str's representing the excluded ids
@ -49,10 +48,10 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Internal
try try
{ {
MessagePackBinary.WriteArrayHeader(writer, 2); MessagePackBinary.WriteArrayHeader(writer, 2);
if (excludedIds != null && excludedIds.Count > 0) if (excludedConnectionIds != null && excludedConnectionIds.Count > 0)
{ {
MessagePackBinary.WriteArrayHeader(writer, excludedIds.Count); MessagePackBinary.WriteArrayHeader(writer, excludedConnectionIds.Count);
foreach (var id in excludedIds) foreach (var id in excludedConnectionIds)
{ {
MessagePackBinary.WriteString(writer, id); MessagePackBinary.WriteString(writer, id);
} }
@ -126,7 +125,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Internal
ValidateArraySize(ref data, 2, "Invocation"); ValidateArraySize(ref data, 2, "Invocation");
// Read excluded Ids // Read excluded Ids
IReadOnlyList<string> excludedIds = null; IReadOnlyList<string> excludedConnectionIds = null;
var idCount = MessagePackUtil.ReadArrayHeader(ref data); var idCount = MessagePackUtil.ReadArrayHeader(ref data);
if (idCount > 0) if (idCount > 0)
{ {
@ -136,12 +135,12 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Internal
ids[i] = MessagePackUtil.ReadString(ref data); ids[i] = MessagePackUtil.ReadString(ref data);
} }
excludedIds = ids; excludedConnectionIds = ids;
} }
// Read payload // Read payload
var message = ReadSerializedHubMessage(ref data); var message = ReadSerializedHubMessage(ref data);
return new RedisInvocation(message, excludedIds); return new RedisInvocation(message, excludedConnectionIds);
} }
public RedisGroupCommand ReadGroupCommand(ReadOnlyMemory<byte> data) public RedisGroupCommand ReadGroupCommand(ReadOnlyMemory<byte> data)

View File

@ -19,7 +19,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
return AddRedis(builder, o => return AddRedis(builder, o =>
{ {
o.Options = ConfigurationOptions.Parse(redisConnectionString); o.Configuration = ConfigurationOptions.Parse(redisConnectionString);
}); });
} }
@ -34,7 +34,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
return AddRedis(builder, o => return AddRedis(builder, o =>
{ {
o.Options = ConfigurationOptions.Parse(redisConnectionString); o.Configuration = ConfigurationOptions.Parse(redisConnectionString);
configure(o); configure(o);
}); });
} }

View File

@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
_channels = new RedisChannels(typeof(THub).FullName); _channels = new RedisChannels(typeof(THub).FullName);
_protocol = new RedisProtocol(hubProtocolResolver.AllProtocols); _protocol = new RedisProtocol(hubProtocolResolver.AllProtocols);
RedisLog.ConnectingToEndpoints(_logger, options.Value.Options.EndPoints, _serverName); RedisLog.ConnectingToEndpoints(_logger, options.Value.Configuration.EndPoints, _serverName);
_ = EnsureRedisServerConnection(); _ = EnsureRedisServerConnection();
} }
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
if (groupNames != null) if (groupNames != null)
{ {
// Copy the groups to an array here because they get removed from this collection // Copy the groups to an array here because they get removed from this collection
// in RemoveGroupAsync // in RemoveFromGroupAsync
foreach (var group in groupNames.ToArray()) foreach (var group in groupNames.ToArray())
{ {
// Use RemoveGroupAsyncCore because the connection is local and we don't want to // Use RemoveGroupAsyncCore because the connection is local and we don't want to
@ -112,9 +112,9 @@ namespace Microsoft.AspNetCore.SignalR.Redis
return PublishAsync(_channels.All, message); return PublishAsync(_channels.All, message);
} }
public override Task SendAllExceptAsync(string methodName, object[] args, IReadOnlyList<string> excludedIds) public override Task SendAllExceptAsync(string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds)
{ {
var message = _protocol.WriteInvocation(methodName, args, excludedIds); var message = _protocol.WriteInvocation(methodName, args, excludedConnectionIds);
return PublishAsync(_channels.All, message); return PublishAsync(_channels.All, message);
} }
@ -148,14 +148,14 @@ namespace Microsoft.AspNetCore.SignalR.Redis
return PublishAsync(_channels.Group(groupName), message); return PublishAsync(_channels.Group(groupName), message);
} }
public override async Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList<string> excludedIds) public override async Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList<string> excludedConnectionIds)
{ {
if (groupName == null) if (groupName == null)
{ {
throw new ArgumentNullException(nameof(groupName)); throw new ArgumentNullException(nameof(groupName));
} }
var message = _protocol.WriteInvocation(methodName, args, excludedIds); var message = _protocol.WriteInvocation(methodName, args, excludedConnectionIds);
await PublishAsync(_channels.Group(groupName), message); await PublishAsync(_channels.Group(groupName), message);
} }
@ -165,7 +165,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
return PublishAsync(_channels.User(userId), message); return PublishAsync(_channels.User(userId), message);
} }
public override async Task AddGroupAsync(string connectionId, string groupName) public override async Task AddToGroupAsync(string connectionId, string groupName)
{ {
if (connectionId == null) if (connectionId == null)
{ {
@ -188,7 +188,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
await SendGroupActionAndWaitForAck(connectionId, groupName, GroupAction.Add); await SendGroupActionAndWaitForAck(connectionId, groupName, GroupAction.Add);
} }
public override async Task RemoveGroupAsync(string connectionId, string groupName) public override async Task RemoveFromGroupAsync(string connectionId, string groupName)
{ {
if (connectionId == null) if (connectionId == null)
{ {
@ -388,7 +388,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
foreach (var connection in _connections) foreach (var connection in _connections)
{ {
if (invocation.ExcludedIds == null || !invocation.ExcludedIds.Contains(connection.ConnectionId)) if (invocation.ExcludedConnectionIds == null || !invocation.ExcludedConnectionIds.Contains(connection.ConnectionId))
{ {
tasks.Add(connection.WriteAsync(invocation.Message).AsTask()); tasks.Add(connection.WriteAsync(invocation.Message).AsTask());
} }
@ -487,7 +487,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
var tasks = new List<Task>(); var tasks = new List<Task>();
foreach (var groupConnection in group.Connections) foreach (var groupConnection in group.Connections)
{ {
if (invocation.ExcludedIds?.Contains(groupConnection.ConnectionId) == true) if (invocation.ExcludedConnectionIds?.Contains(groupConnection.ConnectionId) == true)
{ {
continue; continue;
} }

View File

@ -11,31 +11,31 @@ namespace Microsoft.AspNetCore.SignalR.Redis
{ {
public class RedisOptions public class RedisOptions
{ {
public ConfigurationOptions Options { get; set; } = new ConfigurationOptions public ConfigurationOptions Configuration { get; set; } = new ConfigurationOptions
{ {
// Enable reconnecting by default // Enable reconnecting by default
AbortOnConnectFail = false AbortOnConnectFail = false
}; };
public Func<TextWriter, Task<IConnectionMultiplexer>> Factory { get; set; } public Func<TextWriter, Task<IConnectionMultiplexer>> ConnectionFactory { get; set; }
internal async Task<IConnectionMultiplexer> ConnectAsync(TextWriter log) internal async Task<IConnectionMultiplexer> ConnectAsync(TextWriter log)
{ {
// Factory is publically settable. Assigning to a local variable before null check for thread safety. // Factory is publically settable. Assigning to a local variable before null check for thread safety.
var localFactory = Factory; var factory = ConnectionFactory;
if (localFactory == null) if (factory == null)
{ {
// REVIEW: Should we do this? // REVIEW: Should we do this?
if (Options.EndPoints.Count == 0) if (Configuration.EndPoints.Count == 0)
{ {
Options.EndPoints.Add(IPAddress.Loopback, 0); Configuration.EndPoints.Add(IPAddress.Loopback, 0);
Options.SetDefaultPorts(); Configuration.SetDefaultPorts();
} }
return await ConnectionMultiplexer.ConnectAsync(Options, log); return await ConnectionMultiplexer.ConnectAsync(Configuration, log);
} }
return await localFactory(log); return await factory(log);
} }
} }
} }

View File

@ -18,11 +18,6 @@ namespace Microsoft.AspNetCore.SignalR
_routes = routes; _routes = routes;
} }
public void MapHub<THub>(string path) where THub : Hub
{
MapHub<THub>(new PathString(path), configureOptions: null);
}
public void MapHub<THub>(PathString path) where THub : Hub public void MapHub<THub>(PathString path) where THub : Hub
{ {
MapHub<THub>(path, configureOptions: null); MapHub<THub>(path, configureOptions: null);

View File

@ -636,6 +636,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
// Start a poll // Start a poll
var task = dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app); var task = dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app);
Assert.True(task.IsCompleted);
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
task = dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app);
// Send to the application // Send to the application
var buffer = Encoding.UTF8.GetBytes("Hello World"); var buffer = Encoding.UTF8.GetBytes("Hello World");
@ -745,7 +749,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
} }
[Theory] [Theory]
[InlineData(HttpTransportType.LongPolling, 204)] [InlineData(HttpTransportType.LongPolling, 200)]
[InlineData(HttpTransportType.WebSockets, 404)] [InlineData(HttpTransportType.WebSockets, 404)]
[InlineData(HttpTransportType.ServerSentEvents, 404)] [InlineData(HttpTransportType.ServerSentEvents, 404)]
public async Task EndPointThatOnlySupportsLongPollingRejectsOtherTransports(HttpTransportType transportType, int status) public async Task EndPointThatOnlySupportsLongPollingRejectsOtherTransports(HttpTransportType transportType, int status)
@ -869,6 +873,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
var builder = new ConnectionBuilder(services.BuildServiceProvider()); var builder = new ConnectionBuilder(services.BuildServiceProvider());
builder.UseConnectionHandler<ImmediatelyCompleteConnectionHandler>(); builder.UseConnectionHandler<ImmediatelyCompleteConnectionHandler>();
var app = builder.Build(); var app = builder.Build();
// First poll will 200
await dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app);
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
await dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app); await dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app);
Assert.Equal(StatusCodes.Status204NoContent, context.Response.StatusCode); Assert.Equal(StatusCodes.Status204NoContent, context.Response.StatusCode);
@ -998,6 +1006,9 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
var app = builder.Build(); var app = builder.Build();
var options = new HttpConnectionDispatcherOptions(); var options = new HttpConnectionDispatcherOptions();
var request1 = dispatcher.ExecuteAsync(context1, options, app); var request1 = dispatcher.ExecuteAsync(context1, options, app);
Assert.True(request1.IsCompleted);
request1 = dispatcher.ExecuteAsync(context1, options, app);
var request2 = dispatcher.ExecuteAsync(context2, options, app); var request2 = dispatcher.ExecuteAsync(context2, options, app);
await request1; await request1;
@ -1132,7 +1143,14 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
builder.UseConnectionHandler<BlockingConnectionHandler>(); builder.UseConnectionHandler<BlockingConnectionHandler>();
var app = builder.Build(); var app = builder.Build();
var options = new HttpConnectionDispatcherOptions(); var options = new HttpConnectionDispatcherOptions();
// Initial poll
var task = dispatcher.ExecuteAsync(context, options, app); var task = dispatcher.ExecuteAsync(context, options, app);
Assert.True(task.IsCompleted);
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
// Real long running poll
task = dispatcher.ExecuteAsync(context, options, app);
var buffer = Encoding.UTF8.GetBytes("Hello World"); var buffer = Encoding.UTF8.GetBytes("Hello World");
@ -1166,7 +1184,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
var options = new HttpConnectionDispatcherOptions(); var options = new HttpConnectionDispatcherOptions();
var context1 = MakeRequest("/foo", connection); var context1 = MakeRequest("/foo", connection);
// This is the initial poll to make sure things are setup
var task1 = dispatcher.ExecuteAsync(context1, options, app); var task1 = dispatcher.ExecuteAsync(context1, options, app);
Assert.True(task1.IsCompleted);
task1 = dispatcher.ExecuteAsync(context1, options, app);
var context2 = MakeRequest("/foo", connection); var context2 = MakeRequest("/foo", connection);
var task2 = dispatcher.ExecuteAsync(context2, options, app); var task2 = dispatcher.ExecuteAsync(context2, options, app);
@ -1363,8 +1384,11 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "name") })); context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "name") }));
var connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app); var connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app);
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).AsTask().OrTimeout(); await connectionHandlerTask.OrTimeout();
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app);
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).AsTask().OrTimeout();
await connectionHandlerTask.OrTimeout(); await connectionHandlerTask.OrTimeout();
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
@ -1444,7 +1468,12 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
new Claim(ClaimTypes.StreetAddress, "12345 123rd St. NW") new Claim(ClaimTypes.StreetAddress, "12345 123rd St. NW")
})); }));
// First poll
var connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app); var connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app);
Assert.True(connectionHandlerTask.IsCompleted);
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app);
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).AsTask().OrTimeout(); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).AsTask().OrTimeout();
await connectionHandlerTask.OrTimeout(); await connectionHandlerTask.OrTimeout();
@ -1502,7 +1531,12 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
// "authorize" user // "authorize" user
context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "name") })); context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "name") }));
// Initial poll
var connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app); var connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app);
Assert.True(connectionHandlerTask.IsCompleted);
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
connectionHandlerTask = dispatcher.ExecuteAsync(context, options, app);
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).AsTask().OrTimeout(); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).AsTask().OrTimeout();
await connectionHandlerTask.OrTimeout(); await connectionHandlerTask.OrTimeout();
@ -1587,12 +1621,12 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
var options = new HttpConnectionDispatcherOptions(); var options = new HttpConnectionDispatcherOptions();
options.LongPolling.PollTimeout = TimeSpan.FromMilliseconds(1); // We don't care about the poll itself options.LongPolling.PollTimeout = TimeSpan.FromMilliseconds(1); // We don't care about the poll itself
Assert.Null(connection.Features.Get<IConnectionInherentKeepAliveFeature>());
await dispatcher.ExecuteAsync(context, options, app).OrTimeout(); await dispatcher.ExecuteAsync(context, options, app).OrTimeout();
Assert.NotNull(connection.Features.Get<IConnectionInherentKeepAliveFeature>()); Assert.True(connection.HasInherentKeepAlive);
Assert.Equal(options.LongPolling.PollTimeout, connection.Features.Get<IConnectionInherentKeepAliveFeature>().KeepAliveInterval);
// Check via the feature as well to make sure it's there.
Assert.True(connection.Features.Get<IConnectionInherentKeepAliveFeature>().HasInherentKeepAlive);
} }
} }
@ -1660,6 +1694,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
var options = new HttpConnectionDispatcherOptions(); var options = new HttpConnectionDispatcherOptions();
var pollTask = dispatcher.ExecuteAsync(context, options, app); var pollTask = dispatcher.ExecuteAsync(context, options, app);
Assert.True(pollTask.IsCompleted);
// Now send the second poll
pollTask = dispatcher.ExecuteAsync(context, options, app);
// Issue the delete request and make sure the poll completes // Issue the delete request and make sure the poll completes
var deleteContext = new DefaultHttpContext(); var deleteContext = new DefaultHttpContext();

View File

@ -7,9 +7,9 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.AspNetCore.Http.Connections.Client;
using Microsoft.AspNetCore.Http.Connections.Client.Internal; using Microsoft.AspNetCore.Http.Connections.Client.Internal;
using Microsoft.AspNetCore.Http.Connections.Internal;
using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Logging.Testing;
using Xunit; using Xunit;

View File

@ -9,17 +9,24 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.AspNetCore.Http.Connections.Client;
using Microsoft.AspNetCore.Http.Connections.Client.Internal; using Microsoft.AspNetCore.Http.Connections.Client.Internal;
using Microsoft.AspNetCore.SignalR.Tests;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.SignalR.Client.Tests namespace Microsoft.AspNetCore.SignalR.Client.Tests
{ {
public partial class HttpConnectionTests public partial class HttpConnectionTests
{ {
public class Transport public class Transport : VerifiableLoggedTest
{ {
public Transport(ITestOutputHelper output) : base(output)
{
}
[Theory] [Theory]
[InlineData(HttpTransportType.LongPolling)] [InlineData(HttpTransportType.LongPolling)]
[InlineData(HttpTransportType.ServerSentEvents)] [InlineData(HttpTransportType.ServerSentEvents)]
@ -29,11 +36,6 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
var requestsExecuted = false; var requestsExecuted = false;
var callCount = 0; var callCount = 0;
testHttpHandler.OnRequest((request, next, token) =>
{
return Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
});
testHttpHandler.OnNegotiate((_, cancellationToken) => testHttpHandler.OnNegotiate((_, cancellationToken) =>
{ {
return ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()); return ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent());
@ -51,6 +53,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
return await next(); return await next();
}); });
testHttpHandler.OnRequest((request, next, token) =>
{
return Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
});
Task<string> AccessTokenProvider() Task<string> AccessTokenProvider()
{ {
callCount++; callCount++;
@ -69,6 +76,32 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
Assert.True(requestsExecuted); Assert.True(requestsExecuted);
} }
[Theory]
[InlineData(HttpTransportType.LongPolling, true)]
[InlineData(HttpTransportType.ServerSentEvents, false)]
public async Task HttpConnectionSetsInherentKeepAliveFeature(HttpTransportType transportType, bool expectedValue)
{
using (StartVerifableLog(out var loggerFactory, testName: $"HttpConnectionSetsInherentKeepAliveFeature_{transportType}_{expectedValue}"))
{
var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false);
testHttpHandler.OnNegotiate((_, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
testHttpHandler.OnRequest((request, next, token) => Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent)));
await WithConnectionAsync(
CreateConnection(testHttpHandler, transportType: transportType, loggerFactory: loggerFactory),
async (connection) =>
{
await connection.StartAsync(TransferFormat.Text).OrTimeout();
var feature = connection.Features.Get<IConnectionInherentKeepAliveFeature>();
Assert.NotNull(feature);
Assert.Equal(expectedValue, feature.HasInherentKeepAlive);
});
}
}
[Theory] [Theory]
[InlineData(HttpTransportType.LongPolling)] [InlineData(HttpTransportType.LongPolling)]
[InlineData(HttpTransportType.ServerSentEvents)] [InlineData(HttpTransportType.ServerSentEvents)]
@ -77,10 +110,6 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false); var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false);
var requestsExecuted = false; var requestsExecuted = false;
testHttpHandler.OnRequest((request, next, token) =>
{
return Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
});
testHttpHandler.OnNegotiate((_, cancellationToken) => testHttpHandler.OnNegotiate((_, cancellationToken) =>
{ {
@ -105,6 +134,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
return await next(); return await next();
}); });
testHttpHandler.OnRequest((request, next, token) =>
{
return Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
});
await WithConnectionAsync( await WithConnectionAsync(
CreateConnection(testHttpHandler, transportType: transportType), CreateConnection(testHttpHandler, transportType: transportType),
async (connection) => async (connection) =>
@ -124,11 +158,6 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false); var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false);
var requestsExecuted = false; var requestsExecuted = false;
testHttpHandler.OnRequest((request, next, token) =>
{
return Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
});
testHttpHandler.OnNegotiate((_, cancellationToken) => testHttpHandler.OnNegotiate((_, cancellationToken) =>
{ {
return ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()); return ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent());
@ -145,6 +174,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
return await next(); return await next();
}); });
testHttpHandler.OnRequest((request, next, token) =>
{
return Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
});
await WithConnectionAsync( await WithConnectionAsync(
CreateConnection(testHttpHandler, transportType: transportType), CreateConnection(testHttpHandler, transportType: transportType),
async (connection) => async (connection) =>

View File

@ -109,15 +109,20 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
if (requests == 0) if (requests == 0)
{ {
requests++; requests++;
return ResponseUtils.CreateResponse(HttpStatusCode.OK, "Hello"); return ResponseUtils.CreateResponse(HttpStatusCode.OK);
} }
else if (requests == 1) else if (requests == 1)
{
requests++;
return ResponseUtils.CreateResponse(HttpStatusCode.OK, "Hello");
}
else if (requests == 2)
{ {
requests++; requests++;
// Time out // Time out
return ResponseUtils.CreateResponse(HttpStatusCode.OK); return ResponseUtils.CreateResponse(HttpStatusCode.OK);
} }
else if (requests == 2) else if (requests == 3)
{ {
requests++; requests++;
return ResponseUtils.CreateResponse(HttpStatusCode.OK, "World"); return ResponseUtils.CreateResponse(HttpStatusCode.OK, "World");
@ -147,7 +152,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
} }
[Fact] [Fact]
public async Task LongPollingTransportStopsWhenPollRequestFails() public async Task LongPollingTransportStartAsyncFailsIfFirstRequestFails()
{ {
var mockHttpHandler = new Mock<HttpMessageHandler>(); var mockHttpHandler = new Mock<HttpMessageHandler>();
mockHttpHandler.Protected() mockHttpHandler.Protected()
@ -158,6 +163,39 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
return ResponseUtils.CreateResponse(HttpStatusCode.InternalServerError); return ResponseUtils.CreateResponse(HttpStatusCode.InternalServerError);
}); });
using (var httpClient = new HttpClient(mockHttpHandler.Object))
{
var longPollingTransport = new LongPollingTransport(httpClient);
try
{
var exception = await Assert.ThrowsAsync<HttpRequestException>(() => longPollingTransport.StartAsync(TestUri, TransferFormat.Binary));
Assert.Contains(" 500 ", exception.Message);
}
finally
{
await longPollingTransport.StopAsync();
}
}
}
[Fact]
public async Task LongPollingTransportStopsWhenPollRequestFails()
{
var mockHttpHandler = new Mock<HttpMessageHandler>();
var firstPoll = true;
mockHttpHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Returns<HttpRequestMessage, CancellationToken>(async (request, cancellationToken) =>
{
await Task.Yield();
if (firstPoll)
{
firstPoll = false;
return ResponseUtils.CreateResponse(HttpStatusCode.OK);
}
return ResponseUtils.CreateResponse(HttpStatusCode.InternalServerError);
});
using (var httpClient = new HttpClient(mockHttpHandler.Object)) using (var httpClient = new HttpClient(mockHttpHandler.Object))
{ {
var longPollingTransport = new LongPollingTransport(httpClient); var longPollingTransport = new LongPollingTransport(httpClient);
@ -314,7 +352,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
{ {
var message1Payload = new[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o' }; var message1Payload = new[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o' };
var firstCall = true; var requests = 0;
var mockHttpHandler = new Mock<HttpMessageHandler>(); var mockHttpHandler = new Mock<HttpMessageHandler>();
var sentRequests = new List<HttpRequestMessage>(); var sentRequests = new List<HttpRequestMessage>();
mockHttpHandler.Protected() mockHttpHandler.Protected()
@ -325,9 +363,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
await Task.Yield(); await Task.Yield();
if (firstCall) if (requests == 0)
{ {
firstCall = false; requests++;
return ResponseUtils.CreateResponse(HttpStatusCode.OK);
}
else if (requests == 1)
{
requests++;
return ResponseUtils.CreateResponse(HttpStatusCode.OK, message1Payload); return ResponseUtils.CreateResponse(HttpStatusCode.OK, message1Payload);
} }
@ -349,7 +392,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
var message = await longPollingTransport.Input.ReadAllAsync(); var message = await longPollingTransport.Input.ReadAllAsync();
// Check the provided request // Check the provided request
Assert.Equal(2, sentRequests.Count); Assert.Equal(3, sentRequests.Count);
// Check the messages received // Check the messages received
Assert.Equal(message1Payload, message); Assert.Equal(message1Payload, message);
@ -366,6 +409,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
{ {
var sentRequests = new List<byte[]>(); var sentRequests = new List<byte[]>();
var tcs = new TaskCompletionSource<HttpResponseMessage>(); var tcs = new TaskCompletionSource<HttpResponseMessage>();
var firstPoll = true;
var mockHttpHandler = new Mock<HttpMessageHandler>(); var mockHttpHandler = new Mock<HttpMessageHandler>();
mockHttpHandler.Protected() mockHttpHandler.Protected()
@ -380,6 +424,13 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
} }
else if (request.Method == HttpMethod.Get) else if (request.Method == HttpMethod.Get)
{ {
// First poll completes immediately
if (firstPoll)
{
firstPoll = false;
return ResponseUtils.CreateResponse(HttpStatusCode.OK);
}
cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken)); cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken));
// This is the poll task // This is the poll task
return await tcs.Task; return await tcs.Task;
@ -426,6 +477,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
var sentRequests = new List<byte[]>(); var sentRequests = new List<byte[]>();
var pollTcs = new TaskCompletionSource<HttpResponseMessage>(); var pollTcs = new TaskCompletionSource<HttpResponseMessage>();
var deleteTcs = new TaskCompletionSource<object>(); var deleteTcs = new TaskCompletionSource<object>();
var firstPoll = true;
var mockHttpHandler = new Mock<HttpMessageHandler>(); var mockHttpHandler = new Mock<HttpMessageHandler>();
mockHttpHandler.Protected() mockHttpHandler.Protected()
@ -440,6 +492,13 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
} }
else if (request.Method == HttpMethod.Get) else if (request.Method == HttpMethod.Get)
{ {
// First poll completes immediately
if (firstPoll)
{
firstPoll = false;
return ResponseUtils.CreateResponse(HttpStatusCode.OK);
}
cancellationToken.Register(() => pollTcs.TrySetCanceled(cancellationToken)); cancellationToken.Register(() => pollTcs.TrySetCanceled(cancellationToken));
// This is the poll task // This is the poll task
return await pollTcs.Task; return await pollTcs.Task;
@ -538,7 +597,13 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
{ {
await Task.Yield(); await Task.Yield();
if (Interlocked.Increment(ref numPolls) < 3) if (numPolls == 0)
{
numPolls++;
return ResponseUtils.CreateResponse(HttpStatusCode.OK);
}
if (numPolls++ < 3)
{ {
throw new OperationCanceledException(); throw new OperationCanceledException();
} }

View File

@ -6,6 +6,8 @@
<ItemGroup> <ItemGroup>
<Compile Include="..\..\src\Common\MemoryBufferWriter.cs" Link="MemoryBufferWriter.cs" /> <Compile Include="..\..\src\Common\MemoryBufferWriter.cs" Link="MemoryBufferWriter.cs" />
<Compile Include="..\..\src\Common\TextMessageFormatter.cs" Link="TextMessageFormatter.cs" />
<Compile Include="..\..\src\Common\TextMessageParser.cs" Link="TextMessageParser.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,11 +3,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Http.Connections.Internal;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Microsoft.AspNetCore.SignalR.Client.Tests namespace Microsoft.AspNetCore.SignalR.Client.Tests
@ -40,6 +40,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
public static bool IsLongPollRequest(HttpRequestMessage request) public static bool IsLongPollRequest(HttpRequestMessage request)
{ {
return request.Method == HttpMethod.Get && return request.Method == HttpMethod.Get &&
!IsServerSentEventsRequest(request) &&
(request.RequestUri.PathAndQuery.Contains("?id=") || request.RequestUri.PathAndQuery.Contains("&id=")); (request.RequestUri.PathAndQuery.Contains("?id=") || request.RequestUri.PathAndQuery.Contains("&id="));
} }
@ -49,6 +50,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
(request.RequestUri.PathAndQuery.Contains("?id=") || request.RequestUri.PathAndQuery.Contains("&id=")); (request.RequestUri.PathAndQuery.Contains("?id=") || request.RequestUri.PathAndQuery.Contains("&id="));
} }
public static bool IsServerSentEventsRequest(HttpRequestMessage request)
{
return request.Method == HttpMethod.Get && request.Headers.Accept.Any(h => h.MediaType == "text/event-stream");
}
public static bool IsSocketSendRequest(HttpRequestMessage request) public static bool IsSocketSendRequest(HttpRequestMessage request)
{ {
return request.Method == HttpMethod.Post && return request.Method == HttpMethod.Post &&

View File

@ -12,7 +12,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
using Newtonsoft.Json; using Newtonsoft.Json;

View File

@ -7,10 +7,14 @@ using System.Threading.Tasks;
namespace Microsoft.AspNetCore.SignalR.Client.Tests namespace Microsoft.AspNetCore.SignalR.Client.Tests
{ {
delegate Task<HttpResponseMessage> RequestDelegate(HttpRequestMessage requestMessage, CancellationToken cancellationToken);
public class TestHttpMessageHandler : HttpMessageHandler public class TestHttpMessageHandler : HttpMessageHandler
{ {
private List<HttpRequestMessage> _receivedRequests = new List<HttpRequestMessage>(); private List<HttpRequestMessage> _receivedRequests = new List<HttpRequestMessage>();
private Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handler; private RequestDelegate _app;
private List<Func<RequestDelegate, RequestDelegate>> _middleware = new List<Func<RequestDelegate, RequestDelegate>>();
public IReadOnlyList<HttpRequestMessage> ReceivedRequests public IReadOnlyList<HttpRequestMessage> ReceivedRequests
{ {
@ -23,14 +27,29 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
} }
} }
public TestHttpMessageHandler(bool autoNegotiate = true) public TestHttpMessageHandler(bool autoNegotiate = true, bool handleFirstPoll = true)
{ {
_handler = BaseHandler;
if (autoNegotiate) if (autoNegotiate)
{ {
OnNegotiate((_, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent())); OnNegotiate((_, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
} }
if (handleFirstPoll)
{
var firstPoll = true;
OnRequest(async (request, next, cancellationToken) =>
{
if (ResponseUtils.IsLongPollRequest(request) && firstPoll)
{
firstPoll = false;
return ResponseUtils.CreateResponse(HttpStatusCode.OK);
}
else
{
return await next();
}
});
}
} }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
@ -40,9 +59,21 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
lock (_receivedRequests) lock (_receivedRequests)
{ {
_receivedRequests.Add(request); _receivedRequests.Add(request);
if (_app == null)
{
_middleware.Reverse();
RequestDelegate handler = BaseHandler;
foreach (var middleware in _middleware)
{
handler = middleware(handler);
}
_app = handler;
}
} }
return await _handler(request, cancellationToken); return await _app(request, cancellationToken);
} }
public static TestHttpMessageHandler CreateDefault() public static TestHttpMessageHandler CreateDefault()
@ -80,8 +111,18 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
public void OnRequest(Func<HttpRequestMessage, Func<Task<HttpResponseMessage>>, CancellationToken, Task<HttpResponseMessage>> handler) public void OnRequest(Func<HttpRequestMessage, Func<Task<HttpResponseMessage>>, CancellationToken, Task<HttpResponseMessage>> handler)
{ {
var nextHandler = _handler; void OnRequestCore(Func<RequestDelegate, RequestDelegate> middleware)
_handler = (request, cancellationToken) => handler(request, () => nextHandler(request, cancellationToken), cancellationToken); {
_middleware.Add(middleware);
}
OnRequestCore(next =>
{
return (request, cancellationToken) =>
{
return handler(request, () => next(request, cancellationToken), cancellationToken);
};
});
} }
public void OnGet(string pathAndQuery, Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handler) => OnRequest(HttpMethod.Get, pathAndQuery, handler); public void OnGet(string pathAndQuery, Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handler) => OnRequest(HttpMethod.Get, pathAndQuery, handler);

View File

@ -9,7 +9,6 @@ using System.Linq;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal; using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters

View File

@ -5,7 +5,7 @@ using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.AspNetCore.SignalR.Internal.Formatters; using Microsoft.AspNetCore.Internal;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters

View File

@ -4,7 +4,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using Microsoft.AspNetCore.SignalR.Internal.Formatters; using Microsoft.AspNetCore.Internal;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters

View File

@ -4,7 +4,7 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Text; using System.Text;
using Microsoft.AspNetCore.SignalR.Internal.Formatters; using Microsoft.AspNetCore.Internal;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters

View File

@ -8,7 +8,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;

View File

@ -8,7 +8,6 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol

View File

@ -4,6 +4,11 @@
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks> <TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Include="..\..\src\Common\BinaryMessageFormatter.cs" Link="BinaryMessageFormatter.cs" />
<Compile Include="..\..\src\Common\BinaryMessageParser.cs" Link="BinaryMessageParser.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\xunit.runner.json" Link="xunit.runner.json"> <Content Include="..\xunit.runner.json" Link="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View File

@ -13,14 +13,14 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
return message; return message;
} }
public Task EchoGroup(string group, string message) public Task EchoGroup(string groupName, string message)
{ {
return Clients.Group(group).SendAsync("Echo", message); return Clients.Group(groupName).SendAsync("Echo", message);
} }
public Task AddSelfToGroup(string group) public Task AddSelfToGroup(string groupName)
{ {
return Groups.AddAsync(Context.ConnectionId, group); return Groups.AddToGroupAsync(Context.ConnectionId, groupName);
} }
} }
} }

View File

@ -26,16 +26,16 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
var options = provider.GetService<IOptions<RedisOptions>>(); var options = provider.GetService<IOptions<RedisOptions>>();
Assert.NotNull(options.Value); Assert.NotNull(options.Value);
Assert.NotNull(options.Value.Options); Assert.NotNull(options.Value.Configuration);
Assert.Equal(password, options.Value.Options.Password); Assert.Equal(password, options.Value.Configuration.Password);
Assert.Collection(options.Value.Options.EndPoints, Assert.Collection(options.Value.Configuration.EndPoints,
endpoint => endpoint =>
{ {
var dnsEndpoint = Assert.IsType<DnsEndPoint>(endpoint); var dnsEndpoint = Assert.IsType<DnsEndPoint>(endpoint);
Assert.Equal(host, dnsEndpoint.Host); Assert.Equal(host, dnsEndpoint.Host);
Assert.Equal(port, dnsEndpoint.Port); Assert.Equal(port, dnsEndpoint.Port);
}); });
Assert.Equal(useSsl, options.Value.Options.Ssl); Assert.Equal(useSsl, options.Value.Configuration.Ssl);
} }
} }
} }

View File

@ -110,7 +110,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager.OnConnectedAsync(connection1).OrTimeout(); await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout(); await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddGroupAsync(connection1.ConnectionId, "gunit").OrTimeout(); await manager.AddToGroupAsync(connection1.ConnectionId, "gunit").OrTimeout();
await manager.SendGroupAsync("gunit", "Hello", new object[] { "World" }).OrTimeout(); await manager.SendGroupAsync("gunit", "Hello", new object[] { "World" }).OrTimeout();
@ -134,11 +134,11 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager.OnConnectedAsync(connection1).OrTimeout(); await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.OnConnectedAsync(connection2).OrTimeout(); await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddGroupAsync(connection1.ConnectionId, "gunit").OrTimeout(); await manager.AddToGroupAsync(connection1.ConnectionId, "gunit").OrTimeout();
await manager.AddGroupAsync(connection2.ConnectionId, "gunit").OrTimeout(); await manager.AddToGroupAsync(connection2.ConnectionId, "gunit").OrTimeout();
var excludedIds = new List<string> { client2.Connection.ConnectionId }; var excludedConnectionIds = new List<string> { client2.Connection.ConnectionId };
await manager.SendGroupExceptAsync("gunit", "Hello", new object[] { "World" }, excludedIds).OrTimeout(); await manager.SendGroupExceptAsync("gunit", "Hello", new object[] { "World" }, excludedConnectionIds).OrTimeout();
await AssertMessageAsync(client1); await AssertMessageAsync(client1);
Assert.Null(client2.TryRead()); Assert.Null(client2.TryRead());
@ -257,7 +257,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager1.OnConnectedAsync(connection).OrTimeout(); await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout(); await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
@ -278,7 +278,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager.OnConnectedAsync(connection).OrTimeout(); await manager.OnConnectedAsync(connection).OrTimeout();
await manager.AddGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.OnDisconnectedAsync(connection).OrTimeout(); await manager.OnDisconnectedAsync(connection).OrTimeout();
@ -301,7 +301,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager.OnConnectedAsync(connection).OrTimeout(); await manager.OnConnectedAsync(connection).OrTimeout();
await manager.RemoveGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
} }
} }
@ -319,7 +319,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager1.OnConnectedAsync(connection).OrTimeout(); await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.RemoveGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager2.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
} }
} }
@ -337,7 +337,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager1.OnConnectedAsync(connection).OrTimeout(); await manager1.OnConnectedAsync(connection).OrTimeout();
await manager2.AddGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager2.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout(); await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
@ -358,8 +358,8 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager.OnConnectedAsync(connection).OrTimeout(); await manager.OnConnectedAsync(connection).OrTimeout();
await manager.AddGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.AddGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout(); await manager.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
@ -382,8 +382,8 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager1.OnConnectedAsync(connection).OrTimeout(); await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.AddGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager2.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout(); await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
@ -406,13 +406,13 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
await manager1.OnConnectedAsync(connection).OrTimeout(); await manager1.OnConnectedAsync(connection).OrTimeout();
await manager1.AddGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager1.AddToGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout(); await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
await AssertMessageAsync(client); await AssertMessageAsync(client);
await manager2.RemoveGroupAsync(connection.ConnectionId, "name").OrTimeout(); await manager2.RemoveFromGroupAsync(connection.ConnectionId, "name").OrTimeout();
await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout(); await manager2.SendGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
@ -484,9 +484,9 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
var connection2 = HubConnectionContextUtils.Create(client2.Connection); var connection2 = HubConnectionContextUtils.Create(client2.Connection);
await manager.OnConnectedAsync(connection1).OrTimeout(); await manager.OnConnectedAsync(connection1).OrTimeout();
await manager.AddGroupAsync(connection1.ConnectionId, "group"); await manager.AddToGroupAsync(connection1.ConnectionId, "group");
await manager.OnConnectedAsync(connection2).OrTimeout(); await manager.OnConnectedAsync(connection2).OrTimeout();
await manager.AddGroupAsync(connection2.ConnectionId, "group"); await manager.AddToGroupAsync(connection2.ConnectionId, "group");
await manager.SendGroupAsync("group", "Hello", new object[] { "World" }).OrTimeout(); await manager.SendGroupAsync("group", "Hello", new object[] { "World" }).OrTimeout();
// connection1 will throw when receiving a group message, we are making sure other connections // connection1 will throw when receiving a group message, we are making sure other connections
@ -550,7 +550,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
private RedisHubLifetimeManager<MyHub> CreateLifetimeManager(TestRedisServer server, MessagePackHubProtocolOptions messagePackOptions = null, JsonHubProtocolOptions jsonOptions = null) private RedisHubLifetimeManager<MyHub> CreateLifetimeManager(TestRedisServer server, MessagePackHubProtocolOptions messagePackOptions = null, JsonHubProtocolOptions jsonOptions = null)
{ {
var options = new RedisOptions() { Factory = async (t) => await Task.FromResult(new TestConnectionMultiplexer(server)) }; var options = new RedisOptions() { ConnectionFactory = async (t) => await Task.FromResult(new TestConnectionMultiplexer(server)) };
messagePackOptions = messagePackOptions ?? new MessagePackHubProtocolOptions(); messagePackOptions = messagePackOptions ?? new MessagePackHubProtocolOptions();
jsonOptions = jsonOptions ?? new JsonHubProtocolOptions(); jsonOptions = jsonOptions ?? new JsonHubProtocolOptions();

View File

@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
var decoded = protocol.ReadInvocation(testData.Encoded); var decoded = protocol.ReadInvocation(testData.Encoded);
Assert.Equal(testData.Decoded.ExcludedIds, decoded.ExcludedIds); Assert.Equal(testData.Decoded.ExcludedConnectionIds, decoded.ExcludedConnectionIds);
// Verify the deserialized object has the necessary serialized forms // Verify the deserialized object has the necessary serialized forms
foreach (var hubProtocol in hubProtocols) foreach (var hubProtocol in hubProtocols)
@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
// Actual invocation doesn't matter because we're using a dummy hub protocol. // Actual invocation doesn't matter because we're using a dummy hub protocol.
// But the dummy protocol will check that we gave it the test message to make sure everything flows through properly. // But the dummy protocol will check that we gave it the test message to make sure everything flows through properly.
var encoded = protocol.WriteInvocation(_testMessage.Target, _testMessage.Arguments, testData.Decoded.ExcludedIds); var encoded = protocol.WriteInvocation(_testMessage.Target, _testMessage.Arguments, testData.Decoded.ExcludedConnectionIds);
Assert.Equal(testData.Encoded, encoded); Assert.Equal(testData.Encoded, encoded);
} }

Some files were not shown because too many files have changed in this diff Show More