public ParseResult ParseMessage(ReadOnlySequence <byte> buffer, out SequencePosition consumed, out SequencePosition examined, out byte[]?message) { consumed = buffer.Start; examined = buffer.End; message = null; var start = consumed; while (buffer.Length > 0) { if (!(buffer.PositionOf(ByteLF) is SequencePosition lineEnd)) { // For the case of data: Foo\r\n\r\<Anything 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.GetPosition(1, lineEnd); 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); }
public void Reset() { _internalParserState = InternalParseState.ReadMessagePayload; _data.Clear(); }