Пример #1
0
        public async Task ReceiveMethod_And_SendResult_Test()
        {
            // Method call to receive
            ulong id        = 25;
            var   receiving = new Queue <(RpcMessage, int)>();

            receiving.Enqueue((RpcMessage.Encode(new RpcMethod {
                ID = id,
                Name = "MyMethod"
            }), 0));
            var connection = new ReceivingMockRpcConnection(receiving);
            // Start channel
            var channel = await RpcChannel.Create(new RpcPeerInfo(null, "localhost"),
                                                  connection, new MockRpcMethodExecutor(), backlog : null);

            _ = channel.Start();
            await Task.Delay(200); // Give a moment to execute

            // Check sent message
            Assert.AreEqual(1, connection.SentMessages.Count);
            Assert.AreEqual(new RpcResult {
                MethodID    = id,
                ReturnValue = new byte[] { 42 }
            }, connection.SentMessages[0].DecodeRpcResult());
            channel.Stop();
        }
Пример #2
0
        private async Task Timeout_Test(int timeoutMs, int waitBeforeAssertTimeoutMs)
        {
            ulong id             = 25;
            var   responding     = new Queue <RpcMessage>();
            int   responseTimeMs = 500;
            var   connection     = new RespondingMockRpcConnection(responding, responseTimeMs);
            // Start channel
            var channel = await RpcChannel.Create(new RpcPeerInfo(null, "localhost"),
                                                  connection, new MockRpcMethodExecutor(), backlog : null);

            _ = channel.Start();
            var callTask = channel.Run(new RpcCall {
                Method = new RpcMethod {
                    ID   = id,
                    Name = "MyMethod"
                },
                TimeoutMs = timeoutMs
            });

            // When we give more than 200 ms time to fail, check already after
            // 100 ms, then the task should still be open
            if (waitBeforeAssertTimeoutMs > 200)
            {
                await Task.Delay(100);

                waitBeforeAssertTimeoutMs -= 100;
                Assert.IsFalse(callTask.IsCompleted);
            }
            // Wait a short moment, the method call must have failed
            await Task.Delay(waitBeforeAssertTimeoutMs);

            Assert.IsTrue(callTask.IsCompleted);
            Assert.AreEqual(RpcFailureType.Timeout, callTask.Result.Failure?.Type);
            channel.Stop();
        }
Пример #3
0
        /// <summary>
        /// Creates a new channel with the given information, already established connection,
        /// and optionally the given backlog.
        /// </summary>
        public static async Task <RpcChannel> Create(RpcPeerInfo remoteInfo, IRpcConnection connection,
                                                     IRpcMethodExecutor executor, IRpcBacklog?backlog = null)
        {
            var ret = new RpcChannel(remoteInfo, connection, executor);

            ret.callsQueue = await RpcQueue.Create(remoteInfo.PeerID, backlog);

            return(ret);
        }
Пример #4
0
        public async Task Start_And_Stop_Test()
        {
            var channel = await RpcChannel.Create(new RpcPeerInfo(null, "localhost"),
                                                  new ReceivingMockRpcConnection(new Queue <(RpcMessage, int)>()),
                                                  new MockRpcMethodExecutor(), backlog : null);

            var rpcPeerTask = channel.Start();
            await Task.Delay(500);

            Assert.IsFalse(rpcPeerTask.IsCompleted);
            channel.Stop();
            await Task.Delay(200);

            Assert.IsTrue(rpcPeerTask.IsCompleted);
        }
Пример #5
0
        public override async Task Start()
        {
            while (false == stopper.IsCancellationRequested)
            {
                ClientWebSocket?webSocket = null;
                try {
                    webSocket = new ClientWebSocket();
                    auth.Authenticate(webSocket);
                    await webSocket.ConnectAsync(new Uri(ServerUrl), stopper.Token);

                    Log.Debug($"Connection to server established");
                    serverInfo = RpcPeerInfo.Server(ServerUrl);
                    var connection = new WebSocketRpcConnection(serverInfo, webSocket);
                    channel = await RpcChannel.Create(serverInfo, connection, this, Settings.Backlog);

                    await channel.Start();

                    Log.Debug($"Connection to server closed");
                } catch (Exception ex) {
                    if ((ex as WebSocketException)?.Message.Contains("401") ?? false)
                    {
                        Log.Debug($"Connection to server denied: Unauthorized");
                    }
                    else if (ex is WebSocketException wsEx)
                    {
                        Log.Debug($"Connection to server unexpectedly closed: " + wsEx.WebSocketErrorCode);
                    }
                    else
                    {
                        Log.Debug($"Connection to server unexpectedly closed: " + ex.Message);
                    }
                } finally {
                    webSocket?.Dispose();
                }

                if (false == stopper.IsCancellationRequested)
                {
                    // Reconnect
                    Log.Info($"Trying to reconnect after {Settings.ReconnectTimeMs} ms");
                    await Task.Delay(Settings.ReconnectTimeMs);

                    if (Settings.ReconnectTimeMs >= 30_000) // Repeat logging after a long pause
                    {
                        Log.Info($"Trying to reconnect now");
                    }
                }
            }
        }
