Merge branch 'johluo/more-graceful' into release/2.2

This commit is contained in:
John Luo 2018-08-09 12:00:36 -07:00
commit cef46fdaa7
4 changed files with 24 additions and 24 deletions

View File

@ -79,6 +79,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private readonly object _stateLock = new object(); private readonly object _stateLock = new object();
private int _highestOpenedStreamId; private int _highestOpenedStreamId;
private Http2ConnectionState _state = Http2ConnectionState.Open; private Http2ConnectionState _state = Http2ConnectionState.Open;
private readonly TaskCompletionSource<object> _streamsCompleted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly ConcurrentDictionary<int, Http2Stream> _streams = new ConcurrentDictionary<int, Http2Stream>(); private readonly ConcurrentDictionary<int, Http2Stream> _streams = new ConcurrentDictionary<int, Http2Stream>();
@ -256,6 +257,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, errorCode); _frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, errorCode);
UpdateState(Http2ConnectionState.Closed); UpdateState(Http2ConnectionState.Closed);
} }
if (_streams.IsEmpty)
{
_streamsCompleted.TrySetResult(null);
}
} }
// Ensure aborting each stream doesn't result in unnecessary WINDOW_UPDATE frames being sent. // Ensure aborting each stream doesn't result in unnecessary WINDOW_UPDATE frames being sent.
@ -266,6 +272,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
stream.Abort(connectionError); stream.Abort(connectionError);
} }
await _streamsCompleted.Task;
_frameWriter.Complete(); _frameWriter.Complete();
} }
catch catch
@ -891,13 +899,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{ {
_streams.TryRemove(streamId, out _); _streams.TryRemove(streamId, out _);
if (_state == Http2ConnectionState.Closing && _streams.IsEmpty) if (_streams.IsEmpty)
{ {
_frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, Http2ErrorCode.NO_ERROR); if (_state == Http2ConnectionState.Closing)
UpdateState(Http2ConnectionState.Closed); {
_frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, Http2ErrorCode.NO_ERROR);
UpdateState(Http2ConnectionState.Closed);
// Wake up request processing loop so the connection can complete if there are no pending requests // Wake up request processing loop so the connection can complete if there are no pending requests
Input.CancelPendingRead(); Input.CancelPendingRead();
}
// Complete the task waiting on all streams to finish
_streamsCompleted.TrySetResult(null);
} }
} }
} }

View File

@ -1065,12 +1065,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact] [Fact]
public async Task DATA_Received_NoStreamWindowSpace_ConnectionError() public async Task DATA_Received_NoStreamWindowSpace_ConnectionError()
{ {
// 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));
// _maxData should be 1/4th of the default initial window size + 1. // _maxData should be 1/4th of the default initial window size + 1.
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4);
@ -1093,12 +1087,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Fact] [Fact]
public async Task DATA_Received_NoConnectionWindowSpace_ConnectionError() public async Task DATA_Received_NoConnectionWindowSpace_ConnectionError()
{ {
// 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));
// _maxData should be 1/4th of the default initial window size + 1. // _maxData should be 1/4th of the default initial window size + 1.
Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4); Assert.Equal(Http2PeerSettings.DefaultInitialWindowSize + 1, (uint)_maxData.Length * 4);
@ -3287,7 +3275,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_pair.Application.Output.Complete(new ConnectionResetException(string.Empty)); _pair.Application.Output.Complete(new ConnectionResetException(string.Empty));
var result = await _pair.Application.Input.ReadAsync(); var result = await _pair.Application.Input.ReadAsync();
Assert.True(result.IsCompleted); Assert.False(result.IsCompleted);
Assert.Single(_logger.Messages, m => m.Exception is ConnectionResetException); Assert.Single(_logger.Messages, m => m.Exception is ConnectionResetException);
} }

View File

@ -27,9 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
[MemberData(nameof(H2SpecTestCases))] [MemberData(nameof(H2SpecTestCases))]
public async Task RunIndividualTestCase(H2SpecTestCase testCase) public async Task RunIndividualTestCase(H2SpecTestCase testCase)
{ {
var memoryPoolFactory = new DiagnosticMemoryPoolFactory(allowLateReturn: true); var hostBuilder = TransportSelector.GetWebHostBuilder()
var hostBuilder = TransportSelector.GetWebHostBuilder(memoryPoolFactory.Create)
.UseKestrel(options => .UseKestrel(options =>
{ {
options.Listen(IPAddress.Loopback, 0, listenOptions => options.Listen(IPAddress.Loopback, 0, listenOptions =>