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); }
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); }
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);
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); }
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); } }
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; } }
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); }
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); }
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; }
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); }
public void ReadableBufferMove_ChecksBounds(ReadableBuffer buffer) { Assert.Throws <InvalidOperationException>(() => buffer.Move(buffer.Start, 101)); }
public void ReadableBufferMove_MovesReadCursor(ReadableBuffer buffer) { var cursor = buffer.Move(buffer.Start, 65); Assert.Equal(buffer.Slice(65).Start, cursor); }
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); } }
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); }