示例#1
0
        public async Task WaitForAvailableBidirectionStreamsAsyncWorks()
        {
            using QuicListener listener           = CreateQuicListener(maxBidirectionalStreams: 1);
            using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint);

            Task <QuicConnection> serverTask = listener.AcceptConnectionAsync().AsTask();
            await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds);

            using QuicConnection serverConnection = serverTask.Result;

            // No stream opened yet, should return immediately.
            Assert.True(clientConnection.WaitForAvailableBidirectionalStreamsAsync().IsCompletedSuccessfully);

            // Open one stream, should wait till it closes.
            QuicStream stream   = clientConnection.OpenBidirectionalStream();
            ValueTask  waitTask = clientConnection.WaitForAvailableBidirectionalStreamsAsync();

            Assert.False(waitTask.IsCompleted);
            Assert.Throws <QuicException>(() => clientConnection.OpenBidirectionalStream());

            // Close the streams, the waitTask should finish as a result.
            stream.Dispose();
            QuicStream newStream = await serverConnection.AcceptStreamAsync();

            newStream.Dispose();
            await waitTask.AsTask().WaitAsync(TimeSpan.FromSeconds(10));
        }
示例#2
0
    public override ValueTask <ConnectionContext> ConnectAsync(IFeatureCollection?features = null, CancellationToken cancellationToken = default)
    {
        QuicStream quicStream;

        var streamDirectionFeature = features?.Get <IStreamDirectionFeature>();

        if (streamDirectionFeature != null)
        {
            if (streamDirectionFeature.CanRead)
            {
                quicStream = _connection.OpenBidirectionalStream();
            }
            else
            {
                quicStream = _connection.OpenUnidirectionalStream();
            }
        }
        else
        {
            quicStream = _connection.OpenBidirectionalStream();
        }

        // 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(new ValueTask <ConnectionContext>(context));
    }
        public ValueTask <ConnectionContext> StartBidirectionalStreamAsync()
        {
            var stream  = _connection.OpenBidirectionalStream();
            var context = new QuicStreamContext(stream, this, _context);

            context.Start();
            return(new ValueTask <ConnectionContext>(context));
        }
示例#4
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);
示例#5
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);
        }
示例#6
0
        public async Task Read_StreamAborted_Throws()
        {
            const int ExpectedErrorCode = 0xfffffff;

            await Task.Run(async() =>
            {
                using QuicListener listener = CreateQuicListener();
                ValueTask <QuicConnection> serverConnectionTask = listener.AcceptConnectionAsync();

                using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint);
                await clientConnection.ConnectAsync();

                using QuicConnection serverConnection = await serverConnectionTask;

                await using QuicStream clientStream = clientConnection.OpenBidirectionalStream();
                await clientStream.WriteAsync(new byte[1]);

                await using QuicStream serverStream = await serverConnection.AcceptStreamAsync();
                await serverStream.ReadAsync(new byte[1]);

                clientStream.AbortWrite(ExpectedErrorCode);

                byte[] buffer = new byte[100];
                QuicStreamAbortedException ex = await Assert.ThrowsAsync <QuicStreamAbortedException>(() => serverStream.ReadAsync(buffer).AsTask());
                Assert.Equal(ExpectedErrorCode, ex.ErrorCode);
            }).TimeoutAfter(millisecondsTimeout: 5_000);
        }
示例#7
0
        public static async Task <QuicStreamContext> CreateAndCompleteBidirectionalStreamGracefully(QuicConnection clientConnection, MultiplexedConnectionContext serverConnection)
        {
            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();

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

            await quicStreamContext.DisposeAsync();

            quicStreamContext.Dispose();

            return(quicStreamContext);
        }
