Prevent block leak when socket is closed before connection filter is applied.
This commit is contained in:
parent
a1c598733b
commit
a83bbcfba7
|
|
@ -154,15 +154,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
// Called on Libuv thread
|
// Called on Libuv thread
|
||||||
public virtual void OnSocketClosed()
|
public virtual void OnSocketClosed()
|
||||||
{
|
{
|
||||||
if (_filteredStreamAdapter != null)
|
_frame.FrameStartedTask.ContinueWith((task, state) =>
|
||||||
{
|
{
|
||||||
Task.WhenAll(_readInputTask, _frame.StopAsync()).ContinueWith((task, state) =>
|
var connection = (Connection)state;
|
||||||
|
|
||||||
|
if (_filteredStreamAdapter != null)
|
||||||
{
|
{
|
||||||
var connection = (Connection)state;
|
Task.WhenAll(_readInputTask, _frame.StopAsync()).ContinueWith((task2, state2) =>
|
||||||
connection._filterContext.Connection.Dispose();
|
{
|
||||||
connection._filteredStreamAdapter.Dispose();
|
var connection2 = (Connection)state2;
|
||||||
}, this);
|
connection2._filterContext.Connection.Dispose();
|
||||||
}
|
connection2._filteredStreamAdapter.Dispose();
|
||||||
|
}, connection);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
SocketInput.Dispose();
|
SocketInput.Dispose();
|
||||||
_socketClosedTcs.TrySetResult(null);
|
_socketClosedTcs.TrySetResult(null);
|
||||||
|
|
@ -199,7 +204,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
}
|
}
|
||||||
|
|
||||||
_frame.PrepareRequest = _filterContext.PrepareRequest;
|
_frame.PrepareRequest = _filterContext.PrepareRequest;
|
||||||
|
|
||||||
_frame.Start();
|
_frame.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
protected Stack<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
protected Stack<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||||
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
||||||
|
|
||||||
|
private TaskCompletionSource<object> _frameStartedTcs = new TaskCompletionSource<object>();
|
||||||
private Task _requestProcessingTask;
|
private Task _requestProcessingTask;
|
||||||
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
||||||
protected int _requestAborted;
|
protected int _requestAborted;
|
||||||
|
|
@ -205,6 +206,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
public Stream DuplexStream { get; set; }
|
public Stream DuplexStream { get; set; }
|
||||||
|
|
||||||
|
public Task FrameStartedTask => _frameStartedTcs.Task;
|
||||||
|
|
||||||
public CancellationToken RequestAborted
|
public CancellationToken RequestAborted
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -366,6 +369,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
default(CancellationToken),
|
default(CancellationToken),
|
||||||
TaskCreationOptions.DenyChildAttach,
|
TaskCreationOptions.DenyChildAttach,
|
||||||
TaskScheduler.Default).Unwrap();
|
TaskScheduler.Default).Unwrap();
|
||||||
|
_frameStartedTcs.SetResult(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
|
|
@ -184,6 +185,35 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
|
await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for https://github.com/aspnet/KestrelHttpServer/pull/1197
|
||||||
|
[Fact]
|
||||||
|
public void ConnectionFilterDoesNotLeakBlock()
|
||||||
|
{
|
||||||
|
var loggerFactory = new HandshakeErrorLoggerFactory();
|
||||||
|
|
||||||
|
var hostBuilder = new WebHostBuilder()
|
||||||
|
.UseKestrel(options =>
|
||||||
|
{
|
||||||
|
options.UseHttps(@"TestResources/testCert.pfx", "testPassword");
|
||||||
|
})
|
||||||
|
.UseUrls("https://127.0.0.1:0/")
|
||||||
|
.UseLoggerFactory(loggerFactory)
|
||||||
|
.Configure(app => { });
|
||||||
|
|
||||||
|
using (var host = hostBuilder.Build())
|
||||||
|
{
|
||||||
|
host.Start();
|
||||||
|
|
||||||
|
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
|
||||||
|
{
|
||||||
|
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
|
||||||
|
|
||||||
|
// Close socket immediately
|
||||||
|
socket.LingerState = new LingerOption(true, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class HandshakeErrorLoggerFactory : ILoggerFactory
|
private class HandshakeErrorLoggerFactory : ILoggerFactory
|
||||||
{
|
{
|
||||||
public HttpsConnectionFilterLogger FilterLogger { get; } = new HttpsConnectionFilterLogger();
|
public HttpsConnectionFilterLogger FilterLogger { get; } = new HttpsConnectionFilterLogger();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue