Abort connections not closed during shutdown (#1112).

This commit is contained in:
Cesar Blum Silveira 2016-12-14 16:53:19 -08:00
parent 8b44e8e382
commit b41c4078bd
7 changed files with 50 additions and 13 deletions

View File

@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
return _socketClosedTcs.Task;
}
public virtual void Abort(Exception error = null)
public virtual Task AbortAsync(Exception error = null)
{
// Frame.Abort calls user code while this method is always
// called from a libuv thread.
@ -150,6 +150,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
_frame.Abort(error);
});
return _socketClosedTcs.Task;
}
// Called on Libuv thread
@ -285,7 +287,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (!normalRead)
{
Abort(error);
AbortAsync(error);
}
}

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
@ -22,17 +21,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
public async Task<bool> WalkConnectionsAndCloseAsync(TimeSpan timeout)
{
return await WalkConnectionsAsync((connectionManager, tcs) => connectionManager.WalkConnectionsAndCloseCore(tcs), timeout).ConfigureAwait(false);
}
public async Task<bool> WalkConnectionsAndAbortAsync(TimeSpan timeout)
{
return await WalkConnectionsAsync((connectionManager, tcs) => connectionManager.WalkConnectionsAndAbortCore(tcs), timeout).ConfigureAwait(false);
}
private async Task<bool> WalkConnectionsAsync(Action<ConnectionManager, TaskCompletionSource<object>> action, TimeSpan timeout)
{
var tcs = new TaskCompletionSource<object>();
_thread.Post(state => ((ConnectionManager)state).WalkConnectionsAndCloseCore(tcs), this);
_thread.Post(state => action((ConnectionManager)state, tcs), this);
return await Task.WhenAny(tcs.Task, Task.Delay(timeout)).ConfigureAwait(false) == tcs.Task;
}
private void WalkConnectionsAndCloseCore(TaskCompletionSource<object> tcs)
{
var connectionStopTasks = new List<Task>();
WalkConnectionsCore(connection => connection.StopAsync(), tcs);
}
private void WalkConnectionsAndAbortCore(TaskCompletionSource<object> tcs)
{
WalkConnectionsCore(connection => connection.AbortAsync(), tcs);
}
private void WalkConnectionsCore(Func<Connection, Task> action, TaskCompletionSource<object> tcs)
{
var tasks = new List<Task>();
_thread.Walk(ptr =>
{
@ -41,13 +60,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (connection != null)
{
connectionStopTasks.Add(connection.StopAsync());
tasks.Add(action(connection));
}
});
_threadPool.Run(() =>
{
Task.WaitAll(connectionStopTasks.ToArray());
Task.WaitAll(tasks.ToArray());
tcs.SetResult(null);
});
}

View File

@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
if (cancellationToken.IsCancellationRequested)
{
_connection.Abort();
_connection.AbortAsync();
_cancelled = true;
return TaskUtilities.GetCancelledTask(cancellationToken);
}
@ -304,7 +304,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
// Abort the connection for any failed write
// Queued on threadpool so get it in as first op.
_connection.Abort();
_connection.AbortAsync();
_cancelled = true;
CompleteAllWrites();
@ -374,7 +374,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
// Abort the connection for any failed write
// Queued on threadpool so get it in as first op.
_connection.Abort();
_connection.AbortAsync();
_cancelled = true;
_lastWriteError = error;
}
@ -532,7 +532,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
{
if (cancellationToken.IsCancellationRequested)
{
_connection.Abort();
_connection.AbortAsync();
_cancelled = true;
return TaskUtilities.GetCancelledTask(cancellationToken);
}

View File

@ -43,6 +43,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
void NotAllConnectionsClosedGracefully();
void NotAllConnectionsAborted();
void ApplicationError(string connectionId, Exception ex);
}
}

View File

@ -165,6 +165,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
if (!await ConnectionManager.WalkConnectionsAndCloseAsync(_shutdownTimeout).ConfigureAwait(false))
{
_log.NotAllConnectionsClosedGracefully();
if (!await ConnectionManager.WalkConnectionsAndAbortAsync(TimeSpan.FromSeconds(1)).ConfigureAwait(false))
{
_log.NotAllConnectionsAborted();
}
}
var result = await WaitAsync(PostAsync(state =>
@ -281,7 +286,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
{
lock (_startSync)
{
var tcs = (TaskCompletionSource<int>) parameter;
var tcs = (TaskCompletionSource<int>)parameter;
try
{
_loop.Init(_engine.Libuv);

View File

@ -26,6 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
private static readonly Action<ILogger, string, int, Exception> _connectionDisconnectedWrite;
private static readonly Action<ILogger, string, long, Exception> _connectionHeadResponseBodyWrite;
private static readonly Action<ILogger, Exception> _notAllConnectionsClosedGracefully;
private static readonly Action<ILogger, Exception> _notAllConnectionsAborted;
private static readonly Action<ILogger, string, string, Exception> _connectionBadRequest;
private static readonly Action<ILogger, string, Exception> _connectionReset;
private static readonly Action<ILogger, string, Exception> _requestProcessingError;
@ -54,6 +55,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
_connectionHeadResponseBodyWrite = LoggerMessage.Define<string, long>(LogLevel.Debug, 18, @"Connection id ""{ConnectionId}"" write of ""{count}"" body bytes to non-body HEAD response.");
_connectionReset = LoggerMessage.Define<string>(LogLevel.Debug, 19, @"Connection id ""{ConnectionId}"" reset.");
_requestProcessingError = LoggerMessage.Define<string>(LogLevel.Information, 20, @"Connection id ""{ConnectionId}"" request processing ended abnormally.");
_notAllConnectionsAborted = LoggerMessage.Define(LogLevel.Debug, 21, "Some connections failed to abort during server shutdown.");
}
public KestrelTrace(ILogger logger)
@ -149,6 +151,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
_notAllConnectionsClosedGracefully(_logger, null);
}
public virtual void NotAllConnectionsAborted()
{
_notAllConnectionsAborted(_logger, null);
}
public void ConnectionBadRequest(string connectionId, BadHttpRequestException ex)
{
_connectionBadRequest(_logger, connectionId, ex.Message, ex);

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Testing
{
@ -24,9 +25,10 @@ namespace Microsoft.AspNetCore.Testing
};
}
public override void Abort(Exception error = null)
public override Task AbortAsync(Exception error = null)
{
RequestAbortedSource?.Cancel();
return TaskCache.CompletedTask;
}
public override void OnSocketClosed()