Exemplo n.º 1
0
        public async Task AcceptAsync_ClientClosesConnection_ServerNotified()
        {
            // 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();

            serverConnection.ConnectionClosed.Register(() => connectionClosedTcs.SetResult());

            var acceptStreamTask = serverConnection.AcceptAsync();

            await clientConnection.CloseAsync(256);

            // Assert
            var ex = await Assert.ThrowsAsync <ConnectionResetException>(() => acceptStreamTask.AsTask()).DefaultTimeout();

            var innerEx = Assert.IsType <QuicConnectionAbortedException>(ex.InnerException);

            Assert.Equal(256, innerEx.ErrorCode);

            await connectionClosedTcs.Task.DefaultTimeout();
        }
Exemplo n.º 2
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);
        }
Exemplo n.º 3
0
        public async Task AcceptAsync_ClientClosesConnection_ExceptionThrown()
        {
            // 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();

            var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout();

            // Act
            var acceptTask = serverConnection.AcceptAsync().AsTask();

            await quicConnection.CloseAsync((long)Http3ErrorCode.NoError).DefaultTimeout();

            // Assert
            var ex = await Assert.ThrowsAsync <ConnectionResetException>(() => acceptTask).DefaultTimeout();

            var innerEx = Assert.IsType <QuicConnectionAbortedException>(ex.InnerException);

            Assert.Equal((long)Http3ErrorCode.NoError, innerEx.ErrorCode);

            Assert.Equal((long)Http3ErrorCode.NoError, serverConnection.Features.Get <IProtocolErrorCodeFeature>().Error);
        }
Exemplo n.º 4
0
        public async Task ClientToServerUnidirectionalStream_CompleteWrites_PipeProvidesDataAndCompleteTogether()
        {
            // 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 = quicConnection.OpenUnidirectionalStream();
            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 readResultTask = serverStream.Transport.Input.ReadAsync();

            await clientStream.WriteAsync(TestData, endStream : true).DefaultTimeout();

            // Assert
            var completeReadResult = await readResultTask.DefaultTimeout();

            Assert.Equal(TestData, completeReadResult.Buffer.ToArray());
            Assert.True(completeReadResult.IsCompleted);
        }
Exemplo n.º 5
0
        public async Task ClientCertificate_Required_Sent_Populated()
        {
            // Arrange
            await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory, clientCertificateRequired : true);

            var options  = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint);
            var testCert = TestResources.GetTestCertificate();

            options.ClientAuthenticationOptions.ClientCertificates = new X509CertificateCollection {
                testCert
            };

            // Act
            using var quicConnection = new QuicConnection(options);
            await quicConnection.ConnectAsync().DefaultTimeout();

            var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout();

            // Server waits for stream from client
            var serverStreamTask = serverConnection.AcceptAsync().DefaultTimeout();

            // Client creates stream
            using var clientStream = quicConnection.OpenBidirectionalStream();
            await clientStream.WriteAsync(TestData).DefaultTimeout();

            // Server finishes accepting
            var serverStream = await serverStreamTask.DefaultTimeout();

            // Assert
            AssertTlsConnectionFeature(serverConnection.Features, testCert);
            AssertTlsConnectionFeature(serverStream.Features, testCert);
Exemplo n.º 6
0
        public async Task AcceptAsync_AfterUnbind_Error()
        {
            // Arrange
            await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory);

            // Act
            await connectionListener.UnbindAsync().DefaultTimeout();

            // Assert
            await Assert.ThrowsAsync <ObjectDisposedException>(() => connectionListener.AcceptAndAddFeatureAsync().AsTask()).DefaultTimeout();
        }
Exemplo n.º 7
0
        public async Task AcceptAsync_ClientStartsAndStopsBidirectionStream_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();

            var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout();

            // Act
            var acceptTask = serverConnection.AcceptAsync();

            await using var clientStream = quicConnection.OpenBidirectionalStream();
            await clientStream.WriteAsync(TestData);

            await using var serverStream = await acceptTask.DefaultTimeout();

            await serverStream.Transport.Output.WriteAsync(TestData);

            // 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);

            // Read data from server.
            var data = await clientStream.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout();

            Assert.Equal(TestData, data);

            // Shutdown from client.
            clientStream.Shutdown();

            // Get shutdown from client.
            read = await serverStream.Transport.Input.ReadAsync().DefaultTimeout();

            Assert.True(read.IsCompleted);

            await serverStream.Transport.Output.CompleteAsync();

            await closedTcs.Task.DefaultTimeout();
        }
Exemplo n.º 8
0
        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.AcceptAsync().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      = 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);

            ((IProtocolErrorCodeFeature)serverStream).Error = (long)Http3ErrorCode.InternalError;
            serverStream.Abort(new ConnectionAbortedException("Test message"));

            // TODO - client isn't getting abort?
            readCount = await clientStream.ReadAsync(buffer).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();
        }
