Ejemplo n.º 1
0
        private async void Consume(IReadableChannel channel)
        {
            while (true)
            {
                var result = await channel.ReadAsync();

                var buffer = result.Buffer;

                try
                {
                    if (buffer.IsEmpty && result.IsCompleted)
                    {
                        break;
                    }

                    await WriteAsync(buffer);
                }
                finally
                {
                    channel.Advance(buffer.End);
                }
            }

            channel.Complete();
        }
Ejemplo n.º 2
0
        public static async Task CopyToAsync(this IReadableChannel input, Stream stream, int bufferSize, CancellationToken cancellationToken)
        {
            // TODO: Use bufferSize argument
            while (!cancellationToken.IsCancellationRequested)
            {
                var inputBuffer = await input.ReadAsync();

                try
                {
                    if (inputBuffer.IsEmpty && input.Reading.IsCompleted)
                    {
                        return;
                    }

                    foreach (var memory in inputBuffer)
                    {
                        ArraySegment <byte> buffer;

                        if (!memory.TryGetArray(out buffer))
                        {
                            // Fall back to copies if this was native memory and we were unable to get
                            //  something we could write
                            buffer = new ArraySegment <byte>(memory.Span.CreateArray());
                        }

                        await stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count);
                    }
                }
                finally
                {
                    input.Advance(inputBuffer.End);
                }
            }
        }
Ejemplo n.º 3
0
        public static ValueTask <int> ReadAsync(this IReadableChannel input, Span <byte> destination)
        {
            while (true)
            {
                var awaiter = input.ReadAsync();

                if (!awaiter.IsCompleted)
                {
                    break;
                }


                var result      = awaiter.GetResult();
                var inputBuffer = result.Buffer;

                var fin    = result.IsCompleted;
                var sliced = inputBuffer.Slice(0, destination.Length);
                sliced.CopyTo(destination);
                int actual = sliced.Length;
                input.Advance(sliced.End);

                if (actual != 0)
                {
                    return(new ValueTask <int>(actual));
                }
                else if (fin)
                {
                    return(new ValueTask <int>(0));
                }
            }

            return(new ValueTask <int>(input.ReadAsyncAwaited(destination)));
        }
Ejemplo n.º 4
0
        private static async Task <int> ReadAsyncAwaited(this IReadableChannel input, Span <byte> destination)
        {
            while (true)
            {
                var result = await input.ReadAsync();

                var inputBuffer = result.Buffer;

                var fin = result.IsCompleted;

                var sliced = inputBuffer.Slice(0, destination.Length);
                sliced.CopyTo(destination);
                int actual = sliced.Length;
                input.Advance(sliced.End);

                if (actual != 0)
                {
                    return(actual);
                }
                else if (fin)
                {
                    return(0);
                }
            }
        }
Ejemplo n.º 5
0
        private static async Task CopyCompletedAsync(IReadableChannel input, IWritableChannel channel)
        {
            var inputBuffer = await input.ReadAsync();

            while (true)
            {
                try
                {
                    if (inputBuffer.IsEmpty && input.Reading.IsCompleted)
                    {
                        return;
                    }

                    var buffer = channel.Alloc();

                    buffer.Append(ref inputBuffer);

                    await buffer.FlushAsync();
                }
                finally
                {
                    input.Advance(inputBuffer.End);
                }

                var awaiter = input.ReadAsync();

                if (!awaiter.IsCompleted)
                {
                    // No more data
                    break;
                }

                inputBuffer = await awaiter;
            }
        }
Ejemplo n.º 6
0
        public static async Task CopyToAsync(this IReadableChannel input, IWritableChannel output)
        {
            while (true)
            {
                var result = await input.ReadAsync();

                var inputBuffer = result.Buffer;

                var fin = result.IsCompleted;

                try
                {
                    if (inputBuffer.IsEmpty && fin)
                    {
                        return;
                    }

                    var buffer = output.Alloc();

                    buffer.Append(inputBuffer);

                    await buffer.FlushAsync();
                }
                finally
                {
                    input.Advance(inputBuffer.End);
                }
            }
        }