Пример #6
0
        public async Task Run_RetryUntilWorking()
        {
            var connection = new DivMockRpcConnection(enableReceivingCalls: false);
            var channel    = await RpcChannel.Create(new RpcPeerInfo(null, "localhost"),
                                                     connection, new DivMockRpcMethodExecutor(), backlog : null);

            _ = channel.Start();
            // Set remote execution time very high, so that the response can not be received
            // during the following asserts. Each call should fail because of timeout.
            connection.SetExecutionTimeMs(100_000);
            long startTime = TimeNowMs();
            var  callTask  = channel.Run(new RpcCall {
                Method        = Div.CreateNew(1000).ToMethod(),
                RetryStrategy = RpcRetryStrategy.Retry,
                TimeoutMs     = 100
            });
            await callTask;
            long  duration = TimeNowMs() - startTime;

            Assert.IsTrue(100 <= duration && duration < 200);
            Assert.AreEqual(RpcFailureType.Timeout, callTask.Result.Failure?.Type);
            // It should be repeated, but now we can not receive the result any more.
            // But we should observe the increasing number of sent attempts.
            int attempts = connection.SentDivs.Count;
            int newAttempts;

            for (int i = 0; i < 3; i++)
            {
                await Task.Delay(300);

                newAttempts = connection.SentDivs.Count;
                Assert.IsTrue(newAttempts > attempts && Math.Abs(newAttempts - attempts) <= 3,
                              $"Failed in step {i}, attempts = {attempts}, newAttempts = {newAttempts}");
                attempts = newAttempts;
            }
            // When we set the remote execution time down to 100, the call should be finished.
            attempts = connection.SentDivs.Count;
            connection.SetExecutionTimeMs(50);
            await Task.Delay(300);

            newAttempts = connection.SentDivs.Count;
            Assert.IsTrue(newAttempts == attempts + 1 || newAttempts == attempts + 2);
            channel.Stop();
        }
Пример #7
0
        public async Task Run_And_ReceiveResult_Test()
        {
            // Result to receive
            ulong id         = 25;
            var   responding = new Queue <RpcMessage>();

            responding.Enqueue(RpcMessage.Encode(new RpcResult {
                MethodID    = id,
                ReturnValue = new byte[] { 42 }
            }));
            int responseTimeMs = 500;
            var connection     = new RespondingMockRpcConnection(responding, responseTimeMs);
            // Start channel
            var channel = await RpcChannel.Create(new RpcPeerInfo(null, "localhost"),
                                                  connection, new MockRpcMethodExecutor(), backlog : null);

            _ = channel.Start();
            var callTask = channel.Run(new RpcCall {
                Method = new RpcMethod {
                    ID   = id,
                    Name = "MyMethod"
                }
            });
            // Wait shorter than the response time, nothing should be received yet
            await Task.Delay(responseTimeMs / 2);

            Assert.IsFalse(callTask.IsCompleted);
            // Give remaining time to execute
            await Task.Delay(responseTimeMs / 2 + 200);

            // Check received message
            Assert.IsTrue(callTask.IsCompleted);
            Assert.AreEqual(new RpcResult {
                MethodID    = id,
                ReturnValue = new byte[] { 42 }
            }, callTask.Result);
            channel.Stop();
        }
