From d890c2f1325c045da134a70e030d8ecee318fc6e Mon Sep 17 00:00:00 2001 From: Catalin <catalin.nitulescu@whise.eu> Date: Thu, 20 Feb 2025 22:53:18 +0200 Subject: [PATCH 1/2] Issue #60416 - Fix and coverage test Correction of BufferedReadStream's Position property setter. --- .../WebUtilities/src/BufferedReadStream.cs | 4 +- .../WebUtilities/test/MultipartReaderTests.cs | 72 ++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/Http/WebUtilities/src/BufferedReadStream.cs b/src/Http/WebUtilities/src/BufferedReadStream.cs index 1860e607b9f2..7d9221b915d8 100644 --- a/src/Http/WebUtilities/src/BufferedReadStream.cs +++ b/src/Http/WebUtilities/src/BufferedReadStream.cs @@ -108,8 +108,8 @@ public override long Position if (innerOffset <= _bufferCount) { // Yes, just skip some of the buffered data - _bufferOffset += innerOffset; - _bufferCount -= innerOffset; + _bufferOffset += _bufferCount - innerOffset; + _bufferCount = innerOffset; } else { diff --git a/src/Http/WebUtilities/test/MultipartReaderTests.cs b/src/Http/WebUtilities/test/MultipartReaderTests.cs index 9055e14a10aa..1f0d05901b3a 100644 --- a/src/Http/WebUtilities/test/MultipartReaderTests.cs +++ b/src/Http/WebUtilities/test/MultipartReaderTests.cs @@ -80,7 +80,6 @@ public class MultipartReaderTests "<!DOCTYPE html><title>Content of a.html.</title>\r\n" + "\r\n" + "--9051914041544843365972754266--\r\n"; - private const string TwoPartBodyIncompleteBuffer = "--9051914041544843365972754266\r\n" + "Content-Disposition: form-data; name=\"text\"\r\n" + @@ -93,6 +92,26 @@ public class MultipartReaderTests "Content of a.txt.\r\n" + "\r\n" + "--9051914041544843365"; + private const string ThreePartBodyWithMixedFiles = +"--9051914041544843365972754266\r\n" + +"Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\"\r\n" + +"Content-Type: text/plain\r\n" + +"\r\n" + +"Content of a.txt.\r\n" + +"\r\n" + +"--9051914041544843365972754266\r\n" + +"Content-Disposition: form-data; name=\"file2\"; filename=\"a.html\"\r\n" + +"Content-Type: text/html\r\n" + +"\r\n" + +"<!DOCTYPE html><title>Content of a.html.</title>\r\n" + +"\r\n" + +"--9051914041544843365972754266\r\n" + +"Content-Disposition: form-data; name=\"file3\"; filename=\"a.json\"\r\n" + +"Content-Type: application/json\r\n" + +"\r\n" + +"{ \"Text\": \"Content of a.json.\" }\r\n" + +"\r\n" + +"--9051914041544843365972754266--\r\n"; private static MemoryStream MakeStream(string text) { @@ -315,6 +334,57 @@ public void MultipartReader_BufferSizeMustBeLargerThanBoundary_Throws() }); } + [Fact] + public async Task MultipartReader_ReadMultipartBodyWithFilesForDeferredCopy_Success() + { + var stream = MakeStream(ThreePartBodyWithMixedFiles); + var reader = new MultipartReader(Boundary, stream); + + var section = await reader.ReadNextSectionAsync(); + Assert.NotNull(section); + Assert.Equal(2, section.Headers.Count); + Assert.Equal("form-data; name=\"file1\"; filename=\"a.txt\"", section.Headers["Content-Disposition"][0]); + Assert.Equal("text/plain", section.Headers["Content-Type"][0]); + var stream1 = section.Body; + + section = await reader.ReadNextSectionAsync(); + Assert.NotNull(section); + Assert.Equal(2, section.Headers.Count); + Assert.Equal("form-data; name=\"file2\"; filename=\"a.html\"", section.Headers["Content-Disposition"][0]); + Assert.Equal("text/html", section.Headers["Content-Type"][0]); + var stream2 = section.Body; + + section = await reader.ReadNextSectionAsync(); + Assert.NotNull(section); + Assert.Equal(2, section.Headers.Count); + Assert.Equal("form-data; name=\"file3\"; filename=\"a.json\"", section.Headers["Content-Disposition"][0]); + Assert.Equal("application/json", section.Headers["Content-Type"][0]); + var stream3 = section.Body; + + Assert.Null(await reader.ReadNextSectionAsync()); + + Assert.True(stream1.CanSeek); + Assert.Equal(0, stream1.Seek(0, SeekOrigin.Begin)); + var buffer = new MemoryStream(); + var exception = await Record.ExceptionAsync(() => stream1.CopyToAsync(buffer)); + Assert.Null(exception); + Assert.Equal("Content of a.txt.\r\n", Encoding.ASCII.GetString(buffer.ToArray())); + + Assert.True(stream2.CanSeek); + Assert.Equal(0, stream2.Seek(0, SeekOrigin.Begin)); + buffer = new MemoryStream(); + exception = await Record.ExceptionAsync(() => stream2.CopyToAsync(buffer)); + Assert.Null(exception); + Assert.Equal("<!DOCTYPE html><title>Content of a.html.</title>\r\n", Encoding.ASCII.GetString(buffer.ToArray())); + + Assert.True(stream3.CanSeek); + Assert.Equal(0, stream3.Seek(0, SeekOrigin.Begin)); + buffer = new MemoryStream(); + exception = await Record.ExceptionAsync(() => stream3.CopyToAsync(buffer)); + Assert.Null(exception); + Assert.Equal("{ \"Text\": \"Content of a.json.\" }\r\n", Encoding.ASCII.GetString(buffer.ToArray())); + } + [Fact] public async Task MultipartReader_TwoPartBodyIncompleteBuffer_TwoSectionsReadSuccessfullyThirdSectionThrows() { From 95a4eee4b98926c42c1df78b7e348171f0fc7460 Mon Sep 17 00:00:00 2001 From: Catalin <catalin.nitulescu@whise.eu> Date: Fri, 21 Feb 2025 09:50:49 +0200 Subject: [PATCH 2/2] Corrected test case --- .../WebUtilities/test/MultipartReaderTests.cs | 44 +++---------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/src/Http/WebUtilities/test/MultipartReaderTests.cs b/src/Http/WebUtilities/test/MultipartReaderTests.cs index 1f0d05901b3a..f97045e07b01 100644 --- a/src/Http/WebUtilities/test/MultipartReaderTests.cs +++ b/src/Http/WebUtilities/test/MultipartReaderTests.cs @@ -92,26 +92,6 @@ public class MultipartReaderTests "Content of a.txt.\r\n" + "\r\n" + "--9051914041544843365"; - private const string ThreePartBodyWithMixedFiles = -"--9051914041544843365972754266\r\n" + -"Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\"\r\n" + -"Content-Type: text/plain\r\n" + -"\r\n" + -"Content of a.txt.\r\n" + -"\r\n" + -"--9051914041544843365972754266\r\n" + -"Content-Disposition: form-data; name=\"file2\"; filename=\"a.html\"\r\n" + -"Content-Type: text/html\r\n" + -"\r\n" + -"<!DOCTYPE html><title>Content of a.html.</title>\r\n" + -"\r\n" + -"--9051914041544843365972754266\r\n" + -"Content-Disposition: form-data; name=\"file3\"; filename=\"a.json\"\r\n" + -"Content-Type: application/json\r\n" + -"\r\n" + -"{ \"Text\": \"Content of a.json.\" }\r\n" + -"\r\n" + -"--9051914041544843365972754266--\r\n"; private static MemoryStream MakeStream(string text) { @@ -337,9 +317,11 @@ public void MultipartReader_BufferSizeMustBeLargerThanBoundary_Throws() [Fact] public async Task MultipartReader_ReadMultipartBodyWithFilesForDeferredCopy_Success() { - var stream = MakeStream(ThreePartBodyWithMixedFiles); + var stream = MakeStream(ThreePartBody); var reader = new MultipartReader(Boundary, stream); + await reader.ReadNextSectionAsync(); // skip text field section + var section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(2, section.Headers.Count); @@ -354,35 +336,19 @@ public async Task MultipartReader_ReadMultipartBodyWithFilesForDeferredCopy_Succ Assert.Equal("text/html", section.Headers["Content-Type"][0]); var stream2 = section.Body; - section = await reader.ReadNextSectionAsync(); - Assert.NotNull(section); - Assert.Equal(2, section.Headers.Count); - Assert.Equal("form-data; name=\"file3\"; filename=\"a.json\"", section.Headers["Content-Disposition"][0]); - Assert.Equal("application/json", section.Headers["Content-Type"][0]); - var stream3 = section.Body; - Assert.Null(await reader.ReadNextSectionAsync()); Assert.True(stream1.CanSeek); Assert.Equal(0, stream1.Seek(0, SeekOrigin.Begin)); var buffer = new MemoryStream(); - var exception = await Record.ExceptionAsync(() => stream1.CopyToAsync(buffer)); - Assert.Null(exception); + await stream1.CopyToAsync(buffer); Assert.Equal("Content of a.txt.\r\n", Encoding.ASCII.GetString(buffer.ToArray())); Assert.True(stream2.CanSeek); Assert.Equal(0, stream2.Seek(0, SeekOrigin.Begin)); buffer = new MemoryStream(); - exception = await Record.ExceptionAsync(() => stream2.CopyToAsync(buffer)); - Assert.Null(exception); + await stream2.CopyToAsync(buffer); Assert.Equal("<!DOCTYPE html><title>Content of a.html.</title>\r\n", Encoding.ASCII.GetString(buffer.ToArray())); - - Assert.True(stream3.CanSeek); - Assert.Equal(0, stream3.Seek(0, SeekOrigin.Begin)); - buffer = new MemoryStream(); - exception = await Record.ExceptionAsync(() => stream3.CopyToAsync(buffer)); - Assert.Null(exception); - Assert.Equal("{ \"Text\": \"Content of a.json.\" }\r\n", Encoding.ASCII.GetString(buffer.ToArray())); } [Fact]