예제 #1
0
        public async Task <RSocketClient> ConnectAsync()
        {
            await Transport.ConnectAsync();

            var server = RSocketProtocol.Handler2(this, Transport.Input, CancellationToken.None);

            ////TODO Move defaults to policy object
            new RSocketProtocol.Setup(keepalive: TimeSpan.FromSeconds(60), lifetime: TimeSpan.FromSeconds(180), metadataMimeType: "binary", dataMimeType: "binary").Write(Transport.Output);
            await Transport.Output.FlushAsync();

            return(this);
        }
예제 #2
0
 /// <summary>Binds the RSocket to its Transport and begins handling messages.</summary>
 /// <param name="cancel">Cancellation for the handler. Requesting cancellation will stop message handling.</param>
 /// <returns>The handler task.</returns>
 public Task Connect(CancellationToken cancel = default) => RSocketProtocol.Handler(this, Transport.Input, cancel);
예제 #3
0
 public void Start(CancellationToken cancel = default)
 {
     Handler = RSocketProtocol.Handler2(this, Transport.Input, cancel);
 }
예제 #4
0
 public void Start(CancellationToken cancel = default)
 {
     Handler = RSocketProtocol.Handler(this, Transport.Input, cancel, name: nameof(RSocketServer));
 }
예제 #5
0
        async Task Handler(PipeReader pipereader, CancellationToken cancellation)
        {
            //The original implementation was a state-machine parser with resumability. It doesn't seem like the other implementations follow this pattern and the .NET folks are still figuring this out too - see the internal JSON parser discussion for how they're handling state machine persistence across async boundaries when servicing a Pipeline. So, this version of the handler only processes complete messages at some cost to memory buffer scalability.
            //Note that this means that the Pipeline must be configured to have enough buffering for a complete message before source-quenching. This also means that the downstream consumers don't really have to support resumption, so the interface no longer has the partial buffer methods in it.
            while (!cancellation.IsCancellationRequested)
            {
                var read = await pipereader.ReadAsync(cancellation);

                var buffer = read.Buffer;
                if (buffer.IsEmpty && read.IsCompleted)
                {
                    break;
                }
                var position = buffer.Start;

                //Due to the nature of Pipelines as simple binary pipes, all Transport adapters assemble a standard message frame whether or not the underlying transport signals length, EoM, etc.
                var(Length, IsEndOfMessage) = RSocketProtocol.MessageFramePeek(buffer);
                if (buffer.Length < Length + RSocketProtocol.MESSAGEFRAMESIZE)
                {
                    pipereader.AdvanceTo(buffer.Start, buffer.End);
                    continue;                     //Don't have a complete message yet. Tell the pipe that we've evaluated up to the current buffer end, but cannot yet consume it.
                }

                var    sequence = buffer.Slice(position = buffer.GetPosition(RSocketProtocol.MESSAGEFRAMESIZE, position), Length);
                Header header   = default;
                try
                {
                    await Process(Length, sequence, out header);
                }
                catch (Exception ex)
                {
#if DEBUG
                    Console.WriteLine($"An exception occurred while processing message[{header.Stream}, {header.Type}]: {ex.Message} {ex.StackTrace}");
#endif

                    if (header.Stream > 0)
                    {
                        string errorText = $"{ex.Message}\n{ex.StackTrace}";
                        this.RemoveAndReleaseChannel(header.Stream);
                        await this.SendError(header.Stream, ErrorCodes.Application_Error, errorText, false);
                    }
                    else
                    {
                        if (ex is RSocketErrorException)
                        {
                            await SendErrorAndCloseConnection((RSocketErrorException)ex);
                        }
                        else
                        {
                            string errorText = $"{ex.Message}\n{ex.StackTrace}";
                            await SendErrorAndCloseConnection(new ConnectionErrorException(errorText));
                        }
                        break;
                    }
                }
                pipereader.AdvanceTo(position = buffer.GetPosition(Length, position));
                //TODO UNIT TEST- this should work now too!!! Need to evaluate if there is more than one packet in the pipe including edges like part of the length bytes are there but not all.
            }

            pipereader.Complete();


            //This is the non-async portion of the handler. SequenceReader<T> and the other stack-allocated items cannot be used in an async context.
            Task Process(int framelength, ReadOnlySequence <byte> sequence, out Header header)
            {
                var reader = new SequenceReader <byte>(sequence);

                header = new Header(ref reader, framelength);
                return(this.Process(header, reader));
            }

            async Task SendErrorAndCloseConnection(RSocketErrorException ex)
            {
                await this.SendError(ex.StreamId, ex.ErrorCode, ex.Message, false);

                await this.CloseConnectionAsync();
            }
        }