Skip to content

Commit 3709eda

Browse files
authored
Fix HTTP/2 stream output flow control abort error (#23727)
* Fix stream output flow control abort error * Clean up * Clean up * Add timeouts
1 parent cc3c1d0 commit 3709eda

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamOutputFlowControl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public void Abort()
9494
if (_currentConnectionLevelAwaitable != null &&
9595
_currentConnectionLevelAwaitable.Version == _currentConnectionLevelAwaitableVersion)
9696
{
97-
_currentConnectionLevelAwaitable.SetResult(null);
97+
_currentConnectionLevelAwaitable.TrySetResult(null);
9898
}
9999
}
100100

src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,68 @@ await ExpectAsync(Http2FrameType.DATA,
15941594
await WaitForAllStreamsAsync();
15951595
}
15961596

1597+
[Fact]
1598+
public async Task OutputFlowControl_ConnectionAndRequestAborted_NoException()
1599+
{
1600+
// Ensure the stream window size is bigger than the connection window size
1601+
_clientSettings.InitialWindowSize = _clientSettings.InitialWindowSize * 2;
1602+
1603+
var connectionAbortedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
1604+
var requestAbortedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
1605+
1606+
await InitializeConnectionAsync(async context =>
1607+
{
1608+
// Exceed connection window size
1609+
await context.Response.WriteAsync(new string('!', 65536));
1610+
1611+
await connectionAbortedTcs.Task;
1612+
1613+
try
1614+
{
1615+
context.Abort();
1616+
requestAbortedTcs.SetResult();
1617+
}
1618+
catch (Exception ex)
1619+
{
1620+
requestAbortedTcs.SetException(ex);
1621+
}
1622+
}).DefaultTimeout();
1623+
1624+
await StartStreamAsync(1, _browserRequestHeaders, endStream: true).DefaultTimeout();
1625+
1626+
await ExpectAsync(Http2FrameType.HEADERS,
1627+
withLength: 32,
1628+
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
1629+
withStreamId: 1).DefaultTimeout();
1630+
1631+
await ExpectAsync(Http2FrameType.DATA,
1632+
withLength: 16384,
1633+
withFlags: (byte)(Http2DataFrameFlags.NONE),
1634+
withStreamId: 1).DefaultTimeout();
1635+
1636+
await ExpectAsync(Http2FrameType.DATA,
1637+
withLength: 16384,
1638+
withFlags: (byte)(Http2DataFrameFlags.NONE),
1639+
withStreamId: 1).DefaultTimeout();
1640+
1641+
await ExpectAsync(Http2FrameType.DATA,
1642+
withLength: 16384,
1643+
withFlags: (byte)(Http2DataFrameFlags.NONE),
1644+
withStreamId: 1).DefaultTimeout();
1645+
1646+
await ExpectAsync(Http2FrameType.DATA,
1647+
withLength: 16383,
1648+
withFlags: (byte)(Http2DataFrameFlags.NONE),
1649+
withStreamId: 1).DefaultTimeout();
1650+
1651+
_connection.HandleReadDataRateTimeout();
1652+
1653+
connectionAbortedTcs.SetResult();
1654+
1655+
// Task completing successfully means HttpContext.Abort didn't throw
1656+
await requestAbortedTcs.Task.DefaultTimeout();
1657+
}
1658+
15971659
[Fact]
15981660
public async Task DATA_Sent_DespiteStreamOutputFlowControl_IfEmptyAndEndsStream()
15991661
{

0 commit comments

Comments
 (0)