Skip to content

Commit 9d8556e

Browse files
authored
Added FrameConnection.OnConnectionClosed back (#2028)
1 parent 7854c06 commit 9d8556e

File tree

2 files changed

+36
-15
lines changed

2 files changed

+36
-15
lines changed

src/Kestrel.Core/Internal/FrameConnection.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class FrameConnection : ITimeoutControl
2828

2929
private readonly FrameConnectionContext _context;
3030
private IList<IAdaptedConnection> _adaptedConnections;
31+
private readonly TaskCompletionSource<object> _socketClosedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
3132
private Frame _frame;
3233
private Http2Connection _http2Connection;
3334
private volatile int _http2ConnectionState;
@@ -151,6 +152,7 @@ private async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> htt
151152
}
152153

153154
await adaptedPipelineTask;
155+
await _socketClosedTcs.Task;
154156
}
155157
catch (Exception ex)
156158
{
@@ -189,6 +191,13 @@ internal void CreateFrame<TContext>(IHttpApplication<TContext> httpApplication,
189191
});
190192
}
191193

194+
public void OnConnectionClosed(Exception ex)
195+
{
196+
Abort(ex);
197+
198+
_socketClosedTcs.TrySetResult(null);
199+
}
200+
192201
public Task StopAsync()
193202
{
194203
Debug.Assert(_frame != null, $"{nameof(_frame)} is null");

src/Kestrel.Core/Internal/HttpConnectionMiddleware.cs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
1313
{
1414
public class HttpConnectionMiddleware<TContext>
1515
{
16+
private static Action<Exception, object> _completeTcs = CompleteTcs;
17+
1618
private static long _lastFrameConnectionId = long.MinValue;
1719

1820
private readonly IList<IConnectionAdapter> _connectionAdapters;
@@ -67,30 +69,40 @@ public Task OnConnectionAsync(ConnectionContext connectionContext)
6769
var processingTask = connection.StartRequestProcessing(_application);
6870

6971
var inputTcs = new TaskCompletionSource<object>();
72+
var outputTcs = new TaskCompletionSource<object>();
7073

71-
// Abort the frame when the transport writer completes
72-
connectionContext.Transport.Input.OnWriterCompleted((error, state) =>
73-
{
74-
var tcs = (TaskCompletionSource<object>)state;
75-
76-
if (error != null)
77-
{
78-
tcs.TrySetException(error);
79-
}
80-
else
81-
{
82-
tcs.TrySetResult(null);
83-
}
84-
},
85-
inputTcs);
74+
// The reason we don't fire events directly from these callbacks is because it seems
75+
// like the transport callbacks root the state object (even after it fires)
76+
connectionContext.Transport.Input.OnWriterCompleted(_completeTcs, inputTcs);
77+
connectionContext.Transport.Output.OnReaderCompleted(_completeTcs, outputTcs);
8678

8779
inputTcs.Task.ContinueWith((task, state) =>
8880
{
8981
((FrameConnection)state).Abort(task.Exception?.InnerException);
9082
},
9183
connection, TaskContinuationOptions.ExecuteSynchronously);
9284

85+
outputTcs.Task.ContinueWith((task, state) =>
86+
{
87+
((FrameConnection)state).OnConnectionClosed(task.Exception?.InnerException);
88+
},
89+
connection, TaskContinuationOptions.ExecuteSynchronously);
90+
9391
return processingTask;
9492
}
93+
94+
private static void CompleteTcs(Exception error, object state)
95+
{
96+
var tcs = (TaskCompletionSource<object>)state;
97+
98+
if (error != null)
99+
{
100+
tcs.TrySetException(error);
101+
}
102+
else
103+
{
104+
tcs.TrySetResult(null);
105+
}
106+
}
95107
}
96108
}

0 commit comments

Comments
 (0)