Abort connections not closed during shutdown (#1112).
This commit is contained in:
parent
8b44e8e382
commit
b41c4078bd
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
|||
|
||||
void NotAllConnectionsClosedGracefully();
|
||||
|
||||
void NotAllConnectionsAborted();
|
||||
|
||||
void ApplicationError(string connectionId, Exception ex);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue