Dynamic Hub Change Part 2 (#644)

This commit is contained in:
Mikael Mengistu 2017-07-14 15:54:31 -07:00 committed by GitHub
parent 0ebd33a25c
commit 8ba29b578d
13 changed files with 276 additions and 59 deletions

View File

@ -0,0 +1,51 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
namespace SocketsSample.Hubs
{
public class DynamicChat : DynamicHub
{
public override async Task OnConnectedAsync()
{
await Clients.All.Send($"{Context.ConnectionId} joined");
}
public override async Task OnDisconnectedAsync(Exception ex)
{
await Clients.All.Send($"{Context.ConnectionId} left");
}
public Task Send(string message)
{
return Clients.All.Send($"{Context.ConnectionId}: {message}");
}
public Task SendToGroup(string groupName, string message)
{
return Clients.Group(groupName).Send($"{Context.ConnectionId}@{groupName}: {message}");
}
public async Task JoinGroup(string groupName)
{
await Groups.AddAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).Send($"{Context.ConnectionId} joined {groupName}");
}
public async Task LeaveGroup(string groupName)
{
await Groups.RemoveAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).Send($"{Context.ConnectionId} left {groupName}");
}
public Task Echo(string message)
{
return Clients.Client(Context.ConnectionId).Send($"{Context.ConnectionId}: {message}");
}
}
}

View File

@ -47,7 +47,8 @@ namespace SocketsSample
app.UseSignalR(routes =>
{
routes.MapHub<Chat>("hubs");
routes.MapHub<DynamicChat>("dynamic");
routes.MapHub<Chat>("default");
routes.MapHub<Streaming>("streaming");
});

View File

