public async Task PromoteOnDemandConnection() { var proxy = new ServiceConnectionProxy(); var serverTask1 = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); var defaultConnection = await serverTask1.OrTimeout(); // Try to send a ping message to ask for a on-demand connection var serverTask2 = proxy.WaitForServerConnectionAsync(2); string target = "Target"; await proxy.WriteMessageAsync(new PingMessage() { Messages = new[] { "target", target } }); var onDemandConnection = await serverTask2.OrTimeout(); Assert.Equal(target, ((TestConnection)onDemandConnection).Target); // Try to dispose default connection var serverTask3 = proxy.WaitForServerConnectionAsync(3); AssertTimeout(serverTask3); defaultConnection.Transport.Input.CancelPendingRead(); // There won't be another connection to be created AssertTimeout(serverTask3); // on-demand connection has been promoted. Try to dispose and another connection will be created onDemandConnection.Transport.Input.CancelPendingRead(); await serverTask3.OrTimeout(); }
public async Task ClosingConnectionSendsCloseMessage() { var proxy = new ServiceConnectionProxy(context => { // Just let the connection end immediately return(Task.CompletedTask); }); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); var task = proxy.WaitForConnectionAsync("1"); var closeMessageTask = proxy.WaitForApplicationMessageAsync(typeof(CloseConnectionMessage)); await proxy.WriteMessageAsync(new OpenConnectionMessage("1", null)); var connection = await task.OrTimeout(); CloseConnectionMessage message = (CloseConnectionMessage)await closeMessageTask.OrTimeout(); Assert.Equal(message.ConnectionId, connection.ConnectionId); proxy.Stop(); }
public async Task ServiceConnectionSendsPingMessage() { var proxy = new ServiceConnectionProxy(); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); // TODO: make KeepAliveInterval configurable // Wait 6 sec and receive ping var pingMessageTask = proxy.WaitForApplicationMessageAsync(typeof(PingMessage)); await Task.Delay(TimeSpan.FromSeconds(6)); await proxy.WriteMessageAsync(new PingMessage()); // Check server PingMessage will send after reveive service PingMessage await pingMessageTask.OrTimeout(); // Wait another 6 sec and recived connection message will also trigger ping pingMessageTask = proxy.WaitForApplicationMessageAsync(typeof(PingMessage)); await Task.Delay(TimeSpan.FromSeconds(6)); await proxy.WriteMessageAsync(new OpenConnectionMessage("1", null)); // Check server PingMessage will send after reveive service PingMessage await pingMessageTask.OrTimeout(); proxy.Stop(); }
public async Task ReconnectWhenHavingIntermittentConnectivityFailure() { var connectionFactory = new TestConnectionFactory(new IntermittentConnectivityFailure().ConnectCallback); var proxy = new ServiceConnectionProxy(connectionFactory: connectionFactory); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); // fail 3 times, 1~2 + 2~3 + 4~5 = 7~10 await serverTask.OrTimeout(11 * 1000); var connectionId = Guid.NewGuid().ToString("N"); var connectionTask = proxy.WaitForConnectionAsync(connectionId); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId, null)); await connectionTask.OrTimeout(); Assert.True(proxy.IsConnected); var list = connectionFactory.Times; Assert.True(TimeSpan.FromSeconds(0.9) < list[1] - list[0]); Assert.True(TimeSpan.FromSeconds(2.1) > list[1] - list[0]); Assert.True(TimeSpan.FromSeconds(1.9) < list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(3.1) > list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(3.9) < list[3] - list[2]); Assert.True(TimeSpan.FromSeconds(5.1) > list[3] - list[2]); }
public async Task ReconnectAfterReceivingHandshakeErrorMessage() { var connectionFactory = new TestConnectionFactoryWithHandshakeError(); var proxy = new ServiceConnectionProxy(connectionFactory: connectionFactory); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); var connectionId = Guid.NewGuid().ToString("N"); var connectionTask = proxy.WaitForConnectionAsync(connectionId); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId, null)); // Connection exits so the Task should be timeout Assert.False(Task.WaitAll(new Task[] { connectionTask }, TimeSpan.FromSeconds(1))); await Task.Delay(10 * 1000); Assert.False(proxy.IsConnected); var list = connectionFactory.Times; Assert.True(TimeSpan.FromSeconds(0.9) < list[1] - list[0]); Assert.True(TimeSpan.FromSeconds(2.1) > list[1] - list[0]); Assert.True(TimeSpan.FromSeconds(1.9) < list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(3.1) > list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(3.9) < list[3] - list[2]); Assert.True(TimeSpan.FromSeconds(5.1) > list[3] - list[2]); }
public async Task ReconnectWhenHandshakeThrowException() { var proxy = new ServiceConnectionProxy(connectionFactoryCallback: c => new TestConnectionFactoryWithIntermittentInvalidHandshakeResponseMessage(c)); // Throw exception for 3 times and will be success in the 4th retry var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); // fail 3 times, 0~1 + 1~2 + 2~3 = 3~6 await serverTask.OrTimeout(7 * 1000); var connectionId = Guid.NewGuid().ToString("N"); var connectionTask = proxy.WaitForConnectionAsync(connectionId); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId, null)); await connectionTask.OrTimeout(); var list = proxy.ConnectionFactory.Times; Assert.True(TimeSpan.FromSeconds(0.9) > list[1] - list[0]); Assert.True(TimeSpan.FromSeconds(0.9) < list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(2.1) > list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(1.9) < list[3] - list[2]); Assert.True(TimeSpan.FromSeconds(3.1) > list[3] - list[2]); }
public async Task ReconnectWhenHavingIntermittentConnectivityFailure() { var proxy = new ServiceConnectionProxy(connectionFactoryCallback: c => new TestConnectionFactoryWithConnectivityFailure(c)); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); // fail 3 times, 0~1 + 1~2 + 2~3 = 3~6 await serverTask.OrTimeout(7 * 1000); var connectionId = Guid.NewGuid().ToString("N"); var connectionTask = proxy.WaitForConnectionAsync(connectionId); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId, null)); await connectionTask.OrTimeout(); var list = proxy.ConnectionFactory.Times; // no delay before first retry Assert.True(TimeSpan.FromSeconds(0.9) > list[1] - list[0]); Assert.True(TimeSpan.FromSeconds(0.9) < list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(2.1) > list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(1.9) < list[3] - list[2]); Assert.True(TimeSpan.FromSeconds(3.1) > list[3] - list[2]); }
public async Task ReconnectWhenConnectionThrowException() { var proxy = new ServiceConnectionProxy(); var serverTask1 = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); var serverConnection1 = await serverTask1.OrTimeout(); // Try to wait the second handshake after reconnect var serverTask2 = proxy.WaitForServerConnectionAsync(2); AssertTimeout(serverTask2); // Dispose the connection, then server will throw exception and reconnect serverConnection1.Transport.Input.CancelPendingRead(); await serverTask2.OrTimeout(); // Verify the server connection works well var connectionId = Guid.NewGuid().ToString("N"); var connectionTask = proxy.WaitForConnectionAsync(connectionId); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId, null)); await connectionTask.OrTimeout(); }
public async Task CreateOnDemandConnectionAfterPing() { var proxy = new ServiceConnectionProxy(); var serverTask1 = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask1.OrTimeout(); // Try to send a ping message to ask for a on-demand connection var serverTask2 = proxy.WaitForServerConnectionAsync(2); string target = "Target"; await proxy.WriteMessageAsync(new PingMessage() { Messages = new[] { "target", target } }); var onDemandConnection = await serverTask2.OrTimeout(); Assert.Equal(target, ((TestConnection)onDemandConnection).Target); // Dispose the on-demand connection. Assert it won't reconnection var serverTask3 = proxy.WaitForServerConnectionAsync(3); AssertTimeout(serverTask3); onDemandConnection.Transport.Input.CancelPendingRead(); AssertTimeout(serverTask3); }
public async void ServiceLifetimeManagerIntegrationTest(string methodName, Type messageType) { var proxy = new ServiceConnectionProxy(); var serviceConnectionManager = new ServiceConnectionManager <TestHub>(); serviceConnectionManager.SetServiceConnection(proxy.ServiceConnectionContainer); var serviceLifetimeManager = new ServiceLifetimeManager <TestHub>(serviceConnectionManager, proxy.ClientConnectionManager, HubProtocolResolver, Logger, Marker); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await proxy.WaitForServerConnectionsInited().OrTimeout(); await serverTask.OrTimeout(); var task = proxy.WaitForApplicationMessageAsync(messageType); await InvokeMethod(serviceLifetimeManager, methodName); var message = await task.OrTimeout(); VerifyServiceMessage(methodName, message); }
public async Task WritingMultiSegmentMessageConnectionMessageWritesSingleMessage() { // 10 byte segment size var clientPipeOptions = new PipeOptions(minimumSegmentSize: 10); var proxy = new ServiceConnectionProxy(clientPipeOptions: clientPipeOptions); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); var task = proxy.WaitForConnectionAsync("1"); await proxy.WriteMessageAsync(new OpenConnectionMessage("1", null)); var connection = await task.OrTimeout(); var outputMessage = "This message should be more than 10 bytes"; var dataMessageTask = proxy.WaitForApplicationMessageAsync(typeof(ConnectionDataMessage)); await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes(outputMessage)); ConnectionDataMessage message = (ConnectionDataMessage)await dataMessageTask.OrTimeout(); Assert.Equal(message.ConnectionId, connection.ConnectionId); Assert.Equal(outputMessage, Encoding.ASCII.GetString(message.Payload.ToArray())); proxy.Stop(); }
public async void ServiceLifetimeManagerIntegrationTest(string methodName, Type messageType) { var proxy = new ServiceConnectionProxy(); var serviceConnectionManager = new ServiceConnectionManager <TestHub>(); serviceConnectionManager.SetServiceConnection(proxy.ServiceConnectionContainer); var serviceLifetimeManager = new ServiceLifetimeManager <TestHub>(serviceConnectionManager, proxy.ClientConnectionManager, HubProtocolResolver, Logger, Marker, _globalHubOptions, _localHubOptions); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await proxy.WaitForServerConnectionsInited().OrTimeout(); await serverTask.OrTimeout(); var task = proxy.WaitForApplicationMessageAsync(messageType); var invokeTask = InvokeMethod(serviceLifetimeManager, methodName); if (typeof(IAckableMessage).IsAssignableFrom(messageType)) { await proxy.WriteMessageAsync(new AckMessage(1, (int)AckStatus.Ok)); } // Need to return in time, or it indicate a timeout when sending ack-able messages. await invokeTask.OrTimeout(); var message = await task.OrTimeout(); VerifyServiceMessage(methodName, message); }
public async Task CloseClientsWhenReceiveInstanceOfflinePing() { // Prepare clients var instanceId1 = Guid.NewGuid().ToString(); var connectionId1 = Guid.NewGuid().ToString("N"); var header1 = new HeaderDictionary() { { Constants.AsrsInstanceId, instanceId1 } }; var instanceId2 = Guid.NewGuid().ToString(); var connectionId2 = Guid.NewGuid().ToString("N"); var header2 = new HeaderDictionary() { { Constants.AsrsInstanceId, instanceId2 } }; var proxy = new ServiceConnectionProxy(); var connectionTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await connectionTask.OrTimeout(); // 2 client connects with different instanceIds await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId1, null, header1, null)); connectionTask = proxy.WaitForConnectionAsync(connectionId1); await connectionTask.OrTimeout(); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId2, null, header2, null)); connectionTask = proxy.WaitForConnectionAsync(connectionId2); await connectionTask.OrTimeout(); // Server received instance offline ping on instanceId1 and trigger cleanup related client1 await proxy.WriteMessageAsync(new PingMessage() { Messages = new[] { "offline", instanceId1 } }); // Validate client1 is closed and client2 is still connected var disconnectTask = proxy.WaitForConnectionCloseAsync(connectionId1); await disconnectTask.OrTimeout(); Assert.Single(proxy.ClientConnections); Assert.Equal(connectionId2, proxy.ClientConnections.FirstOrDefault().Key); }
public async Task ServiceConnectionSendsPingMessage() { var proxy = new ServiceConnectionProxy(); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); // Check more than once since it happens periodically for (int i = 0; i < 2; i++) { await ReadServiceMessageAsync <PingMessage>(proxy.ConnectionContext.Application.Input, 6000); } proxy.Stop(); }
public async Task StopReconnectAfterReceivingHandshakeErrorMessage() { var proxy = new ServiceConnectionProxy(connectionFactory: new TestConnectionFactoryWithHandshakeError()); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); var connectionId = Guid.NewGuid().ToString("N"); var connectionTask = proxy.WaitForConnectionAsync(connectionId); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId, null)); // Connection exits so the Task should be timeout Assert.False(Task.WaitAll(new Task[] { connectionTask }, TimeSpan.FromSeconds(1))); }
public async Task ReconnectWhenHavingIntermittentConnectivityFailure() { var connectionFactory = new TestConnectionFactory(new IntermittentConnectivityFailure().ConnectCallback); var proxy = new ServiceConnectionProxy(connectionFactory: connectionFactory); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); var connectionId = Guid.NewGuid().ToString("N"); var connectionTask = proxy.WaitForConnectionAsync(connectionId); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId, null)); await connectionTask.OrTimeout(); }
public async Task ReconnectWhenHandshakeThrowException() { var connectionFactory = new TestConnectionFactory(new IntermittentInvalidHandshakeResponseMessage().ConnectCallback); var proxy = new ServiceConnectionProxy(connectionFactory: connectionFactory); // Throw exception for 3 times and will be success in the 4th retry var serverTask = proxy.WaitForServerConnectionAsync(4); _ = proxy.StartAsync(); await serverTask.OrTimeout(); var connectionId = Guid.NewGuid().ToString("N"); var connectionTask = proxy.WaitForConnectionAsync(connectionId); await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId, null)); await connectionTask.OrTimeout(); }
public async Task ReconnectWhenKeepAliveFailed() { var proxy = new ServiceConnectionProxy(); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); // Wait for 35s to make the server side timeout // Assert the server will reconnect var serverTask2 = proxy.WaitForServerConnectionAsync(2); AssertTimeout(serverTask2); await Task.Delay(TimeSpan.FromSeconds(35)); await serverTask2.OrTimeout(); }
public async Task ThrowingExceptionAfterServiceErrorMessage() { var proxy = new ServiceConnectionProxy(); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); string errorMessage = "Maximum message count limit reached: 100000"; await proxy.WriteMessageAsync(new ServiceErrorMessage(errorMessage)); await Task.Delay(200); var serviceConnection = proxy.ServiceConnections.First().Value; var exception = await Assert.ThrowsAsync <ServiceConnectionNotActiveException>(() => serviceConnection.WriteAsync(new ConnectionDataMessage("1", null))); Assert.Equal(errorMessage, exception.Message); }
public async Task OnDemandConnectionSelectedToSendMessages() { var proxy = new ServiceConnectionProxy(); var serverTask1 = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); var defaultConnection = await serverTask1.OrTimeout(); // Try to send a ping message to ask for an on-demand connection var serverTask2 = proxy.WaitForServerConnectionAsync(2); string target = "Target"; await proxy.WriteMessageAsync(new PingMessage() { Messages = new[] { "target", target } }); var onDemandConnection = (TestConnection)await serverTask2.OrTimeout(); Assert.Equal(target, (onDemandConnection).Target); // pause creation of new ones proxy.NewConnectionsCreationPaused = true; // dispose the only default connection var serverTask3 = proxy.WaitForServerConnectionAsync(3); defaultConnection.Transport.Input.CancelPendingRead(); // There won't be another connection to be created AssertTimeout(serverTask3); // now that the only connection left is on demand one verify that we still receive pings var pingMsgTask = proxy.WaitForApplicationMessageAsync(typeof(PingMessage)); await Task.Delay(TimeSpan.FromSeconds(20)); await pingMsgTask.OrTimeout(); }
public async Task ReconnectAfterReceivingHandshakeErrorMessage() { var proxy = new ServiceConnectionProxy(connectionFactoryCallback: c => new TestConnectionFactoryWithHandshakeError(c)); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await Task.Delay(10 * 1000); // No server connection will be connected AssertTimeout(serverTask); var list = proxy.ConnectionFactory.Times; Assert.True(TimeSpan.FromSeconds(0.9) > list[1] - list[0]); Assert.True(TimeSpan.FromSeconds(0.9) < list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(2.1) > list[2] - list[1]); Assert.True(TimeSpan.FromSeconds(1.9) < list[3] - list[2]); Assert.True(TimeSpan.FromSeconds(3.1) > list[3] - list[2]); }
public async Task WritingMessagesFromConnectionGetsSentAsConnectionData() { var proxy = new ServiceConnectionProxy(); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); var task = proxy.WaitForConnectionAsync("1"); await proxy.WriteMessageAsync(new OpenConnectionMessage("1", null)); var connection = await task.OrTimeout(); var dataMessageTask = proxy.WaitForApplicationMessageAsync(typeof(ConnectionDataMessage)); await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes("Hello World")); ConnectionDataMessage message = (ConnectionDataMessage)await dataMessageTask.OrTimeout(); Assert.Equal(message.ConnectionId, connection.ConnectionId); Assert.Equal("Hello World", Encoding.ASCII.GetString(message.Payload.ToArray())); proxy.Stop(); }
public async Task ServiceConnectionStartsConnection() { var connectionId1 = Guid.NewGuid().ToString("N"); var connectionId2 = Guid.NewGuid().ToString("N"); var proxy = new ServiceConnectionProxy(); var serverTask = proxy.WaitForServerConnectionAsync(1); _ = proxy.StartAsync(); await serverTask.OrTimeout(); Assert.Empty(proxy.ClientConnectionManager.ClientConnections); // Wait for the connection to appear, we need to do this before // sending the message to avoid races var connection1Task = proxy.WaitForConnectionAsync(connectionId1); // Create a new client connection await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId1, null)); var connection1 = await connection1Task.OrTimeout(); Assert.Single(proxy.ClientConnectionManager.ClientConnections); var httpContext1 = connection1.GetHttpContext(); Assert.NotNull(httpContext1); Assert.Empty(httpContext1.Request.Headers); Assert.Empty(httpContext1.Request.Query); Assert.Equal(string.Empty, httpContext1.Request.Path); // Wait for the connection to appear var connectionTask2 = proxy.WaitForConnectionAsync(connectionId2); // Create another client connection const string headerKey1 = "custom-header-1"; const string headerValue1 = "custom-value-1"; const string headerKey2 = "custom-header-2"; var headerValue2 = new[] { "custom-value-2a", "custom-value-2b" }; const string headerKey3 = "custom-header-3"; var headerValue3 = new[] { "custom-value-3a", "custom-value-3b", "custom-value-3c" }; const string path = "/this/is/user/path"; await proxy.WriteMessageAsync(new OpenConnectionMessage(connectionId2, null, new Dictionary <string, StringValues> { { headerKey1, headerValue1 }, { headerKey2, headerValue2 }, { headerKey3, headerValue3 } }, $"?customQuery1=customValue1&customQuery2=customValue2&{Constants.QueryParameter.OriginalPath}={WebUtility.UrlEncode(path)}")); var connection2 = await connectionTask2.OrTimeout(); Assert.Equal(2, proxy.ClientConnectionManager.ClientConnections.Count); var httpContext2 = connection2.GetHttpContext(); Assert.NotNull(httpContext2); Assert.Equal(3, httpContext2.Request.Headers.Count); Assert.Equal(headerValue1, httpContext2.Request.Headers[headerKey1]); Assert.Equal(headerValue2, httpContext2.Request.Headers[headerKey2]); Assert.Equal(headerValue3, httpContext2.Request.Headers[headerKey3]); Assert.Equal(3, httpContext2.Request.Query.Count); Assert.Equal("customValue1", httpContext2.Request.Query["customQuery1"]); Assert.Equal("customValue2", httpContext2.Request.Query["customQuery2"]); Assert.Equal(path, httpContext2.Request.Query[Constants.QueryParameter.OriginalPath]); Assert.Equal(path, httpContext2.Request.Path); // Send a message to client 1 await proxy.WriteMessageAsync(new ConnectionDataMessage(connectionId1, Encoding.ASCII.GetBytes("Hello"))); var item = await connection1.Transport.Input.ReadSingleAsync().OrTimeout(); Assert.Equal("Hello", Encoding.ASCII.GetString(item)); var connection1CloseTask = proxy.WaitForConnectionCloseAsync(connectionId1); // Close client 1 await proxy.WriteMessageAsync(new CloseConnectionMessage(connectionId1, null)); await connection1CloseTask.OrTimeout(); Assert.Single(proxy.ClientConnectionManager.ClientConnections); // Close client 2 var connection2CloseTask = proxy.WaitForConnectionCloseAsync(connectionId2); await proxy.WriteMessageAsync(new CloseConnectionMessage(connectionId2, null)); await connection2CloseTask.OrTimeout(); Assert.Empty(proxy.ClientConnectionManager.ClientConnections); proxy.Stop(); }