示例#8
0
        public async Task BasicTest()
        {
            using (QuicListener listener = new QuicListener(new IPEndPoint(IPAddress.Loopback, 0), sslServerAuthenticationOptions: null, implementationProvider: QuicImplementationProviders.Mock))
            {
                IPEndPoint listenEndPoint = listener.ListenEndPoint;

                await Task.WhenAll(
                    Task.Run(async() =>
                {
                    // Client code
                    using (QuicConnection connection = new QuicConnection(listenEndPoint, sslClientAuthenticationOptions: null, implementationProvider: QuicImplementationProviders.Mock))
                    {
                        await connection.ConnectAsync();
                        using (QuicStream stream = connection.OpenBidirectionalStream())
                        {
                            await stream.WriteAsync(s_data);
                        }
                    }
                }),
                    Task.Run(async() =>
                {
                    // Server code
                    using (QuicConnection connection = await listener.AcceptConnectionAsync())
                    {
                        using (QuicStream stream = await connection.AcceptStreamAsync())
                        {
                            byte[] buffer = new byte[s_data.Length];
                            int bytesRead = await stream.ReadAsync(buffer);
                            Assert.Equal(s_data.Length, bytesRead);
                            Assert.True(s_data.Span.SequenceEqual(buffer));
                        }
                    }
                }));
            }
        }
示例#9
0
        public QuicServerConnection(
            QuicConnection clientConnection,
            QuicServerTransport quicServerTransport,
            IPEndPoint lspHookedLocalEP,
            bool isLspHooked)
        {
            if (clientConnection == null)
            {
                throw new ArgumentNullException(nameof(clientConnection));
            }

            if (quicServerTransport == null)
            {
                throw new ArgumentNullException(nameof(quicServerTransport));
            }

            if (!clientConnection.Connected)
            {
                throw new ArgumentException($"Client has already disconnected");
            }

            this.server = quicServerTransport;

            this.connection = clientConnection;

            this.stream = clientConnection.OpenBidirectionalStream();

            this.thread = new ThreadManager(QuicServerConnectionReceiveLoop, Unblock);

            this.buffer = new BytesBuffer();

            this.localEndPoint = clientConnection.LocalEndPoint as IPEndPoint;

            this.remoteEndPoint = clientConnection.RemoteEndPoint as IPEndPoint;
        }
示例#10
0
        public async Task CallDifferentWriteMethodsWorks()
        {
            using QuicConnection clientConnection = CreateQuicConnection(DefaultListener.ListenEndPoint);

            ValueTask clientTask = clientConnection.ConnectAsync();

            using QuicConnection serverConnection = await DefaultListener.AcceptConnectionAsync();

            await clientTask;

            ReadOnlyMemory <byte>   helloWorld = Encoding.ASCII.GetBytes("Hello world!");
            ReadOnlySequence <byte> ros        = CreateReadOnlySequenceFromBytes(helloWorld.ToArray());

            Assert.False(ros.IsSingleSegment);
            using QuicStream clientStream = clientConnection.OpenBidirectionalStream();
            ValueTask writeTask = clientStream.WriteAsync(ros);

            using QuicStream serverStream = await serverConnection.AcceptStreamAsync();

            await writeTask;

            byte[] memory = new byte[24];
            int    res    = await serverStream.ReadAsync(memory);

            Assert.Equal(12, res);
            ReadOnlyMemory <ReadOnlyMemory <byte> > romrom = new ReadOnlyMemory <ReadOnlyMemory <byte> >(new ReadOnlyMemory <byte>[] { helloWorld, helloWorld });

            await clientStream.WriteAsync(romrom);

            res = await serverStream.ReadAsync(memory);

            Assert.Equal(24, res);
        }
示例#11
0
        internal async Task PingPong(QuicConnection client, QuicConnection server)
        {
            using QuicStream clientStream = client.OpenBidirectionalStream();
            ValueTask t = clientStream.WriteAsync(s_ping);

            using QuicStream serverStream = await server.AcceptStreamAsync();

            byte[] buffer  = new byte[s_ping.Length];
            int    remains = s_ping.Length;

            while (remains > 0)
            {
                int readLength = await serverStream.ReadAsync(buffer, buffer.Length - remains, remains);

                Assert.True(readLength > 0);
                remains -= readLength;
            }
            Assert.Equal(s_ping, buffer);
            await t;

            t       = serverStream.WriteAsync(s_pong);
            remains = s_pong.Length;
            while (remains > 0)
            {
                int readLength = await clientStream.ReadAsync(buffer, buffer.Length - remains, remains);

                Assert.True(readLength > 0);
                remains -= readLength;
            }

            Assert.Equal(s_pong, buffer);
            await t;
        }
