예제 #1
0
        public async Task WriteLargeDataBinary(int length)
        {
            byte[] data = new byte[length];
            new Random(length).NextBytes(data);
            using (var memoryPool = new MemoryPool())
            {
                var pipe = new Pipe(memoryPool);

                var output = pipe.Alloc();
                output.Write(data);
                var foo = output.Memory.IsEmpty; // trying to see if .Memory breaks
                await output.FlushAsync();

                pipe.CompleteWriter();

                int offset = 0;
                while (true)
                {
                    var result = await pipe.ReadAsync();

                    var input = result.Buffer;
                    if (input.Length == 0)
                    {
                        break;
                    }

                    Assert.True(input.Equals(new Span <byte>(data, offset, input.Length)));
                    offset += input.Length;
                    pipe.Advance(input.End);
                }
                Assert.Equal(data.Length, offset);
            }
        }
예제 #2
0
        public async Task WriteLargeDataTextUtf8(int length)
        {
            string data = new string('#', length);

            FillRandomStringData(data, length);
            using (var memoryPool = new MemoryPool())
            {
                var pipe = new Pipe(memoryPool);

                var output = pipe.Alloc();
                output.Append(data, EncodingData.InvariantUtf8);
                var foo = output.Memory.IsEmpty; // trying to see if .Memory breaks
                await output.FlushAsync();

                pipe.CompleteWriter();

                int offset = 0;
                while (true)
                {
                    var result = await pipe.ReadAsync();

                    var input = result.Buffer;
                    if (input.Length == 0)
                    {
                        break;
                    }

                    string s = ReadableBufferExtensions.GetUtf8String(input);
                    Assert.Equal(data.Substring(offset, input.Length), s);
                    offset += input.Length;
                    pipe.Advance(input.End);
                }
                Assert.Equal(data.Length, offset);
            }
        }
예제 #3
0
        public async Task CanAppendSelfWhileNotEmpty()
        {
            byte[] chunk = new byte[512];
            new Random().NextBytes(chunk);
            using (var memoryPool = new MemoryPool())
            {
                var pipe = new Pipe(memoryPool);

                var output = pipe.Alloc();

                for (int i = 0; i < 20; i++)
                {
                    output.Write(chunk);
                }
                var readable = output.AsReadableBuffer();
                Assert.Equal(512 * 20, readable.Length);

                output.Append(readable);
                Assert.Equal(512 * 20, readable.Length);

                readable = output.AsReadableBuffer();
                Assert.Equal(2 * 512 * 20, readable.Length);

                await output.FlushAsync();
            }
        }
예제 #4
0
        private void ProcessReceives()
        {
            _buffer = _input.Alloc(2048);
            var receiveBufferSeg = GetSegmentFromMemory(_buffer.Memory);

            if (!_rio.RioReceive(_requestQueue, ref receiveBufferSeg, 1, RioReceiveFlags.None, 0))
            {
                ThrowError(ErrorType.Receive);
            }
        }
예제 #5
0
 public async Task CanWriteNothingToBuffer()
 {
     using (var memoryPool = new MemoryPool())
     {
         var pipe   = new Pipe(memoryPool);
         var buffer = pipe.Alloc();
         buffer.Advance(0); // doing nothing, the hard way
         await buffer.FlushAsync();
     }
 }
예제 #6
0
        private async void HandshakeWriting()
        {
            var writer = _handshakeOutpipe.Alloc();

            _state.StartHandshake(ref writer);
            if (writer.BytesWritten > 0)
            {
                await writer.FlushAsync();
            }
            else
            {
                writer.Commit();
            }
            while (true)
            {
                while (true)
                {
                    var result = await _handshakeOutpipe.ReadAsync();

                    var buffer = result.Buffer;
                    if (result.IsCompleted && result.Buffer.IsEmpty)
                    {
                        break;
                    }
                    try
                    {
                        while (buffer.Length > 0)
                        {
                            ReadableBuffer messageBuffer;
                            if (buffer.Length <= RecordProcessor.PlainTextMaxSize)
                            {
                                messageBuffer = buffer;
                                buffer        = buffer.Slice(buffer.End);
                            }
                            else
                            {
                                messageBuffer = buffer.Slice(0, RecordProcessor.PlainTextMaxSize);
                                buffer        = buffer.Slice(RecordProcessor.PlainTextMaxSize);
                            }
                            writer = _lowerConnection.Output.Alloc();
                            RecordProcessor.WriteRecord(ref writer, RecordType.Handshake, messageBuffer, _state);
                            await writer.FlushAsync();
                        }
                        _state.DataForCurrentScheduleSent.Set();
                    }
                    finally
                    {
                        _handshakeOutpipe.AdvanceReader(buffer.Start, buffer.End);
                    }
                }
            }
        }