Ejemplo n.º 7
0
        private static async Task <ChannelReadResult> ReadAtLeastSlowAsync(ReadableChannelAwaitable awaitable, IReadableChannel input, int minimumRequiredBytes, CancellationToken cancellationToken)
        {
            var result = await awaitable;

            while (!result.IsCompleted && result.Buffer.Length < minimumRequiredBytes)
            {
                cancellationToken.ThrowIfCancellationRequested();
                input.Advance(
                    consumed: result.Buffer.Start,
                    examined: result.Buffer.End);
                result = await input.ReadAsync(/* cancelToken */);
            }
            return(result);
        }
Ejemplo n.º 8
0
        protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            int remaining = ContentLength;

            while (remaining > 0)
            {
                var result = await _output.ReadAsync();

                var inputBuffer = result.Buffer;

                var fin = result.IsCompleted;

                var consumed = inputBuffer.Start;

                try
                {
                    if (inputBuffer.IsEmpty && fin)
                    {
                        return;
                    }

                    var data = inputBuffer.Slice(0, remaining);

                    foreach (var memory in data)
                    {
                        ArraySegment <byte> buffer;

                        unsafe
                        {
                            if (!memory.TryGetArray(out buffer))
                            {
                                // Fall back to copies if this was native memory and we were unable to get
                                //  something we could write
                                buffer = new ArraySegment <byte>(memory.Span.ToArray());
                            }
                        }

                        await stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count);
                    }

                    consumed   = data.End;
                    remaining -= data.Length;
                }
                finally
                {
                    _output.Advance(consumed);
                }
            }
        }
        public static async Task <ReadableBuffer> ReadToEndAsync(this IReadableChannel input)
        {
            while (true)
            {
                // Wait for more data
                var result = await input.ReadAsync();

                if (result.IsCompleted)
                {
                    // Read all the data, return it
                    return(result.Buffer);
                }

                // Don't advance the buffer so remains in buffer
                input.Advance(result.Buffer.Start, result.Buffer.End);
            }
        }
Ejemplo n.º 10
0
            public async Task Execute(IReadableChannel input, IWritableChannel output)
            {
                while (true)
                {
                    var inputBuffer = await input.ReadAsync();

                    if (inputBuffer.IsEmpty && input.Reading.IsCompleted)
                    {
                        break;
                    }

                    var writerBuffer = output.Alloc(2048);
                    var memory       = inputBuffer.First;
                    if (memory.Length > 0)
                    {
                        unsafe
                        {
                            _inflater.SetInput((IntPtr)memory.UnsafePointer, memory.Length);

                            int written = _inflater.Inflate((IntPtr)memory.UnsafePointer, memory.Length);

                            writerBuffer.Advance(written);

                            var consumed = memory.Length - _inflater.AvailableInput;

                            inputBuffer = inputBuffer.Slice(0, consumed);
                        }
                    }

                    input.Advance(inputBuffer.End);

                    await writerBuffer.FlushAsync();
                }

                input.Complete();

                output.Complete();

                _inflater.Dispose();
            }
Ejemplo n.º 11
0
        // TODO: Pull this up to Channels. We should be able to do it there without allocating a Task<T> in any case (rather than here where we can avoid allocation
        // only if the buffer is already ready and has enough data)
        public static ValueTask <ChannelReadResult> ReadAtLeastAsync(this IReadableChannel input, int minimumRequiredBytes, CancellationToken cancellationToken)
        {
            var awaiter = input.ReadAsync(/* cancellationToken */);

            // Short-cut path!
            if (awaiter.IsCompleted)
            {
                // We have a buffer, is it big enough?
                var result = awaiter.GetResult();

                if (result.IsCompleted || result.Buffer.Length >= minimumRequiredBytes)
                {
                    return(new ValueTask <ChannelReadResult>(result));
                }

                // Buffer wasn't big enough, mark it as examined and continue to the "slow" path below
                input.Advance(
                    consumed: result.Buffer.Start,
                    examined: result.Buffer.End);
            }
            return(new ValueTask <ChannelReadResult>(ReadAtLeastSlowAsync(awaiter, input, minimumRequiredBytes, cancellationToken)));
        }
