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); }
/// <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; } }