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]