// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Buffers; using System.Collections.Generic; using System.Text; using Microsoft.AspNetCore.Sockets.Internal.Formatters; using Microsoft.AspNetCore.Sockets.Tests; using Xunit; namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters { public class TextMessageParserTests { [Theory] [InlineData(0, "0:T:;", MessageType.Text, "")] [InlineData(0, "3:T:ABC;", MessageType.Text, "ABC")] [InlineData(0, "11:T:A\nR\rC\r\n;DEF;", MessageType.Text, "A\nR\rC\r\n;DEF")] [InlineData(4, "12:T:Hello, World;", MessageType.Text, "Hello, World")] public void ReadTextMessage(int chunkSize, string encoded, MessageType messageType, string payload) { var parser = new MessageParser(); var buffer = Encoding.UTF8.GetBytes(encoded); var reader = new BytesReader(buffer.ToChunkedReadOnlyBytes(chunkSize)); Assert.True(parser.TryParseMessage(ref reader, MessageFormat.Text, out var message)); Assert.Equal(reader.Index, buffer.Length); MessageTestUtils.AssertMessage(message, messageType, payload); } [Theory] [InlineData("0:B:;", new byte[0])] [InlineData("8:B:q83vEg==;", new byte[] { 0xAB, 0xCD, 0xEF, 0x12 })] [InlineData("8:B:q83vEjQ=;", new byte[] { 0xAB, 0xCD, 0xEF, 0x12, 0x34 })] [InlineData("8:B:q83vEjRW;", new byte[] { 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56 })] public void ReadBinaryMessage(string encoded, byte[] payload) { var parser = new MessageParser(); var buffer = Encoding.UTF8.GetBytes(encoded); var reader = new BytesReader(buffer); Assert.True(parser.TryParseMessage(ref reader, MessageFormat.Text, out var message)); Assert.Equal(reader.Index, buffer.Length); MessageTestUtils.AssertMessage(message, MessageType.Binary, payload); } [Theory] [InlineData(0)] // Not chunked [InlineData(4)] [InlineData(8)] public void ReadMultipleMessages(int chunkSize) { const string encoded = "0:B:;14:T:Hello,\r\nWorld!;"; var parser = new MessageParser(); var data = Encoding.UTF8.GetBytes(encoded); var buffer = chunkSize > 0 ? data.ToChunkedReadOnlyBytes(chunkSize) : new ReadOnlyBytes(data); var reader = new BytesReader(buffer); var messages = new List(); while (parser.TryParseMessage(ref reader, MessageFormat.Text, out var message)) { messages.Add(message); } Assert.Equal(reader.Index, Encoding.UTF8.GetByteCount(encoded)); Assert.Equal(2, messages.Count); MessageTestUtils.AssertMessage(messages[0], MessageType.Binary, new byte[0]); MessageTestUtils.AssertMessage(messages[1], MessageType.Text, "Hello,\r\nWorld!"); } [Theory] [InlineData("")] [InlineData("ABC")] [InlineData("1230450945")] [InlineData("1:")] [InlineData("10")] [InlineData("5:T:A")] [InlineData("5:T:ABCDE")] public void ReadIncompleteMessages(string encoded) { var parser = new MessageParser(); var buffer = Encoding.UTF8.GetBytes(encoded); var reader = new BytesReader(buffer); Assert.False(parser.TryParseMessage(ref reader, MessageFormat.Text, out _)); } [Theory] [InlineData("X:", "Invalid length: 'X'")] [InlineData("5:X:ABCDEF", "Unknown message type: 'X'")] [InlineData("1:asdf", "Unknown message type: 'a'")] [InlineData("1::", "Unknown message type: ':'")] [InlineData("1:AB:", "Unknown message type: 'A'")] [InlineData("1:TA", "Missing delimiter ':' after type")] [InlineData("1029348109238412903849023841290834901283409128349018239048102394:X:ABCDEF", "Invalid length: '1029348109238412903849023841290834901283409128349018239048102394'")] [InlineData("12ab34:", "Invalid length: '12ab34'")] [InlineData("5:T:ABCDEF", "Missing delimiter ';' after payload")] public void ReadInvalidMessages(string encoded, string expectedMessage) { var parser = new MessageParser(); var buffer = Encoding.UTF8.GetBytes(encoded); var reader = new BytesReader(buffer); var ex = Assert.Throws(() => parser.TryParseMessage(ref reader, MessageFormat.Text, out _)); Assert.Equal(expectedMessage, ex.Message); } [Fact] public void ReadInvalidEncodedMessage() { var parser = new MessageParser(); // Invalid because first character is a UTF-8 "continuation" character // We need to include the ':' so that var buffer = new byte[] { 0x48, 0x65, 0x80, 0x6C, 0x6F, (byte)':' }; var reader = new BytesReader(buffer); var ex = Assert.Throws(() => parser.TryParseMessage(ref reader, MessageFormat.Text, out _)); Assert.Equal("Invalid length", ex.Message); } } }