Explorar o código

HTTP/3: End incoming stream loop when app doesn't read to end (#33674)

James Newton-King %!s(int64=4) %!d(string=hai) anos
pai
achega
d4c2afd460

+ 5 - 0
src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs

@@ -359,6 +359,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 
                     RequestBodyPipe.Writer.Complete();
                 }
+
+                TryClose();
             }
         }
 
@@ -366,6 +368,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
         {
             if (Interlocked.Exchange(ref _isClosed, 1) == 0)
             {
+                // Wake ProcessRequestAsync loop so that it can exit.
+                Input.CancelPendingRead();
+
                 return true;
             }
 

+ 10 - 13
src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3StreamTests.cs

@@ -2186,9 +2186,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
 
             await requestStream.ExpectReceiveEndOfStream();
 
-            // TODO(JamesNK): Check for logging and error after https://github.com/dotnet/aspnetcore/issues/31970
-            // Logged without an exception.
-            // Assert.Contains(LogMessages, m => m.Message.Contains("the application completed without reading the entire request body."));
+            await requestStream.OnStreamCompletedTask.DefaultTimeout();
+
+            // TODO(JamesNK): Check for abort of request side of stream after https://github.com/dotnet/aspnetcore/issues/31970
+            Assert.Contains(LogMessages, m => m.Message.Contains("the application completed without reading the entire request body."));
 
             Assert.Equal(3, receivedHeaders.Count);
             Assert.Contains("date", receivedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
@@ -2263,9 +2264,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
 
             await requestStream.ExpectReceiveEndOfStream();
 
-            // TODO(JamesNK): Check for logging and error after https://github.com/dotnet/aspnetcore/issues/31970
-            // Logged without an exception.
-            // Assert.Contains(LogMessages, m => m.Message.Contains("the application completed without reading the entire request body."));
+            await requestStream.OnStreamCompletedTask.DefaultTimeout();
+            Assert.Contains(LogMessages, m => m.Message.Contains("the application completed without reading the entire request body."));
 
             Assert.Equal(3, receivedHeaders.Count);
             Assert.Contains("date", receivedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
@@ -2321,9 +2321,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
 
             await requestStream.ExpectReceiveEndOfStream();
 
-            // TODO(JamesNK): Check for logging and error after https://github.com/dotnet/aspnetcore/issues/31970
-            // Logged without an exception.
-            // Assert.Contains(LogMessages, m => m.Message.Contains("the application completed without reading the entire request body."));
+            await requestStream.OnStreamCompletedTask.DefaultTimeout();
+            Assert.Contains(LogMessages, m => m.Message.Contains("the application completed without reading the entire request body."));
 
             Assert.Equal(3, receivedHeaders.Count);
             Assert.Contains("date", receivedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
@@ -2512,11 +2511,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
 
             await requestStream.ExpectReceiveEndOfStream();
 
-            // TODO(JamesNK): Await the server aborting the sending half of the request stream.
-            // https://github.com/dotnet/aspnetcore/issues/33575
-            await Task.Delay(1000);
+            await requestStream.OnStreamCompletedTask.DefaultTimeout();
 
-            // Logged without an exception.
+            // TODO(JamesNK): Check for abort of request side of stream after https://github.com/dotnet/aspnetcore/issues/31970
             Assert.Contains(LogMessages, m => m.Message.Contains("the application completed without reading the entire request body."));
         }
     }

+ 7 - 0
src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3TestBase.cs

@@ -343,6 +343,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
             public void OnStreamCompleted(IHttp3Stream stream)
             {
                 _inner.OnStreamCompleted(stream);
+
+                if (_http3TestBase._runningStreams.TryGetValue(stream.StreamId, out var testStream))
+                {
+                    testStream._onStreamCompletedTcs.TrySetResult();
+                }
             }
 
             public void OnStreamConnectionError(Http3ConnectionErrorException ex)
@@ -448,6 +453,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
         public class Http3StreamBase : IProtocolErrorCodeFeature
         {
             internal TaskCompletionSource _onStreamCreatedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+            internal TaskCompletionSource _onStreamCompletedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
             internal TaskCompletionSource _onHeaderReceivedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
 
             internal DuplexPipe.DuplexPipePair _pair;
@@ -457,6 +463,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
             public long Error { get; set; }
 
             public Task OnStreamCreatedTask => _onStreamCreatedTcs.Task;
+            public Task OnStreamCompletedTask => _onStreamCompletedTcs.Task;
             public Task OnHeaderReceivedTask => _onHeaderReceivedTcs.Task;
 
             protected Task SendAsync(ReadOnlySpan<byte> span)