private async Task RunReceiver(UdpClient udpClient, CancellationToken cancellationToken)
        {
            using (this.logger?.BeginScope("LightweightDiscoveryClient.RunReceiver"))
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        var receiveResult = await udpClient.ReceiveAsync().ContextFree();

                        if (LightweightRpcFrame.TryRead(receiveResult.Buffer, MaxDiscoveryFrameSize, out var responseFrame) == RpcFrameState.Full)
                        {
                            switch (responseFrame.RpcOperation)
                            {
                            case ServiceDiscoveryOperations.GetPublishedSingletons:
                                HandlePublishedSingletons(responseFrame);
                                break;

                            case ServiceDiscoveryOperations.GetConnectionInfo:
                                // TODO: HandleConnections(clientId, syncContext, responseFrame);
                                break;
                            }
                        }
                    }
                    catch (Exception x)
                    {
                        if (!cancellationToken.IsCancellationRequested)
                        {
                            this.logger?.LogWarning(x, "Error in LightweightDiscoveryClient.RunReceiver.");
                            throw;
                        }
                    }
                }
            }
        }
Ejemplo n.º 2
0
        internal static async Task <TResponse> SendReceiveAsync <TRequest, TResponse>(LightweightMethodStub methodStub, TRequest request, IRpcSerializer serializer)
            where TRequest : class
            where TResponse : class
        {
            TResponse response;

            var context      = new LightweightCallContext(new TestRpcEndPoint(), null, ImmutableArray <KeyValuePair <string, ImmutableArray <byte> > > .Empty, CancellationToken.None);
            var requestPipe  = new Pipe();
            var responsePipe = new Pipe();
            var duplexPipe   = new DirectDuplexPipe(requestPipe.Reader, responsePipe.Writer);

            await using (var pipeline = new TestPipeline(duplexPipe))
            {
                var payload = new ReadOnlySequence <byte>(serializer.Serialize(request));

                var frame = new LightweightRpcFrame(RpcFrameType.UnaryRequest, null, 1, methodStub.OperationName, RpcOperationFlags.None, 0, payload, null);

                await methodStub.HandleMessage(pipeline, frame, null, context);

                var readResult = await responsePipe.Reader.ReadAsync();

                var  buffer           = readResult.Buffer;
                bool hasResponseFrame = LightweightRpcFrame.TryRead(ref buffer, 65536, out var responseFrame) == RpcFrameState.Full;
                Assert.IsTrue(hasResponseFrame);

                response = (TResponse)serializer.Deserialize(responseFrame.Payload, typeof(TResponse));

                return(response);
            }
        }
Ejemplo n.º 3
0
        internal static TResponse GetResponseFromData <TResponse>(IRpcSerializer serializer, byte[] responseData) where TResponse : class
        {
            TResponse response = null;

            if (LightweightRpcFrame.TryRead(responseData, 65536, out var responseFrame) == RpcFrameState.Full)
            {
                response = (TResponse)serializer.Deserialize(responseFrame.Payload, typeof(TResponse));
            }

            return(response);
        }
        internal async Task EndAsync()
        {
            var responseHeader = new LightweightRpcFrame(
                RpcFrameType.StreamingEnd, this.messageNumber, this.rpcOperation,
                ImmutableArray <KeyValuePair <string, ImmutableArray <byte> > > .Empty);

            var writeState = this.pipelineClient.BeginWrite(responseHeader);

            // Response data is ignored when frame is StreamingEnd,
            // and we have not suitable response to write anyway.
            await this.pipelineClient.EndWriteAsync(writeState, false).ContextFree();
        }
