コード例 #1
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);
        }
コード例 #2
0
        /// <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}");
            }
        }
コード例 #3
0
        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);
        }
コード例 #4
0
        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;
            }
        }