Exemple #1
0
        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);
        }
Exemple #2
0
    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);
    }
Exemple #3
0
    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);
    }
Exemple #4
0
        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);
Exemple #5
0
    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);
Exemple #6
0
        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);
        }