Пример #8
0
        private async Task ProcessClient(HttpListenerContext httpContext)
        {
            string ip = httpContext.GetIP();

            // Check authentication
            var authResult = auth.Authenticate(httpContext.Request);

            if (false == authResult.Success || authResult.ClientID == null)
            {
                Log.Debug($"Connection from {ip} denied" +
                          (authResult.ClientID != null ? $" (client ID {authResult.ClientID})" : ""));
                httpContext.Close(HttpStatusCode.Unauthorized);
                return;
            }
            string clientID = authResult.ClientID;

            // Accept web socket
            var clientInfo = RpcPeerInfo.Client(clientID, ip);
            WebSocketContext context;

            try {
                context = await httpContext.AcceptWebSocketAsync(subProtocol : null);

                Log.Debug($"Connected {clientInfo}");
            } catch (Exception ex) {
                Log.Debug($"Could not accept WebSocket to {clientInfo}: {ex.Message}");
                httpContext.Close(HttpStatusCode.InternalServerError);
                return;
            }

            // WebSocket loop
            WebSocket webSocket = context.WebSocket;

            try {
                var connection = new WebSocketRpcConnection(clientInfo, webSocket);
                var channel    = await RpcChannel.Create(clientInfo, connection, this, Settings.Backlog);

                if (channelsByClientID.TryGetValue(clientID, out var oldChannel))
                {
                    Log.Debug($"Channel for client {clientID} was already open; close it and open a new one after 3 seconds.");
                    oldChannel.Stop();
                    await Task.Delay(3000);
                }
                channelsByClientID[clientID] = channel;
                await channel.Start();

                Log.Debug($"Connection to {clientInfo} closed");
            } catch (Exception ex) {
                if (ex is WebSocketException wsEx)
                {
                    Log.Debug($"Connection to {clientInfo} unexpectedly closed: " + wsEx.WebSocketErrorCode);
                }
                else
                {
                    Log.Debug($"Connection to {clientInfo} unexpectedly closed: " + ex.Message);
                }
            } finally {
                webSocket?.Dispose();
            }
            channelsByClientID.Remove(clientID);
        }
Пример #9
0
        /// <summary>
        /// Sends, receives and processes lot of calls with a number division calculation task,
        /// using the <see cref="DivMockRpcConnection"/>.
        /// At the end, checks if each of the calls, in both directions, was correctly processed.
        /// </summary>
        /// <param name="useTimeouts">Iff true, all <see cref="Div"/>s are sent as retryable
        ///   calls and a timeout of 50 ms is set (the RPC peer is not fast enough
        ///   to process a calls without timeouts)</param>
        private async Task LoadTest(bool useTimeouts)
        {
            int callsCount     = 0;
            var connection     = new DivMockRpcConnection(enableReceivingCalls: true);
            var testDurationMs = 3000;
            // Start channel
            var channel = await RpcChannel.Create(new RpcPeerInfo(null, "localhost"),
                                                  connection, new DivMockRpcMethodExecutor(), backlog : null);

            _ = channel.Start();
            // For the time of the test, send calculations
            long testStartTime = TimeNowMs();

            _ = Task.Run(async() => {
                while (TimeNowMs() - testStartTime < testDurationMs)
                {
                    var div = Div.CreateNew((ulong)callsCount);
                    _       = channel.Run(new RpcCall {
                        Method        = div.ToMethod(),
                        RetryStrategy = useTimeouts ? RpcRetryStrategy.Retry : (RpcRetryStrategy?)null,
                        TimeoutMs     = 50
                    });
                    callsCount++;
                    if (callsCount % 5 == 0)
                    {
                        await Task.Delay(100);
                    }
                }
            });
            // When we test timeouts, let the first second respond very slowly
            if (useTimeouts)
            {
                connection.SetExecutionTimeMs(100);
                await Task.Delay(1000);

                testDurationMs -= 1000;
                connection.SetExecutionTimeMs(0);
            }
            // When the test time is over, tell the mock connection to stop receiving new div tasks
            await Task.Delay(testDurationMs);

            connection.StopReceivingDivs();
            // Wait a short moment to let ongoing computations complete
            await Task.Delay(Debugger.IsAttached? 2000 : 500);  // More time when debugging (much slower taks and/or logging)

            // Check results of sent and received calls
            var sentCallsWithTimeouts = connection.SentDivs.ToList();
            var sentCalls             = sentCallsWithTimeouts.Where((call, index) =>
                                                                    index == sentCallsWithTimeouts.FindLastIndex(it => it.methodID == call.methodID)).ToList(); // Filter out retried calls

            if (useTimeouts)
            {
                Assert.IsTrue(sentCallsWithTimeouts.Count > sentCalls.Count + 10);
            }
            else
            {
                Assert.AreEqual(sentCallsWithTimeouts.Count, sentCalls.Count);
            }
            Assert.AreEqual(callsCount, sentCalls.Count);
            for (int i = 0; i < sentCalls.Count; i++)
            {
                Assert.AreEqual(sentCalls[i].ComputeExpectedResult(), sentCalls[i].result);
            }
            var receivedCalls = connection.ReceivedDivs.ToList();

            Assert.IsTrue(receivedCalls.Count > testDurationMs / 100);
            for (int i = 0; i < receivedCalls.Count; i++)
            {
                Assert.AreEqual(receivedCalls[i].ComputeExpectedResult(), receivedCalls[i].result);
            }
        }