Beispiel #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));
        }
Beispiel #2
0
        /// <summary>
        /// Accepts unidirectional streams (control, QPack, ...) from the server.
        /// </summary>
        private async Task AcceptStreamsAsync()
        {
            try
            {
                while (true)
                {
                    ValueTask <QuicStream> streamTask;

                    lock (SyncObj)
                    {
                        if (ShuttingDown)
                        {
                            return;
                        }

                        // No cancellation token is needed here; we expect the operation to cancel itself when _connection is disposed.
                        streamTask = _connection.AcceptStreamAsync(CancellationToken.None);
                    }

                    QuicStream stream = await streamTask.ConfigureAwait(false);

                    // This process is cleaned up when _connection is disposed, and errors are observed via Abort().
                    _ = ProcessServerStreamAsync(stream);
                }
            }
            catch (Exception ex)
            {
                Abort(ex);
            }
        }
Beispiel #3
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);
        }
Beispiel #4
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));
                        }
                    }
                }));
            }
        }
Beispiel #5
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;
        }
Beispiel #6
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);
        }
        public override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                var stream = await _connection.AcceptStreamAsync(cancellationToken);

                var context = new QuicStreamContext(stream, this, _context);
                context.Start();
                return(context);
            }
            catch (QuicConnectionAbortedException ex)
            {
                // Shutdown initiated by peer, abortive.
                // TODO cancel CTS here?
                _log.LogDebug($"Accept loop ended with exception: {ex.Message}");
            }
            catch (QuicOperationAbortedException)
            {
                // Shutdown initiated by us

                // Allow for graceful closure.
            }

            return(null);
        }
Beispiel #8
0
        private static async Task ServerConnectionTask(QuicConnection connection, CancellationToken cancellationToken)
        {
            try
            {
                while (true)
                {
                    var stream = await connection.AcceptStreamAsync(cancellationToken).ConfigureAwait(false);

                    if (!stream.CanRead || !stream.CanWrite)
                    {
                        await connection.CloseAsync(1, cancellationToken).ConfigureAwait(false);

                        return;
                    }

                    _ = Helpers.Dispatch(() => ServerQuicStreamTask(connection, stream));
                }
            }
            catch (QuicConnectionAbortedException e) when(e.ErrorCode == 0)
            {
                // ignore successful closing
            }
            catch (OperationCanceledException)
            {
            }
            finally
            {
                await connection.CloseAsync(1).ConfigureAwait(false);

                connection.Dispose();
            }
        }
Beispiel #9
0
        private Task EnsureControlStreamAcceptedAsync()
        {
            if (_inboundControlStream != null)
            {
                return(Task.CompletedTask);
            }

            return(EnsureControlStreamAcceptedInternalAsync());

            async Task EnsureControlStreamAcceptedInternalAsync()
            {
                Http3LoopbackStream controlStream;

                while (true)
                {
                    QuicStream quicStream = await _connection.AcceptStreamAsync().ConfigureAwait(false);

                    if (!quicStream.CanWrite)
                    {
                        // control stream accepted
                        controlStream = new Http3LoopbackStream(quicStream);
                        break;
                    }

                    // control streams are unidirectional, so this must be a request stream
                    // keep it for later and wait for another stream
                    _delayedStreams.Enqueue(quicStream);
                }

                long?streamType = await controlStream.ReadIntegerAsync();

                Assert.Equal(Http3LoopbackStream.ControlStream, streamType);

                List <(long settingId, long settingValue)> settings = await controlStream.ReadSettingsAsync();

                (long settingId, long settingValue) = Assert.Single(settings);

                Assert.Equal(Http3LoopbackStream.MaxHeaderListSize, settingId);
                MaxHeaderListSize = settingValue;

                _inboundControlStream = controlStream;
            }
        }
        public async Task <Http3LoopbackStream> AcceptStreamAsync()
        {
            QuicStream quicStream = await _connection.AcceptStreamAsync().ConfigureAwait(false);

            var stream = new Http3LoopbackStream(quicStream);

            _openStreams.Add(checked ((int)quicStream.StreamId), stream);
            _currentStream = stream;

            return(stream);
        }