Ejemplo n.º 5
0
        internal static byte[] GetRequestData <TRequest>(string operationName, int messageNumber, TRequest request, IRpcSerializer serializer)
            where TRequest : class
        {
            byte[] requestData;
            using (var writer = new LightweightRpcFrameWriter(65536))
            {
                var frame = new LightweightRpcFrame(RpcFrameType.UnaryRequest, messageNumber, operationName, RpcOperationFlags.None, 0, null);
                requestData = writer.WriteFrame(frame, request, serializer);
            }

            return(requestData);
        }
        private void HandlePublishedSingletons(LightweightRpcFrame responseFrame)
        {
            var response = ServiceDiscoveryOperations.DiscoverySerializer.Deserialize <RpcPublishedSingletonsResponse>(responseFrame.Payload);

            if (response != null && response.ClientId == this.clientId && response.ConnectionInfo != null)
            {
                bool isNewServer = false;
                bool changed     = false;

                DiscoveredServer?discoveredServer;
                ImmutableArray <DiscoveredService> oldServices = ImmutableArray <DiscoveredService> .Empty;
                lock (this.syncRoot)
                {
                    if (!this.discoveredServers.TryGetValue(response.ConnectionInfo.ServerId, out discoveredServer))
                    {
                        discoveredServer = new DiscoveredServer(response.ConnectionInfo);
                        this.discoveredServers.Add(response.ConnectionInfo.ServerId, discoveredServer);
                        changed = isNewServer = true;
                    }


                    if (responseFrame.MessageNumber > discoveredServer.RequestNoLastFound)
                    {
                        oldServices = discoveredServer.Services;
                        if (discoveredServer.UpdateServicesLocked(response.Services, responseFrame.MessageNumber))
                        {
                            changed = true;
                        }
                    }

                    if (changed)
                    {
                        this.discoveredServices = null;
                    }
                }

                if (isNewServer)
                {
                    this.RaiseEvent(this.ServerDiscovered, new DiscoveredServerEventArgs(discoveredServer.ConnectionInfo));
                }

                this.UpdateDiscoveredServices(discoveredServer.ConnectionInfo, discoveredServer.Services, oldServices);

                if (changed)
                {
                    this.RaiseEvent(this.ServicesChanged, EventArgs.Empty);
                }
            }
        }