Ejemplo n.º 12
0
        public static async Task CopyToAsync(this IReadableChannel input, Stream stream, int bufferSize, CancellationToken cancellationToken)
        {
            // TODO: Use bufferSize argument
            while (!cancellationToken.IsCancellationRequested)
            {
                var result = await input.ReadAsync();

                var inputBuffer = result.Buffer;
                try
                {
                    if (inputBuffer.IsEmpty && result.IsCompleted)
                    {
                        return;
                    }

                    await inputBuffer.CopyToAsync(stream);
                }
                finally
                {
                    input.Advance(inputBuffer.End);
                }
            }
        }
Ejemplo n.º 13
0
        public async Task ProcessAllRequests()
        {
            Reset();

            while (true)
            {
                var buffer = await _input.ReadAsync();

                try
                {
                    if (buffer.IsEmpty && _input.Reading.IsCompleted)
                    {
                        // We're done with this connection
                        return;
                    }
                    Console.WriteLine($"Read: {buffer.Length} bytes");
                    Console.WriteLine(BitConverter.ToString(buffer.ToArray()));
                    Console.WriteLine(buffer.GetAsciiString());
                    var result = _parser.ParseRequest(ref buffer);
                    Console.WriteLine($"Result: {result}");
                    switch (result)
                    {
                        case HttpRequestParser.ParseResult.Incomplete:
                            if (_input.Reading.IsCompleted)
                            {
                                // Didn't get the whole request and the connection ended
                                throw new EndOfStreamException();
                            }
                            // Need more data
                            continue;
                        case HttpRequestParser.ParseResult.Complete:
                            // Done
                            break;
                        case HttpRequestParser.ParseResult.BadRequest:
                            // TODO: Don't throw here;
                            throw new Exception();
                        default:
                            break;
                    }

                }
                catch (Exception)
                {
                    StatusCode = 400;

                    await EndResponse();

                    return;
                }
                finally
                {
                    _input.Advance(buffer.Start, buffer.End);
                }

                var context = _application.CreateContext(this);

                try
                {
                    if (!_isHttp2 && RequestHeaders.ContainsKey("Upgrade") && TryUpgradeToHttp2())
                    {
                        _outputFormatter.Write(_http2SwitchBytes);
                        _isHttp2 = true;

                        /*
                         The first HTTP/2 frame sent by the server MUST be a server connection
                         preface (Section 3.5) consisting of a SETTINGS frame (Section 6.5).
                        */
                        _outputFormatter.Write(_emptySettingsFrame);


                        await _outputFormatter.FlushAsync();
                    }

                    await _application.ProcessRequestAsync(context);
                }
                catch (Exception ex)
                {
                    StatusCode = 500;

                    _application.DisposeContext(context, ex);
                }
                finally
                {
                    await EndResponse();
                }

                if (!KeepAlive)
                {
                    break;
                }

                Reset();
            }
        }
        internal static async Task <HttpRequest> ParseHttpRequest(IReadableChannel _input)
        {
            ReadableBuffer Method = default(ReadableBuffer), Path = default(ReadableBuffer), HttpVersion = default(ReadableBuffer);
            Dictionary <string, ReadableBuffer> Headers = new Dictionary <string, ReadableBuffer>();

            try
            {
                ParsingState _state       = ParsingState.StartLine;
                bool         needMoreData = true;
                while (needMoreData)
                {
                    var buffer = await _input.ReadAsync();

                    var consumed = buffer.Start;
                    needMoreData = true;

                    try
                    {
                        if (buffer.IsEmpty && _input.Reading.IsCompleted)
                        {
                            throw new EndOfStreamException();
                        }

                        if (_state == ParsingState.StartLine)
                        {
                            // Find \n
                            ReadCursor     delim;
                            ReadableBuffer startLine;
                            if (!buffer.TrySliceTo((byte)'\r', (byte)'\n', out startLine, out delim))
                            {
                                continue;
                            }


                            // Move the buffer to the rest
                            buffer = buffer.Slice(delim).Slice(2);

                            ReadableBuffer method;
                            if (!startLine.TrySliceTo((byte)' ', out method, out delim))
                            {
                                throw new Exception();
                            }

                            Method = method.Preserve();

                            // Skip ' '
                            startLine = startLine.Slice(delim).Slice(1);

                            ReadableBuffer path;
                            if (!startLine.TrySliceTo((byte)' ', out path, out delim))
                            {
                                throw new Exception();
                            }

                            Path = path.Preserve();

                            // Skip ' '
                            startLine = startLine.Slice(delim).Slice(1);

                            var httpVersion = startLine;
                            if (httpVersion.IsEmpty)
                            {
                                throw new Exception();
                            }

                            HttpVersion = httpVersion.Preserve();

                            _state   = ParsingState.Headers;
                            consumed = buffer.Start;
                        }

                        // Parse headers
                        // key: value\r\n

                        while (!buffer.IsEmpty)
                        {
                            var ch = buffer.Peek();

                            if (ch == -1)
                            {
                                break;
                            }

                            if (ch == '\r')
                            {
                                // Check for final CRLF.
                                buffer = buffer.Slice(1);
                                ch     = buffer.Peek();
                                buffer = buffer.Slice(1);

                                if (ch == -1)
                                {
                                    break;
                                }
                                else if (ch == '\n')
                                {
                                    consumed     = buffer.Start;
                                    needMoreData = false;
                                    break;
                                }

                                // Headers don't end in CRLF line.
                                throw new Exception();
                            }

                            var headerName  = default(ReadableBuffer);
                            var headerValue = default(ReadableBuffer);

                            // End of the header
                            // \n
                            ReadCursor     delim;
                            ReadableBuffer headerPair;
                            if (!buffer.TrySliceTo((byte)'\n', out headerPair, out delim))
                            {
                                break;
                            }

                            buffer = buffer.Slice(delim).Slice(1);

                            // :
                            if (!headerPair.TrySliceTo((byte)':', out headerName, out delim))
                            {
                                throw new Exception();
                            }

                            headerName = headerName.TrimStart();
                            headerPair = headerPair.Slice(delim).Slice(1);

                            // \r
                            if (!headerPair.TrySliceTo((byte)'\r', out headerValue, out delim))
                            {
                                // Bad request
                                throw new Exception();
                            }

                            headerValue = headerValue.TrimStart();
                            Headers[ToHeaderKey(ref headerName)] = headerValue.Preserve();

                            // Move the consumed
                            consumed = buffer.Start;
                        }
                    }
                    finally
                    {
                        _input.Advance(consumed);
                    }
                }
                var result = new HttpRequest(Method, Path, HttpVersion, Headers);
                Method  = Path = HttpVersion = default(ReadableBuffer);
                Headers = null;
                return(result);
            }
            finally
            {
                Method.Dispose();
                Path.Dispose();
                HttpVersion.Dispose();
                if (Headers != null)
                {
                    foreach (var pair in Headers)
                    {
                        pair.Value.Dispose();
                    }
                }
            }
        }
