From 41f54d001b166daaccc778535a16386ec8e6df52 Mon Sep 17 00:00:00 2001
From: David Fowler <davidfowl@gmail.com>
Date: Thu, 22 Jun 2017 09:23:52 -0700
Subject: [PATCH] Remove dependencies on a bunch of corefxlab things (#576)

* Remove dependencies on a bunch of corefxlab things
- Used Stream instead of IOutput
- Removed pipelines dependency in most places.
---
 SignalR.sln                                   |   4 +-
 build/dependencies.props                      |   1 +
 .../EchoEndPoint.cs                           |   1 -
 .../SocialWeather/SocialWeatherEndPoint.cs    |   1 -
 .../EndPoints/MessagesEndPoint.cs             |   1 -
 src/Common/IOutputExtensions.cs               |  59 --------
 .../HubConnection.cs                          |   2 +-
 .../Formatters/BinaryMessageFormatter.cs      |  22 +--
 .../Formatters/BinaryMessageParser.cs         |  31 ++--
 .../Internal/Formatters/BufferExtensions.cs   |  32 -----
 .../Formatters/TextMessageFormatter.cs        |  33 +++--
 .../Internal/Formatters/TextMessageParser.cs  | 136 +++++++++++++++---
 .../HubProtocolWriteMessageExtensions.cs      |  20 +--
 .../Internal/Protocol/IHubProtocol.cs         |   3 +-
 .../Internal/Protocol/JsonHubProtocol.cs      |   6 +-
 ...Microsoft.AspNetCore.SignalR.Common.csproj |   7 +-
 .../RedisHubLifetimeManager.cs                |   5 +-
 .../DefaultHubLifetimeManager.cs              |   5 +-
 .../HubEndPoint.cs                            |   2 +-
 .../HttpConnectionDispatcher.cs               |   1 -
 .../ServerSentEventsMessageFormatter.cs       |  31 ++--
 .../Transports/ServerSentEventsTransport.cs   |  15 +-
 .../Microsoft.AspNetCore.Sockets.Http.csproj  |   7 +-
 test/Common/ArrayOutput.cs                    |  75 ----------
 test/Common/ByteArrayExtensions.cs            |  47 ------
 .../HubConnectionTests.cs                     |   3 +-
 ...oft.AspNetCore.SignalR.Client.Tests.csproj |   1 -
 .../TestConnection.cs                         |   5 +-
 .../Internal/Protocol/JsonHubProtocolTests.cs |   8 +-
 ...oft.AspNetCore.SignalR.Common.Tests.csproj |   4 -
 .../MessageParserBenchmark.cs                 |  22 +--
 ....AspNetCore.SignalR.Microbenchmarks.csproj |   5 -
 .../Formatters/BinaryMessageFormatterTests.cs |  11 +-
 .../Formatters/BinaryMessageParserTests.cs    |  33 ++---
 .../Formatters/TextMessageFormatterTests.cs   |   6 +-
 .../Formatters/TextMessageParserTests.cs      |  42 +++---
 .../Microsoft.AspNetCore.SignalR.Tests.csproj |   2 -
 .../TestClient.cs                             |   2 +-
 .../Microsoft.AspNetCore.Sockets.Tests.csproj |   1 -
 .../ServerSentEventsMessageFormatterTests.cs  |   3 +-
 40 files changed, 258 insertions(+), 437 deletions(-)
 delete mode 100644 src/Common/IOutputExtensions.cs
 delete mode 100644 src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BufferExtensions.cs
 delete mode 100644 test/Common/ArrayOutput.cs
 delete mode 100644 test/Common/ByteArrayExtensions.cs

diff --git a/SignalR.sln b/SignalR.sln
index 0a0d5e979a2..9436f9d8a2b 100644
--- a/SignalR.sln
+++ b/SignalR.sln
@@ -1,6 +1,6 @@
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.26510.0
+VisualStudioVersion = 15.0.26606.0
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DA69F624-5398-4884-87E4-B816698CDE65}"
 EndProject
@@ -52,8 +52,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Signal
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{6CEC3DC2-5B01-45A8-8F0D-8531315DA90B}"
 	ProjectSection(SolutionItems) = preProject
-		test\Common\ArrayOutput.cs = test\Common\ArrayOutput.cs
-		test\Common\ByteArrayExtensions.cs = test\Common\ByteArrayExtensions.cs
 		test\Common\ChannelExtensions.cs = test\Common\ChannelExtensions.cs
 		test\Common\TaskExtensions.cs = test\Common\TaskExtensions.cs
 	EndProjectSection
diff --git a/build/dependencies.props b/build/dependencies.props
index a2f64ad9f29..7cf7a554477 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -3,6 +3,7 @@
     <AspNetCoreIntegrationTestingVersion>0.4.0-*</AspNetCoreIntegrationTestingVersion>
     <AspNetCoreVersion>2.0.0-*</AspNetCoreVersion>
     <CoreFxLabsVersion>0.1.0-*</CoreFxLabsVersion>
+    <CoreFxVersion>4.4.0-*</CoreFxVersion>
     <GoogleProtobufVersion>3.1.0</GoogleProtobufVersion>
     <InternalAspNetCoreSdkVersion>2.1.0-*</InternalAspNetCoreSdkVersion>
     <JsonNetVersion>10.0.1</JsonNetVersion>
diff --git a/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/EchoEndPoint.cs b/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/EchoEndPoint.cs
index 2852c885ae6..835bbbd2f08 100644
--- a/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/EchoEndPoint.cs
+++ b/client-ts/Microsoft.AspNetCore.SignalR.Test.Server/EchoEndPoint.cs
@@ -2,7 +2,6 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
-using System.IO.Pipelines;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Sockets;
 
diff --git a/samples/SocialWeather/SocialWeatherEndPoint.cs b/samples/SocialWeather/SocialWeatherEndPoint.cs
index 685787d5595..2e8e4fa7da2 100644
--- a/samples/SocialWeather/SocialWeatherEndPoint.cs
+++ b/samples/SocialWeather/SocialWeatherEndPoint.cs
@@ -2,7 +2,6 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.IO;
-using System.IO.Pipelines;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Sockets;
 using Microsoft.Extensions.Logging;
diff --git a/samples/SocketsSample/EndPoints/MessagesEndPoint.cs b/samples/SocketsSample/EndPoints/MessagesEndPoint.cs
index 4d910b1f665..eee54395e21 100644
--- a/samples/SocketsSample/EndPoints/MessagesEndPoint.cs
+++ b/samples/SocketsSample/EndPoints/MessagesEndPoint.cs
@@ -2,7 +2,6 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.Collections.Generic;
-using System.IO.Pipelines;
 using System.Text;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Sockets;