Ejemplo n.º 7
0
#pragma warning disable CA2000 // Dispose objects before losing scope
        public IAsyncStreamingServerCall <TResponse> BeginStreamingServerCall <TRequest, TResponse>(
            RpcFrameType frameType,
            string operation,
            RpcRequestContext?context,
            TRequest request,
            LightweightSerializers <TRequest, TResponse> serializers,
            int callTimeout)
            where TRequest : class
            where TResponse : class
        {
            var streamingCall = new AsyncStreamingServerCall <TResponse>(serializers.Serializer, serializers.ResponseSerializer);
            int messageId     = this.AddAwaitingResponse(streamingCall);

            try
            {
                RpcOperationFlags flags = 0;
                var cancellationToken   = context != null ? context.CancellationToken : default;
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationToken.Register(() => this.CancelCall(messageId, operation));
                    flags |= RpcOperationFlags.CanCancel;
                }

                if (callTimeout > 0 && !RpcProxyOptions.RoundTripCancellationsAndTimeouts)
                {
                    Task.Delay(callTimeout)
                    .ContinueWith(t =>
                                  this.TimeoutCall(messageId, operation, callTimeout), cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)
                    .Forget();
                }

                var frame = new LightweightRpcFrame(frameType, messageId, operation, flags, (uint)callTimeout, context?.Headers);

                var writeState = this.BeginWrite(frame);
                this.WriteRequest(request, serializers.RequestSerializer, writeState);

                return(streamingCall);
            }
            catch (Exception e)
            {
                this.HandleCallError(messageId, e);
                streamingCall.DisposeAsync().Forget();
                throw;
            }
        }
        public async Task WriteAsync(TResponse response)
        {
            var responseHeader = new LightweightRpcFrame(
                RpcFrameType.StreamingResponse, this.messageNumber, this.rpcOperation,
                ImmutableArray <KeyValuePair <string, ImmutableArray <byte> > > .Empty);

            var writeState = this.pipelineClient.BeginWrite(responseHeader);

            try
            {
                this.serializer.Serialize(writeState.Writer, response);
            }
            catch
            {
                this.pipelineClient.AbortWrite(writeState);
                throw;
            }

            await this.pipelineClient.EndWriteAsync(writeState, false).ContextFree();
        }
        /// <summary>
        /// Gets the authentication options to use for a newly connected server.
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="InvalidDataException">Thrown if a connection frame cannot be retrieved from the sock.et</exception>
        /// <exception cref="AuthenticationException">Thrown if no supported authentication scheme was provided by the client.</exception>
        protected async Task <AuthenticationClientOptions> GetAuthenticationOptionsAsync(Stream connectedStream, CancellationToken cancellationToken)
        {
            if (connectedStream is null)
            {
                throw new ArgumentNullException(nameof(connectedStream));
            }

            using var frameWriter = new LightweightRpcFrameWriter(MaxConnectionFrameSize);

            string supportedSchemes = this.authenticationOptions.Name;
            var    requestHeaders   = new List <KeyValuePair <string, ImmutableArray <byte> > >
            {
                new KeyValuePair <string, ImmutableArray <byte> >(WellKnownHeaderKeys.AuthenticationScheme, Rpc.Client.RpcRequestContext.ToHeaderBytes(supportedSchemes)),
            };

            var frameData = frameWriter.WriteFrame(
                new LightweightRpcFrame(
                    RpcFrameType.ConnectionRequest,
                    0, "",
                    requestHeaders));
            await connectedStream.WriteAsync(frameData, 0, frameData.Length, cancellationToken).ContextFree();

            LightweightRpcFrame connectionFrame = await LightweightRpcFrame.ReadFrameAsync(connectedStream, MaxConnectionFrameSize, cancellationToken).ContextFree();

            if (connectionFrame.FrameType == RpcFrameType.ConnectionResponse)
            {
                // TODO: Should additional frame data be verified (e.g. operation name, messsage id, payload)?
                var authenticationSchemesString = connectionFrame.GetHeaderString(WellKnownHeaderKeys.AuthenticationScheme);
                if (!string.Equals(this.authenticationOptions.Name, authenticationSchemesString, StringComparison.OrdinalIgnoreCase))
                {
                    throw new AuthenticationException($"Server does not support authentication scheme(s): {supportedSchemes}.");
                }

                return(this.authenticationOptions);
            }
            else
            {
                throw new InvalidDataException($"Unexpected connection frame type {connectionFrame.FrameType}");
            }
        }
        private async ValueTask <byte[]?> HandleDatagramAsync(LightweightRpcEndPoint endPoint, byte[] data, CancellationToken cancellationToken)
        {
            if (LightweightRpcFrame.TryRead(data, this.MaxRequestSize, out var frame) == RpcFrameState.Full)
            {
                if (frame.FrameType != RpcFrameType.UnaryRequest)
                {
                    this.Logger.LogWarning("Datagram only handles unary requests.");
                    return(null);
                }

                var methodStub = this.GetMethodDefinition(frame.RpcOperation);
                if (methodStub == null)
                {
                    this.Logger.LogWarning("Unknown operation '{Operation}' in datagram frame.", frame.RpcOperation);
                    return(null);
                }

                CancellationToken       actualCancellationToken;
                CancellationTokenSource?timeoutCts = null;
                CancellationTokenSource?linkedCts  = null;
                if (frame.Timeout > 0)
                {
                    timeoutCts = new CancellationTokenSource();
                    timeoutCts.CancelAfter((int)frame.Timeout);
                    if (cancellationToken.CanBeCanceled)
                    {
                        linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
                        actualCancellationToken = linkedCts.Token;
                    }
                    else
                    {
                        actualCancellationToken = timeoutCts.Token;
                    }
                }
                else
                {
                    actualCancellationToken = cancellationToken;
                }

                try
                {
                    var context = new LightweightCallContext(endPoint, null, frame.Headers, actualCancellationToken);

                    using IServiceScope? scope = this.ServiceProvider?.CreateScope();
                    using var frameWriter      = new LightweightRpcFrameWriter(65536);
                    await methodStub.HandleMessage(frameWriter, frame, scope?.ServiceProvider, context).ContextFree();

                    return(frameWriter.GetFrameData());
                }
                catch (Exception x)
                {
                    this.Logger.LogWarning(x, "Error occurred in HandleDatagramAsync.");
                }
                finally
                {
                    linkedCts?.Dispose();
                    timeoutCts?.Dispose();
                }
            }
            else
            {
                this.Logger.LogInformation("Received incomplete datagram frame.");
            }

            return(null);
        }
        public async Task <IImmutableList <DiscoveredService> > FindServicesAsync(CancellationToken cancellationToken)
        {
            if (this.findingServices)
            {
                // To simplify things a bit.
                throw new InvalidOperationException($"Only a single {nameof(FindServicesAsync)} call can be running at the time.");
            }

            this.findingServices = true;
            this.syncContext     = SynchronizationContext.Current;
            this.clientId        = Guid.NewGuid();

            try
            {
                Task receiverTask;

                using (var udpClient = new UdpClient())
                {
                    udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 0));
                    udpClient.JoinMulticastGroup(LightweightDiscoveryEndPoint.DefaultMulticastAddress);

                    receiverTask = this.RunReceiver(udpClient, cancellationToken);

                    using var frameWriter = new LightweightRpcFrameWriter(MaxDiscoveryFrameSize);

                    IPEndPoint discoveryEp   = new IPEndPoint(LightweightDiscoveryEndPoint.DefaultMulticastAddress, LightweightDiscoveryEndPoint.DefaultDiscoveryPort);
                    int        nextRequestNo = 1;
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        var requestHeader = new LightweightRpcFrame(
                            RpcFrameType.UnaryRequest, nextRequestNo++,
                            ServiceDiscoveryOperations.GetPublishedSingletons, null);

                        var requestData = frameWriter.WriteFrame(requestHeader, new RpcDiscoveryRequest(clientId), ServiceDiscoveryOperations.DiscoverySerializer);
                        int nBytesSent  = await udpClient.SendAsync(requestData, requestData.Length, discoveryEp).ContextFree();

                        if (nBytesSent < requestData.Length)
                        {
                            //
                            this.logger?.LogWarning("Failed to send full discovery request (request size: {RequestSize}, bytes sent: {BytesSent}", requestData.Length, nBytesSent);
                        }

                        Task finishedTask = await Task.WhenAny(Task.Delay(1000, cancellationToken), receiverTask).ContextFree();

                        if (finishedTask == receiverTask)
                        {
                            // Probably an error in the receiver. Stop Find.
                            break;
                        }

                        // TODO: Cleanup lost servers (e.g. with no response the last 10 requests).
                    }
                }

                // Will throw in case of receiver error.
                await receiverTask.ContextFree();

                return(this.DiscoveredServices);
            }
            finally
            {
                this.findingServices = false;
                this.syncContext     = null;
                this.clientId        = Guid.Empty;
            }
        }