예제 #7
0
        private unsafe Uv.uv_buf_t OnAlloc(UvStreamHandle handle, int status)
        {
            _inputBuffer = _input.Alloc(2048);

            void *pointer;

            if (!_inputBuffer.Memory.TryGetPointer(out pointer))
            {
                throw new InvalidOperationException("Pointer must be pinned");
            }

            return(handle.Libuv.buf_init((IntPtr)pointer, _inputBuffer.Memory.Length));
        }
예제 #8
0
        public async Task CanAppendSelfWhileEmpty()
        { // not really an expectation; just an accepted caveat
            using (var memoryPool = new MemoryPool())
            {
                var pipe = new Pipe(memoryPool);

                var output   = pipe.Alloc();
                var readable = output.AsReadableBuffer();
                output.Append(readable);
                Assert.Equal(0, output.AsReadableBuffer().Length);

                await output.FlushAsync();
            }
        }
예제 #9
0
        public async Task CanWriteUInt64ToBuffer(ulong value, string valueAsString)
        {
            using (var memoryPool = new MemoryPool())
            {
                var pipe   = new Pipe(memoryPool);
                var buffer = pipe.Alloc();
                buffer.Append(value, EncodingData.InvariantUtf8);
                await buffer.FlushAsync();

                var result = await pipe.ReadAsync();

                var inputBuffer = result.Buffer;

                Assert.Equal(valueAsString, inputBuffer.GetUtf8String());
            }
        }
예제 #10
0
        public void CanReReadDataThatHasNotBeenCommitted_LargeData()
        {
            using (var memoryPool = new MemoryPool())
            {
                var pipe = new Pipe(memoryPool);

                var output = pipe.Alloc();

                byte[]    predictablyGibberish = new byte[512];
                const int SEED   = 1235412;
                Random    random = new Random(SEED);
                for (int i = 0; i < 50; i++)
                {
                    for (int j = 0; j < predictablyGibberish.Length; j++)
                    {
                        // doing it this way to be 100% sure about repeating the PRNG order
                        predictablyGibberish[j] = (byte)random.Next(0, 256);
                    }
                    output.Write(predictablyGibberish);
                }

                var readable = output.AsReadableBuffer();
                Assert.False(readable.IsSingleSpan);
                Assert.False(readable.IsEmpty);
                Assert.Equal(50 * 512, readable.Length);

                random = new Random(SEED);
                int correctCount = 0;
                foreach (var memory in readable)
                {
                    var span = memory.Span;
                    for (int i = 0; i < span.Length; i++)
                    {
                        if (span[i] == (byte)random.Next(0, 256))
                        {
                            correctCount++;
                        }
                    }
                }
                Assert.Equal(50 * 512, correctCount);
            }
        }
예제 #11
0
        public void CanReReadDataThatHasNotBeenCommitted_SmallData()
        {
            using (var memoryPool = new MemoryPool())
            {
                var pipe   = new Pipe(memoryPool);
                var output = pipe.Alloc();

                Assert.True(output.AsReadableBuffer().IsEmpty);
                Assert.Equal(0, output.AsReadableBuffer().Length);


                output.Append("hello world", EncodingData.InvariantUtf8);
                var readable = output.AsReadableBuffer();

                // check that looks about right
                Assert.False(readable.IsEmpty);
                Assert.Equal(11, readable.Length);
                Assert.True(readable.Equals(Encoding.UTF8.GetBytes("hello world")));
                Assert.True(readable.Slice(1, 3).Equals(Encoding.UTF8.GetBytes("ell")));

                // check it all works after we write more
                output.Append("more data", EncodingData.InvariantUtf8);

                // note that the snapshotted readable should not have changed by this
                Assert.False(readable.IsEmpty);
                Assert.Equal(11, readable.Length);
                Assert.True(readable.Equals(Encoding.UTF8.GetBytes("hello world")));
                Assert.True(readable.Slice(1, 3).Equals(Encoding.UTF8.GetBytes("ell")));

                // if we fetch it again, we can see everything
                readable = output.AsReadableBuffer();
                Assert.False(readable.IsEmpty);
                Assert.Equal(20, readable.Length);
                Assert.True(readable.Equals(Encoding.UTF8.GetBytes("hello worldmore data")));
            }
        }
