public override async ValueTask <ConnectionContext> ConnectAsync(IFeatureCollection?features = null, CancellationToken cancellationToken = default) { QuicStream quicStream; var streamDirectionFeature = features?.Get <IStreamDirectionFeature>(); if (streamDirectionFeature != null) { if (streamDirectionFeature.CanRead) { quicStream = await _connection.OpenBidirectionalStreamAsync(cancellationToken); } else { quicStream = await _connection.OpenUnidirectionalStreamAsync(cancellationToken); } } else { quicStream = await _connection.OpenBidirectionalStreamAsync(cancellationToken); } // Only a handful of control streams are created by the server and they last for the // lifetime of the connection. No value in pooling them. QuicStreamContext?context = new QuicStreamContext(this, _context); context.Initialize(quicStream); context.Start(); QuicLog.ConnectedStream(_log, context); return(context); }
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 = await quicConnection.OpenBidirectionalStreamAsync(); await clientStream.WriteAsync(TestData).DefaultTimeout(); // Server finishes accepting var serverStream = await serverStreamTask.DefaultTimeout(); // Assert AssertTlsConnectionFeature(serverConnection.Features, testCert); AssertTlsConnectionFeature(serverStream.Features, testCert);
private static async Task <QuicStream> OpenAndUseStreamAsync(QuicConnection c) { QuicStream s = await c.OpenBidirectionalStreamAsync(); // This will pend await s.ReadAsync(new byte[1]); return(s); }
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 = await quicConnection.OpenBidirectionalStreamAsync(); 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(); }
protected override async Task <StreamPair> CreateConnectedStreamsAsync() { var listener = await QuicListener.ListenAsync(new QuicListenerOptions() { ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), ServerAuthenticationOptions = GetSslServerAuthenticationOptions() }); byte[] buffer = new byte[1] { 42 }; QuicConnection connection1 = null, connection2 = null; QuicStream stream1 = null, stream2 = null; await WhenAllOrAnyFailed( Task.Run(async() => { connection1 = await listener.AcceptConnectionAsync(); stream1 = await connection1.AcceptStreamAsync(); Assert.Equal(1, await stream1.ReadAsync(buffer)); }), Task.Run(async() => { try { connection2 = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions() { RemoteEndPoint = listener.ListenEndPoint, ClientAuthenticationOptions = GetSslClientAuthenticationOptions() }); await connection2.ConnectAsync(); stream2 = await connection2.OpenBidirectionalStreamAsync(); // OpenBidirectionalStream only allocates ID. We will force stream opening // by Writing there and receiving data on the other side. await stream2.WriteAsync(buffer); } catch (Exception ex) { _output?.WriteLine($"Failed to {ex.Message}"); throw; } })); // No need to keep the listener once we have connected connection and streams listener.Dispose(); var result = new StreamPairWithOtherDisposables(stream1, stream2); result.Disposables.Add(connection1); result.Disposables.Add(connection2); return(result); }
public async Task StreamPool_StreamAbortedOnClient_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).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 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, ((QuicStreamAbortedException)ex.InnerException).ErrorCode); // 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 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 = await quicConnection.OpenBidirectionalStreamAsync(); 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); }
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 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 = await clientConnection.OpenBidirectionalStreamAsync(); 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); }
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 = await clientConnection.OpenBidirectionalStreamAsync(); 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); }
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 = await quicConnection.OpenBidirectionalStreamAsync(); 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(); }
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 static async Task <QuicStreamContext> CreateAndCompleteBidirectionalStreamGracefully(QuicConnection clientConnection, MultiplexedConnectionContext serverConnection) { 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(); Assert.True(quicStreamContext.CanWrite); Assert.True(quicStreamContext.CanRead); await quicStreamContext.DisposeAsync(); quicStreamContext.Dispose(); return(quicStreamContext); }
public async ValueTask <Http3LoopbackStream> OpenBidirectionalStreamAsync() { return(new Http3LoopbackStream(await _connection.OpenBidirectionalStreamAsync())); }
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); }