Пример #1
0
        private bool ParsePreface(ReadableBuffer readableBuffer, out ReadCursor consumed, out ReadCursor examined)
        {
            consumed = readableBuffer.Start;
            examined = readableBuffer.End;

            if (readableBuffer.Length < ClientPreface.Length)
            {
                return(false);
            }

            var span = readableBuffer.IsSingleSpan
                ? readableBuffer.First.Span
                : readableBuffer.ToSpan();

            for (var i = 0; i < ClientPreface.Length; i++)
            {
                if (ClientPreface[i] != span[i])
                {
                    throw new Exception("Invalid HTTP/2 connection preface.");
                }
            }

            consumed = examined = readableBuffer.Move(readableBuffer.Start, ClientPreface.Length);
            return(true);
        }
Пример #2
0
        public unsafe bool ParseRequestLine <T>(T handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined) where T : IHttpRequestLineHandler
        {
            consumed = buffer.Start;
            examined = buffer.End;

            // Prepare the first span
            var span      = buffer.First.Span;
            var lineIndex = span.IndexOf(ByteLF);

            if (lineIndex >= 0)
            {
                consumed = buffer.Move(consumed, lineIndex + 1);
                span     = span.Slice(0, lineIndex + 1);
            }
            else if (buffer.IsSingleSpan || !TryGetNewLineSpan(ref buffer, ref span, out consumed))
            {
                // No request line end
                return(false);
            }

            // Fix and parse the span
            fixed(byte *data = &span.DangerousGetPinnableReference())
            {
                ParseRequestLine(handler, data, span.Length);
            }

            examined = consumed;
            return(true);
        }
