diff --git a/src/Microsoft.AspNetCore.Sockets.Common/Internal/Formatters/ServerSentEventsMessageParser.cs b/src/Microsoft.AspNetCore.Sockets.Common/Internal/Formatters/ServerSentEventsMessageParser.cs index b786986dbc..a7086dd648 100644 --- a/src/Microsoft.AspNetCore.Sockets.Common/Internal/Formatters/ServerSentEventsMessageParser.cs +++ b/src/Microsoft.AspNetCore.Sockets.Common/Internal/Formatters/ServerSentEventsMessageParser.cs @@ -23,6 +23,8 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters private static byte[] _sseLineEnding = Encoding.UTF8.GetBytes("\r\n"); private static byte[] _newLine = Encoding.UTF8.GetBytes(Environment.NewLine); + private readonly static int _messageTypeLineLength = "data: X\r\n".Length; + private InternalParseState _internalParserState = InternalParseState.ReadMessageType; private List _data = new List(); private MessageType _messageType = MessageType.Text; @@ -94,7 +96,10 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters switch (_internalParserState) { case InternalParseState.ReadMessageType: - _messageType = GetMessageType(line); + EnsureStartsWithDataPrefix(line); + + + _messageType = ParseMessageType(line); _internalParserState = InternalParseState.ReadMessagePayload; @@ -102,6 +107,8 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters consumed = lineEnd; break; 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(); @@ -198,9 +205,12 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters return line.Length == _sseLineEnding.Length && line.SequenceEqual(_sseLineEnding); } - private MessageType GetMessageType(ReadOnlySpan line) + private MessageType ParseMessageType(ReadOnlySpan line) { - EnsureStartsWithDataPrefix(line); + if (line.Length != _messageTypeLineLength) + { + throw new FormatException("Expected a data format message of the form 'data: '"); + } // Skip the "data: " part of the line var type = line[_dataPrefix.Length]; diff --git a/test/Microsoft.AspNetCore.Sockets.Common.Tests/Internal/Formatters/ServerSentEventsParserTests.cs b/test/Microsoft.AspNetCore.Sockets.Common.Tests/Internal/Formatters/ServerSentEventsParserTests.cs index 302608b9a2..2ecf01c0c8 100644 --- a/test/Microsoft.AspNetCore.Sockets.Common.Tests/Internal/Formatters/ServerSentEventsParserTests.cs +++ b/test/Microsoft.AspNetCore.Sockets.Common.Tests/Internal/Formatters/ServerSentEventsParserTests.cs @@ -48,19 +48,20 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters [InlineData("data: X\r\n", "Unknown message type: 'X'")] [InlineData("data: T\n", "Unexpected '\n' in message. A '\n' character can only be used as part of the newline sequence '\r\n'")] [InlineData("data: X\r\n\r\n", "Unknown message type: 'X'")] - [InlineData("data: Not the message type\r\n\r\n", "Unknown message type: 'N'")] + [InlineData("data: Not the message type\r\n\r\n", "Expected a data format message of the form 'data: '")] [InlineData("data: T\r\ndata: Hello, World\r\r\n\n", "There was an error in the frame format")] - [InlineData("data: Not the message type\r\r\n", "Unknown message type: 'N'")] + [InlineData("data: Not the message type\r\r\n", "Expected a data format message of the form 'data: '")] [InlineData("data: T\r\ndata: Hello, World\n\n", "Unexpected '\n' in message. A '\n' character can only be used as part of the newline sequence '\r\n'")] [InlineData("data: T\r\nfoo: Hello, World\r\n\r\n", "Expected the message prefix 'data: '")] [InlineData("foo: T\r\ndata: Hello, World\r\n\r\n", "Expected the message prefix 'data: '")] [InlineData("food: T\r\ndata: Hello, World\r\n\r\n", "Expected the message prefix 'data: '")] [InlineData("data: T\r\ndata: Hello, World\r\n\n", "There was an error in the frame format")] [InlineData("data: T\r\ndata: Hello\n, World\r\n\r\n", "Unexpected '\n' in message. A '\n' character can only be used as part of the newline sequence '\r\n'")] - [InlineData("data: data: \r\n", "Unknown message type: 'd'")] + [InlineData("data: data: \r\n", "Expected a data format message of the form 'data: '")] [InlineData("data: T\r\ndata: Hello, World\r\n\r\\", "Expected a \\r\\n frame ending")] [InlineData("data: T\r\ndata: Major\r\ndata: Key\rndata: Alert\r\n\r\\", "Expected a \\r\\n frame ending")] [InlineData("data: T\r\ndata: Major\r\ndata: Key\r\ndata: Alert\r\n\r\\", "Expected a \\r\\n frame ending")] + [InlineData("data: This is not a message type\r\n", "Expected a data format message of the form 'data: '")] [InlineData("data: B\r\n SGVsbG8sIFdvcmxk\r\n\r\n", "Expected the message prefix 'data: '")] public void ParseSSEMessageFailureCases(string encodedMessage, string expectedExceptionMessage) { @@ -155,18 +156,20 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters [InlineData("data: ", "X\r\n", "Unknown message type: 'X'")] [InlineData("data: T", "\n", "Unexpected '\n' in message. A '\n' character can only be used as part of the newline sequence '\r\n'")] [InlineData("data: ", "X\r\n\r\n", "Unknown message type: 'X'")] - [InlineData("data: ", "Not the message type\r\n\r\n", "Unknown message type: 'N'")] + [InlineData("data: ", "Not the message type\r\n\r\n", "Expected a data format message of the form 'data: '")] [InlineData("data: T\r\n", "data: Hello, World\r\r\n\n", "There was an error in the frame format")] - [InlineData("data:", " Not the message type\r\r\n", "Unknown message type: 'N'")] + [InlineData("data:", " Not the message type\r\r\n", "Expected a data format message of the form 'data: '")] [InlineData("data: T\r\n", "data: Hello, World\n\n", "Unexpected '\n' in message. A '\n' character can only be used as part of the newline sequence '\r\n'")] [InlineData("data: T\r\nf", "oo: Hello, World\r\n\r\n", "Expected the message prefix 'data: '")] [InlineData("foo", ": T\r\ndata: Hello, World\r\n\r\n", "Expected the message prefix 'data: '")] [InlineData("food:", " T\r\ndata: Hello, World\r\n\r\n", "Expected the message prefix 'data: '")] [InlineData("data: T\r\ndata: Hello, W", "orld\r\n\n", "There was an error in the frame format")] [InlineData("data: T\r\nda", "ta: Hello\n, World\r\n\r\n", "Unexpected '\n' in message. A '\n' character can only be used as part of the newline sequence '\r\n'")] - [InlineData("data:", " data: \r\n", "Unknown message type: 'd'")] + [InlineData("data:", " data: \r\n", "Expected a data format message of the form 'data: '")] [InlineData("data: ", "T\r\ndata: Major\r\ndata: Key\r\ndata: Alert\r\n\r\\", "Expected a \\r\\n frame ending")] + [InlineData("data: ", "This is not a message type\r\n", "Expected a data format message of the form 'data: '")] [InlineData("data: B\r\ndata: SGVs", "bG8sIFdvcmxk\r\n\n\n", "There was an error in the frame format")] + [InlineData("data: T", "his is not a message type\r\n", "Expected a data format message of the form 'data: '")] public async Task ParseMessageAcrossMultipleReadsFailure(string encodedMessagePart1, string encodedMessagePart2, string expectedMessage) { using (var pipeFactory = new PipeFactory())