예제 #12
0
        private async void StartReading()
        {
            try
            {
                while (true)
                {
                    var result = await _lowerConnection.Input.ReadAsync();

                    var buffer = result.Buffer;
                    try
                    {
                        ReadableBuffer messageBuffer;
                        while (RecordProcessor.TryGetFrame(ref buffer, out messageBuffer))
                        {
                            var recordType = RecordProcessor.ReadRecord(ref messageBuffer, _state);
                            if (_state == null)
                            {
                                if (recordType != RecordType.Handshake)
                                {
                                    Alerts.AlertException.ThrowAlert(Alerts.AlertLevel.Fatal, Alerts.AlertDescription.unexpected_message, "Requre a handshake for first message");
                                }
                                _state = VersionStateFactory.GetNewStateMachine(messageBuffer, _listener);
                                HandshakeWriting();
                            }
                            Console.WriteLine($"Received TLS frame {recordType}");
                            if (recordType == RecordType.Handshake)
                            {
                                var writer = _handshakePipe.Alloc();
                                writer.Append(messageBuffer);
                                await writer.FlushAsync();
                                await HandshakeReading();

                                if (_state.State == StateType.HandshakeComplete && !_startedApplicationWrite)
                                {
                                    ApplicationWriting();
                                    _startedApplicationWrite = true;
                                }
                                continue;
                            }
                            if (recordType == RecordType.Alert)
                            {
                                _state.HandleAlertMessage(messageBuffer);
                                continue;
                            }
                            if (recordType == RecordType.Application)
                            {
                                Console.WriteLine("Writing Application Data");
                                var writer = _outputPipe.Alloc();
                                writer.Append(messageBuffer);
                                await writer.FlushAsync();

                                continue;
                            }
                            Alerts.AlertException.ThrowAlert(Alerts.AlertLevel.Fatal, Alerts.AlertDescription.unexpected_message, $"Unknown message type {recordType}");
                        }
                        if (result.IsCompleted)
                        {
                            return;
                        }
                    }
                    finally
                    {
                        _lowerConnection.Input.Advance(buffer.Start, buffer.End);
                    }
                }
            }
            catch
            {
                //nom nom
                Dispose();
            }
        }
