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