示例#12
0
        private static async Task <QuicStream> OpenAndUseStreamAsync(QuicConnection c)
        {
            QuicStream s = c.OpenBidirectionalStream();

            // This will pend
            await s.ReadAsync(new byte[1]);

            return(s);
        }
示例#13
0
        public static async Task <int> RunClient(IPEndPoint serverEp, CancellationToken token)
        {
            using var client = new QuicConnection(new QuicClientConnectionOptions
            {
                RemoteEndPoint = serverEp,
                ClientAuthenticationOptions = new SslClientAuthenticationOptions
                {
                    ApplicationProtocols = new List <SslApplicationProtocol>
                    {
                        new SslApplicationProtocol("echo") // same as used for server
                    }
                }
            });

            await client.ConnectAsync(token);

            try
            {
                await using QuicStream stream = client.OpenBidirectionalStream();

                // spawn a reader task to not let server be flow-control blocked
                _ = Task.Run(async() =>
                {
                    byte[] arr = new byte[4 * 1024];
                    int read;
                    while ((read = await stream.ReadAsync(arr, token)) > 0)
                    {
                        string s = Encoding.ASCII.GetString(arr, 0, read);
                        Console.WriteLine($"Received: {s}");
                    }
                });

                string line;
                while ((line = Console.ReadLine()) != null)
                {
                    // convert into ASCII byte array before sending
                    byte[] bytes = Encoding.ASCII.GetBytes(line);
                    await stream.WriteAsync(bytes, token);

                    // flush the stream to send the data immediately
                    await stream.FlushAsync(token);
                }

                // once all stdin is written, close the stream
                stream.Shutdown();

                // wait until the server receives all data
                await stream.ShutdownWriteCompleted(token);
            }
            finally
            {
                await client.CloseAsync(0, token);
            }

            return(0);
        }
示例#14
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();
    }
        protected override async Task <StreamPair> CreateConnectedStreamsAsync()
        {
            QuicImplementationProvider provider = Provider;
            var listener = new QuicListener(
                provider,
                new IPEndPoint(IPAddress.Loopback, 0),
                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 = new QuicConnection(
                        provider,
                        listener.ListenEndPoint,
                        GetSslClientAuthenticationOptions());
                    await connection2.ConnectAsync();
                    stream2 = connection2.OpenBidirectionalStream();
                    // 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);
        }
示例#16
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);
        }
示例#17
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);
        }
示例#18
0
        public async Task GetStreamIdWithoutStartWorks()
        {
            using QuicListener listener           = CreateQuicListener();
            using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint);

            ValueTask clientTask = clientConnection.ConnectAsync();

            using QuicConnection serverConnection = await listener.AcceptConnectionAsync();

            await clientTask;

            using QuicStream clientStream = clientConnection.OpenBidirectionalStream();
            Assert.Equal(0, clientStream.StreamId);
        }
示例#19
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);
        }
