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 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 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 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 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 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 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 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 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 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(); }