Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 3177ba0

Browse files
author
Cesar Blum Silveira
committed
Wait for frame loop completion to dispose connection stream (#1156).
1 parent 663b825 commit 3177ba0

File tree

4 files changed

+94
-6
lines changed

4 files changed

+94
-6
lines changed

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Connection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public void Start()
135135

136136
public Task StopAsync()
137137
{
138-
_frame.Stop();
138+
_frame.StopAsync();
139139
_frame.SocketInput.CompleteAwaiting();
140140

141141
return _socketClosedTcs.Task;
@@ -156,7 +156,7 @@ public virtual void OnSocketClosed()
156156
{
157157
if (_filteredStreamAdapter != null)
158158
{
159-
_readInputTask.ContinueWith((task, state) =>
159+
Task.WhenAll(_readInputTask, _frame.StopAsync()).ContinueWith((task, state) =>
160160
{
161161
var connection = (Connection)state;
162162
connection._filterContext.Connection.Dispose();

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ public void Start()
365365
this,
366366
default(CancellationToken),
367367
TaskCreationOptions.DenyChildAttach,
368-
TaskScheduler.Default);
368+
TaskScheduler.Default).Unwrap();
369369
}
370370

371371
/// <summary>
@@ -374,7 +374,7 @@ public void Start()
374374
/// Stop will be called on all active connections, and Task.WaitAll() will be called on every
375375
/// return value.
376376
/// </summary>
377-
public Task Stop()
377+
public Task StopAsync()
378378
{
379379
if (!_requestProcessingStopping)
380380
{

test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/HttpsTests.cs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public async Task DoesNotThrowObjectDisposedExceptionOnConnectionAbort()
105105
{
106106
try
107107
{
108-
await httpContext.Response.WriteAsync($"hello, world\r\r", ct);
108+
await httpContext.Response.WriteAsync($"hello, world", ct);
109109
await Task.Delay(1000, ct);
110110
}
111111
catch (TaskCanceledException)
@@ -136,6 +136,54 @@ await sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null,
136136
Assert.False(loggerFactory.ErrorLogger.ObjectDisposedExceptionLogged);
137137
}
138138

139+
[Fact]
140+
public async Task DoesNotThrowObjectDisposedExceptionFromWriteAsyncAfterConnectionIsAborted()
141+
{
142+
var tcs = new TaskCompletionSource<object>();
143+
var x509Certificate2 = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword");
144+
var loggerFactory = new HandshakeErrorLoggerFactory();
145+
var hostBuilder = new WebHostBuilder()
146+
.UseKestrel(options =>
147+
{
148+
options.UseHttps(@"TestResources/testCert.pfx", "testPassword");
149+
})
150+
.UseUrls("https://127.0.0.1:0/")
151+
.UseLoggerFactory(loggerFactory)
152+
.Configure(app => app.Run(async httpContext =>
153+
{
154+
httpContext.Abort();
155+
try
156+
{
157+
await httpContext.Response.WriteAsync($"hello, world");
158+
tcs.SetResult(null);
159+
}
160+
catch (Exception ex)
161+
{
162+
tcs.SetException(ex);
163+
}
164+
}));
165+
166+
using (var host = hostBuilder.Build())
167+
{
168+
host.Start();
169+
170+
using (var socket = await HttpClientSlim.GetSocket(new Uri($"https://127.0.0.1:{host.GetPort()}/")))
171+
using (var stream = new NetworkStream(socket, ownsSocket: false))
172+
using (var sslStream = new SslStream(stream, true, (sender, certificate, chain, errors) => true))
173+
{
174+
await sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null,
175+
enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12,
176+
checkCertificateRevocation: false);
177+
178+
var request = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n");
179+
await sslStream.WriteAsync(request, 0, request.Length);
180+
await sslStream.ReadAsync(new byte[32], 0, 32);
181+
}
182+
}
183+
184+
await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
185+
}
186+
139187
private class HandshakeErrorLoggerFactory : ILoggerFactory
140188
{
141189
public HttpsConnectionFilterLogger FilterLogger { get; } = new HttpsConnectionFilterLogger();

test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using System.Text;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using Microsoft.AspNetCore.Hosting.Server;
10+
using Microsoft.AspNetCore.Http;
911
using Microsoft.AspNetCore.Http.Features;
1012
using Microsoft.AspNetCore.Server.Kestrel;
1113
using Microsoft.AspNetCore.Server.Kestrel.Internal;
@@ -1290,7 +1292,7 @@ public void RequestProcessingAsyncEnablesKeepAliveTimeout()
12901292
var expectedKeepAliveTimeout = (long)serviceContext.ServerOptions.Limits.KeepAliveTimeout.TotalMilliseconds;
12911293
connectionControl.Verify(cc => cc.SetTimeout(expectedKeepAliveTimeout, TimeoutAction.CloseConnection));
12921294

1293-
frame.Stop();
1295+
frame.StopAsync();
12941296
socketInput.IncomingFin();
12951297

12961298
requestProcessingTask.Wait();
@@ -1464,5 +1466,43 @@ public void ManuallySettingTransferEncodingThrowsForNoBodyResponse()
14641466
// Assert
14651467
Assert.Throws<InvalidOperationException>(() => frame.Flush());
14661468
}
1469+
1470+
[Fact]
1471+
public async Task RequestProcessingTaskIsUnwrapped()
1472+
{
1473+
var trace = new KestrelTrace(new TestKestrelTrace());
1474+
var ltp = new LoggingThreadPool(trace);
1475+
using (var pool = new MemoryPool())
1476+
using (var socketInput = new SocketInput(pool, ltp))
1477+
{
1478+
var serviceContext = new ServiceContext
1479+
{
1480+
DateHeaderValueManager = new DateHeaderValueManager(),
1481+
ServerOptions = new KestrelServerOptions(),
1482+
Log = trace
1483+
};
1484+
var listenerContext = new ListenerContext(serviceContext)
1485+
{
1486+
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
1487+
};
1488+
var connectionContext = new ConnectionContext(listenerContext)
1489+
{
1490+
ConnectionControl = Mock.Of<IConnectionControl>(),
1491+
SocketInput = socketInput
1492+
};
1493+
1494+
var frame = new Frame<HttpContext>(application: null, context: connectionContext);
1495+
frame.Start();
1496+
1497+
var data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n");
1498+
socketInput.IncomingData(data, 0, data.Length);
1499+
1500+
var requestProcessingTask = frame.StopAsync();
1501+
Assert.IsNotType(typeof(Task<Task>), requestProcessingTask);
1502+
1503+
await requestProcessingTask.TimeoutAfter(TimeSpan.FromSeconds(10));
1504+
socketInput.IncomingFin();
1505+
}
1506+
}
14671507
}
14681508
}

0 commit comments

Comments
 (0)