Bläddra i källkod

Enable CA1844 (#39634)

Contributes to https://github.com/dotnet/aspnetcore/issues/24055
Fixes https://github.com/dotnet/aspnetcore/issues/39555
Pranav K 4 år sedan
förälder
incheckning
6850cc8187
26 ändrade filer med 149 tillägg och 15 borttagningar
  1. 3 0
      .editorconfig
  2. 6 3
      src/Components/Shared/src/ArrayBuilder.cs
  3. 14 0
      src/Components/Shared/src/ArrayBuilderMemoryStream.cs
  4. 11 3
      src/Hosting/TestHost/src/ResponseBodyReaderStream.cs
  5. 5 0
      src/Hosting/TestHost/src/ResponseBodyWriterStream.cs
  6. 3 0
      src/Http/Http/src/Internal/ReferenceReadStream.cs
  7. 6 0
      src/Http/WebUtilities/src/BufferedReadStream.cs
  8. 6 0
      src/Http/WebUtilities/src/FileBufferingReadStream.cs
  9. 4 0
      src/Http/WebUtilities/src/FileBufferingWriteStream.cs
  10. 13 3
      src/Http/WebUtilities/src/MultipartReaderStream.cs
  11. 3 0
      src/Http/WebUtilities/src/PublicAPI.Unshipped.txt
  12. 15 5
      src/Middleware/ResponseCaching/src/Streams/SegmentWriteStream.cs
  13. 12 0
      src/Mvc/Mvc.Core/src/Infrastructure/NonDisposableStream.cs
  14. 5 0
      src/Mvc/shared/Mvc.Core.TestCommon/NonSeekableReadableStream.cs
  15. 10 0
      src/Servers/HttpSys/src/RequestProcessing/OpaqueStream.cs
  16. 2 0
      src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs
  17. 2 0
      src/Servers/HttpSys/src/RequestProcessing/ResponseBody.cs
  18. 5 0
      src/Servers/IIS/IIS/src/Core/ReadOnlyStream.cs
  19. 6 0
      src/Servers/IIS/IIS/src/Core/WriteOnlyStream.cs
  20. 5 0
      src/Servers/IIS/IIS/src/Core/WriteOnlyStreamInternal.cs
  21. 1 0
      src/Servers/IIS/IIS/src/PublicAPI.Unshipped.txt
  22. 2 0
      src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs
  23. 3 0
      src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseStream.cs
  24. 3 0
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/ReadOnlyStream.cs
  25. 3 0
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/WriteOnlyStream.cs
  26. 1 1
      src/SignalR/common/Shared/PipeWriterStream.cs

+ 3 - 0
.editorconfig

@@ -166,6 +166,9 @@ dotnet_diagnostic.CA1842.severity = warning
 # CA1843: Do not use 'WaitAll' with a single task
 # CA1843: Do not use 'WaitAll' with a single task
 dotnet_diagnostic.CA1843.severity = warning
 dotnet_diagnostic.CA1843.severity = warning
 
 
+# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream'
+dotnet_diagnostic.CA1844.severity = warning
+
 # CA1845: Use span-based 'string.Concat'
 # CA1845: Use span-based 'string.Concat'
 dotnet_diagnostic.CA1845.severity = warning
 dotnet_diagnostic.CA1845.severity = warning
 
 

+ 6 - 3
src/Components/Shared/src/ArrayBuilder.cs

@@ -76,10 +76,13 @@ internal class ArrayBuilder<T> : IDisposable
     }
     }
 
 
     internal int Append(T[] source, int startIndex, int length)
     internal int Append(T[] source, int startIndex, int length)
