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; } } } } }
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); } }
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(); }
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); } } }
#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; } }
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()); } } }
protected override Task OnReceiveLargeFrameAsync(LightweightRpcFrame frame) { throw new NotImplementedException(); }
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); } }