From 75e102f97aa9ea999d5890d8067c12e981bbd864 Mon Sep 17 00:00:00 2001 From: Mikael Mengistu Date: Mon, 11 Dec 2017 17:30:53 -0800 Subject: [PATCH] Support for Others and Caller Client Subsets(#1192) --- samples/ChatSample/Hubs/Chat.cs | 1 - .../ChatSample/PresenceHubLifetimeManager.cs | 2 +- .../DynamicHubClients.cs | 10 +- src/Microsoft.AspNetCore.SignalR.Core/Hub.cs | 4 +- .../HubCallerClients.cs | 47 ++++++++ .../HubEndPoint.cs | 2 +- .../Hub`T.cs | 4 +- .../IHubCallerClients.cs | 7 ++ .../IHubCallerClients`T.cs | 12 ++ .../IHubClients`T.cs | 3 +- .../TypedHubClients.cs | 22 ++-- .../HubEndpointTests.cs | 108 ++++++++++++++++++ 12 files changed, 200 insertions(+), 22 deletions(-) create mode 100644 src/Microsoft.AspNetCore.SignalR.Core/HubCallerClients.cs create mode 100644 src/Microsoft.AspNetCore.SignalR.Core/IHubCallerClients.cs create mode 100644 src/Microsoft.AspNetCore.SignalR.Core/IHubCallerClients`T.cs diff --git a/samples/ChatSample/Hubs/Chat.cs b/samples/ChatSample/Hubs/Chat.cs index 74a0317924..61f77b52b1 100644 --- a/samples/ChatSample/Hubs/Chat.cs +++ b/samples/ChatSample/Hubs/Chat.cs @@ -18,7 +18,6 @@ namespace ChatSample.Hubs public override async Task OnConnectedAsync() { await Clients.Client(Context.ConnectionId).InvokeAsync("SetUsersOnline", await GetUsersOnline()); - await base.OnConnectedAsync(); } diff --git a/samples/ChatSample/PresenceHubLifetimeManager.cs b/samples/ChatSample/PresenceHubLifetimeManager.cs index ac3c61f66a..6bc1a25bdb 100644 --- a/samples/ChatSample/PresenceHubLifetimeManager.cs +++ b/samples/ChatSample/PresenceHubLifetimeManager.cs @@ -111,7 +111,7 @@ namespace ChatSample _hubContext = _serviceProvider.GetRequiredService>(); } - hub.Clients = _hubContext.Clients; + hub.Clients = new HubCallerClients(_hubContext.Clients, connection.ConnectionId); hub.Context = new HubCallerContext(connection); hub.Groups = _hubContext.Groups; diff --git a/src/Microsoft.AspNetCore.SignalR.Core/DynamicHubClients.cs b/src/Microsoft.AspNetCore.SignalR.Core/DynamicHubClients.cs index 6b4f4db8c0..09a15c8db4 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/DynamicHubClients.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/DynamicHubClients.cs @@ -7,17 +7,19 @@ namespace Microsoft.AspNetCore.SignalR { public class DynamicHubClients { - private readonly IHubClients _clients; + private readonly IHubCallerClients _clients; - public DynamicHubClients(IHubClients clients) + public DynamicHubClients(IHubCallerClients clients) { _clients = clients; } public dynamic All => new DynamicClientProxy(_clients.All); public dynamic AllExcept(IReadOnlyList excludedIds) => new DynamicClientProxy(_clients.AllExcept(excludedIds)); - public dynamic User(string userId) => new DynamicClientProxy(_clients.User(userId)); - public dynamic Group(string group) => new DynamicClientProxy(_clients.Group(group)); + public dynamic Caller => new DynamicClientProxy(_clients.Caller); public dynamic Client(string connectionId) => new DynamicClientProxy(_clients.Client(connectionId)); + public dynamic Group(string group) => new DynamicClientProxy(_clients.Group(group)); + public dynamic Others => new DynamicClientProxy(_clients.Others); + public dynamic User(string userId) => new DynamicClientProxy(_clients.User(userId)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SignalR.Core/Hub.cs b/src/Microsoft.AspNetCore.SignalR.Core/Hub.cs index 22ddb91471..7075660886 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/Hub.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/Hub.cs @@ -9,11 +9,11 @@ namespace Microsoft.AspNetCore.SignalR public class Hub : IDisposable { private bool _disposed; - private IHubClients _clients; + private IHubCallerClients _clients; private HubCallerContext _context; private IGroupManager _groups; - public IHubClients Clients + public IHubCallerClients Clients { get { diff --git a/src/Microsoft.AspNetCore.SignalR.Core/HubCallerClients.cs b/src/Microsoft.AspNetCore.SignalR.Core/HubCallerClients.cs new file mode 100644 index 0000000000..1ff7d5d34b --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Core/HubCallerClients.cs @@ -0,0 +1,47 @@ +// 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 class HubCallerClients : IHubCallerClients + { + private string _connectionId; + private IHubClients _hubClients; + private string[] _currentConnectionId; + + public HubCallerClients(IHubClients hubClients, string connectionId) + { + _connectionId = connectionId; + _hubClients = hubClients; + _currentConnectionId = new string[] { _connectionId }; + } + + public IClientProxy Caller => _hubClients.Client(_connectionId); + + public IClientProxy Others => _hubClients.AllExcept(_currentConnectionId); + + public IClientProxy All => _hubClients.All; + + public IClientProxy AllExcept(IReadOnlyList excludedIds) + { + return _hubClients.AllExcept(excludedIds); + } + + public IClientProxy Client(string connectionId) + { + return _hubClients.Client(connectionId); + } + + public IClientProxy Group(string groupName) + { + return _hubClients.Group(groupName); + } + + public IClientProxy User(string userId) + { + return _hubClients.User(userId); + } + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs b/src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs index 0d904a2d83..4dbd7e1d9d 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs @@ -368,7 +368,7 @@ namespace Microsoft.AspNetCore.SignalR private void InitializeHub(THub hub, HubConnectionContext connection) { - hub.Clients = _hubContext.Clients; + hub.Clients = new HubCallerClients(_hubContext.Clients, connection.ConnectionId); hub.Context = new HubCallerContext(connection); hub.Groups = _hubContext.Groups; } diff --git a/src/Microsoft.AspNetCore.SignalR.Core/Hub`T.cs b/src/Microsoft.AspNetCore.SignalR.Core/Hub`T.cs index c7583cb29d..e4c0ba7873 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/Hub`T.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/Hub`T.cs @@ -5,9 +5,9 @@ namespace Microsoft.AspNetCore.SignalR { public class Hub : Hub where T : class { - private IHubClients _clients; + private IHubCallerClients _clients; - public new IHubClients Clients + public new IHubCallerClients Clients { get { diff --git a/src/Microsoft.AspNetCore.SignalR.Core/IHubCallerClients.cs b/src/Microsoft.AspNetCore.SignalR.Core/IHubCallerClients.cs new file mode 100644 index 0000000000..0bc7adb0aa --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Core/IHubCallerClients.cs @@ -0,0 +1,7 @@ +// 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. + +namespace Microsoft.AspNetCore.SignalR +{ + public interface IHubCallerClients : IHubCallerClients { } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Core/IHubCallerClients`T.cs b/src/Microsoft.AspNetCore.SignalR.Core/IHubCallerClients`T.cs new file mode 100644 index 0000000000..471a9e5584 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Core/IHubCallerClients`T.cs @@ -0,0 +1,12 @@ +// 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. + +namespace Microsoft.AspNetCore.SignalR +{ + public interface IHubCallerClients : IHubClients + { + T Caller { get; } + + T Others { get; } + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Core/IHubClients`T.cs b/src/Microsoft.AspNetCore.SignalR.Core/IHubClients`T.cs index 92c75ddc3d..cf62e6ca22 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/IHubClients`T.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/IHubClients`T.cs @@ -1,9 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; -using System.Text; namespace Microsoft.AspNetCore.SignalR { @@ -20,3 +18,4 @@ namespace Microsoft.AspNetCore.SignalR T User(string userId); } } + diff --git a/src/Microsoft.AspNetCore.SignalR.Core/TypedHubClients.cs b/src/Microsoft.AspNetCore.SignalR.Core/TypedHubClients.cs index 6677d6b240..34d9974201 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/TypedHubClients.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/TypedHubClients.cs @@ -5,32 +5,36 @@ using System.Collections.Generic; namespace Microsoft.AspNetCore.SignalR { - internal class TypedHubClients : IHubClients + internal class TypedHubClients : IHubCallerClients { - private IHubClients hubClients; + private IHubCallerClients _hubClients; - public TypedHubClients(IHubClients dynamicContext) + public TypedHubClients(IHubCallerClients dynamicContext) { - hubClients = dynamicContext; + _hubClients = dynamicContext; } - public T All => TypedClientBuilder.Build(hubClients.All); + public T All => TypedClientBuilder.Build(_hubClients.All); - public T AllExcept(IReadOnlyList excludedIds) => TypedClientBuilder.Build(hubClients.AllExcept(excludedIds)); + public T Caller => TypedClientBuilder.Build(_hubClients.Caller); + + public T Others => TypedClientBuilder.Build(_hubClients.Others); + + public T AllExcept(IReadOnlyList excludedIds) => TypedClientBuilder.Build(_hubClients.AllExcept(excludedIds)); public T Client(string connectionId) { - return TypedClientBuilder.Build(hubClients.Client(connectionId)); + return TypedClientBuilder.Build(_hubClients.Client(connectionId)); } public T Group(string groupName) { - return TypedClientBuilder.Build(hubClients.Group(groupName)); + return TypedClientBuilder.Build(_hubClients.Group(groupName)); } public T User(string userId) { - return TypedClientBuilder.Build(hubClients.User(userId)); + return TypedClientBuilder.Build(_hubClients.User(userId)); } } } diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTests.cs index 014f9d7e91..00e1d378b6 100644 --- a/test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTests.cs @@ -826,6 +826,84 @@ namespace Microsoft.AspNetCore.SignalR.Tests } } + [Theory] + [MemberData(nameof(HubTypes))] + public async Task SendToOthers(Type hubType) + { + var serviceProvider = CreateServiceProvider(); + + dynamic endPoint = serviceProvider.GetService(GetEndPointType(hubType)); + + using (var firstClient = new TestClient()) + using (var secondClient = new TestClient()) + { + Task firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection); + Task secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection); + + await Task.WhenAll(firstClient.Connected, secondClient.Connected).OrTimeout(); + + await firstClient.SendInvocationAsync("SendToOthers", "To others").OrTimeout(); + + var secondClientResult = await secondClient.ReadAsync().OrTimeout(); + var invocation = Assert.IsType(secondClientResult); + Assert.Equal("Send", invocation.Target); + Assert.Equal("To others", invocation.Arguments[0]); + + var firstClientResult = await firstClient.ReadAsync().OrTimeout(); + var completion = Assert.IsType(firstClientResult); + + await secondClient.SendInvocationAsync("BroadcastMethod", "To everyone").OrTimeout(); + firstClientResult = await firstClient.ReadAsync().OrTimeout(); + invocation = Assert.IsType(firstClientResult); + Assert.Equal("Broadcast", invocation.Target); + Assert.Equal("To everyone", invocation.Arguments[0]); + + // kill the connections + firstClient.Dispose(); + secondClient.Dispose(); + + await Task.WhenAll(firstEndPointTask, secondEndPointTask).OrTimeout(); + + + } + } + + [Theory] + [MemberData(nameof(HubTypes))] + public async Task SendToCaller(Type hubType) + { + var serviceProvider = CreateServiceProvider(); + + dynamic endPoint = serviceProvider.GetService(GetEndPointType(hubType)); + + using (var firstClient = new TestClient()) + using (var secondClient = new TestClient()) + { + Task firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection); + Task secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection); + + await Task.WhenAll(firstClient.Connected, secondClient.Connected).OrTimeout(); + + await firstClient.SendInvocationAsync("SendToCaller", "To caller").OrTimeout(); + + var firstClientResult = await firstClient.ReadAsync().OrTimeout(); + var invocation = Assert.IsType(firstClientResult); + Assert.Equal("Send", invocation.Target); + Assert.Equal("To caller", invocation.Arguments[0]); + + await firstClient.SendInvocationAsync("BroadcastMethod", "To everyone").OrTimeout(); + var secondClientResult = await secondClient.ReadAsync().OrTimeout(); + invocation = Assert.IsType(secondClientResult); + Assert.Equal("Broadcast", invocation.Target); + Assert.Equal("To everyone", invocation.Arguments[0]); + + // kill the connections + firstClient.Dispose(); + + await firstEndPointTask.OrTimeout(); + } + } + [Theory] [MemberData(nameof(HubTypes))] public async Task SendToAllExcept(Type hubType) @@ -1557,6 +1635,16 @@ namespace Microsoft.AspNetCore.SignalR.Tests { return Clients.AllExcept(excludedIds).Send(message); } + + public Task SendToOthers(string message) + { + return Clients.Others.Send(message); + } + + public Task SendToCaller(string message) + { + return Clients.Caller.Send(message); + } } public interface Test @@ -1735,6 +1823,16 @@ namespace Microsoft.AspNetCore.SignalR.Tests { return Clients.AllExcept(excludedIds).Send(message); } + + public Task SendToOthers(string message) + { + return Clients.Others.Send(message); + } + + public Task SendToCaller(string message) + { + return Clients.Caller.Send(message); + } } public class StreamingHub : TestHub @@ -1930,6 +2028,16 @@ namespace Microsoft.AspNetCore.SignalR.Tests { return Context.Connection.GetHttpContext() != null; } + + public Task SendToOthers(string message) + { + return Clients.Others.InvokeAsync("Send", message); + } + + public Task SendToCaller(string message) + { + return Clients.Caller.InvokeAsync("Send", message); + } } private class InheritedHub : BaseHub