+        => Append(source.AsSpan(startIndex, length));
+
+    internal int Append(ReadOnlySpan<T> source)
     {
     {
         // Expand storage if needed. Using same doubling approach as would
         // Expand storage if needed. Using same doubling approach as would
         // be used if you inserted the items one-by-one.
         // be used if you inserted the items one-by-one.
-        var requiredCapacity = _itemsInUse + length;
+        var requiredCapacity = _itemsInUse + source.Length;
         if (_items.Length < requiredCapacity)
         if (_items.Length < requiredCapacity)
         {
         {
             var candidateCapacity = Math.Max(_items.Length * 2, _minCapacity);
             var candidateCapacity = Math.Max(_items.Length * 2, _minCapacity);
@@ -91,9 +94,9 @@ internal class ArrayBuilder<T> : IDisposable
             GrowBuffer(candidateCapacity);
             GrowBuffer(candidateCapacity);
         }
         }
 
 
-        Array.Copy(source, startIndex, _items, _itemsInUse, length);
+        source.CopyTo(_items.AsSpan(_itemsInUse));
         var startIndexOfAppendedItems = _itemsInUse;
         var startIndexOfAppendedItems = _itemsInUse;
-        _itemsInUse += length;
+        _itemsInUse += source.Length;
         return startIndexOfAppendedItems;
         return startIndexOfAppendedItems;
     }
     }
 
 

+ 14 - 0
src/Components/Shared/src/ArrayBuilderMemoryStream.cs

@@ -51,6 +51,9 @@ internal sealed class ArrayBuilderMemoryStream : Stream
     public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
         => throw new NotSupportedException();
         => throw new NotSupportedException();
 
 
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
+        => throw new NotSupportedException();
+
     /// <inheritdoc />
     /// <inheritdoc />
     public override void Write(byte[] buffer, int offset, int count)
     public override void Write(byte[] buffer, int offset, int count)
     {
     {
@@ -59,6 +62,17 @@ internal sealed class ArrayBuilderMemoryStream : Stream
         ArrayBuilder.Append(buffer, offset, count);
         ArrayBuilder.Append(buffer, offset, count);
     }
     }
 
 
+    public override void Write(ReadOnlySpan<byte> buffer)
+    {
+        ArrayBuilder.Append(buffer);
+    }
+
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
+    {
+        ArrayBuilder.Append(memory.Span);
+        return default;
+    }
+
     /// <inheritdoc />
     /// <inheritdoc />
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     {
     {

+ 11 - 3
src/Hosting/TestHost/src/ResponseBodyReaderStream.cs

@@ -57,6 +57,8 @@ internal class ResponseBodyReaderStream : Stream
 
 
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new NotSupportedException();
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new NotSupportedException();
 
 
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken) => throw new NotSupportedException();
+
     #endregion NotSupported
     #endregion NotSupported
 
 
     public override int Read(byte[] buffer, int offset, int count)
     public override int Read(byte[] buffer, int offset, int count)
@@ -64,9 +66,15 @@ internal class ResponseBodyReaderStream : Stream
         return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
         return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
     }
     }
 
 
-    public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+    public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     {
     {
         VerifyBuffer(buffer, offset, count);
         VerifyBuffer(buffer, offset, count);
+
+        return ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
+    }
+
+    public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+    {
         CheckAborted();
         CheckAborted();
 
 
         if (_readerComplete)
         if (_readerComplete)
@@ -90,9 +98,9 @@ internal class ResponseBodyReaderStream : Stream
         }
         }
 
 
         var readableBuffer = result.Buffer;
         var readableBuffer = result.Buffer;
-        var actual = Math.Min(readableBuffer.Length, count);
+        var actual = Math.Min(readableBuffer.Length, buffer.Length);
         readableBuffer = readableBuffer.Slice(0, actual);
         readableBuffer = readableBuffer.Slice(0, actual);
-        readableBuffer.CopyTo(new Span<byte>(buffer, offset, count));
+        readableBuffer.CopyTo(buffer.Span);
         _pipe.Reader.AdvanceTo(readableBuffer.End);
         _pipe.Reader.AdvanceTo(readableBuffer.End);
         return (int)actual;
         return (int)actual;
     }
     }

+ 5 - 0
src/Hosting/TestHost/src/ResponseBodyWriterStream.cs

@@ -69,4 +69,9 @@ internal class ResponseBodyWriterStream : Stream
     {
     {
         await _responseWriter.WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken);
         await _responseWriter.WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken);
     }
     }
+
+    public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+    {
+        await _responseWriter.WriteAsync(buffer, cancellationToken);
+    }
 }
 }

+ 3 - 0
src/Http/Http/src/Internal/ReferenceReadStream.cs

@@ -123,6 +123,9 @@ internal sealed class ReferenceReadStream : Stream
         throw new NotSupportedException();
         throw new NotSupportedException();
     }
     }
 
 
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+        => throw new NotSupportedException();
+
     public override void SetLength(long value)
     public override void SetLength(long value)
     {
     {
         throw new NotSupportedException();
         throw new NotSupportedException();

+ 6 - 0
src/Http/WebUtilities/src/BufferedReadStream.cs

@@ -189,6 +189,12 @@ public class BufferedReadStream : Stream
         _inner.Write(buffer, offset, count);
         _inner.Write(buffer, offset, count);
     }
     }
 
 
+    /// <inheritdoc/>
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+    {
+        return _inner.WriteAsync(buffer, cancellationToken);
+    }
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     {
     {

+ 6 - 0
src/Http/WebUtilities/src/FileBufferingReadStream.cs

@@ -402,6 +402,12 @@ public class FileBufferingReadStream : Stream
         throw new NotSupportedException();
         throw new NotSupportedException();
     }
     }
 
 