Ejemplo n.º 12
0
        internal Task <TResponse> SendReceiveFrameAsync <TRequest, TResponse>(
            RpcFrameType frameType,
            string operation,
            RpcRequestContext?context,
            TRequest request,
            LightweightSerializers <TRequest, TResponse> serializers,
            int timeout)
            where TRequest : class
        {
            if (TraceEnabled)
            {
                // TODO: Logger.Trace("Begin SendReceiveFrameAsync {Operation}.", operation);
            }

            var tcs = new ResponseCompletionSource <TResponse>(serializers.Serializer, serializers.ResponseSerializer);

            int messageId = this.AddAwaitingResponse(tcs);

            try
            {
                RpcOperationFlags flags = 0;

                var cancellationToken = context != null ? context.CancellationToken : default;
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationToken.Register(() => this.CancelCall(messageId, operation));
                    flags |= RpcOperationFlags.CanCancel;
                }

                var frame = new LightweightRpcFrame(frameType, messageId, operation, flags, (uint)timeout, context?.Headers);

                var writeState = this.BeginWrite(frame);

                var writeTask = this.WriteRequestAsync(request, serializers.RequestSerializer, writeState);
                if (writeTask.IsCompletedSuccessfully)
                {
                    if (timeout == 0 || RpcProxyOptions.RoundTripCancellationsAndTimeouts)
                    {
                        return(tcs.Task);
                    }
                    else
                    {
                        return(AwaitTimeoutResponse(tcs.Task, operation, timeout));
                    }
                }
                else
                {
                    return(AwaitWrite(writeTask, tcs.Task));
                }
            }
            catch (Exception ex)
            {
                this.HandleCallError(messageId, ex);
                throw;
            }

            async Task <TResponse> AwaitTimeoutResponse(Task <TResponse> responseTask, string operation, int timeout)
            {
                var completedTask = await Task.WhenAny(responseTask, Task.Delay(timeout)).ContextFree();

                if (completedTask == responseTask)
                {
                    return(responseTask.AwaiterResult());
                }
                else
                {
                    throw new TimeoutException($"Operation '{operation}' didn't complete within the timeout ({timeout} ms).");
                }
            }

            async Task <TResponse> AwaitWrite(ValueTask writeTask, Task <TResponse> responseTask)
            {
                await writeTask.ContextFree();

                if (timeout == 0 || RpcProxyOptions.RoundTripCancellationsAndTimeouts)
                {
                    return(await responseTask.ContextFree());
                }
                else
                {
                    return(await AwaitTimeoutResponse(responseTask, operation, timeout).ContextFree());
                }
            }
        }