diff --git a/src/Common/IOutputExtensions.cs b/src/Common/IOutputExtensions.cs
deleted file mode 100644
index 19b5d3b1e4e..00000000000
--- a/src/Common/IOutputExtensions.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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.Binary;
-using System.Runtime;
-using System.Runtime.CompilerServices;
-
-namespace System.Buffers
-{
-    internal static class IOutputExtensions
-    {
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static bool TryWriteBigEndian<[Primitive] T>(this IOutput self, T value) where T : struct
-        {
-            var size = Unsafe.SizeOf<T>();
-            if (self.Buffer.Length < size)
-            {
-                self.Enlarge(size);
-                if (self.Buffer.Length < size)
-                {
-                    return false;
-                }
-            }
-
-            self.Buffer.WriteBigEndian(value);
-            self.Advance(size);
-            return true;
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static bool TryWrite(this IOutput self, ReadOnlySpan<byte> data)
-        {
-            while (data.Length > 0)
-            {
-                if (self.Buffer.Length == 0)
-                {
-                    self.Enlarge(data.Length);
-                    if (self.Buffer.Length == 0)
-                    {
-                        // Failed to enlarge
-                        return false;
-                    }
-                }
-
-                var toWrite = Math.Min(self.Buffer.Length, data.Length);
-
-                // Slice based on what we can fit
-                var chunk = data.Slice(0, toWrite);
-                data = data.Slice(toWrite);
-
-                // Copy the chunk
-                chunk.CopyTo(self.Buffer);
-                self.Advance(chunk.Length);
-            }
-
-            return true;
-        }
-    }
-}
diff --git a/src/Microsoft.AspNetCore.SignalR.Client/HubConnection.cs b/src/Microsoft.AspNetCore.SignalR.Client/HubConnection.cs
index fb6678d1b8d..afbc2ad32aa 100644
--- a/src/Microsoft.AspNetCore.SignalR.Client/HubConnection.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Client/HubConnection.cs
@@ -133,7 +133,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
         {
             try
             {
-                var payload = await _protocol.WriteToArrayAsync(invocationMessage);
+                var payload = _protocol.WriteToArray(invocationMessage);
 
                 _logger.LogInformation("Sending Invocation '{invocationId}'", invocationMessage.InvocationId);
 
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BinaryMessageFormatter.cs b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BinaryMessageFormatter.cs
index 3d8df0ca8cd..4a9b3ecab80 100644
--- a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BinaryMessageFormatter.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BinaryMessageFormatter.cs
@@ -2,24 +2,26 @@
 // 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.Buffers;
+using System.IO;
 
 namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
 {
     public static class BinaryMessageFormatter
     {
-        public static bool TryWriteMessage(ReadOnlySpan<byte> payload, IOutput output)
+        public static bool TryWriteMessage(ReadOnlySpan<byte> payload, MemoryStream output)
         {
-            // Try to write the data
-            if (!output.TryWriteBigEndian((long)payload.Length))
-            {
-                return false;
-            }
+            var length = sizeof(long);
+            var buffer = ArrayPool<byte>.Shared.Rent(length);
+            BufferWriter.WriteBigEndian<long>(buffer, payload.Length);
+            output.Write(buffer, 0, length);
+            ArrayPool<byte>.Shared.Return(buffer);
 
-            if (!output.TryWrite(payload))
-            {
-                return false;
-            }
+            buffer = ArrayPool<byte>.Shared.Rent(payload.Length);
+            payload.CopyTo(buffer);
+            output.Write(buffer, 0, payload.Length);
+            ArrayPool<byte>.Shared.Return(buffer);
 
             return true;
         }
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BinaryMessageParser.cs b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BinaryMessageParser.cs
index 8cc02cfa3e4..56c1c52793d 100644
--- a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BinaryMessageParser.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BinaryMessageParser.cs
@@ -3,7 +3,6 @@
 
 using System;
 using System.Binary;
-using System.Buffers;
 
 namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
 {
@@ -16,33 +15,27 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
             _state = default(ParserState);
         }
 
-        public bool TryParseMessage(ref BytesReader buffer, out ReadOnlyBuffer<byte> payload)
+        public bool TryParseMessage(ref ReadOnlySpan<byte> buffer, out ReadOnlyBuffer<byte> payload)
         {
             if (_state.Length == null)
             {
-                var lengthBuffer = buffer.TryReadBytes(sizeof(long));
+                long length = 0;
 
-                if (lengthBuffer == null)
+                if (buffer.Length < sizeof(long))
                 {
                     payload = default(ReadOnlyBuffer<byte>);
                     return false;
                 }
 
-                var length = lengthBuffer.Value.ToSingleSpan();
+                length = buffer.Slice(0, sizeof(long)).ReadBigEndian<long>();
 
-                if (length.Length < sizeof(long))
-                {
-                    payload = default(ReadOnlyBuffer<byte>);
-                    return false;
-                }
-
-                var longLength = length.ReadBigEndian<long>();
-                if (longLength > Int32.MaxValue)
+                if (length > Int32.MaxValue)
                 {
                     throw new FormatException("Messages over 2GB in size are not supported");
                 }
-                buffer.Advance(length.Length);
-                _state.Length = (int)longLength;
+
+                buffer = buffer.Slice(sizeof(long));
+                _state.Length = (int)length;
             }
 
             if (_state.Payload == null)
@@ -50,13 +43,13 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
                 _state.Payload = new byte[_state.Length.Value];
             }
 
-            while (_state.Read < _state.Payload.Length && buffer.Unread.Length > 0)
+            while (_state.Read < _state.Payload.Length && buffer.Length > 0)
             {
                 // Copy what we can from the current unread segment
-                var toCopy = Math.Min(_state.Payload.Length - _state.Read, buffer.Unread.Length);
-                buffer.Unread.Slice(0, toCopy).CopyTo(_state.Payload.Slice(_state.Read));
+                var toCopy = Math.Min(_state.Payload.Length - _state.Read, buffer.Length);
+                buffer.Slice(0, toCopy).CopyTo(new Span<byte>(_state.Payload, _state.Read));
                 _state.Read += toCopy;
-                buffer.Advance(toCopy);
+                buffer = buffer.Slice(toCopy);
             }
 
             if (_state.Read == _state.Payload.Length)
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BufferExtensions.cs b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BufferExtensions.cs
deleted file mode 100644
index c9c1f257dd1..00000000000
--- a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/BufferExtensions.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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.
-
-namespace System.Buffers
-{
-    internal static class BufferExtensions
-    {
-        public static ReadOnlySpan<byte> ToSingleSpan(this ReadOnlyBytes self)
-        {
-            if (self.Rest == null)
-            {
-                return self.First.Span;
-            }
-            else
-            {
-                return self.ToSpan();
-            }
-        }
-
-        public static ReadOnlyBytes? TryReadBytes(this BytesReader self, int count)
-        {
-            try
-            {
-                return self.ReadBytes(count);
-            }
-            catch (ArgumentOutOfRangeException)
-            {
-                return null;
-            }
-        }
-    }
-}
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/TextMessageFormatter.cs b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/TextMessageFormatter.cs
index ed4a67580a5..f66c4880d2e 100644
--- a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/TextMessageFormatter.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/TextMessageFormatter.cs
@@ -2,37 +2,44 @@
 // 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.Buffers;
+using System.Globalization;
+using System.IO;
 using System.Text;
-using System.Text.Formatting;
 
 namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
 {
     public static class TextMessageFormatter
     {
+        private const int Int32OverflowLength = 10;
+
         internal const char FieldDelimiter = ':';
         internal const char MessageDelimiter = ';';
-        
-        public static bool TryWriteMessage(ReadOnlySpan<byte> payload, IOutput output)
+
+        public static bool TryWriteMessage(ReadOnlySpan<byte> payload, Stream output)
         {
             // Calculate the length, it's the number of characters for text messages, but number of base64 characters for binary
-            var length = payload.Length;
 
             // Write the length as a string
-            output.Append(length, TextEncoder.Utf8);
+
+            // Super inefficient...
+            var lengthString = payload.Length.ToString(CultureInfo.InvariantCulture);
+            var buffer = ArrayPool<byte>.Shared.Rent(Int32OverflowLength);
+            var encodedLength = Encoding.UTF8.GetBytes(lengthString, 0, lengthString.Length, buffer, 0);
+            output.Write(buffer, 0, encodedLength);
+            ArrayPool<byte>.Shared.Return(buffer);
 
             // Write the field delimiter ':'
-            output.Append(FieldDelimiter, TextEncoder.Utf8);
+            output.WriteByte((byte)FieldDelimiter);
 
-            // Write the payload
-            if (!output.TryWrite(payload))
-            {
-                return false;
-            }
+            buffer = ArrayPool<byte>.Shared.Rent(payload.Length);
+            payload.CopyTo(buffer);
+            output.Write(buffer, 0, payload.Length);
+            ArrayPool<byte>.Shared.Return(buffer);
 
             // Terminator
-            output.Append(MessageDelimiter, TextEncoder.Utf8);
+            output.WriteByte((byte)MessageDelimiter);
+
             return true;
         }
     }
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/TextMessageParser.cs b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/TextMessageParser.cs
index 6e7357dce29..9793dcf793f 100644
--- a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/TextMessageParser.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Formatters/TextMessageParser.cs
@@ -2,13 +2,14 @@
 // 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.Text;
 
 namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
 {
     public class TextMessageParser
     {
+        private const int Int32OverflowLength = 10;
+
         private ParserState _state;
 
         public void Reset()
@@ -20,9 +21,9 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
         /// Attempts to parse a message from the buffer. Returns 'false' if there is not enough data to complete a message. Throws an
         /// exception if there is a format error in the provided data.
         /// </summary>
-        public bool TryParseMessage(ref BytesReader buffer, out ReadOnlyBuffer<byte> payload)
+        public bool TryParseMessage(ref ReadOnlySpan<byte> buffer, out ReadOnlyBuffer<byte> payload)
         {
-            while (buffer.Unread.Length > 0)
+            while (buffer.Length > 0)
             {
                 switch (_state.Phase)
                 {
@@ -65,53 +66,50 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
             return false;
         }
 
-        private bool TryReadLength(ref BytesReader buffer)
+        private bool TryReadLength(ref ReadOnlySpan<byte> buffer)
         {
             // Read until the first ':' to find the length
-            var lengthBuffer = buffer.ReadBytesUntil((byte)TextMessageFormatter.FieldDelimiter);
+            var found = buffer.IndexOf((byte)TextMessageFormatter.FieldDelimiter);
 
-            if (lengthBuffer == null)
+            if (found == -1)
             {
                 // Insufficient data
                 return false;
             }
 
-            var lengthSpan = lengthBuffer.Value.ToSingleSpan();
+            var lengthSpan = buffer.Slice(0, found);
 
-            // Parse the length
-            if (!PrimitiveParser.TryParseInt32(lengthSpan, out var length, out var consumedByLength, encoder: TextEncoder.Utf8) || consumedByLength < lengthSpan.Length)
+            if (!TryParseInt32(lengthSpan, out var length, out var bytesConsumed) || bytesConsumed < lengthSpan.Length)
             {
-                if (TextEncoder.Utf8.TryDecode(lengthSpan, out var lengthString, out _))
-                {
-                    throw new FormatException($"Invalid length: '{lengthString}'");
-                }
-
-                throw new FormatException("Invalid length");
+                throw new FormatException($"Invalid length: '{Encoding.UTF8.GetString(lengthSpan.ToArray())}'");
             }
 
+            buffer = buffer.Slice(found);
+
             _state.Length = length;
             _state.Phase = ParsePhase.LengthComplete;
             return true;
         }
 
-        private bool TryReadDelimiter(ref BytesReader buffer, char delimiter, ParsePhase nextPhase, string field)
+        private bool TryReadDelimiter(ref ReadOnlySpan<byte> buffer, char delimiter, ParsePhase nextPhase, string field)
         {
-            if (buffer.Unread.Length == 0)
+            if (buffer.Length == 0)
             {
                 return false;
             }
 
-            if (buffer.Unread[0] != delimiter)
+            if (buffer[0] != delimiter)
             {
                 throw new FormatException($"Missing delimiter '{delimiter}' after {field}");
             }
-            buffer.Advance(1);
+
+            buffer = buffer.Slice(1);
 
             _state.Phase = nextPhase;
             return true;
         }
 
-        private void ReadPayload(ref BytesReader buffer)
+        private void ReadPayload(ref ReadOnlySpan<byte> buffer)
         {
             if (_state.Payload == null)
             {
@@ -125,11 +123,103 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
             else
             {
                 // Copy as much as possible from the Unread buffer
-                var toCopy = Math.Min(_state.Length - _state.Read, buffer.Unread.Length);
-                buffer.Unread.Slice(0, toCopy).CopyTo(_state.Payload.Slice(_state.Read));
+                var toCopy = Math.Min(_state.Length - _state.Read, buffer.Length);
+
+                buffer.Slice(0, toCopy).CopyTo(new Span<byte>(_state.Payload, _state.Read));
                 _state.Read += toCopy;
-                buffer.Advance(toCopy);
+                buffer = buffer.Slice(toCopy);
+            }
+        }
+
+        public static bool TryParseInt32(ReadOnlySpan<byte> text, out int value, out int bytesConsumed)
+        {
+            if (text.Length < 1)
+            {
+                bytesConsumed = 0;
+                value = default(int);
+                return false;
+            }
+
+            int indexOfFirstDigit = 0;
+            int sign = 1;
+            if (text[0] == '-')
+            {
+                indexOfFirstDigit = 1;
+                sign = -1;
+            }
+            else if (text[0] == '+')
+            {
+                indexOfFirstDigit = 1;
+            }
+
+            int overflowLength = Int32OverflowLength + indexOfFirstDigit;
+
+            // Parse the first digit separately. If invalid here, we need to return false.
+            int firstDigit = text[indexOfFirstDigit] - 48; // '0'
+            if (firstDigit < 0 || firstDigit > 9)
+            {
+                bytesConsumed = 0;
+                value = default(int);
+                return false;
+            }
+            int parsedValue = firstDigit;
+
+            if (text.Length < overflowLength)
+            {
+                // Length is less than Int32OverflowLength; overflow is not possible
+                for (int index = indexOfFirstDigit + 1; index < text.Length; index++)
+                {
+                    int nextDigit = text[index] - 48; // '0'
+                    if (nextDigit < 0 || nextDigit > 9)
+                    {
+                        bytesConsumed = index;
+                        value = parsedValue * sign;
+                        return true;
+                    }
+                    parsedValue = parsedValue * 10 + nextDigit;
+                }
+            }
+            else
+            {
+                // Length is greater than Int32OverflowLength; overflow is only possible after Int32OverflowLength
+                // digits. There may be no overflow after Int32OverflowLength if there are leading zeroes.
+                for (int index = indexOfFirstDigit + 1; index < overflowLength - 1; index++)
+                {
+                    int nextDigit = text[index] - 48; // '0'
+                    if (nextDigit < 0 || nextDigit > 9)
+                    {
+                        bytesConsumed = index;
+                        value = parsedValue * sign;
+                        return true;
+                    }
+                    parsedValue = parsedValue * 10 + nextDigit;
+                }
+                for (int index = overflowLength - 1; index < text.Length; index++)
+                {
+                    int nextDigit = text[index] - 48; // '0'
+                    if (nextDigit < 0 || nextDigit > 9)
+                    {
+                        bytesConsumed = index;
+                        value = parsedValue * sign;
+                        return true;
+                    }
+                    // If parsedValue > (int.MaxValue / 10), any more appended digits will cause overflow.
+                    // if parsedValue == (int.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow.
+                    bool positive = sign > 0;
+                    bool nextDigitTooLarge = nextDigit > 8 || (positive && nextDigit > 7);
+                    if (parsedValue > int.MaxValue / 10 || parsedValue == int.MaxValue / 10 && nextDigitTooLarge)
+                    {
+                        bytesConsumed = 0;
+                        value = default(int);
+                        return false;
+                    }
+                    parsedValue = parsedValue * 10 + nextDigit;
+                }
             }
+
+            bytesConsumed = text.Length;
+            value = parsedValue * sign;
+            return true;
         }
 
         private struct ParserState
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/HubProtocolWriteMessageExtensions.cs b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/HubProtocolWriteMessageExtensions.cs
index b53f2909ee3..6f499e6cd65 100644
--- a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/HubProtocolWriteMessageExtensions.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/HubProtocolWriteMessageExtensions.cs
@@ -3,34 +3,22 @@
 
 using System;
 using System.IO;
-using System.IO.Pipelines;
-using System.IO.Pipelines.Text.Primitives;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
 {
     public static class HubProtocolWriteMessageExtensions
     {
-        public static async ValueTask<byte[]> WriteToArrayAsync(this IHubProtocol protocol, HubMessage message)
+        public static byte[] WriteToArray(this IHubProtocol protocol, HubMessage message)
         {
-            using (var memoryStream = new MemoryStream())
+            using (var output = new MemoryStream())
             {
-                var pipe = memoryStream.AsPipelineWriter();
-
-                // See https://github.com/dotnet/corefxlab/issues/1460, the TextEncoder is unimportant but required.
-                var output = new PipelineTextOutput(pipe, TextEncoder.Utf8);
-
                 // Encode the message
                 if (!protocol.TryWriteMessage(message, output))
                 {
                     throw new InvalidOperationException("Failed to write message to the output stream");
                 }
-
-                await output.FlushAsync();
-
-                // Create a message
-                return memoryStream.ToArray();
+                
+                return output.ToArray();
             }
         }
     }
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/IHubProtocol.cs b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/IHubProtocol.cs
index a8bae3cc05f..4ba74233219 100644
--- a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/IHubProtocol.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/IHubProtocol.cs
@@ -4,6 +4,7 @@
 using System;
 using System.Buffers;
 using System.Collections.Generic;
+using System.IO;
 
 namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
 {
@@ -11,6 +12,6 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
     {
         bool TryParseMessages(ReadOnlySpan<byte> input, IInvocationBinder binder, out IList<HubMessage> messages);
 
-        bool TryWriteMessage(HubMessage message, IOutput output);
+        bool TryWriteMessage(HubMessage message, Stream output);
     }
 }
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/JsonHubProtocol.cs b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/JsonHubProtocol.cs
index 370f219010e..82ed1fffeb4 100644
--- a/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/JsonHubProtocol.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Common/Internal/Protocol/JsonHubProtocol.cs
@@ -47,11 +47,10 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
 
         public bool TryParseMessages(ReadOnlySpan<byte> input, IInvocationBinder binder, out IList<HubMessage> messages)
         {
-            var reader = new BytesReader(input.ToArray());
             messages = new List<HubMessage>();
 
             var parser = new TextMessageParser();
-            while (parser.TryParseMessage(ref reader, out var payload))
+            while (parser.TryParseMessage(ref input, out var payload))
             {
                 // TODO: Need a span-native JSON parser!
                 using (var memoryStream = new MemoryStream(payload.ToArray()))
@@ -63,9 +62,8 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
             return messages.Count > 0;
         }
 
-        public bool TryWriteMessage(HubMessage message, IOutput output)
+        public bool TryWriteMessage(HubMessage message, Stream output)
         {
-            // TODO: Need IOutput-compatible JSON serializer!
             using (var memoryStream = new MemoryStream())
             {
                 WriteMessage(message, memoryStream);
diff --git a/src/Microsoft.AspNetCore.SignalR.Common/Microsoft.AspNetCore.SignalR.Common.csproj b/src/Microsoft.AspNetCore.SignalR.Common/Microsoft.AspNetCore.SignalR.Common.csproj
index e783c7c4916..2c479940fe8 100644
--- a/src/Microsoft.AspNetCore.SignalR.Common/Microsoft.AspNetCore.SignalR.Common.csproj
+++ b/src/Microsoft.AspNetCore.SignalR.Common/Microsoft.AspNetCore.SignalR.Common.csproj
@@ -12,13 +12,10 @@
     <RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
   </PropertyGroup>
 
-  <ItemGroup>
-    <Compile Include="../Common/IOutputExtensions.cs" Link="IOutputExtensions.cs" />
-  </ItemGroup>
-
   <ItemGroup>
     <PackageReference Include="Newtonsoft.Json" Version="$(JsonNetVersion)" />
-    <PackageReference Include="System.IO.Pipelines.Text.Primitives" Version="$(CoreFxLabsVersion)" />
+    <PackageReference Include="System.Buffers.Primitives" Version="$(CoreFxLabsVersion)" />
+    <PackageReference Include="System.Binary" Version="$(CoreFxLabsVersion)" />
   </ItemGroup>
 
 </Project>
diff --git a/src/Microsoft.AspNetCore.SignalR.Redis/RedisHubLifetimeManager.cs b/src/Microsoft.AspNetCore.SignalR.Redis/RedisHubLifetimeManager.cs
index dd80dc5afc0..13308c22247 100644
--- a/src/Microsoft.AspNetCore.SignalR.Redis/RedisHubLifetimeManager.cs
+++ b/src/Microsoft.AspNetCore.SignalR.Redis/RedisHubLifetimeManager.cs
@@ -5,15 +5,12 @@ using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
-using System.IO.Pipelines;
-using System.IO.Pipelines.Text.Primitives;
 using System.Linq;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.SignalR.Internal.Protocol;
 using Microsoft.AspNetCore.Sockets;
-using Microsoft.AspNetCore.Sockets.Internal.Formatters;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
 using Newtonsoft.Json;
@@ -313,7 +310,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis
         private async Task WriteAsync(ConnectionContext connection, HubMessage hubMessage)
         {
             var protocol = connection.Metadata.Get<IHubProtocol>(HubConnectionMetadataNames.HubProtocol);
-            var data = await protocol.WriteToArrayAsync(hubMessage);
+            var data = protocol.WriteToArray(hubMessage);
 
             while (await connection.Transport.Output.WaitToWriteAsync())
             {
diff --git a/src/Microsoft.AspNetCore.SignalR/DefaultHubLifetimeManager.cs b/src/Microsoft.AspNetCore.SignalR/DefaultHubLifetimeManager.cs
index 15218eb4d6b..4d1ee56803b 100644
--- a/src/Microsoft.AspNetCore.SignalR/DefaultHubLifetimeManager.cs
+++ b/src/Microsoft.AspNetCore.SignalR/DefaultHubLifetimeManager.cs
@@ -4,14 +4,11 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.IO.Pipelines;
-using System.IO.Pipelines.Text.Primitives;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.SignalR.Internal.Protocol;
 using Microsoft.AspNetCore.Sockets;
-using Microsoft.AspNetCore.Sockets.Internal.Formatters;
 
 namespace Microsoft.AspNetCore.SignalR
 {
@@ -125,7 +122,7 @@ namespace Microsoft.AspNetCore.SignalR
         private async Task WriteAsync(ConnectionContext connection, HubMessage hubMessage)
         {
             var protocol = connection.Metadata.Get<IHubProtocol>(HubConnectionMetadataNames.HubProtocol);
-            var payload = await protocol.WriteToArrayAsync(hubMessage);
+            var payload = protocol.WriteToArray(hubMessage);
 
             while (await connection.Transport.Output.WaitToWriteAsync())
             {
diff --git a/src/Microsoft.AspNetCore.SignalR/HubEndPoint.cs b/src/Microsoft.AspNetCore.SignalR/HubEndPoint.cs
index 139920dd059..cd04d3acd7c 100644
--- a/src/Microsoft.AspNetCore.SignalR/HubEndPoint.cs
+++ b/src/Microsoft.AspNetCore.SignalR/HubEndPoint.cs
@@ -230,7 +230,7 @@ namespace Microsoft.AspNetCore.SignalR
 
         private async Task SendMessageAsync(ConnectionContext connection, IHubProtocol protocol, HubMessage hubMessage)
         {
-            var payload = await protocol.WriteToArrayAsync(hubMessage);
+            var payload = protocol.WriteToArray(hubMessage);
 
             while (await connection.Transport.Output.WaitToWriteAsync())
             {
diff --git a/src/Microsoft.AspNetCore.Sockets.Http/HttpConnectionDispatcher.cs b/src/Microsoft.AspNetCore.Sockets.Http/HttpConnectionDispatcher.cs
index 5a7ed4af4ed..e5a5b70d17e 100644
--- a/src/Microsoft.AspNetCore.Sockets.Http/HttpConnectionDispatcher.cs
+++ b/src/Microsoft.AspNetCore.Sockets.Http/HttpConnectionDispatcher.cs
@@ -3,7 +3,6 @@
 
 using System;
 using System.IO;
-using System.IO.Pipelines;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
diff --git a/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/ServerSentEventsMessageFormatter.cs b/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/ServerSentEventsMessageFormatter.cs
index 896134e1a54..54b6a6ca87b 100644
--- a/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/ServerSentEventsMessageFormatter.cs
+++ b/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/ServerSentEventsMessageFormatter.cs
@@ -2,8 +2,8 @@
 // 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.Buffers;
+using System.IO;
 
 namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
 {
@@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
 
         private const byte LineFeed = (byte)'\n';
 
-        public static bool TryWriteMessage(ReadOnlySpan<byte> payload, IOutput output)
+        public static bool TryWriteMessage(ReadOnlySpan<byte> payload, MemoryStream output)
         {
             // Write the payload
             if (!TryWritePayload(payload, output))
@@ -22,15 +22,12 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
                 return false;
             }
 
-            if (!output.TryWrite(Newline))
-            {
-                return false;
-            }
+            output.Write(Newline, 0, Newline.Length);
 
             return true;
         }
 
-        private static bool TryWritePayload(ReadOnlySpan<byte> payload, IOutput output)
+        private static bool TryWritePayload(ReadOnlySpan<byte> payload, Stream output)
         {
             // Short-cut for empty payload
             if (payload.Length == 0)
@@ -88,22 +85,16 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
             return true;
         }
 
-        private static bool TryWriteLine(ReadOnlySpan<byte> line, IOutput output)
+        private static bool TryWriteLine(ReadOnlySpan<byte> payload, Stream output)
         {
-            if (!output.TryWrite(DataPrefix))
-            {
-                return false;
-            }
+            output.Write(DataPrefix, 0, DataPrefix.Length);
 
-            if (!output.TryWrite(line))
-            {
-                return false;
-            }
+            var buffer = ArrayPool<byte>.Shared.Rent(payload.Length);
+            payload.CopyTo(buffer);
+            output.Write(buffer, 0, payload.Length);
+            ArrayPool<byte>.Shared.Return(buffer);
 
-            if (!output.TryWrite(Newline))
-            {
-                return false;
-            }
+            output.Write(Newline, 0, Newline.Length);
 
             return true;
         }
diff --git a/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/ServerSentEventsTransport.cs b/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/ServerSentEventsTransport.cs
index a0d9b8fc998..a2007c6dfc9 100644
--- a/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/ServerSentEventsTransport.cs
+++ b/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/ServerSentEventsTransport.cs
@@ -2,9 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
-using System.IO.Pipelines;
-using System.IO.Pipelines.Text.Primitives;
-using System.Text;
+using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks.Channels;
@@ -44,17 +42,15 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports
             await context.Response.WriteAsync(":\r\n");
             await context.Response.Body.FlushAsync();
 
-            var pipe = context.Response.Body.AsPipelineWriter();
-            var output = new PipelineTextOutput(pipe, TextEncoder.Utf8); // We don't need the Encoder, but it's harmless to set.
-
             try
             {
+                var ms = new MemoryStream();
                 while (await _application.WaitToReadAsync(token))
                 {
                     while (_application.TryRead(out var buffer))
                     {
                         _logger.SSEWritingMessage(_connectionId, buffer.Length);
-                        if (!ServerSentEventsMessageFormatter.TryWriteMessage(buffer, output))
+                        if (!ServerSentEventsMessageFormatter.TryWriteMessage(buffer, ms))
                         {
                             // We ran out of space to write, even after trying to enlarge.
                             // This should only happen in a significant lack-of-memory scenario.
@@ -64,11 +60,12 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports
                             // Throwing InvalidOperationException here, but it's not quite an invalid operation...
                             throw new InvalidOperationException("Ran out of space to format messages!");
                         }
-
-                        await output.FlushAsync();
                     }
                 }
 
+                ms.Seek(0, SeekOrigin.Begin);
+                await ms.CopyToAsync(context.Response.Body);
+
                 await _application.Completion;
             }
             catch (OperationCanceledException)
diff --git a/src/Microsoft.AspNetCore.Sockets.Http/Microsoft.AspNetCore.Sockets.Http.csproj b/src/Microsoft.AspNetCore.Sockets.Http/Microsoft.AspNetCore.Sockets.Http.csproj
index 971786f6cae..93e9f92f600 100644
--- a/src/Microsoft.AspNetCore.Sockets.Http/Microsoft.AspNetCore.Sockets.Http.csproj
+++ b/src/Microsoft.AspNetCore.Sockets.Http/Microsoft.AspNetCore.Sockets.Http.csproj
@@ -11,10 +11,6 @@
     <EnableApiCheck>false</EnableApiCheck>
   </PropertyGroup>
 
-  <ItemGroup>
-    <Compile Include="../Common/IOutputExtensions.cs" Link="IOutputExtensions.cs" />
-  </ItemGroup>
-
   <ItemGroup>
     <ProjectReference Include="..\Microsoft.AspNetCore.Sockets\Microsoft.AspNetCore.Sockets.csproj" />
     <ProjectReference Include="..\Microsoft.AspNetCore.Sockets.Common.Http\Microsoft.AspNetCore.Sockets.Common.Http.csproj" />    
@@ -24,7 +20,8 @@
     <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="$(AspNetCoreVersion)" />
     <PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
     <PackageReference Include="System.Threading.Tasks.Channels" Version="$(CoreFxLabsVersion)" />
-    <PackageReference Include="System.IO.Pipelines.Text.Primitives" Version="$(CoreFxLabsVersion)" />
+    <PackageReference Include="System.Memory" Version="$(CoreFxVersion)" />
+    <PackageReference Include="System.Binary" Version="$(CoreFxLabsVersion)" />
     <PackageReference Include="Newtonsoft.Json" Version="$(JsonNetVersion)" />
   </ItemGroup>
 
diff --git a/test/Common/ArrayOutput.cs b/test/Common/ArrayOutput.cs
deleted file mode 100644
index 463bf205808..00000000000
--- a/test/Common/ArrayOutput.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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.Diagnostics;
-using System.Linq;
-
-namespace Microsoft.AspNetCore.Sockets.Tests.Internal
-{
-    internal class ArrayOutput : IOutput
-    {
-        private IList<ArraySegment<byte>> _buffers = new List<ArraySegment<byte>>();
-
-        private int _chunkSize;
-        private byte[] _activeBuffer;
-        private int _offset;
-
-        public Span<byte> Buffer => _activeBuffer.Slice(_offset);
-
-        public ArrayOutput(int chunkSize)
-        {
-            _chunkSize = chunkSize;
-            AdvanceChunk();
-        }
-
-        public void Advance(int bytes)
-        {
-            // Determine the new location
-            _offset += bytes;
-            Debug.Assert(_offset <= _activeBuffer.Length, "How did we write more data than we had space?");
-        }
-
-        public void Enlarge(int desiredBufferLength = 0)
-        {
-            if (desiredBufferLength == 0 || _activeBuffer.Length - _offset < desiredBufferLength)
-            {
-                AdvanceChunk();
-            }
-        }
-
-        public byte[] ToArray()
-        {
-            var totalLength = _buffers.Sum(b => b.Count) + _offset;
-
-            var arr = new byte[totalLength];
-
-            int offset = 0;
-            foreach (var buffer in _buffers)
-            {
-                System.Buffer.BlockCopy(buffer.Array, 0, arr, offset, buffer.Count);
-                offset += buffer.Count;
-            }
-
-            if (_offset > 0)
-            {
-                System.Buffer.BlockCopy(_activeBuffer, 0, arr, offset, _offset);
-            }
-
-            return arr;
-        }
-
-        private void AdvanceChunk()
-        {
-            if (_activeBuffer != null)
-            {
-                _buffers.Add(new ArraySegment<byte>(_activeBuffer, 0, _offset));
-            }
-
-            _activeBuffer = new byte[_chunkSize];
-            _offset = 0;
-        }
-    }
-}
diff --git a/test/Common/ByteArrayExtensions.cs b/test/Common/ByteArrayExtensions.cs
deleted file mode 100644
index 9b02403b271..00000000000
--- a/test/Common/ByteArrayExtensions.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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.Buffers;
-using System.Collections.Generic;
-
-namespace System
-{
-    internal static class ByteArrayExtensions
-    {
-        public static ReadOnlyBytes ToChunkedReadOnlyBytes(this byte[] data, int chunkSize)
-        {
-            if (chunkSize == 0)
-            {
-                return new ReadOnlyBytes(data);
-            }
-
-            var chunks = new List<byte[]>();
-            for (var i = 0; i < data.Length; i += chunkSize)
-            {
-                var thisChunkSize = Math.Min(chunkSize, data.Length - i);
-                var chunk = new byte[thisChunkSize];
-                for (var j = 0; j < thisChunkSize; j++)
-                {
-                    chunk[j] = data[i + j];
-                }
-                chunks.Add(chunk);
-            }
-
-            chunks.Reverse();
-
-            ReadOnlyBytes? bytes = null;
-            foreach (var chunk in chunks)
-            {
-                if (bytes == null)
-                {
-                    bytes = new ReadOnlyBytes(chunk);
-                }
-                else
-                {
-                    bytes = new ReadOnlyBytes(chunk, bytes);
-                }
-            }
-            return bytes.Value;
-        }
-    }
-}
diff --git a/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.cs b/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.cs
index 7b97e55a18d..cf15f6688c5 100644
--- a/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.cs
@@ -4,6 +4,7 @@
 using System;
 using System.Buffers;
 using System.Collections.Generic;
+using System.IO;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.SignalR.Internal;
 using Microsoft.AspNetCore.SignalR.Internal.Protocol;
@@ -191,7 +192,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
                 throw new InvalidOperationException("No Parsed Message provided");
             }
 
-            public bool TryWriteMessage(HubMessage message, IOutput output)
+            public bool TryWriteMessage(HubMessage message, Stream output)
             {
                 WriteCalls += 1;
 
diff --git a/test/Microsoft.AspNetCore.SignalR.Client.Tests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj b/test/Microsoft.AspNetCore.SignalR.Client.Tests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj
index c564fcbb8aa..71be053e5d3 100644
--- a/test/Microsoft.AspNetCore.SignalR.Client.Tests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj
+++ b/test/Microsoft.AspNetCore.SignalR.Client.Tests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj
@@ -9,7 +9,6 @@
 
   <ItemGroup>
     <Compile Include="..\Common\TaskExtensions.cs" Link="TaskExtensions.cs" />
-    <Compile Include="..\Common\ArrayOutput.cs" Link="ArrayOutput.cs" />
     <Compile Include="..\Common\ChannelExtensions.cs" Link="ChannelExtensions.cs" />
   </ItemGroup>
 
diff --git a/test/Microsoft.AspNetCore.SignalR.Client.Tests/TestConnection.cs b/test/Microsoft.AspNetCore.SignalR.Client.Tests/TestConnection.cs
index 5e5104ced9e..a2ab964f01d 100644
--- a/test/Microsoft.AspNetCore.SignalR.Client.Tests/TestConnection.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Client.Tests/TestConnection.cs
@@ -2,15 +2,14 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.IO;
 using System.Net.Http;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks.Channels;
-using Microsoft.AspNetCore.Sockets;
 using Microsoft.AspNetCore.Sockets.Client;
 using Microsoft.AspNetCore.Sockets.Internal.Formatters;
-using Microsoft.AspNetCore.Sockets.Tests.Internal;
 using Newtonsoft.Json;
 using Xunit;
 
@@ -88,7 +87,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
 
         private byte[] FormatMessageToArray(byte[] message)
         {
-            var output = new ArrayOutput(1024);
+            var output = new MemoryStream();
             Assert.True(TextMessageFormatter.TryWriteMessage(message, output));
             return output.ToArray();
         }
diff --git a/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/JsonHubProtocolTests.cs b/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/JsonHubProtocolTests.cs
index e26aad5d855..d36d1b16d66 100644
--- a/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/JsonHubProtocolTests.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Common.Tests/Internal/Protocol/JsonHubProtocolTests.cs
@@ -1,12 +1,12 @@
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.SignalR.Internal;
 using Microsoft.AspNetCore.SignalR.Internal.Protocol;
 using Microsoft.AspNetCore.Sockets.Internal.Formatters;
-using Microsoft.AspNetCore.Sockets.Tests.Internal;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
 using Xunit;
@@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
         [Theory]
         [MemberData(nameof(ProtocolTestData))]
-        public async Task WriteMessage(HubMessage message, bool camelCase, NullValueHandling nullValueHandling, string expectedOutput)
+        public void WriteMessage(HubMessage message, bool camelCase, NullValueHandling nullValueHandling, string expectedOutput)
         {
             expectedOutput = Frame(expectedOutput);
 
@@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
             };
 
             var protocol = new JsonHubProtocol(jsonSerializer);
-            var encoded = await protocol.WriteToArrayAsync(message);
+            var encoded = protocol.WriteToArray(message);
             var json = Encoding.UTF8.GetString(encoded);
 
             Assert.Equal(expectedOutput, json);
@@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
 
         private static byte[] FormatMessageToArray(byte[] message)
         {
-            var output = new ArrayOutput(1024);
+            var output = new MemoryStream();
             Assert.True(TextMessageFormatter.TryWriteMessage(message, output));
             return output.ToArray();
         }
diff --git a/test/Microsoft.AspNetCore.SignalR.Common.Tests/Microsoft.AspNetCore.SignalR.Common.Tests.csproj b/test/Microsoft.AspNetCore.SignalR.Common.Tests/Microsoft.AspNetCore.SignalR.Common.Tests.csproj
index c9fe4062b99..7518cdb42cd 100644
--- a/test/Microsoft.AspNetCore.SignalR.Common.Tests/Microsoft.AspNetCore.SignalR.Common.Tests.csproj
+++ b/test/Microsoft.AspNetCore.SignalR.Common.Tests/Microsoft.AspNetCore.SignalR.Common.Tests.csproj
@@ -7,10 +7,6 @@
     <TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.0</TargetFrameworks>
   </PropertyGroup>
 
-  <ItemGroup>
-    <Compile Include="..\Common\ArrayOutput.cs" Link="ArrayOutput.cs" />
-  </ItemGroup>
-
   <ItemGroup>
     <ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
diff --git a/test/Microsoft.AspNetCore.SignalR.Microbenchmarks/MessageParserBenchmark.cs b/test/Microsoft.AspNetCore.SignalR.Microbenchmarks/MessageParserBenchmark.cs
index 40443b692a2..20331dea063 100644
--- a/test/Microsoft.AspNetCore.SignalR.Microbenchmarks/MessageParserBenchmark.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Microbenchmarks/MessageParserBenchmark.cs
@@ -1,8 +1,8 @@
 using System;
 using System.Buffers;
+using System.IO;
 using BenchmarkDotNet.Attributes;
 using Microsoft.AspNetCore.Sockets.Internal.Formatters;
-using Microsoft.AspNetCore.Sockets.Tests.Internal;
 
 namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
 {
@@ -12,8 +12,8 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
         private static readonly Random Random = new Random();
         private readonly TextMessageParser _textMessageParser = new TextMessageParser();
         private readonly BinaryMessageParser _binaryMessageParser = new BinaryMessageParser();
-        private ReadOnlyBytes _binaryInput;
-        private ReadOnlyBytes _textInput;
+        private ReadOnlyBuffer<byte> _binaryInput;
+        private ReadOnlyBuffer<byte> _textInput;
 
         [Params(32, 64)]
         public int ChunkSize { get; set; }
@@ -26,30 +26,30 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
         {
             var buffer = new byte[MessageLength];
             Random.NextBytes(buffer);
-            var output = new ArrayOutput(MessageLength + 32);
+            var output = new MemoryStream();
             if (!BinaryMessageFormatter.TryWriteMessage(buffer, output))
             {
                 throw new InvalidOperationException("Failed to format message");
             }
 
-            _binaryInput = output.ToArray().ToChunkedReadOnlyBytes(ChunkSize);
+            _binaryInput = output.ToArray();
 
             buffer = new byte[MessageLength];
             Random.NextBytes(buffer);
-            output = new ArrayOutput(MessageLength + 32);
+            output = new MemoryStream();
             if (!TextMessageFormatter.TryWriteMessage(buffer, output))
             {
                 throw new InvalidOperationException("Failed to format message");
             }
 
-            _textInput = output.ToArray().ToChunkedReadOnlyBytes(ChunkSize);
+            _textInput = output.ToArray();
         }
 
         [Benchmark]
         public void SingleBinaryMessage()
         {
-            var reader = new BytesReader(_binaryInput);
-            if (!_binaryMessageParser.TryParseMessage(ref reader, out _))
+            var buffer = _binaryInput.Span;
+            if (!_binaryMessageParser.TryParseMessage(ref buffer, out _))
             {
                 throw new InvalidOperationException("Failed to parse");
             }
@@ -58,8 +58,8 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
         [Benchmark]
         public void SingleTextMessage()
         {
-            var reader = new BytesReader(_textInput);
-            if (!_textMessageParser.TryParseMessage(ref reader, out _))
+            var buffer = _textInput.Span;
+            if (!_textMessageParser.TryParseMessage(ref buffer, out _))
             {
                 throw new InvalidOperationException("Failed to parse");
             }
diff --git a/test/Microsoft.AspNetCore.SignalR.Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj b/test/Microsoft.AspNetCore.SignalR.Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj
index af3aa9a7b21..f838587df27 100644
--- a/test/Microsoft.AspNetCore.SignalR.Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj
+++ b/test/Microsoft.AspNetCore.SignalR.Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj
@@ -7,11 +7,6 @@
     <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
   </PropertyGroup>
 
-  <ItemGroup>
-    <Compile Include="..\Common\ByteArrayExtensions.cs" Link="ByteArrayExtensions.cs" />
-    <Compile Include="..\Common\ArrayOutput.cs" Link="ArrayOutput.cs" />
-  </ItemGroup>
-
   <ItemGroup>
     <ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
     <PackageReference Include="BenchmarkDotNet" Version="0.10.3" />
diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/BinaryMessageFormatterTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/BinaryMessageFormatterTests.cs
index 595cc5641fd..ef47d6e9bff 100644
--- a/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/BinaryMessageFormatterTests.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/BinaryMessageFormatterTests.cs
@@ -3,6 +3,7 @@
 
 using System;
 using System.Buffers;
+using System.IO;
 using System.Text;
 using Microsoft.AspNetCore.Sockets.Internal.Formatters;
 using Xunit;
@@ -28,7 +29,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
                 Encoding.UTF8.GetBytes("Hello,\r\nWorld!")
             };
 
-            var output = new ArrayOutput(chunkSize: 8); // Use small chunks to test Advance/Enlarge and partial payload writing
+            var output = new MemoryStream(); // Use small chunks to test Advance/Enlarge and partial payload writing
             foreach (var message in messages)
             {
                 Assert.True(BinaryMessageFormatter.TryWriteMessage(message, output));
@@ -46,11 +47,11 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
         [InlineData(0, 256, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xAB, 0xCD, 0xEF, 0x12 }, new byte[] { 0xAB, 0xCD, 0xEF, 0x12 })]
         public void WriteBinaryMessage(int offset, int chunkSize, byte[] encoded, byte[] payload)
         {
-            var output = new ArrayOutput(chunkSize);
+            var output = new MemoryStream();
 
             if (offset > 0)
             {
-                output.Advance(offset);
+                output.Seek(offset, SeekOrigin.Begin);
             }
 
             Assert.True(BinaryMessageFormatter.TryWriteMessage(payload, output));
@@ -66,11 +67,11 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
         public void WriteTextMessage(int offset, int chunkSize, byte[] encoded, string payload)
         {
             var message = Encoding.UTF8.GetBytes(payload);
-            var output = new ArrayOutput(chunkSize);
+            var output = new MemoryStream();
 
             if (offset > 0)
             {
-                output.Advance(offset);
+                output.Seek(offset, SeekOrigin.Begin);
             }
 
             Assert.True(BinaryMessageFormatter.TryWriteMessage(message, output));
diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/BinaryMessageParserTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/BinaryMessageParserTests.cs
index 9ae8d374bd6..d1ba3651ad5 100644
--- a/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/BinaryMessageParserTests.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/BinaryMessageParserTests.cs
@@ -19,9 +19,9 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
         public void ReadMessage(byte[] encoded, string payload)
         {
             var parser = new BinaryMessageParser();
-            var reader = new BytesReader(encoded);
-            Assert.True(parser.TryParseMessage(ref reader, out var message));
-            Assert.Equal(reader.Index, encoded.Length);
+            ReadOnlySpan<byte> span = encoded.AsSpan();
+            Assert.True(parser.TryParseMessage(ref span, out var message));
+            Assert.Equal(0, span.Length);
 
             Assert.Equal(Encoding.UTF8.GetBytes(payload), message.ToArray());
         }
@@ -32,18 +32,14 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
         public void ReadBinaryMessage(byte[] encoded, byte[] payload)
         {
             var parser = new BinaryMessageParser();
-            var reader = new BytesReader(encoded);
-            Assert.True(parser.TryParseMessage(ref reader, out var message));
-            Assert.Equal(reader.Index, encoded.Length);
+            ReadOnlySpan<byte> span = encoded.AsSpan();
+            Assert.True(parser.TryParseMessage(ref span, out var message));
+            Assert.Equal(0, span.Length);
             Assert.Equal(payload, message.ToArray());
         }
 
-        [Theory]
-        [InlineData(0)] // No chunking
-        [InlineData(4)]
-        [InlineData(8)]
-        [InlineData(256)]
-        public void ReadMultipleMessages(int chunkSize)
+        [Fact]
+        public void ReadMultipleMessages()
         {
             var encoded = new byte[]
             {
@@ -53,16 +49,15 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
                     /* body: */ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x0D, 0x0A, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21,
             };
             var parser = new BinaryMessageParser();
-            var buffer = encoded.ToChunkedReadOnlyBytes(chunkSize);
-            var reader = new BytesReader(buffer);
+            ReadOnlySpan<byte> span = encoded.AsSpan();
 
             var messages = new List<byte[]>();
-            while (parser.TryParseMessage(ref reader, out var message))
+            while (parser.TryParseMessage(ref span, out var message))
             {
                 messages.Add(message.ToArray());
             }
 
-            Assert.Equal(encoded.Length, reader.Index);
+            Assert.Equal(0, span.Length);
 
             Assert.Equal(2, messages.Count);
             Assert.Equal(new byte[0], messages[0]);
@@ -75,9 +70,9 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
         public void ReadIncompleteMessages(byte[] encoded)
         {
             var parser = new BinaryMessageParser();
-            var reader = new BytesReader(new ReadOnlyBytes(encoded));
-            Assert.False(parser.TryParseMessage(ref reader, out var message));
-            Assert.Equal(encoded.Length, reader.Index);
+            ReadOnlySpan<byte> span = encoded.AsSpan();
+            Assert.False(parser.TryParseMessage(ref span, out var message));
+            Assert.Equal(0, span.Length);
         }
     }
 }
diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/TextMessageFormatterTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/TextMessageFormatterTests.cs
index 10677c24074..e3c78c89409 100644
--- a/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/TextMessageFormatterTests.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/TextMessageFormatterTests.cs
@@ -2,7 +2,7 @@
 // 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;
 using System.Text;
 using Microsoft.AspNetCore.Sockets.Internal.Formatters;
 using Xunit;
@@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
                 Encoding.UTF8.GetBytes("Hello,\r\nWorld!")
             };
 
-            var output = new ArrayOutput(chunkSize: 8); // Use small chunks to test Advance/Enlarge and partial payload writing
+            var output = new MemoryStream();
             foreach (var message in messages)
             {
                 Assert.True(TextMessageFormatter.TryWriteMessage(message, output));
@@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
         public void WriteMessage(int chunkSize, string encoded, string payload)
         {
             var message = Encoding.UTF8.GetBytes(payload);
-            var output = new ArrayOutput(chunkSize); // Use small chunks to test Advance/Enlarge and partial payload writing
+            var output = new MemoryStream();
 
             Assert.True(TextMessageFormatter.TryWriteMessage(message, output));
 
diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/TextMessageParserTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/TextMessageParserTests.cs
index 6397a7ee17e..ef2caaa8297 100644
--- a/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/TextMessageParserTests.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Tests/Formatters/TextMessageParserTests.cs
@@ -21,35 +21,28 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
         {
             var parser = new TextMessageParser();
             var buffer = Encoding.UTF8.GetBytes(encoded);
-            var reader = new BytesReader(buffer.ToChunkedReadOnlyBytes(chunkSize));
+            ReadOnlySpan<byte> span = buffer.AsSpan();
 
-            Assert.True(parser.TryParseMessage(ref reader, out var message));
-            Assert.Equal(reader.Index, buffer.Length);
+            Assert.True(parser.TryParseMessage(ref span, out var message));
+            Assert.Equal(0, span.Length);
             Assert.Equal(Encoding.UTF8.GetBytes(payload), message.ToArray());
         }
 
-        [Theory]
-        [InlineData(0)] // Not chunked
-        [InlineData(4)]
-        [InlineData(8)]
-        public void ReadMultipleMessages(int chunkSize)
+        [Fact]
+        public void ReadMultipleMessages()
         {
             const string encoded = "0:;14:Hello,\r\nWorld!;";
             var parser = new TextMessageParser();
             var data = Encoding.UTF8.GetBytes(encoded);
-            var buffer = chunkSize > 0 ?
-                data.ToChunkedReadOnlyBytes(chunkSize) :
-                new ReadOnlyBytes(data);
-
-            var reader = new BytesReader(buffer);
+            ReadOnlySpan<byte> span = data.AsSpan();
 
             var messages = new List<byte[]>();
-            while (parser.TryParseMessage(ref reader, out var message))
+            while (parser.TryParseMessage(ref span, out var message))
             {
                 messages.Add(message.ToArray());
             }
 
-            Assert.Equal(reader.Index, Encoding.UTF8.GetByteCount(encoded));
+            Assert.Equal(0, span.Length);
 
             Assert.Equal(2, messages.Count);
             Assert.Equal(new byte[0], messages[0]);
@@ -68,8 +61,8 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
         {
             var parser = new TextMessageParser();
             var buffer = Encoding.UTF8.GetBytes(encoded);
-            var reader = new BytesReader(buffer);
-            Assert.False(parser.TryParseMessage(ref reader, out _));
+            ReadOnlySpan<byte> span = buffer.AsSpan();
+            Assert.False(parser.TryParseMessage(ref span, out _));
         }
 
         [Theory]
@@ -82,8 +75,11 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
         {
             var parser = new TextMessageParser();
             var buffer = Encoding.UTF8.GetBytes(encoded);
-            var reader = new BytesReader(buffer);
-            var ex = Assert.Throws<FormatException>(() => parser.TryParseMessage(ref reader, out _));
+            var ex = Assert.Throws<FormatException>(() =>
+            {
+                ReadOnlySpan<byte> span = buffer.AsSpan();
+                parser.TryParseMessage(ref span, out _);
+            });
             Assert.Equal(expectedMessage, ex.Message);
         }
 
@@ -96,8 +92,12 @@ namespace Microsoft.AspNetCore.Sockets.Common.Tests.Internal.Formatters
             // 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<FormatException>(() => parser.TryParseMessage(ref reader, out _));
-            Assert.Equal("Invalid length", ex.Message);
+            var ex = Assert.Throws<FormatException>(() =>
+            {
+                ReadOnlySpan<byte> span = buffer.AsSpan();
+                parser.TryParseMessage(ref span, out _);
+            });
+            Assert.Equal("Invalid length: 'He�lo'", ex.Message);
         }
     }
 }
diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/Microsoft.AspNetCore.SignalR.Tests.csproj b/test/Microsoft.AspNetCore.SignalR.Tests/Microsoft.AspNetCore.SignalR.Tests.csproj
index 25d5d021c4b..852e041f782 100644
--- a/test/Microsoft.AspNetCore.SignalR.Tests/Microsoft.AspNetCore.SignalR.Tests.csproj
+++ b/test/Microsoft.AspNetCore.SignalR.Tests/Microsoft.AspNetCore.SignalR.Tests.csproj
@@ -16,8 +16,6 @@
 
   <ItemGroup>
     <Compile Include="..\Common\TaskExtensions.cs" Link="TaskExtensions.cs" />
-    <Compile Include="..\Common\ArrayOutput.cs" Link="ArrayOutput.cs" />
-    <Compile Include="..\Common\ByteArrayExtensions.cs" Link="ByteArrayExtensions.cs" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/TestClient.cs b/test/Microsoft.AspNetCore.SignalR.Tests/TestClient.cs
index 7e10ba23982..c21c90cdb23 100644
--- a/test/Microsoft.AspNetCore.SignalR.Tests/TestClient.cs
+++ b/test/Microsoft.AspNetCore.SignalR.Tests/TestClient.cs
@@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
         public async Task<string> SendInvocationAsync(string methodName, params object[] args)
         {
             var invocationId = GetInvocationId();
-            var payload = await _protocol.WriteToArrayAsync(new InvocationMessage(invocationId, nonBlocking: false, target: methodName, arguments: args));
+            var payload = _protocol.WriteToArray(new InvocationMessage(invocationId, nonBlocking: false, target: methodName, arguments: args));
 
             await Application.Output.WriteAsync(payload);
 
diff --git a/test/Microsoft.AspNetCore.Sockets.Tests/Microsoft.AspNetCore.Sockets.Tests.csproj b/test/Microsoft.AspNetCore.Sockets.Tests/Microsoft.AspNetCore.Sockets.Tests.csproj
index e17e1972478..4e0dcb7d967 100644
--- a/test/Microsoft.AspNetCore.Sockets.Tests/Microsoft.AspNetCore.Sockets.Tests.csproj
+++ b/test/Microsoft.AspNetCore.Sockets.Tests/Microsoft.AspNetCore.Sockets.Tests.csproj
@@ -8,7 +8,6 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <Compile Include="..\Common\ArrayOutput.cs" Link="ArrayOutput.cs" />
     <Compile Include="..\Common\TaskExtensions.cs" Link="TaskExtensions.cs" />
   </ItemGroup>
 
diff --git a/test/Microsoft.AspNetCore.Sockets.Tests/ServerSentEventsMessageFormatterTests.cs b/test/Microsoft.AspNetCore.Sockets.Tests/ServerSentEventsMessageFormatterTests.cs
index d5c7d739cc7..44c7939720d 100644
--- a/test/Microsoft.AspNetCore.Sockets.Tests/ServerSentEventsMessageFormatterTests.cs
+++ b/test/Microsoft.AspNetCore.Sockets.Tests/ServerSentEventsMessageFormatterTests.cs
@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.IO;
 using System.Text;
 using Microsoft.AspNetCore.Sockets.Internal.Formatters;
 using Xunit;
@@ -19,7 +20,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
         [InlineData("data: Hello\r\ndata: \r\n\r\n", "Hello\r\n")]
         public void WriteTextMessage(string encoded, string payload)
         {
-            var output = new ArrayOutput(chunkSize: 8); // Use small chunks to test Advance/Enlarge and partial payload writing
+            var output = new MemoryStream();
             Assert.True(ServerSentEventsMessageFormatter.TryWriteMessage(Encoding.UTF8.GetBytes(payload), output));
 
             Assert.Equal(encoded, Encoding.UTF8.GetString(output.ToArray()));
-- 
GitLab