Ejemplo n.º 15
0
            public async Task Execute(IReadableChannel input, IWritableChannel output)
            {
                while (true)
                {
                    var result = await input.ReadAsync();

                    var inputBuffer = result.Buffer;

                    if (inputBuffer.IsEmpty)
                    {
                        if (result.IsCompleted)
                        {
                            break;
                        }

                        input.Advance(inputBuffer.End);
                        continue;
                    }

                    var writerBuffer = output.Alloc();
                    var memory       = inputBuffer.First;

                    unsafe
                    {
                        // TODO: Pin pointer if not pinned
                        void *inPointer;
                        if (memory.TryGetPointer(out inPointer))
                        {
                            _deflater.SetInput((IntPtr)inPointer, memory.Length);
                        }
                        else
                        {
                            throw new InvalidOperationException("Pointer needs to be pinned");
                        }
                    }

                    while (!_deflater.NeedsInput())
                    {
                        unsafe
                        {
                            void *outPointer;
                            writerBuffer.Ensure();
                            if (writerBuffer.Memory.TryGetPointer(out outPointer))
                            {
                                int written = _deflater.ReadDeflateOutput((IntPtr)outPointer, writerBuffer.Memory.Length);
                                writerBuffer.Advance(written);
                            }
                            else
                            {
                                throw new InvalidOperationException("Pointer needs to be pinned");
                            }
                        }
                    }

                    var consumed = memory.Length - _deflater.AvailableInput;

                    inputBuffer = inputBuffer.Slice(0, consumed);

                    input.Advance(inputBuffer.End);

                    await writerBuffer.FlushAsync();
                }

                bool flushed = false;

                do
                {
                    // Need to do more stuff here
                    var writerBuffer = output.Alloc();

                    unsafe
                    {
                        void *pointer;
                        writerBuffer.Ensure();
                        var memory = writerBuffer.Memory;
                        if (memory.TryGetPointer(out pointer))
                        {
                            int compressedBytes;
                            flushed = _deflater.Flush((IntPtr)pointer, memory.Length, out compressedBytes);
                            writerBuffer.Advance(compressedBytes);
                        }
                        else
                        {
                            throw new InvalidOperationException("Pointer needs to be pinned");
                        }
                    }

                    await writerBuffer.FlushAsync();
                }while (flushed);

                bool finished = false;

                do
                {
                    // Need to do more stuff here
                    var writerBuffer = output.Alloc();

                    unsafe
                    {
                        void *pointer;
                        writerBuffer.Ensure();
                        var memory = writerBuffer.Memory;
                        if (memory.TryGetPointer(out pointer))
                        {
                            int compressedBytes;
                            finished = _deflater.Finish((IntPtr)pointer, memory.Length, out compressedBytes);
                            writerBuffer.Advance(compressedBytes);
                        }
                    }

                    await writerBuffer.FlushAsync();
                }while (!finished);

                input.Complete();

                output.Complete();

                _deflater.Dispose();
            }
