public async Task ServiceConnectionWithErrorDisconnectHub() { using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug, expectedErrors: c => true, logChecker: logs => true)) { var hubConfig = Utility.GetActualHubConfig(loggerFactory); var appName = "app1"; var hub = "ErrorDisconnect"; // error connect hub var scm = new TestServiceConnectionHandler(); hubConfig.Resolver.Register(typeof(IServiceConnectionManager), () => scm); var ccm = new ClientConnectionManager(hubConfig, loggerFactory); hubConfig.Resolver.Register(typeof(IClientConnectionManager), () => ccm); DispatcherHelper.PrepareAndGetDispatcher(new TestAppBuilder(), hubConfig, new ServiceOptions { ConnectionString = ConnectionString }, appName, loggerFactory); using (var proxy = new TestServiceConnectionProxy(ccm, loggerFactory: loggerFactory)) { // start the server connection await proxy.StartServiceAsync().OrTimeout(); var clientConnection = Guid.NewGuid().ToString("N"); var connectTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)) .OrTimeout(); // Application layer sends OpenConnectionMessage var openConnectionMessage = new OpenConnectionMessage(clientConnection, new Claim[0], null, $"?transport=webSockets&connectionToken=conn1&connectionData=%5B%7B%22name%22%3A%22{hub}%22%7D%5D"); await proxy.WriteMessageAsync(openConnectionMessage); var connectMessage = (await connectTask)as GroupBroadcastDataMessage; Assert.NotNull(connectMessage); Assert.Equal($"hg-{hub}.note", connectMessage.GroupName); var message = connectMessage.Payloads["json"] .GetJsonMessageFromSingleFramePayload <HubResponseItem>(); Assert.Equal("Connected", message.A[0]); var disconnectTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)) .OrTimeout(); await proxy.WriteMessageAsync(new CloseConnectionMessage(clientConnection)); var disconnectMessage = (await disconnectTask)as GroupBroadcastDataMessage; Assert.NotNull(disconnectMessage); Assert.Equal($"hg-{hub}.note", disconnectMessage.GroupName); message = disconnectMessage.Payloads["json"] .GetJsonMessageFromSingleFramePayload <HubResponseItem>(); Assert.Equal("Disconnected", message.A[0]); // cleaned up clearly Assert.Empty(ccm.ClientConnections); } } }
public async Task ServiceConnectionDispatchGroupMessagesTest() { using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug)) { var hubConfig = Utility.GetActualHubConfig(loggerFactory); var scm = new TestServiceConnectionHandler(); hubConfig.Resolver.Register(typeof(IServiceConnectionManager), () => scm); var ccm = new ClientConnectionManager(hubConfig, loggerFactory); hubConfig.Resolver.Register(typeof(IClientConnectionManager), () => ccm); DispatcherHelper.PrepareAndGetDispatcher(new TestAppBuilder(), hubConfig, new ServiceOptions { ConnectionString = ConnectionString }, "app1", loggerFactory); using (var proxy = new TestServiceConnectionProxy(ccm, loggerFactory: loggerFactory)) { // start the server connection await proxy.StartServiceAsync().OrTimeout(); var clientConnection = Guid.NewGuid().ToString("N"); // Application layer sends OpenConnectionMessage var openConnectionMessage = new OpenConnectionMessage(clientConnection, new Claim[0], null, "?transport=webSockets&connectionToken=conn1"); await proxy.WriteMessageAsync(openConnectionMessage); await proxy.WaitForClientConnectAsync(clientConnection).OrTimeout(); // group message goes into the manager // make sure the tcs is called before writing message var jgTask = scm.WaitForTransportOutputMessageAsync(typeof(JoinGroupMessage)).OrTimeout(); var gbTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)).OrTimeout(); await proxy.WriteMessageAsync(new ConnectionDataMessage(clientConnection, Encoding.UTF8.GetBytes("{\"H\":\"chat\",\"M\":\"JoinGroup\",\"A\":[\"user1\",\"message1\"],\"I\":1}"))); await jgTask; await gbTask; var lgTask = scm.WaitForTransportOutputMessageAsync(typeof(LeaveGroupMessage)).OrTimeout(); gbTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)).OrTimeout(); await proxy.WriteMessageAsync(new ConnectionDataMessage(clientConnection, Encoding.UTF8.GetBytes("{\"H\":\"chat\",\"M\":\"LeaveGroup\",\"A\":[\"user1\",\"message1\"],\"I\":1}"))); await lgTask; await gbTask; var dTask = proxy.WaitForClientDisconnectAsync(clientConnection).OrTimeout(); await proxy.WriteMessageAsync(new CloseConnectionMessage(clientConnection)); await dTask; } } }
public async Task ServiceConnectionWithTransportLayerClosedShouldCleanupEndlessConnectClientConnections() { using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug, expectedErrors: c => true, logChecker: logs => { var errorLogs = logs.Where(s => s.Write.LogLevel == LogLevel.Error).ToList(); Assert.Single(errorLogs); Assert.Equal("ApplicationTaskTimedOut", errorLogs[0].Write.EventId.Name); return(true); })) { var hubConfig = Utility.GetActualHubConfig(loggerFactory); var appName = "app1"; var hub = "EndlessConnect"; var scm = new TestServiceConnectionHandler(); hubConfig.Resolver.Register(typeof(IServiceConnectionManager), () => scm); var ccm = new ClientConnectionManager(hubConfig, loggerFactory); hubConfig.Resolver.Register(typeof(IClientConnectionManager), () => ccm); DispatcherHelper.PrepareAndGetDispatcher(new TestAppBuilder(), hubConfig, new ServiceOptions { ConnectionString = ConnectionString }, appName, loggerFactory); using (var proxy = new TestServiceConnectionProxy(ccm, loggerFactory)) { // start the server connection var connectionTask = proxy.StartAsync(); await proxy.ConnectionInitializedTask.OrTimeout(); var clientConnection = Guid.NewGuid().ToString("N"); var connectTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)) .OrTimeout(); // Application layer sends OpenConnectionMessage var openConnectionMessage = new OpenConnectionMessage(clientConnection, new Claim[0], null, $"?transport=webSockets&connectionToken=conn1&connectionData=%5B%7B%22name%22%3A%22{hub}%22%7D%5D"); await proxy.WriteMessageAsync(openConnectionMessage); var connectMessage = (await connectTask)as GroupBroadcastDataMessage; Assert.NotNull(connectMessage); Assert.Equal($"hg-{hub}.note", connectMessage.GroupName); var message = connectMessage.Payloads["json"] .GetJsonMessageFromSingleFramePayload <HubResponseItem>(); Assert.Equal("Connected", message.A[0]); // close transport layer proxy.TestConnectionContext.Application.Output.Complete(); // wait for application task to timeout await proxy.WaitForConnectionClose.OrTimeout(10000); Assert.Equal(ServiceConnectionStatus.Disconnected, proxy.Status); // cleaned up clearly Assert.Empty(ccm.ClientConnections); } } }
public async Task ServiceConnectionDispatchGroupMessagesTest() { using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug)) { var hubConfig = Utility.GetActualHubConfig(loggerFactory); var appName = "app1"; var hub = "chat"; var scm = new TestServiceConnectionHandler(); hubConfig.Resolver.Register(typeof(IServiceConnectionManager), () => scm); var ccm = new ClientConnectionManager(hubConfig, loggerFactory); hubConfig.Resolver.Register(typeof(IClientConnectionManager), () => ccm); DispatcherHelper.PrepareAndGetDispatcher(new TestAppBuilder(), hubConfig, new ServiceOptions { ConnectionString = ConnectionString }, appName, loggerFactory); using (var proxy = new TestServiceConnectionProxy(ccm, loggerFactory: loggerFactory)) { // start the server connection await proxy.StartServiceAsync().OrTimeout(); var clientConnection = Guid.NewGuid().ToString("N"); var connectTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)).OrTimeout(); // Application layer sends OpenConnectionMessage var openConnectionMessage = new OpenConnectionMessage(clientConnection, new Claim[0], null, $"?transport=webSockets&connectionToken=conn1&connectionData=%5B%7B%22name%22%3A%22{hub}%22%7D%5D"); await proxy.WriteMessageAsync(openConnectionMessage); var connectMessage = (await connectTask)as GroupBroadcastDataMessage; Assert.NotNull(connectMessage); Assert.Equal($"hg-{hub}.note", connectMessage.GroupName); var message = connectMessage.Payloads["json"].GetJsonMessageFromSingleFramePayload <HubResponseItem>(); Assert.Equal("Connected", message.A[0]); // group message goes into the manager // make sure the tcs is called before writing message var jgTask = scm.WaitForTransportOutputMessageAsync(typeof(JoinGroupWithAckMessage)).OrTimeout(); var gbTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)).OrTimeout(); await proxy.WriteMessageAsync(new ConnectionDataMessage(clientConnection, Encoding.UTF8.GetBytes($"{{\"H\":\"{hub}\",\"M\":\"JoinGroup\",\"A\":[\"user1\",\"group1\"],\"I\":1}}"))); var groupMessage = (await jgTask)as JoinGroupWithAckMessage; Assert.NotNull(groupMessage); Assert.Equal($"hg-{hub}.group1", groupMessage.GroupName); var broadcastMessage = (await gbTask)as GroupBroadcastDataMessage; Assert.NotNull(broadcastMessage); Assert.Equal($"hg-{hub}.group1", broadcastMessage.GroupName); var lgTask = scm.WaitForTransportOutputMessageAsync(typeof(LeaveGroupWithAckMessage)).OrTimeout(); gbTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)).OrTimeout(); await proxy.WriteMessageAsync(new ConnectionDataMessage(clientConnection, Encoding.UTF8.GetBytes($"{{\"H\":\"{hub}\",\"M\":\"LeaveGroup\",\"A\":[\"user1\",\"group1\"],\"I\":1}}"))); var leaveGroupMessage = (await lgTask)as LeaveGroupWithAckMessage; Assert.NotNull(leaveGroupMessage); Assert.Equal($"hg-{hub}.group1", leaveGroupMessage.GroupName); broadcastMessage = (await gbTask)as GroupBroadcastDataMessage; Assert.NotNull(broadcastMessage); Assert.Equal($"hg-{hub}.group1", broadcastMessage.GroupName); var disconnectTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)) .OrTimeout(); await proxy.WriteMessageAsync(new CloseConnectionMessage(clientConnection)); var disconnectMessage = (await disconnectTask)as GroupBroadcastDataMessage; Assert.NotNull(disconnectMessage); Assert.Equal($"hg-{hub}.note", disconnectMessage.GroupName); message = disconnectMessage.Payloads["json"].GetJsonMessageFromSingleFramePayload <HubResponseItem>(); Assert.Equal("Disconnected", message.A[0]); // cleaned up clearly Assert.Empty(ccm.ClientConnections); } } }
public async Task ServiceConnectionWithOfflinePingWillTriggerDisconnectClients() { using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug)) { var hubConfig = Utility.GetActualHubConfig(loggerFactory); var appName = "app1"; var hub = "chat"; var scm = new TestServiceConnectionHandler(); hubConfig.Resolver.Register(typeof(IServiceConnectionManager), () => scm); var ccm = new ClientConnectionManager(hubConfig, loggerFactory); hubConfig.Resolver.Register(typeof(IClientConnectionManager), () => ccm); DispatcherHelper.PrepareAndGetDispatcher(new TestAppBuilder(), hubConfig, new ServiceOptions { ConnectionString = ConnectionString }, appName, loggerFactory); using (var proxy = new TestServiceConnectionProxy(ccm, loggerFactory: loggerFactory)) { // prepare 2 clients with different instancesId connected var instanceId1 = Guid.NewGuid().ToString(); var connectionId1 = Guid.NewGuid().ToString("N"); var header1 = new Dictionary <string, StringValues>() { { Constants.AsrsInstanceId, instanceId1 } }; var instanceId2 = Guid.NewGuid().ToString(); var connectionId2 = Guid.NewGuid().ToString("N"); var header2 = new Dictionary <string, StringValues>() { { Constants.AsrsInstanceId, instanceId2 } }; // start the server connection await proxy.StartServiceAsync().OrTimeout(); // Application layer sends OpenConnectionMessage for client1 var connectTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)).OrTimeout(); var openConnectionMessage = new OpenConnectionMessage(connectionId1, new Claim[0], header1, $"?transport=webSockets&connectionToken=conn1&connectionData=%5B%7B%22name%22%3A%22{hub}%22%7D%5D"); await proxy.WriteMessageAsync(openConnectionMessage); // client1 is connected var connectMessage = (await connectTask)as GroupBroadcastDataMessage; Assert.NotNull(connectMessage); Assert.Equal($"hg-{hub}.note", connectMessage.GroupName); var message = connectMessage.Payloads["json"].GetJsonMessageFromSingleFramePayload <HubResponseItem>(); Assert.Equal("Connected", message.A[0]); ccm.ClientConnections.TryGetValue(connectionId1, out var transport1); Assert.NotNull(transport1); // Application layer sends OpenConnectionMessage for client2 connectTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)).OrTimeout(); openConnectionMessage = new OpenConnectionMessage(connectionId2, new Claim[0], header2, $"?transport=webSockets&connectionToken=conn2&connectionData=%5B%7B%22name%22%3A%22{hub}%22%7D%5D"); await proxy.WriteMessageAsync(openConnectionMessage); // client2 is connected connectMessage = (await connectTask)as GroupBroadcastDataMessage; Assert.NotNull(connectMessage); Assert.Equal($"hg-{hub}.note", connectMessage.GroupName); message = connectMessage.Payloads["json"].GetJsonMessageFromSingleFramePayload <HubResponseItem>(); Assert.Equal("Connected", message.A[0]); ccm.ClientConnections.TryGetValue(connectionId2, out var transport2); Assert.NotNull(transport2); // Send ServerOfflinePing on instance1 and will trigger cleanup related client1 connectTask = scm.WaitForTransportOutputMessageAsync(typeof(GroupBroadcastDataMessage)).OrTimeout(); await proxy.WriteMessageAsync(new PingMessage() { Messages = new[] { "offline", instanceId1 } }); // Validate client1 disconnect connectMessage = (await connectTask)as GroupBroadcastDataMessage; Assert.NotNull(connectMessage); Assert.Equal($"hg-{hub}.note", connectMessage.GroupName); message = connectMessage.Payloads["json"] .GetJsonMessageFromSingleFramePayload <HubResponseItem>(); Assert.Equal("Disconnected", message.A[0]); // Validate client2 is still connected Assert.Single(ccm.ClientConnections); Assert.Equal(connectionId2, ccm.ClientConnections.FirstOrDefault().Key); } } }