Exemplo n.º 9
0
        public async Task StreamAbortFeature_AbortWrite_ClientReceivesAbort()
        {
            // 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 = quicConnection.OpenBidirectionalStream();
            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 serverReadTask = serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).AsTask();

            var streamAbortFeature = serverStream.Features.Get <IStreamAbortFeature>();

            streamAbortFeature.AbortRead((long)Http3ErrorCode.InternalError, new ConnectionAbortedException("Test reason"));

            // Assert

            // Server writes data
            await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout();

            // Server completes its output.
            await serverStream.Transport.Output.CompleteAsync().DefaultTimeout();

            // Client successfully reads data to end
            var data = await clientStream.ReadUntilEndAsync().DefaultTimeout();

            Assert.Equal(TestData, data);

            // Client errors when writing
            var clientEx = await Assert.ThrowsAsync <QuicStreamAbortedException>(() => clientStream.WriteAsync(data).AsTask()).DefaultTimeout();

            Assert.Equal((long)Http3ErrorCode.InternalError, clientEx.ErrorCode);

            // Server errors when reading
            var serverEx = await Assert.ThrowsAsync <ConnectionAbortedException>(() => serverReadTask).DefaultTimeout();

            Assert.Equal("Test reason", serverEx.Message);
        }
Exemplo n.º 10
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 = clientConnection.OpenBidirectionalStream();
            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);
        }
Exemplo n.º 11
0
        public async Task BidirectionalStream_ReadAborted_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();

            // Act
            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);

            await clientStream.WriteAsync(TestData).DefaultTimeout();

            // Complete writing.
            await serverStream.Transport.Output.CompleteAsync();

            // Abort read-side of the stream and then complete pipe.
            // This simulates what Kestrel does when a request finishes without
            // reading the request body to the end.
            serverStream.Features.Get <IStreamAbortFeature>().AbortRead((long)Http3ErrorCode.NoError, new ConnectionAbortedException("Test message."));
            await serverStream.Transport.Input.CompleteAsync();

            var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream);

            // Both send and receive loops have exited.
            await quicStreamContext._processingTask.DefaultTimeout();

            Assert.True(quicStreamContext.CanWrite);
            Assert.True(quicStreamContext.CanRead);

            await quicStreamContext.DisposeAsync();

            quicStreamContext.Dispose();

            var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection);

            // Assert
            Assert.Equal(0, quicConnectionContext.StreamPool.Count);
        }
Exemplo n.º 12
0
        public async Task BidirectionalStream_ClientAbortedAfterDisposeCalled_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();

            // Act
            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);

            // Server sends a large response that will make it wait to complete sends.
            await serverStream.Transport.Output.WriteAsync(new byte[1024 * 1024 * 32]).DefaultTimeout();

            // Complete reading and writing.
            await serverStream.Transport.Input.CompleteAsync();

            await serverStream.Transport.Output.CompleteAsync();

            var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream);

            // Server starts disposing
            var disposeTask = quicStreamContext.DisposeAsync();

            // Client aborts while server is draining
            clientStream.AbortRead((long)Http3ErrorCode.RequestCancelled);
            clientStream.AbortWrite((long)Http3ErrorCode.RequestCancelled);

            // Server finishes disposing
            await disposeTask.DefaultTimeout();

            quicStreamContext.Dispose();

            var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection);

            // Assert
            Assert.Equal(0, quicConnectionContext.StreamPool.Count);
        }
Exemplo n.º 13
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);
Exemplo n.º 14
0
        public async Task AcceptAsync_ServerStartsAndStopsUnidirectionStream_ClientAccepts()
        {
            // 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();

            var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout();

            // Act
            var acceptTask = quicConnection.AcceptStreamAsync();

            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);
        }
Exemplo n.º 15
0
        public async Task BidirectionalStream_ClientAbortWrite_ServerReceivesAbort()
        {
            // 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 = quicConnection.OpenBidirectionalStream();
            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.AbortWrite((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, ((QuicStreamAbortedException)ex.InnerException).ErrorCode);

            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();
        }
Exemplo n.º 16
0
        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""."));
        }
Exemplo n.º 17
0
        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 = clientConnection.OpenBidirectionalStream();
            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);
        }
Exemplo n.º 18
0
        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.""."));
        }
Exemplo n.º 19
0
        public async Task BidirectionalStream_ServerReadsDataAndCompletes_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.AcceptAsync().DefaultTimeout();

            // Act
            await using var clientStream = quicConnection.OpenBidirectionalStream();
            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();

            // Complete output.
            await serverStream.Transport.Output.CompleteAsync();

            // Assert
            Assert.True(readResult.IsCompleted);

            var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream);

            // Both send and receive loops have exited.
            await quicStreamContext._processingTask.DefaultTimeout();

            Assert.True(quicStreamContext.CanWrite);
            Assert.True(quicStreamContext.CanRead);

            Assert.Contains(LogMessages, m => m.Message.Contains("send loop completed gracefully"));
        }
Exemplo n.º 20
0
        public async Task BidirectionalStream_ServerReadsDataAndCompletes_GracefullyClosed()
        {
            // 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();

            // Act
            await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection);

            Assert.Contains(LogMessages, m => m.Message.Contains("send loop completed gracefully"));

            var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection);

            Assert.Equal(1, quicConnectionContext.StreamPool.Count);
        }
Exemplo n.º 21
0
        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);

            using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options);
            await clientConnection.ConnectAsync().DefaultTimeout();

            // 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);
        }
Exemplo n.º 22
0
        public async Task BidirectionalStream_MultipleStreamsOnConnection_ReusedFromPool()
        {
            // 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();

            // Act
            var stream1 = await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection);

            var stream2 = await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection);

            Assert.Same(stream1, stream2);

            var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection);

            Assert.Equal(1, quicConnectionContext.StreamPool.Count);
        }
Exemplo n.º 23
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);
        }
Exemplo n.º 24
0
        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 = clientConnection.OpenBidirectionalStream();
            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);
        }