From 70d97dd7b813b1c7fac3c70e875adbabefc34b3a Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Wed, 15 Feb 2017 12:11:52 -0800 Subject: [PATCH] finish binary protocol formatter/parser (#203) --- .../BinaryMessageFormatter.cs | 130 +++++++++++++ .../MessageFormatter.cs | 4 +- .../TextMessageFormatter.cs | 2 +- .../BinaryMessageFormatterTests.cs | 171 ++++++++++++++++++ .../MessageTestUtils.cs | 39 ++++ ...s.Text.cs => TextMessageFormatterTests.cs} | 58 ++---- 6 files changed, 357 insertions(+), 47 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Sockets.Common/BinaryMessageFormatter.cs create mode 100644 test/Microsoft.AspNetCore.Sockets.Common.Tests/BinaryMessageFormatterTests.cs create mode 100644 test/Microsoft.AspNetCore.Sockets.Common.Tests/MessageTestUtils.cs rename test/Microsoft.AspNetCore.Sockets.Common.Tests/{MessageFormatterTests.Text.cs => TextMessageFormatterTests.cs} (74%) diff --git a/src/Microsoft.AspNetCore.Sockets.Common/BinaryMessageFormatter.cs b/src/Microsoft.AspNetCore.Sockets.Common/BinaryMessageFormatter.cs new file mode 100644 index 0000000000..0e36412470 --- /dev/null +++ b/src/Microsoft.AspNetCore.Sockets.Common/BinaryMessageFormatter.cs @@ -0,0 +1,130 @@ +// 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.Binary; +using System.IO.Pipelines; + +namespace Microsoft.AspNetCore.Sockets +{ + internal static class BinaryMessageFormatter + { + private const byte TextTypeFlag = 0x00; + private const byte BinaryTypeFlag = 0x01; + private const byte ErrorTypeFlag = 0x02; + private const byte CloseTypeFlag = 0x03; + + internal static bool TryFormatMessage(Message message, Span buffer, out int bytesWritten) + { + // We can check the size needed right up front! + var sizeNeeded = sizeof(long) + 1 + message.Payload.Buffer.Length; + if (buffer.Length < sizeNeeded) + { + bytesWritten = 0; + return false; + } + + buffer.WriteBigEndian((long)message.Payload.Buffer.Length); + if (!TryFormatType(message.Type, buffer.Slice(sizeof(long), 1))) + { + bytesWritten = 0; + return false; + } + + buffer = buffer.Slice(sizeof(long) + 1); + + message.Payload.Buffer.CopyTo(buffer); + bytesWritten = sizeNeeded; + return true; + } + + internal static bool TryParseMessage(ReadOnlySpan buffer, out Message message, out int bytesConsumed) + { + // Check if we have enough to read the size and type flag + if (buffer.Length < sizeof(long) + 1) + { + message = default(Message); + bytesConsumed = 0; + return false; + } + + // REVIEW: The spec calls for 64-bit length but I'm thinking that's a little ridiculous. + // REVIEW: We don't really have a primitive for storing that much data. For now, I'm using it + // REVIEW: but throwing if the size is over 2GB. + var longLength = buffer.ReadBigEndian(); + if (longLength > Int32.MaxValue) + { + throw new FormatException("Messages over 2GB in size are not supported"); + } + var length = (int)longLength; + + if (!TryParseType(buffer[sizeof(long)], out var messageType)) + { + message = default(Message); + bytesConsumed = 0; + return false; + } + + // Check if we actually have the whole payload + if (buffer.Length < sizeof(long) + 1 + length) + { + message = default(Message); + bytesConsumed = 0; + return false; + } + + // Copy the payload into the buffer + // REVIEW: Copy! Noooooooooo! But how can we capture a segment of the span as an "Owned" reference? + // REVIEW: If we do have to copy, we should at least use a pooled buffer + var buf = new byte[length]; + buffer.Slice(sizeof(long) + 1, length).CopyTo(buf); + + message = new Message(ReadableBuffer.Create(buf).Preserve(), messageType, endOfMessage: true); + bytesConsumed = sizeof(long) + 1 + length; + return true; + } + + private static bool TryParseType(byte type, out MessageType messageType) + { + switch (type) + { + case TextTypeFlag: + messageType = MessageType.Text; + return true; + case BinaryTypeFlag: + messageType = MessageType.Binary; + return true; + case CloseTypeFlag: + messageType = MessageType.Close; + return true; + case ErrorTypeFlag: + messageType = MessageType.Error; + return true; + default: + messageType = default(MessageType); + return false; + } + } + + private static bool TryFormatType(MessageType type, Span buffer) + { + switch (type) + { + case MessageType.Text: + buffer[0] = TextTypeFlag; + return true; + case MessageType.Binary: + buffer[0] = BinaryTypeFlag; + return true; + case MessageType.Close: + buffer[0] = CloseTypeFlag; + return true; + case MessageType.Error: + buffer[0] = ErrorTypeFlag; + return true; + default: + return false; + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Sockets.Common/MessageFormatter.cs b/src/Microsoft.AspNetCore.Sockets.Common/MessageFormatter.cs index db251e6d0e..a4850294a4 100644 --- a/src/Microsoft.AspNetCore.Sockets.Common/MessageFormatter.cs +++ b/src/Microsoft.AspNetCore.Sockets.Common/MessageFormatter.cs @@ -18,14 +18,14 @@ namespace Microsoft.AspNetCore.Sockets } return format == MessageFormat.Text ? TextMessageFormatter.TryFormatMessage(message, buffer, out bytesWritten) : - throw new NotImplementedException(); + BinaryMessageFormatter.TryFormatMessage(message, buffer, out bytesWritten); } public static bool TryParseMessage(ReadOnlySpan buffer, MessageFormat format, out Message message, out int bytesConsumed) { return format == MessageFormat.Text ? TextMessageFormatter.TryParseMessage(buffer, out message, out bytesConsumed) : - throw new NotImplementedException(); + BinaryMessageFormatter.TryParseMessage(buffer, out message, out bytesConsumed); } } } diff --git a/src/Microsoft.AspNetCore.Sockets.Common/TextMessageFormatter.cs b/src/Microsoft.AspNetCore.Sockets.Common/TextMessageFormatter.cs index ce185459bd..39926e0d04 100644 --- a/src/Microsoft.AspNetCore.Sockets.Common/TextMessageFormatter.cs +++ b/src/Microsoft.AspNetCore.Sockets.Common/TextMessageFormatter.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Sockets private const byte CloseTypeFlag = (byte)'C'; private const byte ErrorTypeFlag = (byte)'E'; - public static bool TryFormatMessage(Message message, Span buffer, out int bytesWritten) + internal static bool TryFormatMessage(Message message, Span buffer, out int bytesWritten) { // Calculate the length, it's the number of characters for text messages, but number of base64 characters for binary var length = message.Payload.Buffer.Length; diff --git a/test/Microsoft.AspNetCore.Sockets.Common.Tests/BinaryMessageFormatterTests.cs b/test/Microsoft.AspNetCore.Sockets.Common.Tests/BinaryMessageFormatterTests.cs new file mode 100644 index 0000000000..b3f888a8b9 --- /dev/null +++ b/test/Microsoft.AspNetCore.Sockets.Common.Tests/BinaryMessageFormatterTests.cs @@ -0,0 +1,171 @@ +// 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.Collections.Generic; +using System.IO.Pipelines; +using Xunit; + +namespace Microsoft.AspNetCore.Sockets.Tests +{ + public partial class BinaryMessageFormatterTests + { + [Fact] + public void WriteMultipleMessages() + { + var expectedEncoding = new byte[] + { + /* length: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* type: */ 0x01, // Binary + /* body: */ + /* length: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, + /* type: */ 0x00, // Text + /* body: */ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x0D, 0x0A, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, + /* length: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* type: */ 0x03, // Close + /* body: */ 0x41, + /* length: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, + /* type: */ 0x02, // Error + /* body: */ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6F, 0x72 + }; + + var messages = new[] + { + MessageTestUtils.CreateMessage(new byte[0]), + MessageTestUtils.CreateMessage("Hello,\r\nWorld!",MessageType.Text), + MessageTestUtils.CreateMessage("A", MessageType.Close), + MessageTestUtils.CreateMessage("Server Error", MessageType.Error) + }; + + var array = new byte[256]; + var buffer = array.Slice(); + var totalConsumed = 0; + foreach (var message in messages) + { + Assert.True(MessageFormatter.TryFormatMessage(message, buffer, MessageFormat.Binary, out var consumed)); + buffer = buffer.Slice(consumed); + totalConsumed += consumed; + } + + Assert.Equal(expectedEncoding, array.Slice(0, totalConsumed).ToArray()); + } + + [Theory] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, new byte[0])] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0xAB, 0xCD, 0xEF, 0x12 }, new byte[] { 0xAB, 0xCD, 0xEF, 0x12 })] + public void WriteBinaryMessage(byte[] encoded, byte[] payload) + { + var message = MessageTestUtils.CreateMessage(payload); + var buffer = new byte[256]; + + Assert.True(MessageFormatter.TryFormatMessage(message, buffer, MessageFormat.Binary, out var bytesWritten)); + + var encodedSpan = buffer.Slice(0, bytesWritten); + Assert.Equal(encoded, encodedSpan.ToArray()); + } + + [Theory] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, MessageType.Text, "")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x41, 0x42, 0x43 }, MessageType.Text, "ABC")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x41, 0x0A, 0x52, 0x0D, 0x43, 0x0D, 0x0A, 0x3B, 0x44, 0x45, 0x46 }, MessageType.Text, "A\nR\rC\r\n;DEF")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 }, MessageType.Close, "")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x03, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x43, 0x6C, 0x6F, 0x73, 0x65, 0x64 }, MessageType.Close, "Connection Closed")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, MessageType.Error, "")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6F, 0x72 }, MessageType.Error, "Server Error")] + public void WriteTextMessage(byte[] encoded, MessageType messageType, string payload) + { + var message = MessageTestUtils.CreateMessage(payload, messageType); + var buffer = new byte[256]; + + Assert.True(MessageFormatter.TryFormatMessage(message, buffer, MessageFormat.Binary, out var bytesWritten)); + + var encodedSpan = buffer.Slice(0, bytesWritten); + Assert.Equal(encoded, encodedSpan.ToArray()); + } + + [Fact] + public void WriteInvalidMessages() + { + var message = new Message(ReadableBuffer.Create(new byte[0]).Preserve(), MessageType.Binary, endOfMessage: false); + var ex = Assert.Throws(() => + MessageFormatter.TryFormatMessage(message, Span.Empty, MessageFormat.Binary, out var written)); + Assert.Equal("Cannot format message where endOfMessage is false using this format", ex.Message); + } + + [Theory] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, MessageType.Text, "")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x41, 0x42, 0x43 }, MessageType.Text, "ABC")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x41, 0x0A, 0x52, 0x0D, 0x43, 0x0D, 0x0A, 0x3B, 0x44, 0x45, 0x46 }, MessageType.Text, "A\nR\rC\r\n;DEF")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 }, MessageType.Close, "")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x03, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x43, 0x6C, 0x6F, 0x73, 0x65, 0x64 }, MessageType.Close, "Connection Closed")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, MessageType.Error, "")] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6F, 0x72 }, MessageType.Error, "Server Error")] + public void ReadTextMessage(byte[] encoded, MessageType messageType, string payload) + { + Assert.True(MessageFormatter.TryParseMessage(encoded, MessageFormat.Binary, out var message, out var consumed)); + Assert.Equal(consumed, encoded.Length); + + MessageTestUtils.AssertMessage(message, messageType, payload); + } + + [Theory] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, new byte[0])] + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0xAB, 0xCD, 0xEF, 0x12 }, new byte[] { 0xAB, 0xCD, 0xEF, 0x12 })] + public void ReadBinaryMessage(byte[] encoded, byte[] payload) + { + Assert.True(MessageFormatter.TryParseMessage(encoded, MessageFormat.Binary, out var message, out var consumed)); + Assert.Equal(consumed, encoded.Length); + + MessageTestUtils.AssertMessage(message, MessageType.Binary, payload); + } + + [Fact] + public void ReadMultipleMessages() + { + var encoded = new byte[] + { + /* length: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* type: */ 0x01, // Binary + /* body: */ + /* length: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, + /* type: */ 0x00, // Text + /* body: */ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x0D, 0x0A, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, + /* length: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* type: */ 0x03, // Close + /* body: */ 0x41, + /* length: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, + /* type: */ 0x02, // Error + /* body: */ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6F, 0x72 + }; + var buffer = encoded.Slice(); + + var messages = new List(); + var consumedTotal = 0; + while (MessageFormatter.TryParseMessage(buffer, MessageFormat.Binary, out var message, out var consumed)) + { + messages.Add(message); + consumedTotal += consumed; + buffer = buffer.Slice(consumed); + } + + Assert.Equal(consumedTotal, encoded.Length); + + Assert.Equal(4, messages.Count); + MessageTestUtils.AssertMessage(messages[0], MessageType.Binary, new byte[0]); + MessageTestUtils.AssertMessage(messages[1], MessageType.Text, "Hello,\r\nWorld!"); + MessageTestUtils.AssertMessage(messages[2], MessageType.Close, "A"); + MessageTestUtils.AssertMessage(messages[3], MessageType.Error, "Server Error"); + } + + [Theory] + [InlineData(new byte[0])] // Empty + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 })] // Just length + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00 })] // Not enough data for payload + [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 })] // Invalid Type + public void ReadInvalidMessages(byte[] encoded) + { + Assert.False(MessageFormatter.TryParseMessage(encoded, MessageFormat.Binary, out var message, out var consumed)); + Assert.Equal(0, consumed); + } + } +} diff --git a/test/Microsoft.AspNetCore.Sockets.Common.Tests/MessageTestUtils.cs b/test/Microsoft.AspNetCore.Sockets.Common.Tests/MessageTestUtils.cs new file mode 100644 index 0000000000..986af40f7f --- /dev/null +++ b/test/Microsoft.AspNetCore.Sockets.Common.Tests/MessageTestUtils.cs @@ -0,0 +1,39 @@ +using System.IO.Pipelines; +using System.Text; +using Xunit; + +namespace Microsoft.AspNetCore.Sockets.Tests +{ + internal static class MessageTestUtils + { + public static void AssertMessage(Message message, MessageType messageType, byte[] payload) + { + Assert.True(message.EndOfMessage); + Assert.Equal(messageType, message.Type); + Assert.Equal(payload, message.Payload.Buffer.ToArray()); + } + + public static void AssertMessage(Message message, MessageType messageType, string payload) + { + Assert.True(message.EndOfMessage); + Assert.Equal(messageType, message.Type); + Assert.Equal(payload, Encoding.UTF8.GetString(message.Payload.Buffer.ToArray())); + } + + public static Message CreateMessage(byte[] payload, MessageType type = MessageType.Binary) + { + return new Message( + ReadableBuffer.Create(payload).Preserve(), + type, + endOfMessage: true); + } + + public static Message CreateMessage(string payload, MessageType type) + { + return new Message( + ReadableBuffer.Create(Encoding.UTF8.GetBytes(payload)).Preserve(), + type, + endOfMessage: true); + } + } +} diff --git a/test/Microsoft.AspNetCore.Sockets.Common.Tests/MessageFormatterTests.Text.cs b/test/Microsoft.AspNetCore.Sockets.Common.Tests/TextMessageFormatterTests.cs similarity index 74% rename from test/Microsoft.AspNetCore.Sockets.Common.Tests/MessageFormatterTests.Text.cs rename to test/Microsoft.AspNetCore.Sockets.Common.Tests/TextMessageFormatterTests.cs index fef2b1e883..ffb21dd8dd 100644 --- a/test/Microsoft.AspNetCore.Sockets.Common.Tests/MessageFormatterTests.Text.cs +++ b/test/Microsoft.AspNetCore.Sockets.Common.Tests/TextMessageFormatterTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -9,7 +9,7 @@ using Xunit; namespace Microsoft.AspNetCore.Sockets.Tests { - public class MessageFormatterTests + public class TextMessageFormatterTests { [Fact] public void WriteMultipleMessages() @@ -17,10 +17,10 @@ namespace Microsoft.AspNetCore.Sockets.Tests const string expectedEncoding = "0:B:;14:T:Hello,\r\nWorld!;1:C:A;12:E:Server Error;"; var messages = new[] { - CreateMessage(new byte[0]), - CreateMessage("Hello,\r\nWorld!",MessageType.Text), - CreateMessage("A", MessageType.Close), - CreateMessage("Server Error", MessageType.Error) + MessageTestUtils.CreateMessage(new byte[0]), + MessageTestUtils.CreateMessage("Hello,\r\nWorld!",MessageType.Text), + MessageTestUtils.CreateMessage("A", MessageType.Close), + MessageTestUtils.CreateMessage("Server Error", MessageType.Error) }; var array = new byte[256]; @@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests [InlineData("8:B:q83vEg==;", new byte[] { 0xAB, 0xCD, 0xEF, 0x12 })] public void WriteBinaryMessage(string encoded, byte[] payload) { - var message = CreateMessage(payload); + var message = MessageTestUtils.CreateMessage(payload); var buffer = new byte[256]; Assert.True(MessageFormatter.TryFormatMessage(message, buffer, MessageFormat.Text, out var bytesWritten)); @@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests [InlineData("12:E:Server Error;", MessageType.Error, "Server Error")] public void WriteTextMessage(string encoded, MessageType messageType, string payload) { - var message = CreateMessage(payload, messageType); + var message = MessageTestUtils.CreateMessage(payload, messageType); var buffer = new byte[256]; Assert.True(MessageFormatter.TryFormatMessage(message, buffer, MessageFormat.Text, out var bytesWritten)); @@ -93,7 +93,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests Assert.True(MessageFormatter.TryParseMessage(buffer, MessageFormat.Text, out var message, out var consumed)); Assert.Equal(consumed, buffer.Length); - AssertMessage(message, messageType, payload); + MessageTestUtils.AssertMessage(message, messageType, payload); } [Theory] @@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests Assert.True(MessageFormatter.TryParseMessage(buffer, MessageFormat.Text, out var message, out var consumed)); Assert.Equal(consumed, buffer.Length); - AssertMessage(message, MessageType.Binary, payload); + MessageTestUtils.AssertMessage(message, MessageType.Binary, payload); } [Fact] @@ -127,10 +127,10 @@ namespace Microsoft.AspNetCore.Sockets.Tests Assert.Equal(consumedTotal, Encoding.UTF8.GetByteCount(encoded)); Assert.Equal(4, messages.Count); - AssertMessage(messages[0], MessageType.Binary, new byte[0]); - AssertMessage(messages[1], MessageType.Text, "Hello,\r\nWorld!"); - AssertMessage(messages[2], MessageType.Close, "A"); - AssertMessage(messages[3], MessageType.Error, "Server Error"); + MessageTestUtils.AssertMessage(messages[0], MessageType.Binary, new byte[0]); + MessageTestUtils.AssertMessage(messages[1], MessageType.Text, "Hello,\r\nWorld!"); + MessageTestUtils.AssertMessage(messages[2], MessageType.Close, "A"); + MessageTestUtils.AssertMessage(messages[3], MessageType.Error, "Server Error"); } [Theory] @@ -152,35 +152,5 @@ namespace Microsoft.AspNetCore.Sockets.Tests Assert.False(MessageFormatter.TryParseMessage(buffer, MessageFormat.Text, out var message, out var consumed)); Assert.Equal(0, consumed); } - - private static void AssertMessage(Message message, MessageType messageType, byte[] payload) - { - Assert.True(message.EndOfMessage); - Assert.Equal(messageType, message.Type); - Assert.Equal(payload, message.Payload.Buffer.ToArray()); - } - - private static void AssertMessage(Message message, MessageType messageType, string payload) - { - Assert.True(message.EndOfMessage); - Assert.Equal(messageType, message.Type); - Assert.Equal(payload, Encoding.UTF8.GetString(message.Payload.Buffer.ToArray())); - } - - private static Message CreateMessage(byte[] payload, MessageType type = MessageType.Binary) - { - return new Message( - ReadableBuffer.Create(payload).Preserve(), - type, - endOfMessage: true); - } - - private static Message CreateMessage(string payload, MessageType type) - { - return new Message( - ReadableBuffer.Create(Encoding.UTF8.GetBytes(payload)).Preserve(), - type, - endOfMessage: true); - } } }