Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/SignalR/server/Core/src/IHubClients`T.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface IHubClients<T>
/// </summary>
/// <param name="connectionId">The connection ID.</param>
/// <returns>A client caller.</returns>
T Single(string connectionId) => throw new NotImplementedException();
T Single(string connectionId) => Client(connectionId);

/// <summary>
/// Gets a <typeparamref name="T" /> that can be used to invoke methods on all clients connected to the hub.
Expand Down
2 changes: 1 addition & 1 deletion src/SignalR/server/Core/src/Internal/HubClients.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public HubClients(HubLifetimeManager<THub> lifetimeManager)

public ISingleClientProxy Single(string connectionId)
{
return new SingleClientProxyWithInvoke<THub>(_lifetimeManager, connectionId);
return new SingleClientProxy<THub>(_lifetimeManager, connectionId);
}

public IClientProxy All { get; }
Expand Down
5 changes: 0 additions & 5 deletions src/SignalR/server/Core/src/Internal/HubClients`T.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ public HubClients(HubLifetimeManager<THub> lifetimeManager)

public T All { get; }

public T Single(string connectionId)
{
return TypedClientBuilder<T>.Build(new SingleClientProxyWithInvoke<THub>(_lifetimeManager, connectionId));
}

public T AllExcept(IReadOnlyList<string> excludedConnectionIds)
{
return TypedClientBuilder<T>.Build(new AllClientsExceptProxy<THub>(_lifetimeManager, excludedConnectionIds));
Expand Down
21 changes: 2 additions & 19 deletions src/SignalR/server/Core/src/Internal/Proxies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,23 +122,6 @@ public Task SendCoreAsync(string method, object?[] args, CancellationToken cance
}
}

internal sealed class SingleClientProxy<THub> : IClientProxy where THub : Hub
{
private readonly string _connectionId;
private readonly HubLifetimeManager<THub> _lifetimeManager;

public SingleClientProxy(HubLifetimeManager<THub> lifetimeManager, string connectionId)
{
_lifetimeManager = lifetimeManager;
_connectionId = connectionId;
}

public Task SendCoreAsync(string method, object?[] args, CancellationToken cancellationToken = default)
{
return _lifetimeManager.SendConnectionAsync(_connectionId, method, args, cancellationToken);
}
}

internal sealed class MultipleClientProxy<THub> : IClientProxy where THub : Hub
{
private readonly HubLifetimeManager<THub> _lifetimeManager;
Expand All @@ -156,12 +139,12 @@ public Task SendCoreAsync(string method, object?[] args, CancellationToken cance
}
}