示例#20
0
        protected override async Task <StreamPair> CreateConnectedStreamsAsync()
        {
            QuicImplementationProvider provider = Provider;
            var protocol = new SslApplicationProtocol("quictest");

            var listener = new QuicListener(
                provider,
                new IPEndPoint(IPAddress.Loopback, 0),
                new SslServerAuthenticationOptions {
                ApplicationProtocols = new List <SslApplicationProtocol> {
                    protocol
                }
            });

            listener.Start();

            QuicConnection connection1 = null, connection2 = null;
            QuicStream     stream1 = null, stream2 = null;

            await WhenAllOrAnyFailed(
                Task.Run(async() =>
            {
                connection1 = await listener.AcceptConnectionAsync();
                stream1     = await connection1.AcceptStreamAsync();
            }),
                Task.Run(async() =>
            {
                connection2 = new QuicConnection(
                    provider,
                    listener.ListenEndPoint,
                    new SslClientAuthenticationOptions()
                {
                    ApplicationProtocols = new List <SslApplicationProtocol>()
                    {
                        protocol
                    }
                });
                await connection2.ConnectAsync();
                stream2 = connection2.OpenBidirectionalStream();
            }));

            var result = new StreamPairWithOtherDisposables(stream1, stream2);

            result.Disposables.Add(connection1);
            result.Disposables.Add(connection2);
            result.Disposables.Add(listener);

            return(result);
        }
示例#21
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);
        }
示例#22
0
        private static async Task CreateAndTestBidirectionalStream(QuicConnection c1, QuicConnection c2)
        {
            using QuicStream s1 = c1.OpenBidirectionalStream();
            Assert.True(s1.CanRead);
            Assert.True(s1.CanWrite);

            ValueTask writeTask = s1.WriteAsync(s_data);

            using QuicStream s2 = await c2.AcceptStreamAsync();
            await ReceiveDataAsync(s_data, s2);

            await writeTask;

            await TestBidirectionalStream(s1, s2);
        }
示例#23
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();
        }
示例#24
0
        public async Task BasicTest()
        {
            using QuicListener listener = CreateQuicListener();

            for (int i = 0; i < 100; i++)
            {
                Task listenTask = Task.Run(async() =>
                {
                    using QuicConnection connection = await listener.AcceptConnectionAsync();
                    await using QuicStream stream   = await connection.AcceptStreamAsync();

                    byte[] buffer = new byte[s_data.Length];
                    int bytesRead = await stream.ReadAsync(buffer);

                    Assert.Equal(s_data.Length, bytesRead);
                    Assert.True(s_data.Span.SequenceEqual(buffer));

                    await stream.WriteAsync(s_data, endStream: true);
                    await stream.ShutdownWriteCompleted();

                    await connection.CloseAsync(errorCode: 0);
                });

                Task clientTask = Task.Run(async() =>
                {
                    using QuicConnection connection = CreateQuicConnection(listener.ListenEndPoint);
                    await connection.ConnectAsync();
                    await using QuicStream stream = connection.OpenBidirectionalStream();

                    await stream.WriteAsync(s_data, endStream: true);

                    byte[] memory = new byte[12];
                    int bytesRead = await stream.ReadAsync(memory);

                    Assert.Equal(s_data.Length, bytesRead);
                    // TODO this failed once...
                    Assert.True(s_data.Span.SequenceEqual(memory));
                    await stream.ShutdownWriteCompleted();

                    await connection.CloseAsync(errorCode: 0);
                });

                await(new[] { listenTask, clientTask }).WhenAllOrAnyFailed(millisecondsTimeout: 10000);
            }
        }
示例#25
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);
    }
示例#26
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"));
        }
示例#27
0
        public async Task QuicStream()
        {
            using var connection = new QuicConnection(new IPEndPoint(IPAddress.Loopback, QuicPort), new SslClientAuthenticationOptions()
            {
                TargetHost = "localhost"
            });
            await connection.ConnectAsync();

            await using var stream = connection.OpenBidirectionalStream();

            BinaryPrimitives.WriteInt32LittleEndian(_sendBuffer, DataLength);
            await stream.WriteAsync(_sendBuffer, 0, 4);

            await stream.FlushAsync();

            var read = 0;

            while (read < DataLength)
            {
                read += await stream.ReadAsync(_recvBuffer);
            }
        }
