From 5a9e891e40bba1ab43d64bd975e272547bb4a76a Mon Sep 17 00:00:00 2001 From: Korolev Dmitry Date: Mon, 6 Oct 2025 13:35:52 +0200 Subject: [PATCH 1/2] window update fix backport --- .../Core/src/Internal/Http2/Http2Connection.cs | 8 ++++++-- .../Http2/Http2ConnectionTests.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs index 05321d3e73e1..64880e0dfb0f 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs @@ -1156,8 +1156,12 @@ private Task ProcessWindowUpdateFrameAsync() { if (stream.RstStreamReceived) { - // Hard abort, do not allow any more frames on this stream. - throw CreateReceivedFrameStreamAbortedException(stream); + // WINDOW_UPDATE received after we have already processed an inbound RST_STREAM for this stream. + // RFC 7540 (Sections 5.1, 6.9) / RFC 9113 do not explicitly define semantics for WINDOW_UPDATE on a + // stream in the "closed" state due to a reset by client. We surface it as a stream error (STREAM_CLOSED) + // rather than aborting the entire connection to keep behavior deterministic and consistent with other servers. + // https://github.com/dotnet/aspnetcore/issues/63726 + throw new Http2StreamErrorException(_incomingFrame.StreamId, CoreStrings.Http2StreamAborted, Http2ErrorCode.STREAM_CLOSED); } if (!stream.TryUpdateOutputWindow(_incomingFrame.WindowUpdateSizeIncrement)) diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs index d858d0dcfd28..7a83c65d41d0 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs @@ -3584,12 +3584,10 @@ public async Task RST_STREAM_IncompleteRequest_AdditionalResetFrame_IgnoreAdditi AssertConnectionNoError(); } - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/53744")] [Fact] public async Task RST_STREAM_IncompleteRequest_AdditionalWindowUpdateFrame_ConnectionAborted() { var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var headers = new[] { new KeyValuePair(InternalHeaderNames.Method, "POST"), @@ -3597,15 +3595,17 @@ public async Task RST_STREAM_IncompleteRequest_AdditionalWindowUpdateFrame_Conne new KeyValuePair(InternalHeaderNames.Scheme, "http"), }; await InitializeConnectionAsync(context => tcs.Task); - await StartStreamAsync(1, headers, endStream: false); await SendDataAsync(1, new byte[1], endStream: false); await SendRstStreamAsync(1); await SendWindowUpdateAsync(1, 1024); - tcs.TrySetResult(); - await WaitForConnectionErrorAsync(ignoreNonGoAwayFrames: false, expectedLastStreamId: 1, - Http2ErrorCode.STREAM_CLOSED, CoreStrings.FormatHttp2ErrorStreamAborted(Http2FrameType.WINDOW_UPDATE, 1)); + await WaitForStreamErrorAsync(expectedStreamId: 1, Http2ErrorCode.STREAM_CLOSED, CoreStrings.Http2StreamAborted); + + tcs.TrySetResult(); // Don't let the response start until after the abort + + await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); + AssertConnectionNoError(); } [Fact] From 132fc55043d5084eabab0b9f6d8494c27fab2dbb Mon Sep 17 00:00:00 2001 From: Korolev Dmitry Date: Mon, 6 Oct 2025 13:37:11 +0200 Subject: [PATCH 2/2] nit --- .../test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs index 7a83c65d41d0..e289b6833dc6 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs @@ -3588,6 +3588,7 @@ public async Task RST_STREAM_IncompleteRequest_AdditionalResetFrame_IgnoreAdditi public async Task RST_STREAM_IncompleteRequest_AdditionalWindowUpdateFrame_ConnectionAborted() { var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var headers = new[] { new KeyValuePair(InternalHeaderNames.Method, "POST"), @@ -3595,6 +3596,7 @@ public async Task RST_STREAM_IncompleteRequest_AdditionalWindowUpdateFrame_Conne new KeyValuePair(InternalHeaderNames.Scheme, "http"), }; await InitializeConnectionAsync(context => tcs.Task); + await StartStreamAsync(1, headers, endStream: false); await SendDataAsync(1, new byte[1], endStream: false); await SendRstStreamAsync(1);