+    /// <inheritdoc/>
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+    {
+        throw new NotSupportedException();
+    }
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     {
     {

+ 4 - 0
src/Http/WebUtilities/src/FileBufferingWriteStream.cs

@@ -101,6 +101,10 @@ public sealed class FileBufferingWriteStream : Stream
     public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
         => throw new NotSupportedException();
         => throw new NotSupportedException();
 
 
+    /// <inheritdoc/>
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
+        => throw new NotSupportedException();
+
     /// <inheritdoc />
     /// <inheritdoc />
     public override void Write(byte[] buffer, int offset, int count)
     public override void Write(byte[] buffer, int offset, int count)
     {
     {

+ 13 - 3
src/Http/WebUtilities/src/MultipartReaderStream.cs

@@ -123,6 +123,11 @@ internal sealed class MultipartReaderStream : Stream
         throw new NotSupportedException();
         throw new NotSupportedException();
     }
     }
 
 
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+    {
+        throw new NotSupportedException();
+    }
+
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     {
     {
         throw new NotSupportedException();
         throw new NotSupportedException();
@@ -207,7 +212,10 @@ internal sealed class MultipartReaderStream : Stream
         return UpdatePosition(read);
         return UpdatePosition(read);
     }
     }
 
 
-    public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+    public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+        => ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
+
+    public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
     {
     {
         if (_finished)
         if (_finished)
         {
         {
@@ -231,7 +239,9 @@ internal sealed class MultipartReaderStream : Stream
             if (matchOffset > bufferedData.Offset)
             if (matchOffset > bufferedData.Offset)
             {
             {
                 // Sync, it's already buffered
                 // Sync, it's already buffered
-                read = _innerStream.Read(buffer, offset, Math.Min(count, matchOffset - bufferedData.Offset));
+                var slice = buffer[..Math.Min(buffer.Length, matchOffset - bufferedData.Offset)];
+
+                read = _innerStream.Read(slice.Span);
                 return UpdatePosition(read);
                 return UpdatePosition(read);
             }
             }
 
 
@@ -259,7 +269,7 @@ internal sealed class MultipartReaderStream : Stream
         }
         }
 
 
         // No possible boundary match within the buffered data, return the data from the buffer.
         // No possible boundary match within the buffered data, return the data from the buffer.
-        read = _innerStream.Read(buffer, offset, Math.Min(count, bufferedData.Count));
+        read = _innerStream.Read(buffer.Span[..Math.Min(buffer.Length, bufferedData.Count)]);
         return UpdatePosition(read);
         return UpdatePosition(read);
     }
     }
 
 

+ 3 - 0
src/Http/WebUtilities/src/PublicAPI.Unshipped.txt

@@ -1,4 +1,7 @@
 #nullable enable
 #nullable enable
+override Microsoft.AspNetCore.WebUtilities.BufferedReadStream.WriteAsync(System.ReadOnlyMemory<byte> buffer, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
+override Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.WriteAsync(System.ReadOnlyMemory<byte> buffer, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
+override Microsoft.AspNetCore.WebUtilities.FileBufferingWriteStream.ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask<int>
 override Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.WriteLineAsync(char value) -> System.Threading.Tasks.Task!
 override Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.WriteLineAsync(char value) -> System.Threading.Tasks.Task!
 override Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.WriteLineAsync(char[]! values, int index, int count) -> System.Threading.Tasks.Task!
 override Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.WriteLineAsync(char[]! values, int index, int count) -> System.Threading.Tasks.Task!
 override Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.WriteLineAsync(string? value) -> System.Threading.Tasks.Task!
 override Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.WriteLineAsync(string? value) -> System.Threading.Tasks.Task!

+ 15 - 5
src/Middleware/ResponseCaching/src/Streams/SegmentWriteStream.cs

@@ -143,7 +143,12 @@ internal class SegmentWriteStream : Stream
             throw new ObjectDisposedException("The stream has been closed for writing.");
             throw new ObjectDisposedException("The stream has been closed for writing.");
         }
         }
 
 
-        while (count > 0)
+        Write(buffer.AsSpan(offset, count));
+    }
+
+    public override void Write(ReadOnlySpan<byte> buffer)
+    {
+        while (!buffer.IsEmpty)
         {
         {
             if ((int)_bufferStream.Length == _segmentSize)
             if ((int)_bufferStream.Length == _segmentSize)
             {
             {
@@ -151,11 +156,10 @@ internal class SegmentWriteStream : Stream
                 _bufferStream.SetLength(0);
                 _bufferStream.SetLength(0);
             }
             }
 
 
-            var bytesWritten = Math.Min(count, _segmentSize - (int)_bufferStream.Length);
+            var bytesWritten = Math.Min(buffer.Length, _segmentSize - (int)_bufferStream.Length);
 
 
-            _bufferStream.Write(buffer, offset, bytesWritten);
-            count -= bytesWritten;
-            offset += bytesWritten;
+            _bufferStream.Write(buffer.Slice(0, bytesWritten));
+            buffer = buffer.Slice(bytesWritten);
             _length += bytesWritten;
             _length += bytesWritten;
         }
         }
     }
     }
@@ -166,6 +170,12 @@ internal class SegmentWriteStream : Stream
         return Task.CompletedTask;
         return Task.CompletedTask;
     }
     }
 
 
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+    {
+        Write(buffer.Span);
+        return default;
+    }
+
     public override void WriteByte(byte value)
     public override void WriteByte(byte value)
     {
     {
         if (!CanWrite)
         if (!CanWrite)

+ 12 - 0
src/Mvc/Mvc.Core/src/Infrastructure/NonDisposableStream.cs

@@ -82,6 +82,12 @@ internal class NonDisposableStream : Stream
         return _innerStream.ReadAsync(buffer, offset, count, cancellationToken);
         return _innerStream.ReadAsync(buffer, offset, count, cancellationToken);
     }
     }
 
 
+    /// <inheritdoc />
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
+    {
+        return _innerStream.ReadAsync(buffer, cancellationToken);
+    }
+
     /// <inheritdoc />
     /// <inheritdoc />
     public override IAsyncResult BeginRead(
     public override IAsyncResult BeginRead(
         byte[] buffer,
         byte[] buffer,
@@ -163,6 +169,12 @@ internal class NonDisposableStream : Stream
         return _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
         return _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
     }
     }
 
 
