public void GlobalSetup() { var serviceCollection = new ServiceCollection(); serviceCollection.AddSignalRCore(); var provider = serviceCollection.BuildServiceProvider(); var serviceScopeFactory = provider.GetService <IServiceScopeFactory>(); var hubLifetimeManager = new DefaultHubLifetimeManager <TestHub>(NullLogger <DefaultHubLifetimeManager <TestHub> > .Instance); _dispatcher = new DefaultHubDispatcher <TestHub>( serviceScopeFactory, new HubContext <TestHub>(hubLifetimeManager), enableDetailedErrors: false, disableImplicitFromServiceParameters: true, new Logger <DefaultHubDispatcher <TestHub> >(NullLoggerFactory.Instance), hubFilters: null, hubLifetimeManager); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), pair.Application, pair.Transport); var contextOptions = new HubConnectionContextOptions() { KeepAliveInterval = TimeSpan.Zero, StreamBufferCapacity = 10, }; _connectionContext = new NoErrorHubConnectionContext(connection, contextOptions, NullLoggerFactory.Instance); _connectionContext.Protocol = new FakeHubProtocol(); }
/// <inheritdoc /> public override Task SendGroupsAsync(IReadOnlyList <string> groupNames, string methodName, object?[] args, CancellationToken cancellationToken = default) { // Each task represents the list of tasks for each of the writes within a group List <Task>? tasks = null; SerializedHubMessage?message = null; foreach (var groupName in groupNames) { if (string.IsNullOrEmpty(groupName)) { throw new InvalidOperationException("Cannot send to an empty group name."); } var group = _groups[groupName]; if (group != null) { DefaultHubLifetimeManager <THub> .SendToGroupConnections(methodName, args, group, null, null, ref tasks, ref message, cancellationToken); } } if (tasks != null) { return(Task.WhenAll(tasks)); } return(Task.CompletedTask); }
public async Task InvokeGroupAsyncWritesToAllConnectionsInGroupOutput() { using (var client1 = new TestClient()) using (var client2 = new TestClient()) { var output1 = Channel.CreateUnbounded <HubMessage>(); var output2 = Channel.CreateUnbounded <HubMessage>(); var manager = new DefaultHubLifetimeManager <MyHub>(); var connection1 = new HubConnectionContext(output1, client1.Connection); var connection2 = new HubConnectionContext(output2, client2.Connection); await manager.OnConnectedAsync(connection1); await manager.OnConnectedAsync(connection2); await manager.AddGroupAsync(connection1.ConnectionId, "gunit"); await manager.InvokeGroupAsync("gunit", "Hello", new object[] { "World" }); Assert.True(output1.In.TryRead(out var item)); var message = item as InvocationMessage; Assert.NotNull(message); Assert.Equal("Hello", message.Target); Assert.Single(message.Arguments); Assert.Equal("World", (string)message.Arguments[0]); Assert.False(output2.In.TryRead(out item)); } }
public async Task InvokeAllAsyncDoesNotWriteToDisconnectedConnectionsOutput() { using (var client1 = new TestClient()) using (var client2 = new TestClient()) { var output1 = Channel.CreateUnbounded <HubMessage>(); var output2 = Channel.CreateUnbounded <HubMessage>(); var manager = new DefaultHubLifetimeManager <MyHub>(); var connection1 = new HubConnectionContext(output1, client1.Connection); var connection2 = new HubConnectionContext(output2, client2.Connection); await manager.OnConnectedAsync(connection1).OrTimeout(); await manager.OnConnectedAsync(connection2).OrTimeout(); await manager.OnDisconnectedAsync(connection2).OrTimeout(); await manager.InvokeAllAsync("Hello", new object[] { "World" }).OrTimeout(); Assert.True(output1.Reader.TryRead(out var item)); var message = Assert.IsType <InvocationMessage>(item); Assert.Equal("Hello", message.Target); Assert.Single(message.Arguments); Assert.Equal("World", (string)message.Arguments[0]); Assert.False(output2.Reader.TryRead(out item)); } }
// Tasks and message are passed by ref so they can be lazily created inside the method post-filtering, // while still being re-usable when sending to multiple groups private static void SendToGroupConnections(string methodName, object?[] args, ConcurrentDictionary <string, HubConnectionContext> connections, Func <HubConnectionContext, object?, bool>?include, object?state, ref List <Task>?tasks, ref SerializedHubMessage?message, CancellationToken cancellationToken) { // foreach over ConcurrentDictionary avoids allocating an enumerator foreach (var connection in connections) { if (include != null && !include(connection.Value, state)) { continue; } if (message == null) { message = DefaultHubLifetimeManager <THub> .CreateSerializedInvocationMessage(methodName, args); } var task = connection.Value.WriteAsync(message, cancellationToken); if (!task.IsCompletedSuccessfully) { if (tasks == null) { tasks = new List <Task>(); } tasks.Add(task.AsTask()); } else { // If it's a IValueTaskSource backed ValueTask, // inform it its result has been read so it can reset task.GetAwaiter().GetResult(); } } }
/// <inheritdoc /> public override Task SendGroupAsync(string groupName, string methodName, object?[] args, CancellationToken cancellationToken = default) { if (groupName == null) { throw new ArgumentNullException(nameof(groupName)); } var group = _groups[groupName]; if (group != null) { // Can't optimize for sending to a single connection in a group because // group might be modified inbetween checking and sending List <Task>? tasks = null; SerializedHubMessage?message = null; DefaultHubLifetimeManager <THub> .SendToGroupConnections(methodName, args, group, null, null, ref tasks, ref message, cancellationToken); if (tasks != null) { return(Task.WhenAll(tasks)); } } return(Task.CompletedTask); }
public async Task SendAllAsyncSendsToAllConnectionsEvenWhenSomeFailToSend() { using (var client = new TestClient()) using (var client2 = new TestClient()) { var manager = new DefaultHubLifetimeManager <MyHub>(new Logger <DefaultHubLifetimeManager <MyHub> >(NullLoggerFactory.Instance)); var connectionMock = HubConnectionContextUtils.CreateMock(client.Connection); var connectionMock2 = HubConnectionContextUtils.CreateMock(client2.Connection); var tcs = new TaskCompletionSource <object>(); var tcs2 = new TaskCompletionSource <object>(); // Force an exception when writing to connection connectionMock.Setup(m => m.WriteAsync(It.IsAny <HubMessage>())).Callback(() => tcs.TrySetResult(null)).Throws(new Exception("Message")); connectionMock2.Setup(m => m.WriteAsync(It.IsAny <HubMessage>())).Callback(() => tcs2.TrySetResult(null)).Throws(new Exception("Message")); var connection = connectionMock.Object; var connection2 = connectionMock2.Object; await manager.OnConnectedAsync(connection).OrTimeout(); await manager.OnConnectedAsync(connection2).OrTimeout(); await manager.SendAllAsync("Hello", new object[] { "World" }).OrTimeout(); // Check that all connections were "written" to await tcs.Task.OrTimeout(); await tcs2.Task.OrTimeout(); } }
public void GlobalSetup() { _hubLifetimeManager = new DefaultHubLifetimeManager <Hub>(NullLogger <DefaultHubLifetimeManager <Hub> > .Instance); IHubProtocol protocol; if (Protocol == "json") { protocol = new JsonHubProtocol(); } else { protocol = new MessagePackHubProtocol(); } for (var i = 0; i < Connections; ++i) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), pair.Application, pair.Transport); var hubConnection = new HubConnectionContext(connection, Timeout.InfiniteTimeSpan, NullLoggerFactory.Instance); hubConnection.Protocol = protocol; _hubLifetimeManager.OnConnectedAsync(hubConnection).Wait(); } _hubContext = new HubContext <Hub>(_hubLifetimeManager); }
public async Task InvokeAllAsyncDoesNotWriteToDisconnectedConnectionsOutput() { using (var client1 = new TestClient()) using (var client2 = new TestClient()) { var manager = new DefaultHubLifetimeManager <MyHub>(); var connection1 = HubConnectionContextUtils.Create(client1.Connection); var connection2 = HubConnectionContextUtils.Create(client2.Connection); await manager.OnConnectedAsync(connection1).OrTimeout(); await manager.OnConnectedAsync(connection2).OrTimeout(); await manager.OnDisconnectedAsync(connection2).OrTimeout(); await manager.SendAllAsync("Hello", new object[] { "World" }).OrTimeout(); var message = Assert.IsType <InvocationMessage>(client1.TryRead()); Assert.Equal("Hello", message.Target); Assert.Single(message.Arguments); Assert.Equal("World", (string)message.Arguments[0]); Assert.Null(client2.TryRead()); } }
public async Task SendGroupAsyncWritesToAllConnectionsInGroupOutput() { using (var client1 = new TestClient()) using (var client2 = new TestClient()) { var manager = new DefaultHubLifetimeManager <MyHub>(new Logger <DefaultHubLifetimeManager <MyHub> >(NullLoggerFactory.Instance)); var connection1 = HubConnectionContextUtils.Create(client1.Connection); var connection2 = HubConnectionContextUtils.Create(client2.Connection); await manager.OnConnectedAsync(connection1).OrTimeout(); await manager.OnConnectedAsync(connection2).OrTimeout(); await manager.AddGroupAsync(connection1.ConnectionId, "gunit").OrTimeout(); await manager.SendGroupAsync("gunit", "Hello", new object[] { "World" }).OrTimeout(); var message = Assert.IsType <InvocationMessage>(client1.TryRead()); Assert.Equal("Hello", message.Target); Assert.Single(message.Arguments); Assert.Equal("World", (string)message.Arguments[0]); Assert.Null(client2.TryRead()); } }
public void GlobalSetup() { _hubLifetimeManager = new DefaultHubLifetimeManager <Hub>(NullLogger <DefaultHubLifetimeManager <Hub> > .Instance); IHubProtocol protocol; if (Protocol == "json") { protocol = new NewtonsoftJsonHubProtocol(); } else { protocol = new MessagePackHubProtocol(); } var options = new PipeOptions(); for (var i = 0; i < Connections; ++i) { var pair = DuplexPipe.CreateConnectionPair(options, options); var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), pair.Application, pair.Transport); var hubConnection = new HubConnectionContext(connection, Timeout.InfiniteTimeSpan, NullLoggerFactory.Instance); hubConnection.Protocol = protocol; _hubLifetimeManager.OnConnectedAsync(hubConnection).GetAwaiter().GetResult(); _hubLifetimeManager.AddToGroupAsync(connection.ConnectionId, TestGroupName).GetAwaiter().GetResult(); _ = ConsumeAsync(connection.Application); } _hubContext = new HubContext <Hub>(_hubLifetimeManager); }
public async Task SendConnectionAsyncDoesNotThrowIfConnectionFailsToWrite() { using (var client = new TestClient()) { var manager = new DefaultHubLifetimeManager <MyHub>(new Logger <DefaultHubLifetimeManager <MyHub> >(NullLoggerFactory.Instance)); var connectionMock = HubConnectionContextUtils.CreateMock(client.Connection); // Force an exception when writing to connection connectionMock.Setup(m => m.WriteAsync(It.IsAny <HubMessage>())).Throws(new Exception("Message")); var connection = connectionMock.Object; await manager.OnConnectedAsync(connection).OrTimeout(); await manager.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout(); } }
public async Task InvokeConnectionAsyncWritesToConnectionOutput() { using (var client = new TestClient()) { var manager = new DefaultHubLifetimeManager <MyHub>(); var connection = HubConnectionContextUtils.Create(client.Connection); await manager.OnConnectedAsync(connection).OrTimeout(); await manager.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout(); var message = Assert.IsType <InvocationMessage>(client.TryRead()); Assert.Equal("Hello", message.Target); Assert.Single(message.Arguments); Assert.Equal("World", (string)message.Arguments[0]); } }
private Task SendToAllConnections(string methodName, object?[] args, Func <HubConnectionContext, object?, bool>?include, object?state = null, CancellationToken cancellationToken = default) { List <Task>? tasks = null; SerializedHubMessage?message = null; // foreach over HubConnectionStore avoids allocating an enumerator foreach (var connection in _connections) { if (include != null && !include(connection, state)) { continue; } if (message == null) { message = DefaultHubLifetimeManager <THub> .CreateSerializedInvocationMessage(methodName, args); } var task = connection.WriteAsync(message, cancellationToken); if (!task.IsCompletedSuccessfully) { if (tasks == null) { tasks = new List <Task>(); } tasks.Add(task.AsTask()); } else { // If it's a IValueTaskSource backed ValueTask, // inform it its result has been read so it can reset task.GetAwaiter().GetResult(); } } if (tasks == null) { return(Task.CompletedTask); } // Some connections are slow return(Task.WhenAll(tasks)); }
public async Task InvokeConnectionAsyncThrowsIfConnectionFailsToWrite() { using (var client = new TestClient()) { // Force an exception when writing to connection var output = new Mock <Channel <HubMessage> >(); output.Setup(o => o.Out.WaitToWriteAsync(It.IsAny <CancellationToken>())).Throws(new Exception("Message")); var manager = new DefaultHubLifetimeManager <MyHub>(); var connection = new HubConnectionContext(output.Object, client.Connection); await manager.OnConnectedAsync(connection).OrTimeout(); var exception = await Assert.ThrowsAsync <Exception>(() => manager.InvokeConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout()); Assert.Equal("Message", exception.Message); } }
public async Task InvokeConnectionAsyncThrowsIfConnectionFailsToWrite() { using (var client = new TestClient()) { // Force an exception when writing to connection var manager = new DefaultHubLifetimeManager <MyHub>(); var connectionMock = HubConnectionContextUtils.CreateMock(client.Connection); connectionMock.Setup(m => m.WriteAsync(It.IsAny <HubMessage>())).Throws(new Exception("Message")); var connection = connectionMock.Object; await manager.OnConnectedAsync(connection).OrTimeout(); var exception = await Assert.ThrowsAsync <Exception>(() => manager.SendConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout()); Assert.Equal("Message", exception.Message); } }
public async Task InvokeConnectionAsyncWritesToConnectionOutput() { using (var client = new TestClient()) { var output = Channel.CreateUnbounded <HubMessage>(); var manager = new DefaultHubLifetimeManager <MyHub>(); var connection = new HubConnectionContext(output, client.Connection); await manager.OnConnectedAsync(connection).OrTimeout(); await manager.InvokeConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout(); Assert.True(output.Reader.TryRead(out var item)); var message = Assert.IsType <InvocationMessage>(item); Assert.Equal("Hello", message.Target); Assert.Single(message.Arguments); Assert.Equal("World", (string)message.Arguments[0]); } }
public void GlobalSetup() { _hubLifetimeManager = new DefaultHubLifetimeManager <Hub>(); var options = new UnboundedChannelOptions { AllowSynchronousContinuations = true }; for (var i = 0; i < Connections; ++i) { var transportToApplication = Channel.CreateUnbounded <byte[]>(options); var applicationToTransport = Channel.CreateUnbounded <byte[]>(options); var application = ChannelConnection.Create <byte[]>(input: applicationToTransport, output: transportToApplication); var transport = ChannelConnection.Create <byte[]>(input: transportToApplication, output: applicationToTransport); var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), transport, application); _hubLifetimeManager.OnConnectedAsync(new HubConnectionContext(connection, Timeout.InfiniteTimeSpan, NullLoggerFactory.Instance)).Wait(); } _hubContext = new HubContext <Hub>(_hubLifetimeManager); }
public void GlobalSetup() { _hubLifetimeManager = new DefaultHubLifetimeManager <Hub>(NullLogger <DefaultHubLifetimeManager <Hub> > .Instance); _connectionIds = new List <string>(); _subsetConnectionIds = new List <string>(); _groupNames = new List <string>(); _userIdentifiers = new List <string>(); var jsonHubProtocol = new NewtonsoftJsonHubProtocol(); for (int i = 0; i < 100; i++) { string connectionId = "connection-" + i; string groupName = "group-" + i % 10; string userIdentifier = "user-" + i % 20; AddUnique(_connectionIds, connectionId); AddUnique(_groupNames, groupName); AddUnique(_userIdentifiers, userIdentifier); if (i % 3 == 0) { _subsetConnectionIds.Add(connectionId); } var connectionContext = new TestConnectionContext { ConnectionId = connectionId, Transport = new TestDuplexPipe(ForceAsync) }; var contextOptions = new HubConnectionContextOptions() { KeepAliveInterval = TimeSpan.Zero, }; var hubConnectionContext = new HubConnectionContext(connectionContext, contextOptions, NullLoggerFactory.Instance); hubConnectionContext.UserIdentifier = userIdentifier; hubConnectionContext.Protocol = jsonHubProtocol; _hubLifetimeManager.OnConnectedAsync(hubConnectionContext).GetAwaiter().GetResult(); _hubLifetimeManager.AddToGroupAsync(connectionId, groupName); } }
/// <inheritdoc /> public override Task SendGroupExceptAsync(string groupName, string methodName, object?[] args, IReadOnlyList <string> excludedConnectionIds, CancellationToken cancellationToken = default) { if (groupName == null) { throw new ArgumentNullException(nameof(groupName)); } var group = _groups[groupName]; if (group != null) { List <Task>? tasks = null; SerializedHubMessage?message = null; DefaultHubLifetimeManager <THub> .SendToGroupConnections(methodName, args, group, (connection, state) => !((IReadOnlyList <string>)state !).Contains(connection.ConnectionId), excludedConnectionIds, ref tasks, ref message, cancellationToken); if (tasks != null) { return(Task.WhenAll(tasks)); } } return(Task.CompletedTask); }
public async Task SendConnectionAsyncOnNonExistentConnectionNoops() { var manager = new DefaultHubLifetimeManager <MyHub>(new Logger <DefaultHubLifetimeManager <MyHub> >(NullLoggerFactory.Instance)); await manager.SendConnectionAsync("NotARealConnectionId", "Hello", new object[] { "World" }).OrTimeout(); }
public async Task RemoveGroupOnNonExistentConnectionNoops() { var manager = new DefaultHubLifetimeManager <MyHub>(); await manager.RemoveGroupAsync("NotARealConnectionId", "MyGroup"); }
public async Task InvokeConnectionAsyncOnNonExistentConnectionNoops() { var manager = new DefaultHubLifetimeManager <MyHub>(); await manager.InvokeConnectionAsync("NotARealConnectionId", "Hello", new object[] { "World" }); }
public async Task RemoveGroupOnNonExistentConnectionNoops() { var manager = new DefaultHubLifetimeManager <MyHub>(new Logger <DefaultHubLifetimeManager <MyHub> >(NullLoggerFactory.Instance)); await manager.RemoveGroupAsync("NotARealConnectionId", "MyGroup").OrTimeout(); }