Ejemplo n.º 16
0
            public async Task Execute(IReadableChannel input, IWritableChannel output)
            {
                while (true)
                {
                    var result = await input.ReadAsync();

                    var inputBuffer = result.Buffer;

                    if (inputBuffer.IsEmpty)
                    {
                        if (result.IsCompleted)
                        {
                            break;
                        }

                        input.Advance(inputBuffer.End);
                        continue;
                    }

                    var writerBuffer = output.Alloc();
                    var memory       = inputBuffer.First;
                    if (memory.Length > 0)
                    {
                        unsafe
                        {
                            void *pointer;
                            if (memory.TryGetPointer(out pointer))
                            {
                                _inflater.SetInput((IntPtr)pointer, memory.Length);

                                void *writePointer;
                                writerBuffer.Ensure();
                                if (writerBuffer.Memory.TryGetPointer(out writePointer))
                                {
                                    int written = _inflater.Inflate((IntPtr)writePointer, writerBuffer.Memory.Length);
                                    writerBuffer.Advance(written);
                                }
                                else
                                {
                                    throw new InvalidOperationException("Pointer needs to be pinned");
                                }
                            }
                            else
                            {
                                throw new InvalidOperationException("Pointer needs to be pinned");
                            }

                            var consumed = memory.Length - _inflater.AvailableInput;

                            inputBuffer = inputBuffer.Slice(0, consumed);
                        }
                    }

                    input.Advance(inputBuffer.End);

                    await writerBuffer.FlushAsync();
                }

                input.Complete();

                output.Complete();

                _inflater.Dispose();
            }
Ejemplo n.º 17
0
        public async Task ProcessAllRequests()
        {
            Reset();

            while (true)
            {
                var result = await _input.ReadAsync();

                var buffer = result.Buffer;

                try
                {
                    if (buffer.IsEmpty && result.IsCompleted)
                    {
                        // We're done with this connection
                        return;
                    }

                    var parserResult = _parser.ParseRequest(ref buffer);

                    switch (parserResult)
                    {
                    case HttpRequestParser.ParseResult.Incomplete:
                        if (result.IsCompleted)
                        {
                            // Didn't get the whole request and the connection ended
                            throw new EndOfStreamException();
                        }
                        // Need more data
                        continue;

                    case HttpRequestParser.ParseResult.Complete:
                        // Done
                        break;

                    case HttpRequestParser.ParseResult.BadRequest:
                        // TODO: Don't throw here;
                        throw new Exception();

                    default:
                        break;
                    }
                }
                catch (Exception)
                {
                    StatusCode = 400;

                    await EndResponse();

                    return;
                }
                finally
                {
                    _input.Advance(buffer.Start, buffer.End);
                }

                var context = _application.CreateContext(this);

                try
                {
                    await _application.ProcessRequestAsync(context);
                }
                catch (Exception ex)
                {
                    StatusCode = 500;

                    _application.DisposeContext(context, ex);
                }
                finally
                {
                    await EndResponse();
                }

                if (!KeepAlive)
                {
                    break;
                }

                Reset();
            }
        }