Пример #3
0
        private static Span <byte> TryGetNewLineSpan(ref ReadableBuffer buffer, out ReadCursor end)
        {
            var start = buffer.Start;

            if (ReadCursorOperations.Seek(start, buffer.End, out end, ByteLF) != -1)
            {
                // Move 1 byte past the \n
                end = buffer.Move(end, 1);
                return(buffer.Slice(start, end).ToSpan());
            }

            return(default);
Пример #4
0
        private static bool TryGetNewLineSpan(ref ReadableBuffer buffer, ref Span <byte> span, out ReadCursor end)
        {
            var start = buffer.Start;

            if (ReadCursorOperations.Seek(start, buffer.End, out end, ByteLF) != -1)
            {
                // Move 1 byte past the \n
                end  = buffer.Move(end, 1);
                span = buffer.Slice(start, end).ToSpan();
                return(true);
            }
            return(false);
        }
Пример #5
0
        public void SegmentStartIsConsideredInBoundsCheck()
        {
            // 0               50           100    0             50             100
            // [                ##############] -> [##############                ]
            //                         ^c1            ^c2
            var bufferSegment1 = new BufferSegment();

            bufferSegment1.SetMemory(new OwnedArray <byte>(new byte[100]), 50, 99);

            var bufferSegment2 = new BufferSegment();

            bufferSegment2.SetMemory(new OwnedArray <byte>(new byte[100]), 0, 50);
            bufferSegment1.SetNext(bufferSegment2);

            var readableBuffer = new ReadableBuffer(new ReadCursor(bufferSegment1, 0), new ReadCursor(bufferSegment2, 50));

            var c1 = readableBuffer.Move(readableBuffer.Start, 25); // segment 1 index 75
            var c2 = readableBuffer.Move(readableBuffer.Start, 55); // segment 2 index 5

            var sliced = readableBuffer.Slice(c1, c2);

            Assert.Equal(30, sliced.Length);
        }
        private static void FindAllNewLines(ReadableBuffer buffer)
        {
            var start = buffer.Start;
            var end   = buffer.End;

            while (true)
            {
                if (ReadCursorOperations.Seek(start, end, out var found, (byte)'\n') == -1)
                {
                    break;
                }

                start = buffer.Move(found, 1);
            }
        }
Пример #7
0
            private void ReadChunkedData(ReadableBuffer buffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
            {
                var actual = Math.Min(buffer.Length, _inputLength);

                consumed = buffer.Move(buffer.Start, actual);
                examined = consumed;

                Copy(buffer.Slice(0, actual), writableBuffer);

                _inputLength -= actual;
                AddAndCheckConsumedBytes(actual);

                if (_inputLength == 0)
                {
                    _mode = Mode.Suffix;
                }
            }
Пример #8
0
        public void MovePrefersNextSegment()
        {
            var bufferSegment1 = new BufferSegment();

            bufferSegment1.SetMemory(new OwnedArray <byte>(new byte[100]), 49, 99);

            var bufferSegment2 = new BufferSegment();

            bufferSegment2.SetMemory(new OwnedArray <byte>(new byte[100]), 0, 0);
            bufferSegment1.SetNext(bufferSegment2);

            var readableBuffer = new ReadableBuffer(new ReadCursor(bufferSegment1, 0), new ReadCursor(bufferSegment2, 0));

            var c1 = readableBuffer.Move(readableBuffer.Start, 50);

            Assert.Equal(0, c1.Index);
            Assert.Equal(bufferSegment2, c1.Segment);
        }
Пример #9
0
            protected override bool Read(ReadableBuffer readableBuffer, WritableBuffer writableBuffer, out ReadCursor consumed, out ReadCursor examined)
            {
                if (_inputLength == 0)
                {
                    throw new InvalidOperationException("Attempted to read from completed Content-Length request body.");
                }

                var actual = (int)Math.Min(readableBuffer.Length, _inputLength);

                _inputLength -= actual;

                consumed = readableBuffer.Move(readableBuffer.Start, actual);
                examined = consumed;

                Copy(readableBuffer.Slice(0, actual), writableBuffer);

                return(_inputLength == 0);
            }
Пример #10
0
        public unsafe bool ParseRequestLine(TRequestHandler handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)
        {
            consumed = buffer.Start;
            examined = buffer.End;

            // Prepare the first span
            var span      = buffer.First.Span;
            var lineIndex = span.IndexOf(ByteLF);

            if (lineIndex >= 0)
            {
                consumed = buffer.Move(consumed, lineIndex + 1);
                span     = span.Slice(0, lineIndex + 1);
            }
            else if (buffer.IsSingleSpan)
            {
                // No request line end
                return(false);
            }
            else if (TryGetNewLine(ref buffer, out var found))
            {
                span     = buffer.Slice(consumed, found).ToSpan();
                consumed = found;
            }
Пример #11
0
        public static bool ReadFrame(ReadableBuffer readableBuffer, Http2Frame frame, out ReadCursor consumed, out ReadCursor examined)
        {
            consumed = readableBuffer.Start;
            examined = readableBuffer.End;

            if (readableBuffer.Length < Http2Frame.HeaderLength)
            {
                return(false);
            }

            var headerSlice = readableBuffer.Slice(0, Http2Frame.HeaderLength);

            headerSlice.CopyTo(frame.Raw);

            if (readableBuffer.Length < Http2Frame.HeaderLength + frame.Length)
            {
                return(false);
            }

            readableBuffer.Slice(Http2Frame.HeaderLength, frame.Length).CopyTo(frame.Payload);
            consumed = examined = readableBuffer.Move(readableBuffer.Start, Http2Frame.HeaderLength + frame.Length);

            return(true);
        }
Пример #12
0
 public void ReadableBufferMove_ChecksBounds(ReadableBuffer buffer)
 {
     Assert.Throws <InvalidOperationException>(() => buffer.Move(buffer.Start, 101));
 }
Пример #13
0
        public void ReadableBufferMove_MovesReadCursor(ReadableBuffer buffer)
        {
            var cursor = buffer.Move(buffer.Start, 65);

            Assert.Equal(buffer.Slice(65).Start, cursor);
        }
Пример #14
0
        private static async void WriteToSocket(TSocket tsocket, IPipeReader reader)
        {
            Exception error = null;

            try
            {
                while (true)
                {
                    var readResult = await reader.ReadAsync();

                    ReadableBuffer buffer = readResult.Buffer;
                    ReadCursor     end    = buffer.Start;
                    try
                    {
                        if ((buffer.IsEmpty && readResult.IsCompleted) || readResult.IsCancelled)
                        {
                            // EOF or TransportThread stopped
                            break;
                        }
                        if (!buffer.IsEmpty)
                        {
                            var result = TrySend(tsocket.Fd, ref buffer);
                            if (result.Value == buffer.Length)
                            {
                                end = buffer.End;
                            }
                            else if (result.IsSuccess)
                            {
                                end = buffer.Move(buffer.Start, result.Value);
                            }
                            else if (result == PosixResult.EAGAIN || result == PosixResult.EWOULDBLOCK)
                            {
                                if (!await Writable(tsocket))
                                {
                                    // TransportThread stopped
                                    break;
                                }
                            }
                            else
                            {
                                error = result.AsException();
                                break;
                            }
                        }
                    }
                    finally
                    {
                        // We need to call Advance to end the read
                        reader.Advance(end);
                    }
                }
            }
            catch (Exception ex)
            {
                error = ex;
            }
            finally
            {
                tsocket.ConnectionContext.OnConnectionClosed(error);
                reader.Complete(error);

                tsocket.StopReadFromSocket();

                CleanupSocketEnd(tsocket);
            }
        }
Пример #15
0
        public unsafe bool ParseHeaders <T>(T handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined, out int consumedBytes) where T : IHttpHeadersHandler
        {
            consumed      = buffer.Start;
            examined      = buffer.End;
            consumedBytes = 0;

            var bufferEnd = buffer.End;

            var reader = new ReadableBufferReader(buffer);
            var start  = default(ReadableBufferReader);
            var done   = false;

            try
            {
                while (!reader.End)
                {
                    var span      = reader.Span;
                    var remaining = span.Length - reader.Index;

                    fixed(byte *pBuffer = &span.DangerousGetPinnableReference())
                    {
                        while (remaining > 0)
                        {
                            var index = reader.Index;
                            int ch1;
                            int ch2;

                            // Fast path, we're still looking at the same span
                            if (remaining >= 2)
                            {
                                ch1 = pBuffer[index];
                                ch2 = pBuffer[index + 1];
                            }
                            else
                            {
                                // Store the reader before we look ahead 2 bytes (probably straddling
                                // spans)
                                start = reader;

                                // Possibly split across spans
                                ch1 = reader.Take();
                                ch2 = reader.Take();
                            }

                            if (ch1 == ByteCR)
                            {
                                // Check for final CRLF.
                                if (ch2 == -1)
                                {
                                    // Reset the reader so we don't consume anything
                                    reader = start;
                                    return(false);
                                }
                                else if (ch2 == ByteLF)
                                {
                                    // If we got 2 bytes from the span directly so skip ahead 2 so that
                                    // the reader's state matches what we expect
                                    if (index == reader.Index)
                                    {
                                        reader.Skip(2);
                                    }

                                    done = true;
                                    return(true);
                                }

                                // Headers don't end in CRLF line.
                                RejectRequest(RequestRejectionReason.InvalidRequestHeadersNoCRLF);
                            }

                            // We moved the reader so look ahead 2 bytes so reset both the reader
                            // and the index
                            if (index != reader.Index)
                            {
                                reader = start;
                                index  = reader.Index;
                            }

                            var endIndex = new ReadOnlySpan <byte>(pBuffer + index, remaining).IndexOf(ByteLF);
                            var length   = 0;

                            if (endIndex != -1)
                            {
                                length = endIndex + 1;
                                var pHeader = pBuffer + index;

                                TakeSingleHeader(pHeader, length, handler);
                            }
                            else
                            {
                                var current = reader.Cursor;

                                // Split buffers
                                if (ReadCursorOperations.Seek(current, bufferEnd, out var lineEnd, ByteLF) == -1)
                                {
                                    // Not there
                                    return(false);
                                }

                                // Make sure LF is included in lineEnd
                                lineEnd = buffer.Move(lineEnd, 1);
                                var headerSpan = buffer.Slice(current, lineEnd).ToSpan();
                                length = headerSpan.Length;

                                fixed(byte *pHeader = &headerSpan.DangerousGetPinnableReference())
                                {
                                    TakeSingleHeader(pHeader, length, handler);
                                }

                                // We're going to the next span after this since we know we crossed spans here
                                // so mark the remaining as equal to the headerSpan so that we end up at 0
                                // on the next iteration
                                remaining = length;
                            }

                            // Skip the reader forward past the header line
                            reader.Skip(length);
                            remaining -= length;
                        }
                    }
                }

                return(false);
            }
            finally
            {
                consumed      = reader.Cursor;
                consumedBytes = reader.ConsumedBytes;

                if (done)
                {
                    examined = consumed;
                }
            }
        }
        public ParseResult ParseMessage(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined, out byte[] message)
        {
            consumed = buffer.Start;
            examined = buffer.End;
            message  = null;

            var start = consumed;
            var end   = examined;

            while (buffer.Length > 0)
            {
                if (ReadCursorOperations.Seek(start, end, out var lineEnd, ByteLF) == -1)
                {
                    // For the case of  data: Foo\r\n\r\<Anytine except \n>
                    if (_internalParserState == InternalParseState.ReadEndOfMessage)
                    {
                        if (ConvertBufferToSpan(buffer.Slice(start, buffer.End)).Length > 1)
                        {
                            throw new FormatException("Expected a \\r\\n frame ending");
                        }
                    }

                    // Partial message. We need to read more.
                    return(ParseResult.Incomplete);
                }

                lineEnd = buffer.Move(lineEnd, 1);
                var line = ConvertBufferToSpan(buffer.Slice(start, lineEnd));
                buffer = buffer.Slice(line.Length);

                if (line.Length <= 1)
                {
                    throw new FormatException("There was an error in the frame format");
                }

                // Skip comments
                if (line[0] == ByteColon)
                {
                    start    = lineEnd;
                    consumed = lineEnd;
                    continue;
                }

                if (IsMessageEnd(line))
                {
                    _internalParserState = InternalParseState.ReadEndOfMessage;
                }

                // To ensure that the \n was preceded by a \r
                // since messages can't contain \n.
                // data: foo\n\bar should be encoded as
                // data: foo\r\n
                // data: bar\r\n
                else if (line[line.Length - _sseLineEnding.Length] != ByteCR)
                {
                    throw new FormatException("Unexpected '\n' in message. A '\n' character can only be used as part of the newline sequence '\r\n'");
                }
                else
                {
                    EnsureStartsWithDataPrefix(line);
                }

                var payload = Array.Empty <byte>();
                switch (_internalParserState)
                {
                case InternalParseState.ReadMessagePayload:
                    EnsureStartsWithDataPrefix(line);

                    // Slice away the 'data: '
                    var payloadLength = line.Length - (_dataPrefix.Length + _sseLineEnding.Length);
                    var newData       = line.Slice(_dataPrefix.Length, payloadLength).ToArray();
                    _data.Add(newData);

                    start    = lineEnd;
                    consumed = lineEnd;
                    break;

                case InternalParseState.ReadEndOfMessage:
                    if (_data.Count == 1)
                    {
                        payload = _data[0];
                    }
                    else if (_data.Count > 1)
                    {
                        // Find the final size of the payload
                        var payloadSize = 0;
                        foreach (var dataLine in _data)
                        {
                            payloadSize += dataLine.Length;
                        }

                        payloadSize += _newLine.Length * _data.Count;

                        // Allocate space in the payload buffer for the data and the new lines.
                        // Subtract newLine length because we don't want a trailing newline.
                        payload = new byte[payloadSize - _newLine.Length];

                        var offset = 0;
                        foreach (var dataLine in _data)
                        {
                            dataLine.CopyTo(payload, offset);
                            offset += dataLine.Length;
                            if (offset < payload.Length)
                            {
                                _newLine.CopyTo(payload, offset);
                                offset += _newLine.Length;
                            }
                        }
                    }

                    message  = payload;
                    consumed = lineEnd;
                    examined = consumed;
                    return(ParseResult.Completed);
                }

                if (buffer.Length > 0 && buffer.First.Span[0] == ByteCR)
                {
                    _internalParserState = InternalParseState.ReadEndOfMessage;
                }
            }
            return(ParseResult.Incomplete);
        }