+    /// <inheritdoc />
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+    {
+        return _innerStream.WriteAsync(buffer, cancellationToken);
+    }
+
     /// <inheritdoc />
     /// <inheritdoc />
     public override void WriteByte(byte value)
     public override void WriteByte(byte value)
     {
     {

+ 5 - 0
src/Mvc/shared/Mvc.Core.TestCommon/NonSeekableReadableStream.cs

@@ -72,5 +72,10 @@ public class NonSeekableReadStream : Stream
         count = Math.Max(count, 1);
         count = Math.Max(count, 1);
         return _inner.ReadAsync(buffer, offset, count, cancellationToken);
         return _inner.ReadAsync(buffer, offset, count, cancellationToken);
     }
     }
+
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
+    {
+        return _inner.ReadAsync(buffer, cancellationToken);
+    }
 }
 }
 
 

+ 10 - 0
src/Servers/HttpSys/src/RequestProcessing/OpaqueStream.cs

@@ -100,6 +100,11 @@ internal class OpaqueStream : Stream
         return _requestStream.ReadAsync(buffer, offset, count, cancellationToken);
         return _requestStream.ReadAsync(buffer, offset, count, cancellationToken);
     }
     }
 
 
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+    {
+        return _requestStream.ReadAsync(buffer, cancellationToken);
+    }
+
     public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
     public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
     {
     {
         return _requestStream.CopyToAsync(destination, bufferSize, cancellationToken);
         return _requestStream.CopyToAsync(destination, bufferSize, cancellationToken);
@@ -134,6 +139,11 @@ internal class OpaqueStream : Stream
         return _responseStream.WriteAsync(buffer, offset, count, cancellationToken);
         return _responseStream.WriteAsync(buffer, offset, count, cancellationToken);
     }
     }
 
 
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+    {
+        return _responseStream.WriteAsync(buffer, cancellationToken);
+    }
+
     public override void Flush()
     public override void Flush()
     {
     {
         _responseStream.Flush();
         _responseStream.Flush();

+ 2 - 0
src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs

@@ -9,7 +9,9 @@ using Microsoft.Extensions.Logging;
 
 
 namespace Microsoft.AspNetCore.Server.HttpSys;
 namespace Microsoft.AspNetCore.Server.HttpSys;
 
 
+#pragma warning disable CA1844 // Provide memory-based overrides of async methods when subclassing 'Stream'. Fixing this is too gnarly.
 internal partial class RequestStream : Stream
 internal partial class RequestStream : Stream
+#pragma warning restore CA1844 // Provide memory-based overrides of async methods when subclassing 'Stream'
 {
 {
     private const int MaxReadSize = 0x20000; // http.sys recommends we limit reads to 128k
     private const int MaxReadSize = 0x20000; // http.sys recommends we limit reads to 128k
 
 

+ 2 - 0
src/Servers/HttpSys/src/RequestProcessing/ResponseBody.cs

@@ -9,7 +9,9 @@ using static Microsoft.AspNetCore.HttpSys.Internal.UnsafeNclNativeMethods;
 
 
 namespace Microsoft.AspNetCore.Server.HttpSys;
 namespace Microsoft.AspNetCore.Server.HttpSys;
 
 
+#pragma warning disable CA1844 // Provide memory-based overrides of async methods when subclassing 'Stream'. Fixing this is too gnarly.
 internal class ResponseBody : Stream
 internal class ResponseBody : Stream
+#pragma warning restore CA1844
 {
 {
     private readonly RequestContext _requestContext;
     private readonly RequestContext _requestContext;
     private long _leftToWrite = long.MinValue;
     private long _leftToWrite = long.MinValue;

+ 5 - 0
src/Servers/IIS/IIS/src/Core/ReadOnlyStream.cs

@@ -54,4 +54,9 @@ internal abstract class ReadOnlyStream : Stream
     {
     {
         throw new NotSupportedException();
         throw new NotSupportedException();
     }
     }
+
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+    {
+        throw new NotSupportedException();
+    }
 }
 }

+ 6 - 0
src/Servers/IIS/IIS/src/Core/WriteOnlyStream.cs

@@ -47,6 +47,12 @@ public abstract class WriteOnlyStream : Stream
         throw new NotSupportedException();
         throw new NotSupportedException();
     }
     }
 
 
+    ///<inheritdoc/>
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
+    {
+        throw new NotSupportedException();
+    }
+
     ///<inheritdoc/>
     ///<inheritdoc/>
     public override long Seek(long offset, SeekOrigin origin)
     public override long Seek(long offset, SeekOrigin origin)
     {
     {

+ 5 - 0
src/Servers/IIS/IIS/src/Core/WriteOnlyStreamInternal.cs

@@ -46,6 +46,11 @@ internal abstract class WriteOnlyStreamInternal : Stream
         throw new NotSupportedException();
         throw new NotSupportedException();
     }
     }
 
 
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
+    {
+        throw new NotSupportedException();
+    }
+
     ///<inheritdoc/>
     ///<inheritdoc/>
     public override long Seek(long offset, SeekOrigin origin)
     public override long Seek(long offset, SeekOrigin origin)
     {
     {

+ 1 - 0
src/Servers/IIS/IIS/src/PublicAPI.Unshipped.txt

@@ -1 +1,2 @@
 #nullable enable
 #nullable enable
+override Microsoft.AspNetCore.Server.IIS.Core.WriteOnlyStream.ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask<int>

+ 2 - 0
src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs

@@ -64,6 +64,8 @@ internal sealed class HttpRequestStream : Stream
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
         => throw new NotSupportedException();
         => throw new NotSupportedException();
 
 
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+        => throw new NotSupportedException();
 
 
     public override long Seek(long offset, SeekOrigin origin)
     public override long Seek(long offset, SeekOrigin origin)
     {
     {

+ 3 - 0
src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseStream.cs

@@ -43,6 +43,9 @@ internal sealed class HttpResponseStream : Stream
     public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
       => throw new NotSupportedException();
       => throw new NotSupportedException();
 
 
+    public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
+      => throw new NotSupportedException();
+
     public override void Flush()
     public override void Flush()
     {
     {
         if (!_bodyControl.AllowSynchronousIO)
         if (!_bodyControl.AllowSynchronousIO)

+ 3 - 0
src/Servers/Kestrel/Core/src/Internal/Infrastructure/ReadOnlyStream.cs

@@ -20,4 +20,7 @@ internal abstract class ReadOnlyStream : Stream
 
 
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
         => throw new NotSupportedException();
         => throw new NotSupportedException();
+
+    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+        => throw new NotSupportedException();
 }
 }

+ 3 - 0
src/Servers/Kestrel/Core/src/Internal/Infrastructure/WriteOnlyStream.cs

@@ -20,4 +20,7 @@ internal abstract class WriteOnlyStream : Stream
 
 
     public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
       => throw new NotSupportedException();
       => throw new NotSupportedException();
+
+    public override ValueTask<int> ReadAsync(Memory<byte> memory, CancellationToken cancellationToken)
+        => throw new NotSupportedException();
 }
 }

+ 1 - 1
src/SignalR/common/Shared/PipeWriterStream.cs

@@ -58,7 +58,7 @@ internal class PipeWriterStream : Stream
         return WriteCoreAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
         return WriteCoreAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
     }
     }
 
 
-#if NETCOREAPP
+#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
     public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default)
     public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default)
     {
     {
         return WriteCoreAsync(source, cancellationToken);
         return WriteCoreAsync(source, cancellationToken);