예제 #13
0
        private async void ReceiveFromSocketAndPushToWriterAsync()
        {
            SocketAsyncEventArgs args = null;

            try
            {
                // if the consumer says they don't want the data, we need to shut down the receive
                GC.KeepAlive(_input.Writing.ContinueWith(delegate
                {// GC.KeepAlive here just to shut the compiler up
                    try { Socket.Shutdown(SocketShutdown.Receive); } catch { }
                }));

                // wait for someone to be interested in data before we
                // start allocating buffers and probing the socket
                await _input.ReadingStarted;

                args = GetOrCreateSocketAsyncEventArgs();
                while (!_input.Writing.IsCompleted)
                {
                    bool           haveWriteBuffer = false;
                    WritableBuffer buffer          = default(WritableBuffer);
                    var            initialSegment  = default(ArraySegment <byte>);

                    try
                    {
                        int bytesFromInitialDataBuffer = 0;

                        if (Socket.Available == 0)
                        {
                            // now, this gets a bit messy unfortunately, because support for the ideal option
                            // (zero-length reads) is platform dependent
                            switch (_bufferStyle)
                            {
                            case BufferStyle.Unknown:
                                try
                                {
                                    initialSegment = await ReceiveInitialDataUnknownStrategyAsync(args);
                                }
                                catch
                                {
                                    initialSegment = default(ArraySegment <byte>);
                                }
                                if (initialSegment.Array == null)
                                {
                                    continue;     // redo from start
                                }
                                break;

                            case BufferStyle.UseZeroLengthBuffer:
                                // if we already have a buffer, use that (but: zero count); otherwise use a shared
                                // zero-length; this avoids constantly changing the buffer that the args use, which
                                // avoids some overheads
                                args.SetBuffer(args.Buffer ?? _zeroLengthBuffer, 0, 0);

                                // await async for the io work to be completed
                                await Socket.ReceiveSignalAsync(args);

                                break;

                            case BufferStyle.UseSmallBuffer:
                                // We need  to do a speculative receive with a *cheap* buffer while we wait for input; it would be *nice* if
                                // we could do a zero-length receive, but this is not supported equally on all platforms (fine on Windows, but
                                // linux hates it). The key aim here is to make sure that we don't tie up an entire block from the memory pool
                                // waiting for input on a socket; fine for 1 socket, not so fine for 100,000 sockets

                                // do a short receive while we wait (async) for data
                                initialSegment = LeaseSmallBuffer();
                                args.SetBuffer(initialSegment.Array, initialSegment.Offset, initialSegment.Count);

                                // await async for the io work to be completed
                                await Socket.ReceiveSignalAsync(args);

                                break;
                            }
                            if (args.SocketError != SocketError.Success)
                            {
                                throw new SocketException((int)args.SocketError);
                            }

                            // note we can't check BytesTransferred <= 0, as we always
                            // expect 0; but if we returned, we expect data to be
                            // buffered *on the socket*, else EOF
                            if ((bytesFromInitialDataBuffer = args.BytesTransferred) <= 0)
                            {
                                if (ReferenceEquals(initialSegment.Array, _zeroLengthBuffer))
                                {
                                    // sentinel value that means we should just
                                    // consume sync (we expect there to be data)
                                    initialSegment = default(ArraySegment <byte>);
                                }
                                else
                                {
                                    // socket reported EOF
                                    RecycleSmallBuffer(ref initialSegment);
                                }
                                if (Socket.Available == 0)
                                {
                                    // yup, definitely an EOF
                                    break;
                                }
                            }
                        }


                        // note that we will try to coalesce things here to reduce the number of flushes; we
                        // certainly want to coalesce the initial buffer (from the speculative receive) with the initial
                        // data, but we probably don't want to buffer indefinitely; for now, it will buffer up to 4 pages
                        // before flushing (entirely arbitrarily) - might want to make this configurable later
                        buffer          = _input.Alloc(SmallBufferSize * 2);
                        haveWriteBuffer = true;

                        const int FlushInputEveryBytes = 4 * MemoryPool.MaxPooledBlockLength;

                        if (initialSegment.Array != null)
                        {
                            // need to account for anything that we got in the speculative receive
                            if (bytesFromInitialDataBuffer != 0)
                            {
                                buffer.Write(new Span <byte>(initialSegment.Array, initialSegment.Offset, bytesFromInitialDataBuffer));
                            }
                            // make the small buffer available to other consumers
                            RecycleSmallBuffer(ref initialSegment);
                        }

                        bool isEOF = false;
                        while (Socket.Available != 0 && buffer.BytesWritten < FlushInputEveryBytes)
                        {
                            buffer.Ensure(); // ask for *something*, then use whatever is available (usually much much more)
                            SetBuffer(buffer.Memory, args);
                            // await async for the io work to be completed
                            await Socket.ReceiveSignalAsync(args);

                            // either way, need to validate
                            if (args.SocketError != SocketError.Success)
                            {
                                throw new SocketException((int)args.SocketError);
                            }
                            int len = args.BytesTransferred;
                            if (len <= 0)
                            {
                                // socket reported EOF
                                isEOF = true;
                                break;
                            }

                            // record what data we filled into the buffer
                            buffer.Advance(len);
                        }
                        if (isEOF)
                        {
                            break;
                        }
                    }
                    finally
                    {
                        RecycleSmallBuffer(ref initialSegment);
                        if (haveWriteBuffer)
                        {
                            await buffer.FlushAsync();
                        }
                    }
                }
                _input.CompleteWriter();
            }
            catch (Exception ex)
            {
                // don't trust signal after an error; someone else could
                // still have it and invoke Set
                if (args != null)
                {
                    args.UserToken = null;
                }
                _input?.CompleteWriter(ex);
            }
            finally
            {
                RecycleSocketAsyncEventArgs(args);
            }
        }
예제 #14
0
 public WritableBuffer Alloc(int minimumSize = 0)
 {
     return(_pipe.Alloc(minimumSize));
 }