fix h/3 ResponseContent with large buffer (#56892)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Thu, 5 Aug 2021 18:23:17 +0000 (11:23 -0700)
committerGitHub <noreply@github.com>
Thu, 5 Aug 2021 18:23:17 +0000 (11:23 -0700)
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs
src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs

index 45b7447..8ec6cdb 100644 (file)
@@ -1028,6 +1028,11 @@ namespace System.Net.Http
                         totalBytesRead += bytesRead;
                         _responseDataPayloadRemaining -= bytesRead;
                         buffer = buffer.Slice(bytesRead);
+
+                        if (_responseDataPayloadRemaining == 0)
+                        {
+                            break;
+                        }
                     }
                 }
 
@@ -1085,6 +1090,11 @@ namespace System.Net.Http
                         totalBytesRead += bytesRead;
                         _responseDataPayloadRemaining -= bytesRead;
                         buffer = buffer.Slice(bytesRead);
+
+                        if (_responseDataPayloadRemaining == 0)
+                        {
+                            break;
+                        }
                     }
                 }
 
index aced777..4132bce 100644 (file)
@@ -8,6 +8,7 @@ using System.Linq;
 using System.Net.Security;
 using System.Net.Sockets;
 using System.Security.Authentication;
+using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
 using System.Threading;
@@ -801,5 +802,59 @@ namespace System.Net.Quic.Tests
                 await Assert.ThrowsAsync<QuicOperationAbortedException>(() => serverStream.ReadAsync(buffer).AsTask());
             }).WaitAsync(TimeSpan.FromMilliseconds(PassingTestTimeoutMilliseconds));
         }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public async Task BigWrite_SmallRead_Success(bool closeWithData)
+        {
+            const int size = 100;
+            (QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection();
+            using (clientConnection)
+            using (serverConnection)
+            {
+                byte[] buffer = new byte[1] { 42 };
+
+                QuicStream clientStream = clientConnection.OpenBidirectionalStream();
+                Task<QuicStream> t = serverConnection.AcceptStreamAsync().AsTask();
+                await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientStream.WriteAsync(buffer).AsTask(), t, PassingTestTimeoutMilliseconds);
+                QuicStream serverStream = t.Result;
+                Assert.Equal(1, await serverStream.ReadAsync(buffer));
+
+                // streams are new established and in good shape.
+                using (clientStream)
+                using (serverStream)
+                {
+                    byte[] expected = RandomNumberGenerator.GetBytes(size);
+                    byte[] actual = new byte[size];
+
+                    // should be small enough to fit.
+                    await serverStream.WriteAsync(expected, closeWithData);
+
+                    // Add delay to have chance to receive the 100b block before ReadAsync starts.
+                    await Task.Delay(10);
+                    int remaining = size;
+                    int readLength;
+                    while (remaining > 0)
+                    {
+                        readLength = await clientStream.ReadAsync(new Memory<byte>(actual, size - remaining, 1));
+                        Assert.Equal(1, readLength);
+                        remaining--;
+                    }
+
+                    Assert.Equal(expected, actual);
+
+                    if (!closeWithData)
+                    {
+                        serverStream.Shutdown();
+                    }
+
+                    readLength = await clientStream.ReadAsync(actual);
+                    Assert.Equal(0, readLength);
+
+                    Assert.Equal(expected, actual);
+                }
+            }
+        }
     }
 }