@ -78,6 +78,8 @@ function getText(id) {
}
let transportType = signalR.TransportType[getParameterByName('transport')] || signalR.TransportType.WebSockets;
let hubRoute = getParameterByName('hubType') || "default";
console.log('Hub Route:' + hubRoute);
document.getElementById('head1').innerHTML = signalR.TransportType[transportType];
@ -89,7 +91,8 @@ var connection;
click('connect', event => {
connectButton.disabled = true;
disconnectButton.disabled = false;
let http = new signalR.HttpConnection(`http://${document.location.host}/hubs`, { transport: transportType });
console.log(`http://${document.location.host}/${hubRoute}`);
let http = new signalR.HttpConnection(`http://${document.location.host}/${hubRoute}`, { transport: transportType });
connection = new signalR.HubConnection(http);
connection.on('Send', msg => {
addLine('message-list', msg);

View File

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
@ -18,6 +18,12 @@
<li><a href="hubs.html?transport=ServerSentEvents">Server Sent Events</a></li>
<li><a href="hubs.html?transport=WebSockets">Web Sockets</a></li>
</ul>
<h1>ASP.NET Core SignalR (Dynamic Hubs)</h1>
<ul>
<li><a href="hubs.html?transport=LongPolling&hubType=dynamic">Long polling</a></li>
<li><a href="hubs.html?transport=ServerSentEvents&hubType=dynamic">Server Sent Events</a></li>
<li><a href="hubs.html?transport=WebSockets&hubType=dynamic">Web Sockets</a></li>
</ul>
<h1>ASP.NET Core SignalR (Streaming)</h1>
<ul>
<li><a href="streaming.html?transport=LongPolling">Long polling</a></li>

View File

@ -0,0 +1,23 @@
// 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.Dynamic;
namespace Microsoft.AspNetCore.SignalR
{
public class DynamicClientProxy : DynamicObject
{
private readonly IClientProxy _clientProxy;
public DynamicClientProxy(IClientProxy clientProxy)
{
_clientProxy = clientProxy;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = _clientProxy.InvokeAsync(binder.Name, args);
return true;
}
}
}

View File

@ -0,0 +1,16 @@
// 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 class DynamicHub : Hub
{
private DynamicHubClients _clients;
public new DynamicHubClients Clients
{
get { return _clients ?? new DynamicHubClients(base.Clients); }
set { _clients = value; }
}
}
}

View File

@ -0,0 +1,20 @@
// 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 class DynamicHubClients
{
private readonly IHubClients _clients;
public DynamicHubClients(IHubClients clients)
{
_clients = clients;
}
public dynamic All => new DynamicClientProxy(_clients.All);
public dynamic User(string userId) => new DynamicClientProxy(_clients.User(userId));
public dynamic Group(string group) => new DynamicClientProxy(_clients.Group(group));
public dynamic Client(string connectionId) => new DynamicClientProxy(_clients.Client(connectionId));
}
}

View File

@ -39,18 +39,19 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
}
[Theory]
[MemberData(nameof(HubProtocolsXTransports))]
public async Task CheckFixedMessage(IHubProtocol protocol, TransportType transport)
[MemberData(nameof(HubProtocolsXTransportsXHubPaths))]
public async Task CheckFixedMessage(IHubProtocol protocol, TransportType transportType, string path)
{
using (StartLog(out var loggerFactory))
{
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + "/hubs"), transport, loggerFactory);
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + path), transportType, loggerFactory);
var connection = new HubConnection(httpConnection, protocol, loggerFactory);
try
{
await connection.StartAsync().OrTimeout();
var result = await connection.InvokeAsync<string>(nameof(TestHub.HelloWorld)).OrTimeout();
var result = await connection.InvokeAsync<string>("HelloWorld").OrTimeout();
Assert.Equal("Hello World!", result);
}
@ -67,20 +68,19 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
}
[Theory]
[MemberData(nameof(HubProtocolsXTransports))]
public async Task CanSendAndReceiveMessage(IHubProtocol protocol, TransportType transport)
[MemberData(nameof(HubProtocolsXTransportsXHubPaths))]
public async Task CanSendAndReceiveMessage(IHubProtocol protocol, TransportType transportType, string path)
{
using (StartLog(out var loggerFactory))
{
const string originalMessage = "SignalR";
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + "/hubs"), transport, loggerFactory);
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + path), transportType, loggerFactory);
var connection = new HubConnection(httpConnection, protocol, loggerFactory);
try
{
await connection.StartAsync().OrTimeout();
var result = await connection.InvokeAsync<string>(nameof(TestHub.Echo), originalMessage).OrTimeout();
var result = await connection.InvokeAsync<string>("Echo", originalMessage).OrTimeout();
Assert.Equal(originalMessage, result);
}
@ -97,20 +97,20 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
}
[Theory]
[MemberData(nameof(HubProtocolsXTransports))]
public async Task MethodsAreCaseInsensitive(IHubProtocol protocol, TransportType transport)
[MemberData(nameof(HubProtocolsXTransportsXHubPaths))]
public async Task MethodsAreCaseInsensitive(IHubProtocol protocol, TransportType transportType, string path)
{
using (StartLog(out var loggerFactory))
{
const string originalMessage = "SignalR";
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + "/hubs"), transport, loggerFactory);
var uriString = "http://test/" + path;
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + path), transportType, loggerFactory);
var connection = new HubConnection(httpConnection, protocol, loggerFactory);
try
{
await connection.StartAsync().OrTimeout();
var result = await connection.InvokeAsync<string>(nameof(TestHub.Echo).ToLowerInvariant(), originalMessage).OrTimeout();
var result = await connection.InvokeAsync<string>("echo", originalMessage).OrTimeout();
Assert.Equal(originalMessage, result);
}
@ -127,14 +127,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
}
[Theory]
[MemberData(nameof(HubProtocolsXTransports))]
public async Task CanInvokeClientMethodFromServer(IHubProtocol protocol, TransportType transport)
[MemberData(nameof(HubProtocolsXTransportsXHubPaths))]
public async Task CanInvokeClientMethodFromServer(IHubProtocol protocol, TransportType transportType, string path)
{
using (StartLog(out var loggerFactory))
{
const string originalMessage = "SignalR";
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + "/hubs"), transport, loggerFactory);
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + path), transportType, loggerFactory);
var connection = new HubConnection(httpConnection, protocol, loggerFactory);
try
{
@ -143,7 +143,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
var tcs = new TaskCompletionSource<string>();
connection.On<string>("Echo", tcs.SetResult);
await connection.InvokeAsync(nameof(TestHub.CallEcho), originalMessage).OrTimeout();
await connection.InvokeAsync("CallEcho", originalMessage).OrTimeout();
Assert.Equal(originalMessage, await tcs.Task.OrTimeout());
}
@ -160,12 +160,12 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
}
[Theory]
[MemberData(nameof(HubProtocolsXTransports))]
public async Task CanStreamClientMethodFromServer(IHubProtocol protocol, TransportType transport)
[MemberData(nameof(HubProtocolsXTransportsXHubPaths))]
public async Task CanStreamClientMethodFromServer(IHubProtocol protocol, TransportType transportType, string path)
{
using (StartLog(out var loggerFactory))
{
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + "/hubs"), transport, loggerFactory);
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + path), transportType, loggerFactory);
var connection = new HubConnection(httpConnection, protocol, loggerFactory);
try
{
@ -173,7 +173,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
var tcs = new TaskCompletionSource<string>();
var results = await connection.Stream<string>(nameof(TestHub.Stream)).ReadAllAsync().OrTimeout();
var results = await connection.Stream<string>("Stream").ReadAllAsync().OrTimeout();
Assert.Equal(new[] { "a", "b", "c" }, results.ToArray());
}
@ -190,12 +190,12 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
}
[Theory]
[MemberData(nameof(HubProtocolsXTransports))]
public async Task ServerClosesConnectionIfHubMethodCannotBeResolved(IHubProtocol hubProtocol, TransportType transport)
[MemberData(nameof(HubProtocolsXTransportsXHubPaths))]
public async Task ServerClosesConnectionIfHubMethodCannotBeResolved(IHubProtocol hubProtocol, TransportType transportType, string hubPath)
{
using (StartLog(out var loggerFactory))
{
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + "/hubs"), transport, loggerFactory);
var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + hubPath), transportType, loggerFactory);
var connection = new HubConnection(httpConnection, hubProtocol, loggerFactory);
try
{
@ -218,17 +218,22 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
}
}
public static IEnumerable<object []> HubProtocolsXTransports()
public static IEnumerable<object[]> HubProtocolsXTransportsXHubPaths()
{
foreach (var protocol in HubProtocols)
{
foreach (var transport in TransportTypes())
{
yield return new object[] { protocol, transport };
foreach (var hubPath in HubPaths)
{
yield return new object[] { protocol, transport, hubPath };
}
}
}
}
public static string[] HubPaths = new[] { "/default", "/dynamic" };
public static IEnumerable<IHubProtocol> HubProtocols =>
new IHubProtocol[]
{
@ -236,6 +241,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
new MessagePackHubProtocol(),
};
public static IEnumerable<TransportType> TransportTypes()
{
if (WebsocketsSupported())

View File

@ -4,6 +4,7 @@
using System;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Microsoft.CSharp;
namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
{
@ -29,4 +30,32 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
return new[] { "a", "b", "c" }.ToObservable();
}
}
public class DynamicTestHub : DynamicHub
{
public string HelloWorld()
{
return "Hello World!";
}
public string Echo(string message)
{
return message;
}
public async Task CallEcho(string message)
{
await Clients.Client(Context.ConnectionId).Echo(message);
}
public IObservable<string> Stream()
{
return new[] { "a", "b", "c" }.ToObservable();
}
public Task SendMessage(string message)
{
return Clients.All.Send(message);
}
}
}

