Esempio n. 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();
        }
Esempio n. 2
0
 public void Encode_RpcMethod_Failure()
 {
     // With message
     byte[] actual = RpcMessage.Encode(testResultFailure).Data;
     CollectionAssert.AreEqual(testResultFailureBytes, actual);
     // Without message
     actual = RpcMessage.Encode(testResultFailure_NoMessage).Data;
     CollectionAssert.AreEqual(testResultFailureBytes_NoMessage, actual);
 }
Esempio n. 3
0
 public void Encode_RpcMethod()
 {
     // With parameters
     byte[] actual = RpcMessage.Encode(testMethod).Data;
     CollectionAssert.AreEqual(testMethodBytes, actual);
     // Without parameters
     actual = RpcMessage.Encode(testMethod_NoParams).Data;
     CollectionAssert.AreEqual(testMethodBytes_NoParams, actual);
 }
Esempio n. 4
0
 public DivMockRpcConnection(bool enableReceivingCalls)
 {
     if (enableReceivingCalls)
     {
         // While running, create calculation tasks which the peer can receive
         Task.Run(async() => {
             ulong nextID = 0;
             while (isRunning && isReceivingMoreDivs)
             {
                 var div = Div.CreateNew(nextID++);
                 receiving.Enqueue(RpcMessage.Encode(div.ToMethod()));
                 await Task.Delay(50);
             }
         });
     }
 }
Esempio n. 5
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();
        }
Esempio n. 6
0
 public async Task Send(RpcMessage message, CancellationToken cancellationToken)
 {
     if (message.IsRpcMethod())
     {
         // Peer sent us a div task. "Execute" the method (by waiting executionTimeMs) and
         // add the result to the receiving queue and log the div.
         var div = Div.FromMethod(message.DecodeRpcMethod());
         Log.Trace($"Div {div.methodID} sent to DivMock");
         SentDivs.Enqueue(div);
         Action setResult = () => {
             div.result = div.ComputeExpectedResult();
             receiving.Enqueue(RpcMessage.Encode(div.result));
             Log.Trace($"Div {div.methodID} executed on DivMock");
         };
         if (executionTimeMs > 0)
         {
             _ = Task.Delay(executionTimeMs).ContinueWith(_ => setResult());
         }
         else
         {
             setResult(); // Immediate execution on the same thread
         }
     }
     else if (message.IsRpcResult())
     {
         // Peer sent the result from a div call. Log it.
         var result = message.DecodeRpcResult();
         var div    = ReceivedDivs.AsEnumerable().FirstOrDefault(it => it.methodID == result.MethodID);
         if (div == null)
         {
             Debug.WriteLine("Unexpected method ID");
         }
         else
         {
             div.result = result;
         }
     }
 }
Esempio n. 7
0
 public void Encode_RpcResult_Success()
 {
     byte[] actual = RpcMessage.Encode(testResultSuccess).Data;
     CollectionAssert.AreEqual(testResultSuccessBytes, actual);
 }
Esempio n. 8
0
        /// <summary>
        /// Runs the sending operations in a loop, as long as the websocket is open.
        /// It is sending both the results from remote calls and its own calls.
        /// It also handles closing requests from this side (triggered by calling <see cref="Stop"/>).
        /// </summary>
        private async Task SendLoop()
        {
            try {
                var messagePart = new StringBuilder();
                while (connection.IsOpen())
                {
                    bool didSomething = false;
                    // Result in the queue? Then send it.
                    if (resultsQueue.TryDequeue(out var queuedResult))
                    {
                        Log.Trace($"Sending result {queuedResult.MethodID} to {RemotePeer}");
                        await connection.Send(RpcMessage.Encode(queuedResult), cancellationToken.Token);

                        didSomething = true;
                    }
                    // Current call ran into a timeout?
                    if (currentCall != null && currentCall.Result == null &&
                        currentCall.StartTime + GetTimeoutMs(currentCall) < TimeNowMs())
                    {
                        currentCall.Result = RpcResult.Timeout(currentCall.Method.ID);
                    }
                    // Current call finished?
                    if (currentCall != null && currentCall.Result is RpcResult result)
                    {
                        string logMsg = $"Method {currentCall.Method.ID} {currentCall.Method.Name} finished " +
                                        (result.Failure != null ? "with failure " + result.Failure?.Type : "successfully");
                        if (result.IsRetryNeeded() && currentCall.IsRetryable())
                        {
                            Log.Trace(logMsg + ", trying it again");
                            currentCall.ResetStartTimeAndResult();
                        }
                        else
                        {
                            var discarded = await callsQueue.Dequeue();

                            Log.Trace(logMsg + $", dequeuing [{discarded?.Method.ID}]");
                        }
                        if (result.Failure?.Type == RpcFailureType.Timeout)
                        {
                            // When we receive a timeout, we immediately close the connection.
                            // Maybe the connection does not exist any more, although the network lib thinks that it exists.
                            // We experienced this problem when the router switched routes, e.g. from LAN to mobile net
                            Log.Info("Closing connection because of timeout");
                            cancellationToken.Cancel();
                            await connection.Close();

                            break;
                        }
                        if (openCalls.TryGetValue(result.MethodID, out var callExecution))
                        {
                            callExecution.Finish(result);
                        }
                        currentCall = null; // Take next call from queue
                    }
                    // Next call already queued? Then see if we can send it already.
                    if (currentCall == null && await callsQueue.Peek() is RpcCall call)
                    {
                        // Send it. Do not dequeue it yet, only after is has been finished.
                        var method = RpcMessage.Encode(call.Method);
                        Log.Trace($"Sending method {call.Method.ID} {call.Method.Name} to {RemotePeer}, {method.Data.Length} bytes");
                        currentCall = call;
                        await connection.Send(method, cancellationToken.Token);

                        didSomething = true;
                    }
                    // Close nicely, when locally requested
                    if (cancellationToken.IsCancellationRequested)
                    {
                        await connection.Close();

                        didSomething = true;
                    }
                    // When we had something to do, immediately continue. Otherwise, wait a short moment
                    // or until we get notified that the next item is here
                    if (false == didSomething)
                    {
                        sendingWaiter = CreateAsyncTaskCompletionSource <bool>(); // async continuation is crucial
                        await Task.WhenAny(Task.Delay(100), sendingWaiter.Task);
                    }
                }
                Log.Debug($"SendLoop: Connection to {RemotePeer} closed.");
            } catch (Exception ex) {
                Log.Debug($"Unexpectedly closed connection to {RemotePeer}: {ex.Message}");
            }
        }