public async Task BidirectionalStream_ClientAbortWrite_ServerReceivesAbort() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); await using var quicConnection = await QuicConnection.ConnectAsync(options); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act await using var clientStream = await quicConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); await clientStream.WriteAsync(TestData).DefaultTimeout(); await using var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); var closedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); serverStream.ConnectionClosed.Register(() => closedTcs.SetResult()); clientStream.Abort(QuicAbortDirection.Write, (long)Http3ErrorCode.InternalError); // Receive abort from client. var ex = await Assert.ThrowsAsync <ConnectionResetException>(() => serverStream.Transport.Input.ReadAsync().AsTask()).DefaultTimeout(); // Server completes its output. await serverStream.Transport.Output.CompleteAsync(); // Assert Assert.Equal((long)Http3ErrorCode.InternalError, ((QuicException)ex.InnerException).ApplicationErrorCode.Value); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Assert.Equal((long)Http3ErrorCode.InternalError, quicStreamContext.Error); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); await closedTcs.Task.DefaultTimeout(); }
public async Task AcceptAsync_ServerStartsAndStopsUnidirectionStream_ClientAccepts() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); await using var quicConnection = await QuicConnection.ConnectAsync(options); var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var acceptTask = quicConnection.AcceptInboundStreamAsync(); await using var serverStream = await serverConnection.ConnectAsync(); await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout(); await using var clientStream = await acceptTask.DefaultTimeout(); // Assert Assert.NotNull(clientStream); // Read data from server. var data = new List <byte>(); var buffer = new byte[1024]; var readCount = 0; while ((readCount = await clientStream.ReadAsync(buffer).DefaultTimeout()) != -1) { data.AddRange(buffer.AsMemory(0, readCount).ToArray()); if (data.Count == TestData.Length) { break; } } Assert.Equal(TestData, data); // Complete server. await serverStream.Transport.Output.CompleteAsync().DefaultTimeout(); // Receive complete in client. readCount = await clientStream.ReadAsync(buffer).DefaultTimeout(); Assert.Equal(0, readCount); }
public async Task ServerToClientUnidirectionalStream_ServerAborts_ClientGetsAbort() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var quicConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await quicConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var features = new FeatureCollection(); features.Set <IStreamDirectionFeature>(new DefaultStreamDirectionFeature(canRead: false, canWrite: true)); var serverStream = await serverConnection.ConnectAsync(features).DefaultTimeout(); await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout(); await using var clientStream = await quicConnection.AcceptStreamAsync(); var data = await clientStream.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, data); Logger.LogInformation("Server aborting stream"); ((IProtocolErrorCodeFeature)serverStream).Error = (long)Http3ErrorCode.InternalError; serverStream.Abort(new ConnectionAbortedException("Test message")); var ex = await Assert.ThrowsAsync <QuicStreamAbortedException>(() => clientStream.ReadAsync(new byte[1024]).AsTask()).DefaultTimeout(); // Assert Assert.Equal((long)Http3ErrorCode.InternalError, ex.ErrorCode); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Assert.True(quicStreamContext.CanWrite); Assert.False(quicStreamContext.CanRead); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); Assert.Contains(TestSink.Writes, m => m.Message.Contains(@"shutting down writes because: ""Test message"".")); }
public async Task AcceptAsync_ClientStartsAndStopsUnidirectionStream_ServerAccepts() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var quicConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await quicConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var acceptTask = serverConnection.AcceptAsync(); await using var clientStream = await quicConnection.OpenUnidirectionalStreamAsync(); await clientStream.WriteAsync(TestData); await using var serverStream = await acceptTask.DefaultTimeout(); // Assert Assert.NotNull(serverStream); Assert.False(serverStream.ConnectionClosed.IsCancellationRequested); var closedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); serverStream.ConnectionClosed.Register(() => closedTcs.SetResult()); // Read data from client. var read = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, read.Buffer.ToArray()); serverStream.Transport.Input.AdvanceTo(read.Buffer.End); // Shutdown client. clientStream.Shutdown(); // Receive shutdown on server. read = await serverStream.Transport.Input.ReadAsync().DefaultTimeout(); Assert.True(read.IsCompleted); await closedTcs.Task.DefaultTimeout(); }
public async Task AcceptAsync_CancellationThenAccept_AcceptStreamAfterCancellation() { // Arrange var connectionClosedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); // Act var acceptTask = connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await acceptTask.DefaultTimeout(); // Wait for stream and then cancel var cts = new CancellationTokenSource(); var acceptStreamTask = serverConnection.AcceptAsync(cts.Token); cts.Cancel(); var serverStream = await acceptStreamTask.DefaultTimeout(); Assert.Null(serverStream); // Wait for stream after cancellation acceptStreamTask = serverConnection.AcceptAsync(); await using var clientStream = await clientConnection.OpenBidirectionalStreamAsync(); await clientStream.WriteAsync(TestData); // Assert serverStream = await acceptStreamTask.DefaultTimeout(); Assert.NotNull(serverStream); var read = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, read.Buffer.ToArray()); serverStream.Transport.Input.AdvanceTo(read.Buffer.End); }
public async Task ServerToClientUnidirectionalStream_ServerWritesDataAndCompletes_GracefullyClosed() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var quicConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await quicConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var features = new FeatureCollection(); features.Set <IStreamDirectionFeature>(new DefaultStreamDirectionFeature(canRead: false, canWrite: true)); var serverStream = await serverConnection.ConnectAsync(features).DefaultTimeout(); await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout(); await using var clientStream = await quicConnection.AcceptStreamAsync(); var data = await clientStream.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, data); await serverStream.Transport.Output.CompleteAsync(); var readCount = await clientStream.ReadAsync(new byte[1024]).DefaultTimeout(); // Assert Assert.Equal(0, readCount); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Assert.True(quicStreamContext.CanWrite); Assert.False(quicStreamContext.CanRead); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); Assert.Contains(TestSink.Writes, m => m.Message.Contains(@"shutting down writes because: ""The QUIC transport's send loop completed gracefully."".")); }
public async Task StreamPool_ManyConcurrentStreams_StreamPoolFull() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); var testHeartbeatFeature = new TestHeartbeatFeature(); serverConnection.Features.Set <IConnectionHeartbeatFeature>(testHeartbeatFeature); // Act var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(0, quicConnectionContext.StreamPool.Count); var pauseCompleteTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var allConnectionsOnServerTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var streamTasks = new List <Task>(); var requestState = new RequestState(clientConnection, serverConnection, allConnectionsOnServerTcs, pauseCompleteTcs.Task); const int StreamsSent = 101; for (var i = 0; i < StreamsSent; i++) { streamTasks.Add(SendStream(requestState)); } await allConnectionsOnServerTcs.Task.DefaultTimeout(); pauseCompleteTcs.SetResult(); await Task.WhenAll(streamTasks).DefaultTimeout(); // Assert // Up to 100 streams are pooled. Assert.Equal(100, quicConnectionContext.StreamPool.Count);
public async Task AcceptAsync_ClientCreatesConnection_ServerAccepts() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); // Act var acceptTask = connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); await using var clientConnection = await QuicConnection.ConnectAsync(options); // Assert await using var serverConnection = await acceptTask.DefaultTimeout(); Assert.False(serverConnection.ConnectionClosed.IsCancellationRequested); await serverConnection.DisposeAsync().AsTask().DefaultTimeout(); // ConnectionClosed isn't triggered because the server initiated close. Assert.False(serverConnection.ConnectionClosed.IsCancellationRequested); }
public async Task BidirectionalStream_ServerReadsDataAndCompletes_GracefullyClosed() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); await using var clientConnection = await QuicConnection.ConnectAsync(options); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection, Logger); Assert.Contains(LogMessages, m => m.Message.Contains("send loop completed gracefully")); var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(1, quicConnectionContext.StreamPool.Count); Assert.Contains(TestSink.Writes, m => m.Message.Contains(@"shutting down writes because: ""The QUIC transport's send loop completed gracefully."".")); }
public async Task BidirectionalStream_MultipleStreamsOnConnection_ReusedFromPool() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); await using var clientConnection = await QuicConnection.ConnectAsync(options); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var stream1 = await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection, Logger); var stream2 = await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection, Logger); Assert.Same(stream1, stream2); var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(1, quicConnectionContext.StreamPool.Count); }
public async Task ClientToServerUnidirectionalStream_ServerReadsData_GracefullyClosed() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var quicConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await quicConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act await using var clientStream = await quicConnection.OpenUnidirectionalStreamAsync(); await clientStream.WriteAsync(TestData, endStream : true).DefaultTimeout(); await using var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); // Input should be completed. readResult = await serverStream.Transport.Input.ReadAsync().DefaultTimeout(); // Assert Assert.True(readResult.IsCompleted); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Assert.False(quicStreamContext.CanWrite); Assert.True(quicStreamContext.CanRead); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); }
public async Task StreamPool_Heartbeat_ExpiredStreamRemoved() { // Arrange var now = new DateTimeOffset(2021, 7, 6, 12, 0, 0, TimeSpan.Zero); var testSystemClock = new TestSystemClock { UtcNow = now }; await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory, testSystemClock); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); var testHeartbeatFeature = new TestHeartbeatFeature(); serverConnection.Features.Set <IConnectionHeartbeatFeature>(testHeartbeatFeature); // Act & Assert var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(0, quicConnectionContext.StreamPool.Count); var stream1 = await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection); Assert.Equal(1, quicConnectionContext.StreamPool.Count); QuicStreamContext pooledStream = quicConnectionContext.StreamPool._array[0]; Assert.Same(stream1, pooledStream); Assert.Equal(now.Ticks + QuicConnectionContext.StreamPoolExpiryTicks, pooledStream.PoolExpirationTicks); now = now.AddMilliseconds(100); testSystemClock.UtcNow = now; testHeartbeatFeature.RaiseHeartbeat(); // Not removed. Assert.Equal(1, quicConnectionContext.StreamPool.Count); var stream2 = await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection); Assert.Equal(1, quicConnectionContext.StreamPool.Count); pooledStream = quicConnectionContext.StreamPool._array[0]; Assert.Same(stream1, pooledStream); Assert.Equal(now.Ticks + QuicConnectionContext.StreamPoolExpiryTicks, pooledStream.PoolExpirationTicks); Assert.Same(stream1, stream2); now = now.AddTicks(QuicConnectionContext.StreamPoolExpiryTicks); testSystemClock.UtcNow = now; testHeartbeatFeature.RaiseHeartbeat(); // Not removed. Assert.Equal(1, quicConnectionContext.StreamPool.Count); now = now.AddTicks(1); testSystemClock.UtcNow = now; testHeartbeatFeature.RaiseHeartbeat(); // Removed. Assert.Equal(0, quicConnectionContext.StreamPool.Count); }
public async Task BidirectionalStream_ServerWritesDataAndDisposes_ClientReadsData(int dataLength) { // Arrange var testData = new byte[dataLength]; for (int i = 0; i < dataLength; i++) { testData[i] = (byte)i; } await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act Logger.LogInformation("Client starting stream."); var clientStream = await clientConnection.OpenBidirectionalStreamAsync(); await clientStream.WriteAsync(TestData, endStream : true).DefaultTimeout(); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); Logger.LogInformation("Server accepted stream."); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); // Input should be completed. readResult = await serverStream.Transport.Input.ReadAsync().DefaultTimeout(); Assert.True(readResult.IsCompleted); Logger.LogInformation("Server sending data."); await serverStream.Transport.Output.WriteAsync(testData).DefaultTimeout(); Logger.LogInformation("Server completing pipes."); await serverStream.Transport.Input.CompleteAsync().DefaultTimeout(); await serverStream.Transport.Output.CompleteAsync().DefaultTimeout(); Logger.LogInformation("Client reading until end of stream."); var data = await clientStream.ReadUntilEndAsync().DefaultTimeout(); Assert.Equal(testData.Length, data.Length); Assert.Equal(testData, data); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Logger.LogInformation("Server waiting for send and receiving loops to complete."); await quicStreamContext._processingTask.DefaultTimeout(); Assert.True(quicStreamContext.CanWrite); Assert.True(quicStreamContext.CanRead); Logger.LogInformation("Server disposing stream."); await quicStreamContext.DisposeAsync().DefaultTimeout(); quicStreamContext.Dispose(); var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(1, quicConnectionContext.StreamPool.Count); }
public async Task StreamPool_StreamAbortedOnClientAndServer_NotPooled() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); await using var clientConnection = await QuicConnection.ConnectAsync(options); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); var testHeartbeatFeature = new TestHeartbeatFeature(); serverConnection.Features.Set <IConnectionHeartbeatFeature>(testHeartbeatFeature); // Act & Assert var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(0, quicConnectionContext.StreamPool.Count); var clientStream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); await clientStream.WriteAsync(TestData).DefaultTimeout(); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); clientStream.Abort(QuicAbortDirection.Write, (long)Http3ErrorCode.InternalError); // Receive abort form client. var serverEx = await Assert.ThrowsAsync <ConnectionResetException>(() => serverStream.Transport.Input.ReadAsync().AsTask()).DefaultTimeout(); Assert.Equal("Stream aborted by peer (258).", serverEx.Message); Assert.Equal((long)Http3ErrorCode.InternalError, ((QuicException)serverEx.InnerException).ApplicationErrorCode.Value); serverStream.Features.Get <IProtocolErrorCodeFeature>().Error = (long)Http3ErrorCode.RequestRejected; serverStream.Abort(new ConnectionAbortedException("Test message.")); // Complete server. await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); var buffer = new byte[1024]; var clientEx = await Assert.ThrowsAsync <QuicException>(() => clientStream.ReadAsync(buffer).AsTask()).DefaultTimeout(); Assert.Equal(QuicError.StreamAborted, clientEx.QuicError); Assert.Equal((long)Http3ErrorCode.RequestRejected, clientEx.ApplicationErrorCode.Value); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); await quicStreamContext.DisposeAsync(); Assert.Equal(0, quicConnectionContext.StreamPool.Count); }
public async Task BidirectionalStream_ClientAbortedAfterDisposeCalled_NotPooled() { // Arrange using var httpEventSource = new HttpEventSourceListener(LoggerFactory); await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); await using var clientConnection = await QuicConnection.ConnectAsync(options); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act Logger.LogInformation("Client starting stream."); var clientStream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); await clientStream.WriteAsync(TestData).DefaultTimeout(); var readTask = clientStream.ReadUntilEndAsync(); Logger.LogInformation("Server accepted stream."); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); // Server sends a large response that will make it wait to complete sends. Logger.LogInformation("Server writing a large response."); await serverStream.Transport.Output.WriteAsync(new byte[1024 * 1024 * 32]).DefaultTimeout(); // Complete reading and writing. Logger.LogInformation("Server complete reading and writing."); await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); Logger.LogInformation("Client wait to finish reading."); await readTask.DefaultTimeout(); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Server starts disposing Logger.LogInformation("Server starts disposing."); var disposeTask = quicStreamContext.DisposeAsync(); // Client aborts while server is draining clientStream.Abort(QuicAbortDirection.Read, (long)Http3ErrorCode.RequestCancelled); clientStream.Abort(QuicAbortDirection.Write, (long)Http3ErrorCode.RequestCancelled); // Server finishes disposing Logger.LogInformation("Wait for server finish disposing."); await disposeTask.DefaultTimeout(); quicStreamContext.Dispose(); var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); // Assert Assert.Equal(0, quicConnectionContext.StreamPool.Count); }