Ejemplo n.º 18
0
 public static void Advance(this IReadableChannel input, ReadCursor cursor)
 {
     input.Advance(cursor, cursor);
 }
Ejemplo n.º 19
0
            public async Task Execute(IReadableChannel input, IWritableChannel output)
            {
                while (true)
                {
                    var inputBuffer = await input.ReadAsync();

                    if (inputBuffer.IsEmpty && input.Reading.IsCompleted)
                    {
                        break;
                    }

                    var writerBuffer = output.Alloc(2048);
                    var memory       = inputBuffer.First;

                    unsafe
                    {
                        _deflater.SetInput((IntPtr)memory.UnsafePointer, memory.Length);
                    }

                    while (!_deflater.NeedsInput())
                    {
                        unsafe
                        {
                            int written = _deflater.ReadDeflateOutput((IntPtr)writerBuffer.Memory.UnsafePointer, writerBuffer.Memory.Length);
                            writerBuffer.Advance(written);
                        }
                    }

                    var consumed = memory.Length - _deflater.AvailableInput;

                    inputBuffer = inputBuffer.Slice(0, consumed);

                    input.Advance(inputBuffer.End);

                    await writerBuffer.FlushAsync();
                }

                bool flushed;

                do
                {
                    // Need to do more stuff here
                    var writerBuffer = output.Alloc(2048);
                    var memory       = writerBuffer.Memory;

                    unsafe
                    {
                        int compressedBytes;
                        flushed = _deflater.Flush((IntPtr)memory.UnsafePointer, memory.Length, out compressedBytes);
                        writerBuffer.Advance(compressedBytes);
                    }

                    await writerBuffer.FlushAsync();
                }while (flushed);

                bool finished;

                do
                {
                    // Need to do more stuff here
                    var writerBuffer = output.Alloc(2048);
                    var memory       = writerBuffer.Memory;

                    unsafe
                    {
                        int compressedBytes;
                        finished = _deflater.Finish((IntPtr)memory.UnsafePointer, memory.Length, out compressedBytes);
                        writerBuffer.Advance(compressedBytes);
                    }

                    await writerBuffer.FlushAsync();
                }while (!finished);

                input.Complete();

                output.Complete();

                _deflater.Dispose();
            }