示例#28
0
        /// <summary>
        ///     Runs a client against a `msquicsample -server ...` server from the msquic repo
        /// </summary>
        private static async Task MsQuicSampleClient()
        {
            // port 4567 is hardcoded in msquicsample executable
            var serverAddress = IPEndPoint.Parse("127.0.0.1:4567");

            using var connection = new QuicConnection(serverAddress,
                                                      new SslClientAuthenticationOptions
            {
                ApplicationProtocols = new List <SslApplicationProtocol>
                {
                    // make sure we report the same protocol
                    new SslApplicationProtocol("sample")
                }
            });

            await connection.ConnectAsync();

            await using var stream = connection.OpenBidirectionalStream();

            var buffer = new byte[1024];

            new Random().NextBytes(buffer);
            await stream.WriteAsync(buffer);

            await stream.ShutdownWriteCompleted();

            var totalRead = 0;
            int read;

            do
            {
                read = await stream.ReadAsync(buffer.AsMemory(totalRead));

                totalRead += read;
            } while (read > 0);

            Console.WriteLine($"Received: {BitConverter.ToString(buffer, 0, totalRead)}");
        }
示例#29
0
        private Task <HttpResponseMessage> SendWithoutWaitingAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            QuicStream         quicStream = null;
            Http3RequestStream stream;

            try
            {
                // TODO: do less work in this lock, try to get rid of goto.
                lock (SyncObj)
                {
                    if (_connection == null)
                    {
                        goto retryRequest;
                    }

                    quicStream = _connection.OpenBidirectionalStream();
                    if (_lastProcessedStreamId != -1 && quicStream.StreamId > _lastProcessedStreamId)
                    {
                        goto retryRequest;
                    }

                    stream = new Http3RequestStream(request, this, quicStream);
                    _activeRequests.Add(quicStream.StreamId, stream);
                }

                return(stream.SendAsync(cancellationToken));
            }
            catch (QuicConnectionAbortedException ex)
            {
                // This will happen if we aborted _connection somewhere.
                Abort(ex);
            }

retryRequest:
            // We lost a race between GOAWAY/abort and our pool sending the request to this connection.
            quicStream?.Dispose();
            return(Task.FromException <HttpResponseMessage>(new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnSameOrNextProxy)));
        }
示例#30
0
        public async Task MultipleReadsAndWrites()
        {
            for (int j = 0; j < 100; j++)
            {
                Task listenTask = Task.Run(async() =>
                {
                    // Connection isn't being accepted, interesting.
                    using QuicConnection connection = await DefaultListener.AcceptConnectionAsync();
                    await using QuicStream stream   = await connection.AcceptStreamAsync();
                    byte[] buffer = new byte[s_data.Length];

                    while (true)
                    {
                        int bytesRead = await stream.ReadAsync(buffer);
                        if (bytesRead == 0)
                        {
                            break;
                        }
                        Assert.Equal(s_data.Length, bytesRead);
                        Assert.True(s_data.Span.SequenceEqual(buffer));
                    }

                    for (int i = 0; i < 5; i++)
                    {
                        await stream.WriteAsync(s_data);
                    }
                    await stream.WriteAsync(Memory <byte> .Empty, endStream: true);
                    await stream.ShutdownWriteCompleted();
                    await connection.CloseAsync();
                });

                Task clientTask = Task.Run(async() =>
                {
                    using QuicConnection connection = CreateQuicConnection(DefaultListener.ListenEndPoint);
                    await connection.ConnectAsync();
                    await using QuicStream stream = connection.OpenBidirectionalStream();

                    for (int i = 0; i < 5; i++)
                    {
                        await stream.WriteAsync(s_data);
                    }

                    await stream.WriteAsync(Memory <byte> .Empty, endStream: true);

                    byte[] memory = new byte[12];
                    while (true)
                    {
                        int res = await stream.ReadAsync(memory);
                        if (res == 0)
                        {
                            break;
                        }
                        Assert.True(s_data.Span.SequenceEqual(memory));
                    }

                    await stream.ShutdownWriteCompleted();
                    await connection.CloseAsync();
                });

                await(new[] { listenTask, clientTask }).WhenAllOrAnyFailed(millisecondsTimeout: 1000000);
            }
        }