diff --git a/src/libraries/Common/src/System/IO/DelegatingStream.cs b/src/libraries/Common/src/System/IO/DelegatingStream.cs index 316d81d43f058cf97f6f8ba776c22f9cbbc4a2c7..96d7b360cbfa769ba0e582912424935b705d444f 100644 --- a/src/libraries/Common/src/System/IO/DelegatingStream.cs +++ b/src/libraries/Common/src/System/IO/DelegatingStream.cs @@ -122,6 +122,16 @@ public override int EndRead(IAsyncResult asyncResult) return _innerStream.EndRead(asyncResult); } + public override void CopyTo(Stream destination, int bufferSize) + { + _innerStream.CopyTo(destination, bufferSize); + } + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return _innerStream.CopyToAsync(destination, bufferSize, cancellationToken); + } + #endregion Read #region Write @@ -175,11 +185,6 @@ public override void EndWrite(IAsyncResult asyncResult) { _innerStream.EndWrite(asyncResult); } - - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - return _innerStream.CopyToAsync(destination, bufferSize, cancellationToken); - } #endregion Write } } diff --git a/src/libraries/Common/src/System/IO/ReadOnlyMemoryStream.cs b/src/libraries/Common/src/System/IO/ReadOnlyMemoryStream.cs index 5e2ecc2cf50b5886f69b5fa989b512ccd495330d..5e73fd57dda0d1419825baa278a2952630ee33d0 100644 --- a/src/libraries/Common/src/System/IO/ReadOnlyMemoryStream.cs +++ b/src/libraries/Common/src/System/IO/ReadOnlyMemoryStream.cs @@ -159,6 +159,7 @@ public override void CopyTo(Stream destination, int bufferSize) if (_content.Length > _position) { destination.Write(_content.Span.Slice(_position)); + _position = _content.Length; } } @@ -166,9 +167,16 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio { ValidateCopyToArguments(destination, bufferSize); EnsureNotClosed(); - return _content.Length > _position ? - destination.WriteAsync(_content.Slice(_position), cancellationToken).AsTask() : - Task.CompletedTask; + if (_content.Length > _position) + { + ReadOnlyMemory content = _content.Slice(_position); + _position = _content.Length; + return destination.WriteAsync(content, cancellationToken).AsTask(); + } + else + { + return Task.CompletedTask; + } } #endif diff --git a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs index 1086ffeed62a0467714898326ef5128b95be1d60..d8479fcf84465fff35b838d9b20756d065d78084 100644 --- a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs +++ b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs @@ -399,10 +399,17 @@ protected async Task ValidateMisuseExceptionsAsync(Stream stream) Assert.Throws(() => { stream.Seek(-1, SeekOrigin.Begin); }); Assert.Throws(() => { stream.Seek(-stream.Position - 1, SeekOrigin.Current); }); Assert.Throws(() => { stream.Seek(-stream.Length - 1, SeekOrigin.End); }); - Assert.Throws(() => { stream.Seek(0, (SeekOrigin)(-1)); }); - Assert.Throws(() => { stream.Seek(0, (SeekOrigin)3); }); - Assert.Throws(() => { stream.Seek(0, ~SeekOrigin.Begin); }); - Assert.Throws(() => { stream.SetLength(-1); }); + Assert.ThrowsAny(() => { stream.Seek(0, (SeekOrigin)(-1)); }); + Assert.ThrowsAny(() => { stream.Seek(0, (SeekOrigin)3); }); + Assert.ThrowsAny(() => { stream.Seek(0, ~SeekOrigin.Begin); }); + if (CanSetLength) + { + Assert.Throws(() => { stream.SetLength(-1); }); + } + else + { + Assert.Throws(() => { stream.SetLength(0); }); + } } else { @@ -616,10 +623,11 @@ protected sealed unsafe class NativeMemoryManager : MemoryManager private readonly int _length; private IntPtr _ptr; public int PinRefCount; + private readonly string _ctorStack = Environment.StackTrace; public NativeMemoryManager(int length) => _ptr = Marshal.AllocHGlobal(_length = length); - ~NativeMemoryManager() => Assert.False(true, $"{nameof(NativeMemoryManager)} being finalized"); + ~NativeMemoryManager() => Assert.False(true, $"{nameof(NativeMemoryManager)} being finalized. Created at {_ctorStack}"); public override Memory Memory => CreateMemory(_length); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/MultipartContent.cs b/src/libraries/System.Net.Http/src/System/Net/Http/MultipartContent.cs index 863d7ab9ec3dc4b75213d0ebe5241f5809449b74..e82af6762b9c8bddba6a2940d2c8cc827c56a2a1 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/MultipartContent.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/MultipartContent.cs @@ -490,6 +490,12 @@ public override int Read(byte[] buffer, int offset, int count) } } + public override int ReadByte() + { + Span buffer = stackalloc byte[1]; + return Read(buffer) == 1 ? buffer[0] : -1; + } + public override int Read(Span buffer) { if (buffer.Length == 0) @@ -632,6 +638,8 @@ public override long Seek(long offset, SeekOrigin origin) public override long Length => _length; public override void Flush() { } + public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; + public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override void Write(ReadOnlySpan buffer) { throw new NotSupportedException(); } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/StreamContent.cs b/src/libraries/System.Net.Http/src/System/Net/Http/StreamContent.cs index 8be52a024a95b7ebe5c44df7a88793d13fb30eae..c5d0366690fd654f9bfe7cfddfd734512dceabf1 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/StreamContent.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/StreamContent.cs @@ -138,60 +138,44 @@ private void PrepareContent() private sealed class ReadOnlyStream : DelegatingStream { - public override bool CanWrite + public ReadOnlyStream(Stream innerStream) : base(innerStream) { - get { return false; } } - public override int WriteTimeout - { - get { throw new NotSupportedException(SR.net_http_content_readonly_stream); } - set { throw new NotSupportedException(SR.net_http_content_readonly_stream); } - } + public override bool CanWrite => false; - public ReadOnlyStream(Stream innerStream) - : base(innerStream) - { - } + public override void Flush() { } - public override void Flush() - { + public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + public override void SetLength(long value) => throw new NotSupportedException(SR.net_http_content_readonly_stream); - } - public override Task FlushAsync(CancellationToken cancellationToken) - { + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => throw new NotSupportedException(SR.net_http_content_readonly_stream); - } - public override void SetLength(long value) - { + public override void EndWrite(IAsyncResult asyncResult) => throw new NotSupportedException(SR.net_http_content_readonly_stream); - } - public override void Write(byte[] buffer, int offset, int count) - { + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(SR.net_http_content_readonly_stream); - } - public override void Write(ReadOnlySpan buffer) - { + public override void Write(ReadOnlySpan buffer) => throw new NotSupportedException(SR.net_http_content_readonly_stream); - } - public override void WriteByte(byte value) - { + public override void WriteByte(byte value) => throw new NotSupportedException(SR.net_http_content_readonly_stream); - } - public override Task WriteAsync(byte[] buffer, int offset, int count, Threading.CancellationToken cancellationToken) - { + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new NotSupportedException(SR.net_http_content_readonly_stream); - } - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => throw new NotSupportedException(SR.net_http_content_readonly_stream); + + public override int WriteTimeout + { + get => throw new InvalidOperationException(SR.net_http_content_readonly_stream); + set => throw new InvalidOperationException(SR.net_http_content_readonly_stream); } } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs index dd6edac95f0c9ec08ffa3e183df79cc7c4bbe708..df9b4d7c08662a773810c8909b9efe18143ac635 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs @@ -4,14 +4,20 @@ using System.Buffers; using System.Collections.Generic; using System.IO; +using System.IO.Tests; using System.Threading; using System.Threading.Tasks; using Xunit; namespace System.Net.Http.Functional.Tests { - public class ReadOnlyMemoryContentTest + public class ReadOnlyMemoryContentTest : StandaloneStreamConformanceTests { + protected override Task CreateReadOnlyStreamCore(byte[]? initialData) => new ReadOnlyMemoryContent(initialData).ReadAsStreamAsync(); + protected override Task CreateWriteOnlyStreamCore(byte[]? initialData) => Task.FromResult(null); + protected override Task CreateReadWriteStreamCore(byte[]? initialData) => Task.FromResult(null); + protected override bool CanSetLength => false; + public static IEnumerable ContentLengthsAndUseArrays() { foreach (int length in new[] { 0, 1, 4096 }) @@ -55,8 +61,8 @@ public static IEnumerable UseArraysAndReadStreamAsync() [MemberData(nameof(ContentLengthsAndUseArrays))] public void ContentLength_LengthMatchesArrayLength(int contentLength, bool useArray) { - Memory memory; - using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out _, out IMemoryOwner memoryOwner)) + using (memoryOwner) { Assert.Equal(contentLength, content.Headers.ContentLength); } @@ -67,32 +73,30 @@ public void ContentLength_LengthMatchesArrayLength(int contentLength, bool useAr public async Task ReadAsStreamAsync_TrivialMembersHaveExpectedValuesAndBehavior(bool useArray, bool readStreamAsync) { const int ContentLength = 42; - Memory memory; - - using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) + using (Stream stream = await content.ReadAsStreamAsync(readStreamAsync)) { - using (Stream stream = await content.ReadAsStreamAsync(readStreamAsync)) - { - // property values - Assert.Equal(ContentLength, stream.Length); - Assert.Equal(0, stream.Position); - Assert.True(stream.CanRead); - Assert.True(stream.CanSeek); - Assert.False(stream.CanWrite); - - // not supported - Assert.Throws(() => stream.SetLength(12345)); - Assert.Throws(() => stream.WriteByte(0)); - Assert.Throws(() => stream.Write(new byte[1], 0, 1)); - Assert.Throws(() => stream.Write(new ReadOnlySpan(new byte[1]))); - await Assert.ThrowsAsync(async () => await stream.WriteAsync(new byte[1], 0, 1)); - await Assert.ThrowsAsync(async () => await stream.WriteAsync(new ReadOnlyMemory(new byte[1]))); - - // nops - stream.Flush(); - await stream.FlushAsync(); - } + // property values + Assert.Equal(ContentLength, stream.Length); + Assert.Equal(0, stream.Position); + Assert.True(stream.CanRead); + Assert.True(stream.CanSeek); + Assert.False(stream.CanWrite); + + // not supported + Assert.Throws(() => stream.SetLength(12345)); + Assert.Throws(() => stream.WriteByte(0)); + Assert.Throws(() => stream.Write(new byte[1], 0, 1)); + Assert.Throws(() => stream.Write(new ReadOnlySpan(new byte[1]))); + await Assert.ThrowsAsync(async () => await stream.WriteAsync(new byte[1], 0, 1)); + await Assert.ThrowsAsync(async () => await stream.WriteAsync(new ReadOnlyMemory(new byte[1]))); + + // nops + stream.Flush(); + await stream.FlushAsync(); } } @@ -101,61 +105,59 @@ public async Task ReadAsStreamAsync_TrivialMembersHaveExpectedValuesAndBehavior( public async Task ReadAsStreamAsync_Seek(bool useArray, bool readStreamAsync) { const int ContentLength = 42; - Memory memory; - - using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) + using (Stream s = await content.ReadAsStreamAsync(readStreamAsync)) { - using (Stream s = await content.ReadAsStreamAsync(readStreamAsync)) + foreach (int pos in new[] { 0, ContentLength / 2, ContentLength - 1 }) { - foreach (int pos in new[] { 0, ContentLength / 2, ContentLength - 1 }) - { - s.Position = pos; - Assert.Equal(pos, s.Position); - Assert.Equal(memory.Span[pos], s.ReadByte()); - } - - foreach (int pos in new[] { 0, ContentLength / 2, ContentLength - 1 }) - { - Assert.Equal(0, s.Seek(0, SeekOrigin.Begin)); - Assert.Equal(memory.Span[0], s.ReadByte()); - } - - Assert.Equal(ContentLength, s.Seek(0, SeekOrigin.End)); - Assert.Equal(s.Position, s.Length); - Assert.Equal(-1, s.ReadByte()); + s.Position = pos; + Assert.Equal(pos, s.Position); + Assert.Equal(memory.Span[pos], s.ReadByte()); + } - Assert.Equal(0, s.Seek(-ContentLength, SeekOrigin.End)); - Assert.Equal(0, s.Position); + foreach (int pos in new[] { 0, ContentLength / 2, ContentLength - 1 }) + { + Assert.Equal(0, s.Seek(0, SeekOrigin.Begin)); Assert.Equal(memory.Span[0], s.ReadByte()); - - s.Position = 0; - Assert.Equal(0, s.Seek(0, SeekOrigin.Current)); - Assert.Equal(0, s.Position); - - Assert.Equal(1, s.Seek(1, SeekOrigin.Current)); - Assert.Equal(1, s.Position); - Assert.Equal(memory.Span[1], s.ReadByte()); - Assert.Equal(2, s.Position); - Assert.Equal(3, s.Seek(1, SeekOrigin.Current)); - Assert.Equal(1, s.Seek(-2, SeekOrigin.Current)); - - Assert.Equal(int.MaxValue, s.Seek(int.MaxValue, SeekOrigin.Begin)); - Assert.Equal(int.MaxValue, s.Position); - Assert.Equal(int.MaxValue, s.Seek(0, SeekOrigin.Current)); - Assert.Equal(int.MaxValue, s.Position); - Assert.Equal(int.MaxValue, s.Seek(int.MaxValue - ContentLength, SeekOrigin.End)); - Assert.Equal(int.MaxValue, s.Position); - Assert.Equal(-1, s.ReadByte()); - Assert.Equal(int.MaxValue, s.Position); - - Assert.Throws("value", () => s.Position = -1); - Assert.Throws(() => s.Seek(-1, SeekOrigin.Begin)); - - AssertExtensions.Throws("value", () => s.Position = (long)int.MaxValue + 1); - AssertExtensions.Throws("offset", () => s.Seek((long)int.MaxValue + 1, SeekOrigin.Begin)); - - Assert.ThrowsAny(() => s.Seek(0, (SeekOrigin)42)); } + + Assert.Equal(ContentLength, s.Seek(0, SeekOrigin.End)); + Assert.Equal(s.Position, s.Length); + Assert.Equal(-1, s.ReadByte()); + + Assert.Equal(0, s.Seek(-ContentLength, SeekOrigin.End)); + Assert.Equal(0, s.Position); + Assert.Equal(memory.Span[0], s.ReadByte()); + + s.Position = 0; + Assert.Equal(0, s.Seek(0, SeekOrigin.Current)); + Assert.Equal(0, s.Position); + + Assert.Equal(1, s.Seek(1, SeekOrigin.Current)); + Assert.Equal(1, s.Position); + Assert.Equal(memory.Span[1], s.ReadByte()); + Assert.Equal(2, s.Position); + Assert.Equal(3, s.Seek(1, SeekOrigin.Current)); + Assert.Equal(1, s.Seek(-2, SeekOrigin.Current)); + + Assert.Equal(int.MaxValue, s.Seek(int.MaxValue, SeekOrigin.Begin)); + Assert.Equal(int.MaxValue, s.Position); + Assert.Equal(int.MaxValue, s.Seek(0, SeekOrigin.Current)); + Assert.Equal(int.MaxValue, s.Position); + Assert.Equal(int.MaxValue, s.Seek(int.MaxValue - ContentLength, SeekOrigin.End)); + Assert.Equal(int.MaxValue, s.Position); + Assert.Equal(-1, s.ReadByte()); + Assert.Equal(int.MaxValue, s.Position); + + Assert.Throws("value", () => s.Position = -1); + Assert.Throws(() => s.Seek(-1, SeekOrigin.Begin)); + + AssertExtensions.Throws("value", () => s.Position = (long)int.MaxValue + 1); + AssertExtensions.Throws("offset", () => s.Seek((long)int.MaxValue + 1, SeekOrigin.Begin)); + + Assert.ThrowsAny(() => s.Seek(0, (SeekOrigin)42)); } } @@ -163,20 +165,17 @@ public async Task ReadAsStreamAsync_Seek(bool useArray, bool readStreamAsync) [MemberData(nameof(ContentLengthsAndUseArraysAndReadStreamAsync))] public async Task ReadAsStreamAsync_ReadByte_MatchesInput(int contentLength, bool useArray, bool readStreamAsync) { - Memory memory; - - using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) + using (Stream stream = await content.ReadAsStreamAsync(readStreamAsync)) { - using (Stream stream = await content.ReadAsStreamAsync(readStreamAsync)) + for (int i = 0; i < contentLength; i++) { - for (int i = 0; i < contentLength; i++) - { - Assert.Equal(memory.Span[i], stream.ReadByte()); - Assert.Equal(i + 1, stream.Position); - } - Assert.Equal(-1, stream.ReadByte()); - Assert.Equal(stream.Length, stream.Position); + Assert.Equal(memory.Span[i], stream.ReadByte()); + Assert.Equal(i + 1, stream.Position); } + Assert.Equal(-1, stream.ReadByte()); + Assert.Equal(stream.Length, stream.Position); } } @@ -185,26 +184,24 @@ public async Task ReadAsStreamAsync_ReadByte_MatchesInput(int contentLength, boo public async Task ReadAsStreamAsync_Read_InvalidArguments(bool useArray, bool readStreamAsync) { const int ContentLength = 42; - Memory memory; - - using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) + using (Stream stream = await content.ReadAsStreamAsync(readStreamAsync)) { - using (Stream stream = await content.ReadAsStreamAsync(readStreamAsync)) - { - AssertExtensions.Throws("buffer", () => stream.Read(null, 0, 0)); - AssertExtensions.Throws("buffer", () => { stream.ReadAsync(null, 0, 0); }); + AssertExtensions.Throws("buffer", () => stream.Read(null, 0, 0)); + AssertExtensions.Throws("buffer", () => { stream.ReadAsync(null, 0, 0); }); - AssertExtensions.Throws("offset", () => stream.Read(new byte[1], -1, 1)); - AssertExtensions.Throws("offset", () => stream.Read(new byte[1], -1, 1)); + AssertExtensions.Throws("offset", () => stream.Read(new byte[1], -1, 1)); + AssertExtensions.Throws("offset", () => stream.Read(new byte[1], -1, 1)); - AssertExtensions.Throws("count", () => stream.Read(new byte[1], 0, -1)); - AssertExtensions.Throws("count", () => stream.Read(new byte[1], 0, -1)); + AssertExtensions.Throws("count", () => stream.Read(new byte[1], 0, -1)); + AssertExtensions.Throws("count", () => stream.Read(new byte[1], 0, -1)); - Assert.ThrowsAny(() => { stream.ReadAsync(new byte[1], 2, 0); }); - Assert.ThrowsAny(() => { stream.ReadAsync(new byte[1], 2, 0); }); - Assert.ThrowsAny(() => { stream.ReadAsync(new byte[1], 0, 2); }); - Assert.ThrowsAny(() => { stream.ReadAsync(new byte[1], 0, 2); }); - } + Assert.ThrowsAny(() => { stream.ReadAsync(new byte[1], 2, 0); }); + Assert.ThrowsAny(() => { stream.ReadAsync(new byte[1], 2, 0); }); + Assert.ThrowsAny(() => { stream.ReadAsync(new byte[1], 0, 2); }); + Assert.ThrowsAny(() => { stream.ReadAsync(new byte[1], 0, 2); }); } } @@ -233,8 +230,8 @@ public async Task ReadAsStreamAsync_ReadMultipleBytes_MatchesInput(int mode, boo { const int ContentLength = 1024; - Memory memory; - using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) { var buffer = new byte[3]; @@ -274,8 +271,8 @@ public async Task ReadAsStreamAsync_ReadWithCancelableToken_MatchesInput(bool us { const int ContentLength = 100; - Memory memory; - using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) { var buffer = new byte[1]; var cts = new CancellationTokenSource(); @@ -307,9 +304,8 @@ public async Task ReadAsStreamAsync_ReadWithCanceledToken_MatchesInput(bool useA { const int ContentLength = 2; - Memory memory; - - using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) { using (Stream stream = await content.ReadAsStreamAsync(readStreamAsync)) { @@ -324,10 +320,9 @@ public async Task ReadAsStreamAsync_ReadWithCanceledToken_MatchesInput(bool useA [MemberData(nameof(ContentLengthsAndUseArrays))] public async Task CopyToAsync_AllContentCopied(int contentLength, bool useArray) { - Memory memory; - using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) { - var destination = new MemoryStream(); await content.CopyToAsync(destination); @@ -339,10 +334,9 @@ public async Task CopyToAsync_AllContentCopied(int contentLength, bool useArray) [MemberData(nameof(ContentLengthsAndUseArraysAndReadStreamAsync))] public async Task ReadAsStreamAsync_CopyTo_AllContentCopied(int contentLength, bool useArray, bool readStreamAsync) { - Memory memory; - using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) { - var destination = new MemoryStream(); using (Stream s = await content.ReadAsStreamAsync(readStreamAsync)) { @@ -358,8 +352,8 @@ public async Task ReadAsStreamAsync_CopyTo_AllContentCopied(int contentLength, b public async Task ReadAsStreamAsync_CopyTo_InvalidArguments(bool useArray, bool readStreamAsync) { const int ContentLength = 42; - Memory memory; - using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(ContentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) { using (Stream s = await content.ReadAsStreamAsync(readStreamAsync)) { @@ -384,10 +378,9 @@ public async Task ReadAsStreamAsync_CopyTo_InvalidArguments(bool useArray, bool [MemberData(nameof(ContentLengthsAndUseArraysAndReadStreamAsync))] public async Task ReadAsStreamAsync_CopyToAsync_AllContentCopied(int contentLength, bool useArray, bool readStreamAsync) { - Memory memory; - using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out memory, out IMemoryOwner memoryOwner)) + using (ReadOnlyMemoryContent content = CreateContent(contentLength, useArray, out Memory memory, out IMemoryOwner memoryOwner)) + using (memoryOwner) { - var destination = new MemoryStream(); using (Stream s = await content.ReadAsStreamAsync(readStreamAsync)) { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/StreamContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/StreamContentTest.cs index 464378cf61228f7a8ddd1c6493b8289b70e59db9..9d432aea93eef1532f176e8acd5b3092a8701e56 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/StreamContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/StreamContentTest.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.IO.Tests; using System.Threading.Tasks; using Xunit; @@ -10,31 +11,19 @@ namespace System.Net.Http.Functional.Tests { - public class StreamContentTest + public class StreamContentTest : StandaloneStreamConformanceTests { - private readonly ITestOutputHelper _output; - - public StreamContentTest(ITestOutputHelper output) - { - _output = output; - } + protected override Task CreateReadOnlyStreamCore(byte[]? initialData) => initialData is null ? Task.FromResult(null) : new StreamContent(new MemoryStream(initialData)).ReadAsStreamAsync(); + protected override Task CreateWriteOnlyStreamCore(byte[]? initialData) => Task.FromResult(null); + protected override Task CreateReadWriteStreamCore(byte[]? initialData) => Task.FromResult(null); + protected override bool CanSetLength => false; [Fact] - public void Ctor_NullStream_ThrowsArgumentNullException() + public void Ctor_InvalidArguments_Throws() { Assert.Throws(() => new StreamContent(null)); - } - - [Fact] - public void Ctor_ZeroBufferSize_ThrowsArgumentOutOfRangeException() - { - Assert.Throws(() => new StreamContent(new MemoryStream(), 0)); - } - - [Fact] - public void Ctor_NullStreamAndZeroBufferSize_ThrowsArgumentNullException() - { Assert.Throws(() => new StreamContent(null, 0)); + Assert.Throws(() => new StreamContent(new MemoryStream(), 0)); } [Fact] @@ -207,80 +196,6 @@ public async Task ContentReadStream_GetPropertyPartiallyConsumed_ReturnOriginalS Assert.NotSame(source, stream); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task ContentReadStream_CheckResultProperties_ValuesRepresentReadOnlyStream(bool readStreamAsync) - { - byte[] data = new byte[10]; - for (int i = 0; i < data.Length; i++) - { - data[i] = (byte)i; - } - - var source = new MockStream(data); - - var content = new StreamContent(source); - Stream contentReadStream = await content.ReadAsStreamAsync(readStreamAsync); - - // The following checks verify that the stream returned passes all read-related properties to the - // underlying MockStream and throws when using write-related members. - - Assert.False(contentReadStream.CanWrite); - Assert.True(contentReadStream.CanRead); - Assert.Equal(source.Length, contentReadStream.Length); - - Assert.Equal(1, source.CanSeekCount); - _output.WriteLine(contentReadStream.CanSeek.ToString()); - Assert.Equal(2, source.CanSeekCount); - - contentReadStream.Position = 3; // No exception. - Assert.Equal(3, contentReadStream.Position); - - byte byteOnIndex3 = (byte)contentReadStream.ReadByte(); - Assert.Equal(data[3], byteOnIndex3); - - byte[] byteOnIndex4 = new byte[1]; - int result = await contentReadStream.ReadAsync(byteOnIndex4, 0, 1); - Assert.Equal(1, result); - - Assert.Equal(data[4], byteOnIndex4[0]); - - byte[] byteOnIndex5 = new byte[1]; - Assert.Equal(1, contentReadStream.Read(byteOnIndex5, 0, 1)); - Assert.Equal(data[5], byteOnIndex5[0]); - - byte[] byteOnIndex6 = new byte[1]; - Assert.Equal(1, contentReadStream.Read(new Span(byteOnIndex6, 0, 1))); - Assert.Equal(data[6], byteOnIndex6[0]); - - contentReadStream.ReadTimeout = 123; - Assert.Equal(123, source.ReadTimeout); - Assert.Equal(123, contentReadStream.ReadTimeout); - - Assert.Equal(0, source.CanTimeoutCount); - _output.WriteLine(contentReadStream.CanTimeout.ToString()); - Assert.Equal(1, source.CanTimeoutCount); - - Assert.Equal(0, source.SeekCount); - contentReadStream.Seek(0, SeekOrigin.Begin); - Assert.Equal(1, source.SeekCount); - - Assert.Throws(() => { contentReadStream.WriteTimeout = 5; }); - Assert.Throws(() => contentReadStream.WriteTimeout.ToString()); - Assert.Throws(() => contentReadStream.Flush()); - Assert.Throws(() => contentReadStream.SetLength(1)); - Assert.Throws(() => contentReadStream.Write(null, 0, 0)); - Assert.Throws(() => contentReadStream.Write(new Span(Array.Empty()))); - Assert.Throws(() => contentReadStream.WriteByte(1)); - - Assert.Equal(0, source.DisposeCount); - contentReadStream.Dispose(); - Assert.Equal(1, source.DisposeCount); - } - - #region Helper methods - private class MockStream : MemoryStream { private bool _canSeek; @@ -362,7 +277,5 @@ private void SetBufferSize(int count) } } } - - #endregion } }