Ejemplo n.º 13
0
 protected override Task OnReceiveLargeFrameAsync(LightweightRpcFrame frame)
 {
     throw new NotImplementedException();
 }
Ejemplo n.º 14
0
        public async Task SimpleServiceServerTest()
        {
            Pipe requestPipe  = new Pipe();
            Pipe responsePipe = new Pipe();

            var serializer              = new ProtobufRpcSerializer();
            var serviceImpl             = new TestBlockingSimpleServiceImpl();
            var hostMock                = new Mock <IRpcServerCore>();
            var serviceImplProviderMock = new Mock <IRpcServiceActivator>();

            serviceImplProviderMock.Setup(p => p.GetActivatedService <ISimpleService>(It.IsAny <IServiceProvider>(), It.IsAny <RpcObjectId>())).Returns(new ActivatedService <ISimpleService>(serviceImpl, null));

            hostMock.Setup(p => p.ServiceActivator).Returns(serviceImplProviderMock.Object);
            hostMock.Setup(p => p.CallInterceptors).Returns(ImmutableArrayList <RpcServerCallInterceptor> .Empty);

            var serviceRegistrator = new RpcServiceDefinitionsBuilder();

            serviceRegistrator.RegisterService <ISimpleService>();

            _ = RpcServerId.NewId();
            using (var host = new LightweightRpcServer(Mock.Of <IRpcServicePublisher>(), serviceImplProviderMock.Object, serviceRegistrator, null, new RpcServerOptions {
                Serializer = serializer
            }))
            {
                host.AddEndPoint(new InprocRpcEndPoint(new DirectDuplexPipe(requestPipe.Reader, responsePipe.Writer)));

                host.Start();

                var objectId = RpcObjectId.NewId();

                var requestFrame = new LightweightRpcFrame(RpcFrameType.UnaryRequest, 1, "SciTech.Rpc.Tests.SimpleService.Add", ImmutableArray <KeyValuePair <string, ImmutableArray <byte> > > .Empty);

                using (var frameWriter = new BufferWriterStreamImpl())
                {
                    var writeState = requestFrame.BeginWrite(frameWriter);

                    var request = new RpcObjectRequest <int, int>(objectId, 5, 6);
                    serializer.Serialize(frameWriter, request, request.GetType());
                    int frameLength = checked ((int)frameWriter.Length);

                    LightweightRpcFrame.EndWrite(frameLength, writeState);

                    frameWriter.CopyTo(requestPipe.Writer);
                }

                await requestPipe.Writer.FlushAsync();

                RpcResponse <int> response = null;
                while (response == null)
                {
                    var readResult = await responsePipe.Reader.ReadAsync();

                    if (!readResult.IsCanceled)
                    {
                        var buffer = readResult.Buffer;
                        if (LightweightRpcFrame.TryRead(ref buffer, LightweightRpcFrame.DefaultMaxFrameLength, out var responseFrame) == RpcFrameState.Full)
                        {
                            Assert.AreEqual(requestFrame.RpcOperation, responseFrame.RpcOperation);
                            Assert.AreEqual(requestFrame.MessageNumber, responseFrame.MessageNumber);

                            response = (RpcResponse <int>)serializer.Deserialize(responseFrame.Payload, typeof(RpcResponse <int>));

                            responsePipe.Reader.AdvanceTo(buffer.Start);
                        }
                        else
                        {
                            if (readResult.IsCompleted)
                            {
                                break;
                            }

                            responsePipe.Reader.AdvanceTo(buffer.Start, buffer.End);
                        }
                    }