View File

@ -9,6 +9,10 @@
<RuntimeIdentifier Condition="'$(TargetFramework)' != 'netcoreapp2.0'">win7-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="Microsoft.CSharp" Version="$(CoreFxVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\ServerFixture.cs" Link="ServerFixture.cs" />
<Compile Include="..\Common\TaskExtensions.cs" Link="TaskExtensions.cs" />

View File

@ -17,7 +17,8 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
{
app.UseSignalR(routes =>
{
routes.MapHub<TestHub>("hubs");
routes.MapHub<TestHub>("default");
routes.MapHub<DynamicTestHub>("dynamic");
});
}
}

View File

@ -2,6 +2,7 @@
// 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.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
@ -9,6 +10,7 @@ using System.Threading.Tasks.Channels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.AspNetCore.SignalR.Tests.Common;
using Microsoft.CSharp;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
@ -142,16 +144,17 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
[Fact]
public async Task HubMethodsAreCaseInsensitive()
[Theory]
[MemberData(nameof(HubTypes))]
public async Task HubMethodsAreCaseInsensitive(Type hubType)
{
var serviceProvider = CreateServiceProvider();
var endPoint = serviceProvider.GetService<HubEndPoint<MethodHub>>();
dynamic endPoint = serviceProvider.GetService(GetEndPointType(hubType));
using (var client = new TestClient())
{
var endPointTask = endPoint.OnConnectedAsync(client.Connection);
Task endPointTask = endPoint.OnConnectedAsync(client.Connection);
var result = (await client.InvokeAsync("echo", "hello").OrTimeout()).Result;
@ -437,22 +440,23 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
[Fact]
public async Task BroadcastHubMethod_SendsToAllClients()
[Theory]
[MemberData(nameof(HubTypes))]
public async Task BroadcastHubMethodSendsToAllClients(Type hubType)
{
var serviceProvider = CreateServiceProvider();
var endPoint = serviceProvider.GetService<HubEndPoint<MethodHub>>();
dynamic endPoint = serviceProvider.GetService(GetEndPointType(hubType));
using (var firstClient = new TestClient())
using (var secondClient = new TestClient())
{
var firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection);
var secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection);
Task firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection);
Task secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection);
await Task.WhenAll(firstClient.Connected, secondClient.Connected).OrTimeout();
await firstClient.SendInvocationAsync(nameof(MethodHub.BroadcastMethod), "test").OrTimeout();
await firstClient.SendInvocationAsync("BroadcastMethod", "test").OrTimeout();
foreach (var result in await Task.WhenAll(
firstClient.Read(),
@ -472,22 +476,23 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
[Fact]
public async Task HubsCanAddAndSendToGroup()
[Theory]
[MemberData(nameof(HubTypes))]
public async Task HubsCanAddAndSendToGroup(Type hubType)
{
var serviceProvider = CreateServiceProvider();
var endPoint = serviceProvider.GetService<HubEndPoint<MethodHub>>();
dynamic endPoint = serviceProvider.GetService(GetEndPointType(hubType));
using (var firstClient = new TestClient())
using (var secondClient = new TestClient())
{
var firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection);
var secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection);
Task firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection);
Task secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection);
await Task.WhenAll(firstClient.Connected, secondClient.Connected).OrTimeout();
var result = (await firstClient.InvokeAsync(nameof(MethodHub.GroupSendMethod), "testGroup", "test").OrTimeout()).Result;
var result = (await firstClient.InvokeAsync("GroupSendMethod", "testGroup", "test").OrTimeout()).Result;
// check that 'firstConnection' hasn't received the group send
Assert.Null(firstClient.TryRead());
@ -534,22 +539,23 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
[Fact]
public async Task HubsCanSendToUser()
[Theory]
[MemberData(nameof(HubTypes))]
public async Task HubsCanSendToUser(Type hubType)
{
var serviceProvider = CreateServiceProvider();
var endPoint = serviceProvider.GetService<HubEndPoint<MethodHub>>();
dynamic endPoint = serviceProvider.GetService(GetEndPointType(hubType));
using (var firstClient = new TestClient())
using (var secondClient = new TestClient())
{
var firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection);
var secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection);
Task firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection);
Task secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection);
await Task.WhenAll(firstClient.Connected, secondClient.Connected).OrTimeout();
await firstClient.SendInvocationAsync(nameof(MethodHub.ClientSendMethod), secondClient.Connection.User.Identity.Name, "test").OrTimeout();
await firstClient.SendInvocationAsync("ClientSendMethod", secondClient.Connection.User.Identity.Name, "test").OrTimeout();
// check that 'secondConnection' has received the group send
var hubMessage = await secondClient.Read().OrTimeout();
@ -566,22 +572,23 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
[Fact]
public async Task HubsCanSendToConnection()
[Theory]
[MemberData(nameof(HubTypes))]
public async Task HubsCanSendToConnection(Type hubType)
{
var serviceProvider = CreateServiceProvider();
var endPoint = serviceProvider.GetService<HubEndPoint<MethodHub>>();
dynamic endPoint = serviceProvider.GetService(GetEndPointType(hubType));
using (var firstClient = new TestClient())
using (var secondClient = new TestClient())
{
var firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection);
var secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection);
Task firstEndPointTask = endPoint.OnConnectedAsync(firstClient.Connection);
Task secondEndPointTask = endPoint.OnConnectedAsync(secondClient.Connection);
await Task.WhenAll(firstClient.Connected, secondClient.Connected).OrTimeout();
await firstClient.SendInvocationAsync(nameof(MethodHub.ConnectionSendMethod), secondClient.Connection.ConnectionId, "test").OrTimeout();
await firstClient.SendInvocationAsync("ConnectionSendMethod", secondClient.Connection.ConnectionId, "test").OrTimeout();
// check that 'secondConnection' has received the group send
var hubMessage = await secondClient.Read().OrTimeout();
@ -721,6 +728,12 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
public static IEnumerable<Type[]> HubTypes()
{
yield return new Type[] { typeof(DynamicTestHub) };
yield return new Type[] { typeof(MethodHub) };
}
private static Type GetEndPointType(Type hubType)
{
var endPointType = typeof(HubEndPoint<>);
@ -744,6 +757,46 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return services.BuildServiceProvider();
}
private class DynamicTestHub : DynamicHub
{
public override Task OnConnectedAsync()
{
var tcs = (TaskCompletionSource<bool>)Context.Connection.Metadata["ConnectedTask"];
tcs?.TrySetResult(true);
return base.OnConnectedAsync();
}
public string Echo(string data)
{
return data;
}
public Task ClientSendMethod(string userId, string message)
{
return Clients.User(userId).Send(message);
}
public Task ConnectionSendMethod(string connectionId, string message)
{
return Clients.Client(connectionId).Send(message);
}
public Task GroupAddMethod(string groupName)
{
return Groups.AddAsync(Context.ConnectionId, groupName);
}
public Task GroupSendMethod(string groupName, string message)
{
return Clients.Group(groupName).Send(message);
}
public Task BroadcastMethod(string message)
{
return Clients.All.Broadcast(message);
}
}
public class StreamingHub : TestHub
{
public IObservable<string> CounterObservable(int count)

View File

@ -14,6 +14,10 @@
<RuntimeIdentifier Condition="'$(TargetFramework)' != 'netcoreapp2.0'">win7-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="Microsoft.CSharp" Version="$(CoreFxVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\ServerFixture.cs" Link="ServerFixture.cs" />
<Compile Include="..\Common\TaskExtensions.cs" Link="TaskExtensions.cs" />