Prevent block leak when socket is closed before connection filter is applied.

This commit is contained in:
Cesar Blum Silveira 2016-11-04 11:39:27 -07:00
parent a1c598733b
commit a83bbcfba7
3 changed files with 46 additions and 8 deletions

View File

@ -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();
} }

View File

@ -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>

View File

@ -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();