Beispiel #11
0
        public async Task QuicStream_ReadWrite_Random_Success(int readSize, int writeSize)
        {
            byte[] testBuffer = new byte[8192];
            new Random().NextBytes(testBuffer);


            await Task.WhenAll(DoWrite(), DoRead());

            async Task DoWrite()
            {
                using QuicConnection clientConnection = CreateQuicConnection(DefaultListener.ListenEndPoint);
                await clientConnection.ConnectAsync();

                await using QuicStream clientStream = clientConnection.OpenUnidirectionalStream();

                ReadOnlyMemory <byte> sendBuffer = testBuffer;

                while (sendBuffer.Length != 0)
                {
                    ReadOnlyMemory <byte> chunk = sendBuffer.Slice(0, Math.Min(sendBuffer.Length, writeSize));
                    await clientStream.WriteAsync(chunk);

                    sendBuffer = sendBuffer.Slice(chunk.Length);
                }

                clientStream.Shutdown();
                await clientStream.ShutdownWriteCompleted();
            }

            async Task DoRead()
            {
                using QuicConnection serverConnection = await DefaultListener.AcceptConnectionAsync();

                await using QuicStream serverStream = await serverConnection.AcceptStreamAsync();

                byte[] receiveBuffer  = new byte[testBuffer.Length];
                int    totalBytesRead = 0;

                while (totalBytesRead != receiveBuffer.Length)
                {
                    int bytesRead = await serverStream.ReadAsync(receiveBuffer.AsMemory(totalBytesRead, Math.Min(receiveBuffer.Length - totalBytesRead, readSize)));

                    if (bytesRead == 0)
                    {
                        break;
                    }

                    totalBytesRead += bytesRead;
                }

                Assert.True(receiveBuffer.AsSpan().SequenceEqual(testBuffer));
            }
        }
Beispiel #12
0
        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);
        }
Beispiel #13
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();
        }
Beispiel #14
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);
        }
Beispiel #15
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);
        }
Beispiel #16
0
        public override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                var stream = await _connection.AcceptStreamAsync(cancellationToken);

                return(new QuicStreamContext(stream, this, _context));
            }
            catch (QuicException ex)
            {
                // Accept on graceful close throws an aborted exception rather than returning null.
                _log.LogDebug($"Accept loop ended with exception: {ex.Message}");
            }

            return(null);
        }
Beispiel #17
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);
        }
Beispiel #18
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""."));
        }
Beispiel #19
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);
            }
        }
Beispiel #20
0
        public async Task AbortiveConnectionFromClient()
        {
            using QuicConnection clientConnection = CreateQuicConnection(DefaultListener.ListenEndPoint);

            ValueTask clientTask = clientConnection.ConnectAsync();

            using QuicConnection serverConnection = await DefaultListener.AcceptConnectionAsync();

            await clientTask;
            // Close connection on client, verifying server connection is aborted.
            await clientConnection.CloseAsync();

            QuicStream stream = await serverConnection.AcceptStreamAsync();

            // Providers are alaways wrapped right now by a QuicStream. All fields are null here.
            // TODO make sure this returns null.
            Assert.Throws <NullReferenceException>(() => stream.CanRead);
        }
Beispiel #21
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.""."));
        }
Beispiel #22
0
        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() =>
            {
                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);
            }));

            var result = new StreamPairWithOtherDisposables(stream1, stream2);

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

            return(result);
        }
Beispiel #23
0
        public void Start(bool isBlocking, IPEndPoint requiredLocalEP)
        {
            ValidateListenerDisposed();

            QuicConnection serverConnection = AsyncUtil.RunSync <QuicConnection>(()
                                                                                 => this.listener.AcceptConnectionAsync().AsTask());

            QuicStream serverStream = AsyncUtil.RunSync <QuicStream>(()
                                                                     => serverConnection.AcceptStreamAsync().AsTask());

            if (this.lspHooked)
            {
                IPEndPoint actualListenedLocalEP = this.listener.ListenEndPoint as IPEndPoint;

                LspConsole.Instance.InterceptTraffic(this.server.SocketConfig.Type, isBlocking, requiredLocalEP, actualListenedLocalEP);
            }

            this.lspHookedLocalEP = requiredLocalEP;

            this.thread.Start();
        }
Beispiel #24
0
        public static async Task HandleServerConnection(QuicConnection connection, CancellationToken token)
        {
            try
            {
                QuicStream stream = await connection.AcceptStreamAsync(token);

                byte[] buffer = new byte[4 * 1024];

                int read;
                while ((read = await stream.ReadAsync(buffer, token)) > 0)
                {
                    // echo the read data back
                    await stream.WriteAsync(buffer, 0, read, token);

                    await stream.FlushAsync(token);
                }
            }
            finally
            {
                // gracefully close the connection with 0 error code
                await connection.CloseAsync(0);
            }
        }
Beispiel #25
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);
            }
        }
Beispiel #26
0
        public async Task MultipleStreamsOnSingleConnection()
        {
            Task listenTask = Task.Run(async() =>
            {
                {
                    using QuicConnection connection = await DefaultListener.AcceptConnectionAsync();
                    await using QuicStream stream   = await connection.AcceptStreamAsync();
                    await using QuicStream stream2  = 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));
                    }

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

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

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

                    await connection.CloseAsync();
                }
            });

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

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

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

                while (true)
                {
                    int res = await stream2.ReadAsync(memory);
                    if (res == 0)
                    {
                        break;
                    }
                    Assert.True(s_data.Span.SequenceEqual(memory));
                }

                await connection.CloseAsync();
            });

            await(new[] { listenTask, clientTask }).WhenAllOrAnyFailed(millisecondsTimeout: 60000);
        }
Beispiel #27
0
    public override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            var stream = await _connection.AcceptStreamAsync(cancellationToken);

            QuicStreamContext?context = null;

            // Only use pool for bidirectional streams. Just a handful of unidirecitonal
            // streams are created for a connection and they live for the lifetime of the connection.
            if (stream.CanRead && stream.CanWrite)
            {
                lock (_poolLock)
                {
                    StreamPool.TryPop(out context);
                }
            }

            if (context == null)
            {
                context = new QuicStreamContext(this, _context);
            }
            else
            {
                context.ResetFeatureCollection();
                context.ResetItems();
            }

            context.Initialize(stream);
            context.Start();

            QuicLog.AcceptedStream(_log, context);

            return(context);
        }
        catch (QuicConnectionAbortedException ex)
        {
            // Shutdown initiated by peer, abortive.
            _error = ex.ErrorCode;
            QuicLog.ConnectionAborted(_log, this, ex.ErrorCode, ex);

            ThreadPool.UnsafeQueueUserWorkItem(state =>
            {
                state.CancelConnectionClosedToken();
            },
                                               this,
                                               preferLocal: false);

            // Throw error so consumer sees the connection is aborted by peer.
            throw new ConnectionResetException(ex.Message, ex);
        }
        catch (QuicOperationAbortedException ex)
        {
            lock (_shutdownLock)
            {
                // This error should only happen when shutdown has been initiated by the server.
                // If there is no abort reason and we have this error then the connection is in an
                // unexpected state. Abort connection and throw reason error.
                if (_abortReason == null)
                {
                    Abort(new ConnectionAbortedException("Unexpected error when accepting stream.", ex));
                }

                _abortReason !.Throw();
            }
        }
        catch (OperationCanceledException)
        {
            Debug.Assert(cancellationToken.IsCancellationRequested, "Error requires cancellation is requested.");

            lock (_shutdownLock)
            {
                // Connection has been aborted. Throw reason exception.
                _abortReason?.Throw();
            }
        }
        catch (Exception ex)
        {
            Debug.Fail($"Unexpected exception in {nameof(QuicConnectionContext)}.{nameof(AcceptAsync)}: {ex}");
            throw;
        }

        // Return null for graceful closure or cancellation.
        return(null);
    }
