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.OpenOutboundStreamAsync(QuicStreamType.Bidirectional, cancellationToken); } else { quicStream = await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional, cancellationToken); } } else { quicStream = await _connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional, 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); }
private static async Task <QuicStream> OpenAndUseStreamAsync(QuicConnection c) { QuicStream s = await c.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); // This will pend await s.ReadAsync(new byte[1]); return(s); }
public static async Task <QuicStreamContext> CreateAndCompleteBidirectionalStreamGracefully(QuicConnection clientConnection, MultiplexedConnectionContext serverConnection, ILogger logger) { logger.LogInformation("Client starting stream."); var clientStream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); logger.LogInformation("Client sending data."); await clientStream.WriteAsync(TestData, completeWrites : true).DefaultTimeout(); logger.LogInformation("Server accepting stream."); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); logger.LogInformation("Server reading data."); 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. logger.LogInformation("Server completing input and output."); await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Both send and receive loops have exited. logger.LogInformation("Server verifying stream is finished."); await quicStreamContext._processingTask.DefaultTimeout(); Assert.True(quicStreamContext.CanWrite); Assert.True(quicStreamContext.CanRead); logger.LogInformation("Server disposing stream."); await quicStreamContext.DisposeAsync(); quicStreamContext.Dispose(); return(quicStreamContext); }
public async ValueTask <Http3LoopbackStream> OpenUnidirectionalStreamAsync() { return(new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional))); }
protected override async Task <StreamPair> CreateConnectedStreamsAsync() { var listener = await QuicListener.ListenAsync(new QuicListenerOptions() { ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), ApplicationProtocols = new List <SslApplicationProtocol>() { new SslApplicationProtocol("quictest") }, ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(new QuicServerConnectionOptions() { DefaultStreamErrorCode = QuicTestBase.DefaultStreamErrorCodeServer, DefaultCloseErrorCode = QuicTestBase.DefaultCloseErrorCodeServer, ServerAuthenticationOptions = GetSslServerAuthenticationOptions() }) }); byte[] buffer = new byte[1] { 42 }; QuicConnection connection1 = null, connection2 = null; QuicStream stream1 = null, stream2 = null; try { await WhenAllOrAnyFailed( Task.Run(async() => { connection1 = await listener.AcceptConnectionAsync(); stream1 = await connection1.AcceptInboundStreamAsync(); Assert.Equal(1, await stream1.ReadAsync(buffer)); }), Task.Run(async() => { try { connection2 = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions() { DefaultStreamErrorCode = QuicTestBase.DefaultStreamErrorCodeClient, DefaultCloseErrorCode = QuicTestBase.DefaultCloseErrorCodeClient, RemoteEndPoint = listener.LocalEndPoint, ClientAuthenticationOptions = GetSslClientAuthenticationOptions() }); stream2 = await connection2.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); // 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 await listener.DisposeAsync(); var result = new StreamPairWithOtherDisposables(stream1, stream2); result.Disposables.Add(connection1); result.Disposables.Add(connection2); return(result); } catch { if (stream1 is not null) { await stream1.DisposeAsync(); } if (stream2 is not null) { await stream2.DisposeAsync(); } if (connection1 is not null) { await connection1.DisposeAsync(); } if (connection2 is not null) { await connection2.DisposeAsync(); } throw; } }