public async Task StreamPool_StreamAbortedOnClientAndServer_NotPooled() { // 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 & Assert var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(0, quicConnectionContext.StreamPool.Count); var clientStream = clientConnection.OpenBidirectionalStream(); 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.AbortWrite((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, ((QuicStreamAbortedException)serverEx.InnerException).ErrorCode); 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 <QuicStreamAbortedException>(() => clientStream.ReadAsync(buffer).AsTask()).DefaultTimeout(); Assert.Equal((long)Http3ErrorCode.RequestRejected, clientEx.ErrorCode); 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 StreamPool_StreamAbortedOnClient_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 ex = await Assert.ThrowsAsync <ConnectionResetException>(() => serverStream.Transport.Input.ReadAsync().AsTask()).DefaultTimeout(); Assert.Equal("Stream aborted by peer (258).", ex.Message); Assert.Equal((long)Http3ErrorCode.InternalError, ((QuicException)ex.InnerException).ApplicationErrorCode.Value); // Complete reading and then abort. await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); 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 StreamPool_StreamAbortedOnServerAfterComplete_NotPooled() { // 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 & Assert var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(0, quicConnectionContext.StreamPool.Count); var clientStream = await clientConnection.OpenBidirectionalStreamAsync(); await clientStream.WriteAsync(TestData, endStream : true).DefaultTimeout(); 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(); Assert.True(readResult.IsCompleted); // Complete reading and writing. await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); serverStream.Abort(new ConnectionAbortedException("Test message")); await quicStreamContext.DisposeAsync(); Assert.Equal(0, quicConnectionContext.StreamPool.Count); }
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++) { // TODO: Race condition in QUIC library. // Delay between sending streams to avoid // https://github.com/dotnet/runtime/issues/55249 await Task.Delay(100); 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 StreamPool_ManyConcurrentStreams_StreamPoolFull() { // 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 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 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); }