Ejemplo n.º 20
0
        private async Task <WebSocketCloseResult> ReceiveLoop(Func <WebSocketFrame, object, Task> messageHandler, object state, CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                // WebSocket Frame layout (https://tools.ietf.org/html/rfc6455#section-5.2):
                //  0                   1                   2                   3
                //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                // +-+-+-+-+-------+-+-------------+-------------------------------+
                // |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
                // |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
                // |N|V|V|V|       |S|             |   (if payload len==126/127)   |
                // | |1|2|3|       |K|             |                               |
                // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
                // |     Extended payload length continued, if payload len == 127  |
                // + - - - - - - - - - - - - - - - +-------------------------------+
                // |                               |Masking-key, if MASK set to 1  |
                // +-------------------------------+-------------------------------+
                // | Masking-key (continued)       |          Payload Data         |
                // +-------------------------------- - - - - - - - - - - - - - - - +
                // :                     Payload Data continued ...                :
                // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
                // |                     Payload Data continued ...                |
                // +---------------------------------------------------------------+

                // Read at least 2 bytes
                var result = await _inbound.ReadAtLeastAsync(2, cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();
                if (result.IsCompleted && result.Buffer.Length < 2)
                {
                    return(WebSocketCloseResult.AbnormalClosure);
                }
                var buffer = result.Buffer;

                // Read the opcode
                var opcodeByte = buffer.ReadBigEndian <byte>();
                buffer = buffer.Slice(1);

                var fin       = (opcodeByte & 0x80) != 0;
                var opcodeNum = opcodeByte & 0x0F;
                var opcode    = (WebSocketOpcode)opcodeNum;

                if ((opcodeByte & 0x70) != 0)
                {
                    // Reserved bits set, this frame is invalid, close our side and terminate immediately
                    return(await CloseFromProtocolError(cancellationToken, 0, default(ReadableBuffer), "Reserved bits, which are required to be zero, were set."));
                }
                else if ((opcodeNum >= 0x03 && opcodeNum <= 0x07) || (opcodeNum >= 0x0B && opcodeNum <= 0x0F))
                {
                    // Reserved opcode
                    return(await CloseFromProtocolError(cancellationToken, 0, default(ReadableBuffer), $"Received frame using reserved opcode: 0x{opcodeNum:X}"));
                }

                // Read the first byte of the payload length
                var lenByte = buffer.ReadBigEndian <byte>();
                buffer = buffer.Slice(1);

                var masked     = (lenByte & 0x80) != 0;
                var payloadLen = (lenByte & 0x7F);

                // Mark what we've got so far as consumed
                _inbound.Advance(buffer.Start);

                // Calculate the rest of the header length
                var headerLength = masked ? 4 : 0;
                if (payloadLen == 126)
                {
                    headerLength += 2;
                }
                else if (payloadLen == 127)
                {
                    headerLength += 8;
                }

                uint maskingKey = 0;

                if (headerLength > 0)
                {
                    result = await _inbound.ReadAtLeastAsync(headerLength, cancellationToken);

                    cancellationToken.ThrowIfCancellationRequested();
                    if (result.IsCompleted && result.Buffer.Length < headerLength)
                    {
                        return(WebSocketCloseResult.AbnormalClosure);
                    }
                    buffer = result.Buffer;

                    // Read extended payload length (if any)
                    if (payloadLen == 126)
                    {
                        payloadLen = buffer.ReadBigEndian <ushort>();
                        buffer     = buffer.Slice(sizeof(ushort));
                    }
                    else if (payloadLen == 127)
                    {
                        var longLen = buffer.ReadBigEndian <ulong>();
                        buffer = buffer.Slice(sizeof(ulong));
                        if (longLen > int.MaxValue)
                        {
                            throw new WebSocketException($"Frame is too large. Maximum frame size is {int.MaxValue} bytes");
                        }
                        payloadLen = (int)longLen;
                    }

                    // Read masking key
                    if (masked)
                    {
                        var maskingKeyStart = buffer.Start;
                        maskingKey = buffer.Slice(0, 4).ReadBigEndian <uint>();
                        buffer     = buffer.Slice(4);
                    }

                    // Mark the length and masking key consumed
                    _inbound.Advance(buffer.Start);
                }

                var payload = default(ReadableBuffer);
                if (payloadLen > 0)
                {
                    result = await _inbound.ReadAtLeastAsync(payloadLen, cancellationToken);

                    cancellationToken.ThrowIfCancellationRequested();
                    if (result.IsCompleted && result.Buffer.Length < payloadLen)
                    {
                        return(WebSocketCloseResult.AbnormalClosure);
                    }
                    buffer = result.Buffer;

                    payload = buffer.Slice(0, payloadLen);

                    if (masked)
                    {
                        // Unmask
                        MaskingUtilities.ApplyMask(ref payload, maskingKey);
                    }
                }

                // Run the callback, if we're not cancelled.
                cancellationToken.ThrowIfCancellationRequested();

                var frame = new WebSocketFrame(fin, opcode, payload);

                if (frame.Opcode.IsControl() && !frame.EndOfMessage)
                {
                    // Control frames cannot be fragmented.
                    return(await CloseFromProtocolError(cancellationToken, payloadLen, payload, "Control frames may not be fragmented"));
                }
                else if (_currentMessageType != WebSocketOpcode.Continuation && opcode.IsMessage() && opcode != 0)
                {
                    return(await CloseFromProtocolError(cancellationToken, payloadLen, payload, "Received non-continuation frame during a fragmented message"));
                }
                else if (_currentMessageType == WebSocketOpcode.Continuation && frame.Opcode == WebSocketOpcode.Continuation)
                {
                    return(await CloseFromProtocolError(cancellationToken, payloadLen, payload, "Continuation Frame was received when expecting a new message"));
                }

                if (frame.Opcode == WebSocketOpcode.Close)
                {
                    // Allowed frame lengths:
                    //  0 - No body
                    //  2 - Code with no reason phrase
                    //  >2 - Code and reason phrase (must be valid UTF-8)
                    if (frame.Payload.Length > 125)
                    {
                        return(await CloseFromProtocolError(cancellationToken, payloadLen, payload, "Close frame payload too long. Maximum size is 125 bytes"));
                    }
                    else if ((frame.Payload.Length == 1) || (frame.Payload.Length > 2 && !Utf8Validator.ValidateUtf8(payload.Slice(2))))
                    {
                        return(await CloseFromProtocolError(cancellationToken, payloadLen, payload, "Close frame payload invalid"));
                    }

                    ushort?actualStatusCode;
                    var    closeResult = HandleCloseFrame(payload, frame, out actualStatusCode);

                    // Verify the close result
                    if (actualStatusCode != null)
                    {
                        var statusCode = actualStatusCode.Value;
                        if (statusCode < 1000 || statusCode == 1004 || statusCode == 1005 || statusCode == 1006 || (statusCode > 1011 && statusCode < 3000))
                        {
                            return(await CloseFromProtocolError(cancellationToken, payloadLen, payload, $"Invalid close status: {statusCode}."));
                        }
                    }

                    // Make the payload as consumed
                    if (payloadLen > 0)
                    {
                        _inbound.Advance(payload.End);
                    }

                    return(closeResult);
                }
                else
                {
                    if (frame.Opcode == WebSocketOpcode.Ping)
                    {
                        // Check the ping payload length
                        if (frame.Payload.Length > 125)
                        {
                            // Payload too long
                            return(await CloseFromProtocolError(cancellationToken, payloadLen, payload, "Ping frame exceeded maximum size of 125 bytes"));
                        }

                        await SendCoreAsync(
                            frame.EndOfMessage,
                            WebSocketOpcode.Pong,
                            payloadAllocLength : 0,
                            payloadLength : payload.Length,
                            payloadWriter : AppendPayloadWriter,
                            payload : payload,
                            cancellationToken : cancellationToken);
                    }
                    var effectiveOpcode = opcode == WebSocketOpcode.Continuation ? _currentMessageType : opcode;
                    if (effectiveOpcode == WebSocketOpcode.Text && !_validator.ValidateUtf8Frame(frame.Payload, frame.EndOfMessage))
                    {
                        // Drop the frame and immediately close with InvalidPayload
                        return(await CloseFromProtocolError(cancellationToken, payloadLen, payload, "An invalid Text frame payload was received", statusCode : WebSocketCloseStatus.InvalidPayloadData));
                    }
                    else if (_options.PassAllFramesThrough || (frame.Opcode != WebSocketOpcode.Ping && frame.Opcode != WebSocketOpcode.Pong))
                    {
                        await messageHandler(frame, state);
                    }
                }

                if (fin)
                {
                    // Reset the UTF8 validator
                    _validator.Reset();

                    // If it's a non-control frame, reset the message type tracker
                    if (opcode.IsMessage())
                    {
                        _currentMessageType = WebSocketOpcode.Continuation;
                    }
                }
                // If there isn't a current message type, and this was a fragmented message frame, set the current message type
                else if (!fin && _currentMessageType == WebSocketOpcode.Continuation && opcode.IsMessage())
                {
                    _currentMessageType = opcode;
                }

                // Mark the payload as consumed
                if (payloadLen > 0)
                {
                    _inbound.Advance(payload.End);
                }
            }
            return(WebSocketCloseResult.AbnormalClosure);
        }