internal sealed class SingleClientProxyWithInvoke<THub> : ISingleClientProxy where THub : Hub
internal sealed class SingleClientProxy<THub> : ISingleClientProxy where THub : Hub
{
private readonly string _connectionId;
private readonly HubLifetimeManager<THub> _lifetimeManager;

public SingleClientProxyWithInvoke(HubLifetimeManager<THub> lifetimeManager, string connectionId)
public SingleClientProxy(HubLifetimeManager<THub> lifetimeManager, string connectionId)
{
_lifetimeManager = lifetimeManager;
_connectionId = connectionId;
Expand Down
7 changes: 1 addition & 6 deletions src/SignalR/server/Core/src/Internal/TypedHubClients.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public TypedHubClients(IHubCallerClients dynamicContext)
_hubClients = dynamicContext;
}

public T Single(string connectionId) => TypedClientBuilder<T>.Build(_hubClients.Single(connectionId));
public T Client(string connectionId) => TypedClientBuilder<T>.Build(_hubClients.Single(connectionId));

public T All => TypedClientBuilder<T>.Build(_hubClients.All);

Expand All @@ -22,11 +22,6 @@ public TypedHubClients(IHubCallerClients dynamicContext)

public T AllExcept(IReadOnlyList<string> excludedConnectionIds) => TypedClientBuilder<T>.Build(_hubClients.AllExcept(excludedConnectionIds));

public T Client(string connectionId)
{
return TypedClientBuilder<T>.Build(_hubClients.Client(connectionId));
}

public T Group(string groupName)
{
return TypedClientBuilder<T>.Build(_hubClients.Group(groupName));
Expand Down
2 changes: 1 addition & 1 deletion src/SignalR/server/SignalR/test/ClientProxyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public async Task SingleClientProxyWithInvoke_ThrowsNotSupported()
{
var hubLifetimeManager = new EmptyHubLifetimeManager<FakeHub>();

var proxy = new SingleClientProxyWithInvoke<FakeHub>(hubLifetimeManager, "");
var proxy = new SingleClientProxy<FakeHub>(hubLifetimeManager, "");
var ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await proxy.InvokeAsync<int>("method")).DefaultTimeout();
Assert.Equal("EmptyHubLifetimeManager`1 does not support client return values.", ex.Message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ public Task SendToCaller(string message)
}
}

public class HubT : Hub<Test>
public class HubT : Hub<ITest>
{
public override Task OnConnectedAsync()
{
Expand Down Expand Up @@ -524,16 +524,24 @@ public Task SendToCaller(string message)
{
return Clients.Caller.Send(message);
}

public async Task<ClientResults> GetClientResultThreeWays(int singleValue, int clientValue, int callerValue) =>
new ClientResults(
await Clients.Single(Context.ConnectionId).GetClientResult(singleValue),
await Clients.Client(Context.ConnectionId).GetClientResult(clientValue),
await Clients.Caller.GetClientResult(callerValue));
}

public interface Test
public interface ITest
{
Task Send(string message);
Task Broadcast(string message);

Task<int> GetClientResult(int value);
}

public record ClientResults(int SingleResult, int ClientResult, int CallerResult);

public class OnConnectedThrowsHub : Hub
{
public override Task OnConnectedAsync()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,26 +145,75 @@ public async Task CanUseClientResultsWithIHubContextT()
var connectionHandler = serviceProvider.GetService<HubConnectionHandler<HubT>>();

using var client = new TestClient();
var connectionId = client.Connection.ConnectionId;

var connectionHandlerTask = await client.ConnectAsync(connectionHandler);

// Wait for a connection, or for the endpoint to fail.
await client.Connected.OrThrowIfOtherFails(connectionHandlerTask).DefaultTimeout();

var context = serviceProvider.GetRequiredService<IHubContext<HubT, Test>>();
var resultTask = context.Clients.Single(client.Connection.ConnectionId).GetClientResult(1);
var context = serviceProvider.GetRequiredService<IHubContext<HubT, ITest>>();

var message = await client.ReadAsync().DefaultTimeout();
var invocation = Assert.IsType<InvocationMessage>(message);
async Task AssertClientResult(Task<int> resultTask)
{
var message = await client.ReadAsync().DefaultTimeout();
var invocation = Assert.IsType<InvocationMessage>(message);

Assert.Single(invocation.Arguments);
Assert.Equal(1L, invocation.Arguments[0]);
Assert.Equal("GetClientResult", invocation.Target);
Assert.Single(invocation.Arguments);
Assert.Equal(1L, invocation.Arguments[0]);
Assert.Equal("GetClientResult", invocation.Target);

await client.SendHubMessageAsync(CompletionMessage.WithResult(invocation.InvocationId, 2)).DefaultTimeout();
await client.SendHubMessageAsync(CompletionMessage.WithResult(invocation.InvocationId, 2)).DefaultTimeout();

var result = await resultTask.DefaultTimeout();
Assert.Equal(2, result);
var result = await resultTask.DefaultTimeout();
Assert.Equal(2, result);
}

await AssertClientResult(context.Clients.Single(connectionId).GetClientResult(1));
await AssertClientResult(context.Clients.Client(connectionId).GetClientResult(1));
}
}

[Fact]
public async Task CanReturnClientResultToTypedHubThreeWays()
{
using (StartVerifiableLog())
{
var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(builder =>
{
// Waiting for a client result blocks the hub dispatcher pipeline, need to allow multiple invocations
builder.AddSignalR(o => o.MaximumParallelInvocationsPerClient = 2);
}, LoggerFactory);
var connectionHandler = serviceProvider.GetService<HubConnectionHandler<HubT>>();

using var client = new TestClient(invocationBinder: new GetClientResultThreeWaysInvocationBinder());

var connectionHandlerTask = await client.ConnectAsync(connectionHandler).DefaultTimeout();

var invocationId = await client.SendHubMessageAsync(new InvocationMessage(
invocationId: "1",
nameof(HubT.GetClientResultThreeWays),
new object[] { 5, 6, 7 })).DefaultTimeout();

// Send back "value + 4" to all three invocations.
for (int i = 0; i < 3; i++)
{
// Hub asks client for a result, this is an invocation message with an ID.
var invocationMessage = Assert.IsType<InvocationMessage>(await client.ReadAsync().DefaultTimeout());
Assert.NotNull(invocationMessage.InvocationId);
var res = 4 + (int)invocationMessage.Arguments[0];
await client.SendHubMessageAsync(CompletionMessage.WithResult(invocationMessage.InvocationId, res)).DefaultTimeout();
}

var completion = Assert.IsType<CompletionMessage>(await client.ReadAsync().DefaultTimeout());
Assert.Equal(new ClientResults(9, 10, 11), completion.Result);
}
}

private class GetClientResultThreeWaysInvocationBinder : IInvocationBinder
{
public IReadOnlyList<Type> GetParameterTypes(string methodName) => new[] { typeof(int) };
public Type GetReturnType(string invocationId) => typeof(ClientResults);
public Type GetStreamItemType(string streamId) => throw new NotImplementedException();
}
}