Fix bug in graceful shutdown
This commit is contained in:
parent
bd3a8a3511
commit
62a30a731d
|
|
@ -910,8 +910,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
Input.CancelPendingRead();
|
Input.CancelPendingRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete the task waiting on all streams to finish
|
|
||||||
_streamsCompleted.TrySetResult(null);
|
if (_state != Http2ConnectionState.Open)
|
||||||
|
{
|
||||||
|
// Complete the task waiting on all streams to finish
|
||||||
|
_streamsCompleted.TrySetResult(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3324,6 +3324,54 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
VerifyGoAway(await ReceiveFrameAsync(), 0, Http2ErrorCode.NO_ERROR);
|
VerifyGoAway(await ReceiveFrameAsync(), 0, Http2ErrorCode.NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StopProcessingNextRequestSendsGracefulGOAWAYAndWaitsForStreamsToComplete()
|
||||||
|
{
|
||||||
|
var task = Task.CompletedTask;
|
||||||
|
await InitializeConnectionAsync(context => task);
|
||||||
|
|
||||||
|
// Send and receive an unblocked request
|
||||||
|
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
|
await ExpectAsync(Http2FrameType.HEADERS,
|
||||||
|
withLength: 55,
|
||||||
|
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
|
||||||
|
withStreamId: 1);
|
||||||
|
await ExpectAsync(Http2FrameType.DATA,
|
||||||
|
withLength: 0,
|
||||||
|
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
||||||
|
withStreamId: 1);
|
||||||
|
|
||||||
|
// Send a blocked request
|
||||||
|
var tcs = new TaskCompletionSource<object>(TaskContinuationOptions.RunContinuationsAsynchronously);
|
||||||
|
task = tcs.Task;
|
||||||
|
await StartStreamAsync(3, _browserRequestHeaders, endStream: false);
|
||||||
|
|
||||||
|
// Close pipe
|
||||||
|
_pair.Application.Output.Complete();
|
||||||
|
|
||||||
|
// Assert connection closed
|
||||||
|
await _closedStateReached.Task.DefaultTimeout();
|
||||||
|
VerifyGoAway(await ReceiveFrameAsync(), 3, Http2ErrorCode.NO_ERROR);
|
||||||
|
|
||||||
|
// Assert connection shutdown is still blocked
|
||||||
|
// ProcessRequestsAsync completes the connection's Input pipe
|
||||||
|
var readTask = _pair.Application.Input.ReadAsync();
|
||||||
|
_pair.Application.Input.CancelPendingRead();
|
||||||
|
var result = await readTask;
|
||||||
|
Assert.False(result.IsCompleted);
|
||||||
|
|
||||||
|
// Unblock the request and ProcessRequestsAsync
|
||||||
|
tcs.TrySetResult(null);
|
||||||
|
await _connectionTask;
|
||||||
|
|
||||||
|
// Assert connection's Input pipe is completed
|
||||||
|
readTask = _pair.Application.Input.ReadAsync();
|
||||||
|
_pair.Application.Input.CancelPendingRead();
|
||||||
|
result = await readTask;
|
||||||
|
Assert.True(result.IsCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task StopProcessingNextRequestSendsGracefulGOAWAYThenFinalGOAWAYWhenAllStreamsComplete()
|
public async Task StopProcessingNextRequestSendsGracefulGOAWAYThenFinalGOAWAYWhenAllStreamsComplete()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -964,12 +964,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ContentLength_Received_SingleDataFrameUnderSize_Reset()
|
public async Task ContentLength_Received_SingleDataFrameUnderSize_Reset()
|
||||||
{
|
{
|
||||||
// I hate doing this, but it avoids exceptions from MemoryPool.Dipose() in debug mode. The problem is since
|
|
||||||
// the stream's ProcessRequestsAsync loop is never awaited by the connection, it's not really possible to
|
|
||||||
// observe when all the blocks are returned. This can be removed after we implement graceful shutdown.
|
|
||||||
Dispose();
|
|
||||||
InitializeConnectionFields(new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(), allowLateReturn: true));
|
|
||||||
|
|
||||||
var headers = new[]
|
var headers = new[]
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
|
@ -997,12 +991,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ContentLength_Received_MultipleDataFramesOverSize_Reset()
|
public async Task ContentLength_Received_MultipleDataFramesOverSize_Reset()
|
||||||
{
|
{
|
||||||
// I hate doing this, but it avoids exceptions from MemoryPool.Dipose() in debug mode. The problem is since
|
|
||||||
// the stream's ProcessRequestsAsync loop is never awaited by the connection, it's not really possible to
|
|
||||||
// observe when all the blocks are returned. This can be removed after we implement graceful shutdown.
|
|
||||||
Dispose();
|
|
||||||
InitializeConnectionFields(new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(), allowLateReturn: true));
|
|
||||||
|
|
||||||
var headers = new[]
|
var headers = new[]
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
|
@ -1033,12 +1021,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ContentLength_Received_MultipleDataFramesUnderSize_Reset()
|
public async Task ContentLength_Received_MultipleDataFramesUnderSize_Reset()
|
||||||
{
|
{
|
||||||
// I hate doing this, but it avoids exceptions from MemoryPool.Dipose() in debug mode. The problem is since
|
|
||||||
// the stream's ProcessRequestsAsync loop is never awaited by the connection, it's not really possible to
|
|
||||||
// observe when all the blocks are returned. This can be removed after we implement graceful shutdown.
|
|
||||||
Dispose();
|
|
||||||
InitializeConnectionFields(new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(), allowLateReturn: true));
|
|
||||||
|
|
||||||
var headers = new[]
|
var headers = new[]
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue