Wait for frame loop completion to dispose connection stream (#1156).
This commit is contained in:
parent
663b825d97
commit
3177ba0aae
|
|
@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
public Task StopAsync()
|
||||
{
|
||||
_frame.Stop();
|
||||
_frame.StopAsync();
|
||||
_frame.SocketInput.CompleteAwaiting();
|
||||
|
||||
return _socketClosedTcs.Task;
|
||||
|
|
@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
if (_filteredStreamAdapter != null)
|
||||
{
|
||||
_readInputTask.ContinueWith((task, state) =>
|
||||
Task.WhenAll(_readInputTask, _frame.StopAsync()).ContinueWith((task, state) =>
|
||||
{
|
||||
var connection = (Connection)state;
|
||||
connection._filterContext.Connection.Dispose();
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
this,
|
||||
default(CancellationToken),
|
||||
TaskCreationOptions.DenyChildAttach,
|
||||
TaskScheduler.Default);
|
||||
TaskScheduler.Default).Unwrap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -374,7 +374,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
/// Stop will be called on all active connections, and Task.WaitAll() will be called on every
|
||||
/// return value.
|
||||
/// </summary>
|
||||
public Task Stop()
|
||||
public Task StopAsync()
|
||||
{
|
||||
if (!_requestProcessingStopping)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
try
|
||||
{
|
||||
await httpContext.Response.WriteAsync($"hello, world\r\r", ct);
|
||||
await httpContext.Response.WriteAsync($"hello, world", ct);
|
||||
await Task.Delay(1000, ct);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
|
|
@ -136,6 +136,54 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
Assert.False(loggerFactory.ErrorLogger.ObjectDisposedExceptionLogged);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DoesNotThrowObjectDisposedExceptionFromWriteAsyncAfterConnectionIsAborted()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
var x509Certificate2 = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword");
|
||||
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 => app.Run(async httpContext =>
|
||||
{
|
||||
httpContext.Abort();
|
||||
try
|
||||
{
|
||||
await httpContext.Response.WriteAsync($"hello, world");
|
||||
tcs.SetResult(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.SetException(ex);
|
||||
}
|
||||
}));
|
||||
|
||||
using (var host = hostBuilder.Build())
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var socket = await HttpClientSlim.GetSocket(new Uri($"https://127.0.0.1:{host.GetPort()}/")))
|
||||
using (var stream = new NetworkStream(socket, ownsSocket: false))
|
||||
using (var sslStream = new SslStream(stream, true, (sender, certificate, chain, errors) => true))
|
||||
{
|
||||
await sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null,
|
||||
enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12,
|
||||
checkCertificateRevocation: false);
|
||||
|
||||
var request = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n");
|
||||
await sslStream.WriteAsync(request, 0, request.Length);
|
||||
await sslStream.ReadAsync(new byte[32], 0, 32);
|
||||
}
|
||||
}
|
||||
|
||||
await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
|
||||
}
|
||||
|
||||
private class HandshakeErrorLoggerFactory : ILoggerFactory
|
||||
{
|
||||
public HttpsConnectionFilterLogger FilterLogger { get; } = new HttpsConnectionFilterLogger();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ using System.IO;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
||||
|
|
@ -1290,7 +1292,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
var expectedKeepAliveTimeout = (long)serviceContext.ServerOptions.Limits.KeepAliveTimeout.TotalMilliseconds;
|
||||
connectionControl.Verify(cc => cc.SetTimeout(expectedKeepAliveTimeout, TimeoutAction.CloseConnection));
|
||||
|
||||
frame.Stop();
|
||||
frame.StopAsync();
|
||||
socketInput.IncomingFin();
|
||||
|
||||
requestProcessingTask.Wait();
|
||||
|
|
@ -1464,5 +1466,43 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
// Assert
|
||||
Assert.Throws<InvalidOperationException>(() => frame.Flush());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequestProcessingTaskIsUnwrapped()
|
||||
{
|
||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||
var ltp = new LoggingThreadPool(trace);
|
||||
using (var pool = new MemoryPool())
|
||||
using (var socketInput = new SocketInput(pool, ltp))
|
||||
{
|
||||
var serviceContext = new ServiceContext
|
||||
{
|
||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||
ServerOptions = new KestrelServerOptions(),
|
||||
Log = trace
|
||||
};
|
||||
var listenerContext = new ListenerContext(serviceContext)
|
||||
{
|
||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
||||
};
|
||||
var connectionContext = new ConnectionContext(listenerContext)
|
||||
{
|
||||
ConnectionControl = Mock.Of<IConnectionControl>(),
|
||||
SocketInput = socketInput
|
||||
};
|
||||
|
||||
var frame = new Frame<HttpContext>(application: null, context: connectionContext);
|
||||
frame.Start();
|
||||
|
||||
var data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n");
|
||||
socketInput.IncomingData(data, 0, data.Length);
|
||||
|
||||
var requestProcessingTask = frame.StopAsync();
|
||||
Assert.IsNotType(typeof(Task<Task>), requestProcessingTask);
|
||||
|
||||
await requestProcessingTask.TimeoutAfter(TimeSpan.FromSeconds(10));
|
||||
socketInput.IncomingFin();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue