Skip to content

Commit d85c8ff

Browse files
authored
Raise HTTP/2 stream end event in tests with lifetime handler (#41230)
1 parent 8ab4b36 commit d85c8ff

File tree

3 files changed

+39
-7
lines changed

3 files changed

+39
-7
lines changed

src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,15 @@ internal partial class Http2Connection : IHttp2StreamLifetimeHandler, IHttpStrea
7171
internal readonly Http2KeepAlive? _keepAlive;
7272
internal readonly Dictionary<int, Http2Stream> _streams = new Dictionary<int, Http2Stream>();
7373
internal PooledStreamStack<Http2Stream> StreamPool;
74-
internal Action<Http2Stream>? _onStreamCompleted;
74+
internal IHttp2StreamLifetimeHandler _streamLifetimeHandler;
7575

7676
public Http2Connection(HttpConnectionContext context)
7777
{
7878
var httpLimits = context.ServiceContext.ServerOptions.Limits;
7979
var http2Limits = httpLimits.Http2;
8080

8181
_context = context;
82+
_streamLifetimeHandler = this;
8283

8384
// Capture the ExecutionContext before dispatching HTTP/2 middleware. Will be restored by streams when processing request
8485
_context.InitialExecutionContext = ExecutionContext.Capture();
@@ -753,7 +754,7 @@ private Http2StreamContext CreateHttp2StreamContext()
753754
_context.LocalEndPoint,
754755
_context.RemoteEndPoint,
755756
_incomingFrame.StreamId,
756-
streamLifetimeHandler: this,
757+
_streamLifetimeHandler,
757758
_clientSettings,
758759
_serverSettings,
759760
_frameWriter,
@@ -1230,7 +1231,6 @@ void IHttp2StreamLifetimeHandler.OnStreamCompleted(Http2Stream stream)
12301231
{
12311232
_completedStreams.Enqueue(stream);
12321233
_streamCompletionAwaitable.Complete();
1233-
_onStreamCompleted?.Invoke(stream);
12341234
}
12351235

12361236
private void UpdateCompletedStreams()

src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,16 +564,13 @@ await InitializeConnectionAsync(async context =>
564564
throw new InvalidOperationException("Put the stream into an invalid state by throwing after writing to response.");
565565
});
566566

567-
var streamCompletedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
568-
_connection._onStreamCompleted = _ => streamCompletedTcs.TrySetResult();
569-
570567
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
571568

572569
var stream = _connection._streams[1];
573570
serverTcs.SetResult();
574571

575572
// Wait for the stream to be completed
576-
await streamCompletedTcs.Task;
573+
await WaitForStreamAsync(stream.StreamId).DefaultTimeout();
577574

578575
// TriggerTick will trigger the stream to be returned to the pool so we can assert it
579576
TriggerTick();

src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ protected void CreateConnection()
461461
timeoutControl: _mockTimeoutControl.Object);
462462

463463
_connection = new Http2Connection(httpConnectionContext);
464+
_connection._streamLifetimeHandler = new LifetimeHandlerInterceptor(_connection._streamLifetimeHandler, this);
464465

465466
var httpConnection = new HttpConnection(httpConnectionContext);
466467
httpConnection.Initialize(_connection);
@@ -470,6 +471,35 @@ protected void CreateConnection()
470471
_timeoutControl.Initialize(_serviceContext.SystemClock.UtcNow.Ticks);
471472
}
472473

474+
private class LifetimeHandlerInterceptor : IHttp2StreamLifetimeHandler
475+
{
476+
private readonly IHttp2StreamLifetimeHandler _inner;
477+
private readonly Http2TestBase _httpTestBase;
478+
479+
public LifetimeHandlerInterceptor(IHttp2StreamLifetimeHandler inner, Http2TestBase httpTestBase)
480+
{
481+
_inner = inner;
482+
_httpTestBase = httpTestBase;
483+
}
484+
485+
public void DecrementActiveClientStreamCount()
486+
{
487+
_inner.DecrementActiveClientStreamCount();
488+
}
489+
490+
public void OnStreamCompleted(Http2Stream stream)
491+
{
492+
_inner.OnStreamCompleted(stream);
493+
494+
// Stream in test might not have been started with StartStream method.
495+
// In that case there isn't a record of a running stream.
496+
if (_httpTestBase._runningStreams.TryGetValue(stream.StreamId, out var tcs))
497+
{
498+
tcs.TrySetResult();
499+
}
500+
}
501+
}
502+
473503
protected void InitializeConnectionWithoutPreface(RequestDelegate application)
474504
{
475505
if (_connection == null)
@@ -681,6 +711,11 @@ protected Task SendHeadersWithPaddingAndPriorityAsync(int streamId, IEnumerable<
681711
return FlushAsync(writableBuffer);
682712
}
683713

714+
protected Task WaitForStreamAsync(int streamId)
715+
{
716+
return _runningStreams[streamId].Task;
717+
}
718+
684719
protected Task WaitForAllStreamsAsync()
685720
{
686721
return Task.WhenAll(_runningStreams.Values.Select(tcs => tcs.Task)).DefaultTimeout();

0 commit comments

Comments
 (0)