Beispiel #28
0
        public async Task SetListenerTimeoutWorksWithSmallTimeout()
        {
            var quicOptions = new QuicListenerOptions();

            quicOptions.IdleTimeout = TimeSpan.FromSeconds(1);
            quicOptions.ServerAuthenticationOptions = GetSslServerAuthenticationOptions();
            quicOptions.ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0);

            using QuicListener listener = new QuicListener(QuicImplementationProviders.MsQuic, quicOptions);

            QuicClientConnectionOptions options = new QuicClientConnectionOptions()
            {
                RemoteEndPoint = listener.ListenEndPoint,
                ClientAuthenticationOptions = GetSslClientAuthenticationOptions(),
            };

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

            using QuicConnection serverConnection = serverTask.Result;

            await Assert.ThrowsAsync <QuicOperationAbortedException>(async() => await serverConnection.AcceptStreamAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(100)));
        }
Beispiel #29
0
        protected static async Task <StreamPair> CreateConnectedStreamsAsync()
        {
            QuicImplementationProvider provider = ImplementationProvider;
            var protocol = new SslApplicationProtocol("quictest");

            QuicListener listener = new QuicListener(provider, new QuicListenerOptions()
            {
                ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0),
                ServerAuthenticationOptions = new SslServerAuthenticationOptions {
                    ApplicationProtocols = new List <SslApplicationProtocol> {
                        protocol
                    }
                },
                CertificateFilePath = "Certs/cert.crt",
                PrivateKeyFilePath  = "Certs/cert.key"
            });

            listener.Start();

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

            await Task.WhenAll(
                Task.Run(async() =>
            {
                connection1 = await listener.AcceptConnectionAsync();
                stream1     = await connection1.AcceptStreamAsync();

                // Hack to force stream creation
                byte[] buffer = new byte[1];
                await stream1.ReadAsync(buffer);
            }),
                Task.Run(async() =>
            {
                connection2 = new QuicConnection(
                    provider,
                    listener.ListenEndPoint,
                    new SslClientAuthenticationOptions()
                {
                    ApplicationProtocols = new List <SslApplicationProtocol>()
                    {
                        protocol
                    }
                });
                await connection2.ConnectAsync();
                stream2 = connection2.OpenBidirectionalStream();

                // Hack to force stream creation
                byte[] buffer = new byte[1];
                await stream2.WriteAsync(buffer);
                await stream2.FlushAsync();
            }));

            var result = new StreamPair(stream1, stream2);

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

            return(result);
        }
Beispiel #30
0
        public async Task SetListenerTimeoutWorksWithSmallTimeout()
        {
            var quicOptions = new QuicListenerOptions();

            quicOptions.IdleTimeout = TimeSpan.FromSeconds(10);
            quicOptions.ServerAuthenticationOptions = GetSslServerAuthenticationOptions();
            quicOptions.ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0);

            using QuicListener listener = new QuicListener(QuicImplementationProviders.MsQuic, quicOptions);

            QuicClientConnectionOptions options = new QuicClientConnectionOptions()
            {
                RemoteEndPoint = listener.ListenEndPoint,
                ClientAuthenticationOptions = GetSslClientAuthenticationOptions(),
            };

            using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options);
            ValueTask clientTask = clientConnection.ConnectAsync();

            using QuicConnection serverConnection = await listener.AcceptConnectionAsync();

            await clientTask;

            await Assert.ThrowsAsync <QuicOperationAbortedException>(async() => await serverConnection.AcceptStreamAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(100)));
        }