Пример #1
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);
        }
Пример #2
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));
                        }
                    }
                }));
            }
        }
Пример #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);
        }
Пример #4
0
        public async Task BasicTest()
        {
            await RunClientServer(
                iterations : 100,
                serverFunction : async connection =>
            {
                await using QuicStream stream = await connection.AcceptStreamAsync();

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

                Assert.Equal(s_data.Length, bytesRead);
                Assert.Equal(s_data, buffer);

                await stream.WriteAsync(s_data, endStream: true);
                await stream.ShutdownCompleted();
            },
                clientFunction : async connection =>
            {
                await using QuicStream stream = connection.OpenBidirectionalStream();

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

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

                Assert.Equal(s_data.Length, bytesRead);
                Assert.Equal(s_data, buffer);

                await stream.ShutdownCompleted();
            }
                );
        }
Пример #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;
        }
Пример #6
0
 private static async Task DoWrites(QuicStream writer, int writeCount)
 {
     for (int i = 0; i < writeCount; i++)
     {
         await writer.WriteAsync(new byte[1]);
     }
 }
Пример #7
0
        private async Task ProcessSends()
        {
            // Resolve `output` PipeReader via the IDuplexPipe interface prior to loop start for performance.
            var output = Output;

            while (true)
            {
                var result = await output.ReadAsync();

                if (result.IsCanceled)
                {
                    break;
                }

                var buffer = result.Buffer;

                var end         = buffer.End;
                var isCompleted = result.IsCompleted;
                if (!buffer.IsEmpty)
                {
                    await _stream.WriteAsync(buffer, endStream : isCompleted);
                }

                output.AdvanceTo(end);

                if (isCompleted)
                {
                    // Once the stream pipe is closed, shutdown the stream.
                    break;
                }
            }
        }
Пример #8
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);
        }
Пример #9
0
            message);                         // message

        private async ValueTask SendSettingsAsync()
        {
            try
            {
                _clientControl = _connection.OpenUnidirectionalStream();
                await _clientControl.WriteAsync(_pool.Settings.Http3SettingsFrame, CancellationToken.None).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Abort(ex);
            }
        }
Пример #10
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));
            }
        }
Пример #11
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);
        }
Пример #12
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);
        }
Пример #13
0
        private static async Task SendAndReceiveEOFAsync(QuicStream s1, QuicStream s2)
        {
            byte[] readBuffer = new byte[1];

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

            await s1.ShutdownWriteCompleted();

            int bytesRead = await s2.ReadAsync(readBuffer);

            Assert.Equal(0, bytesRead);

            // Another read should still give EOF
            bytesRead = await s2.ReadAsync(readBuffer);

            Assert.Equal(0, bytesRead);
        }
Пример #14
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);
            }
        }
Пример #15
0
        public async Task ReadWrite_Random_Success(int readSize, int writeSize)
        {
            byte[] testBuffer = new byte[8192];
            new Random().NextBytes(testBuffer);

            await RunClientServer(
                async clientConnection =>
            {
                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 serverConnection =>
            {
                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));
            });
        }
Пример #16
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);
        }
Пример #17
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);
            }
        }
Пример #18
0
        public async Task CloseAsync_WithOpenStream_LocalAndPeerStreamsFailWithQuicOperationAbortedException(int writesBeforeClose)
        {
            if (IsMockProvider)
            {
                return;
            }

            using var sync = new SemaphoreSlim(0);

            await RunClientServer(
                async clientConnection =>
            {
                using QuicStream clientStream = await clientConnection.OpenBidirectionalStreamAsync();
                await DoWrites(clientStream, writesBeforeClose);

                // Wait for peer to receive data
                await sync.WaitAsync();

                await clientConnection.CloseAsync(ExpectedErrorCode);

                await Assert.ThrowsAsync <QuicOperationAbortedException>(async() => await clientStream.ReadAsync(new byte[1]));
                await Assert.ThrowsAsync <QuicOperationAbortedException>(async() => await clientStream.WriteAsync(new byte[1]));
            },
                async serverConnection =>
            {
                using QuicStream serverStream = await serverConnection.AcceptStreamAsync();
                await DoReads(serverStream, writesBeforeClose);

                sync.Release();

                // Since the peer did the abort, we should receive the abort error code in the exception.
                QuicConnectionAbortedException ex;
                ex = await Assert.ThrowsAsync <QuicConnectionAbortedException>(async() => await serverStream.ReadAsync(new byte[1]));
                Assert.Equal(ExpectedErrorCode, ex.ErrorCode);
                ex = await Assert.ThrowsAsync <QuicConnectionAbortedException>(async() => await serverStream.WriteAsync(new byte[1]));
                Assert.Equal(ExpectedErrorCode, ex.ErrorCode);
            });
        }
Пример #19
0
        private static async Task SendAndReceiveDataAsync(ReadOnlyMemory <byte> data, QuicStream s1, QuicStream s2)
        {
            await s1.WriteAsync(data);

            await ReceiveDataAsync(data, s2);
        }
Пример #20
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);
        }
Пример #21
0
 public async Task SendUnidirectionalStreamTypeAsync(long streamType)
 {
     var buffer       = new byte[MaximumVarIntBytes];
     int bytesWritten = EncodeHttpInteger(streamType, buffer);
     await _stream.WriteAsync(buffer.AsMemory(0, bytesWritten)).ConfigureAwait(false);
 }
        protected override async Task <StreamPair> CreateConnectedStreamsAsync()
        {
            var listener = await QuicListener.ListenAsync(new QuicListenerOptions()
            {
                ListenEndPoint       = new IPEndPoint(IPAddress.Loopback, 0),
                ApplicationProtocols = new List <SslApplicationProtocol>()
                {
                    new SslApplicationProtocol("quictest")
                },
                ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(new QuicServerConnectionOptions()
                {
                    DefaultStreamErrorCode      = QuicTestBase.DefaultStreamErrorCodeServer,
                    DefaultCloseErrorCode       = QuicTestBase.DefaultCloseErrorCodeServer,
                    ServerAuthenticationOptions = GetSslServerAuthenticationOptions()
                })
            });

            byte[] buffer = new byte[1] {
                42
            };
            QuicConnection connection1 = null, connection2 = null;
            QuicStream     stream1 = null, stream2 = null;

            try
            {
                await WhenAllOrAnyFailed(
                    Task.Run(async() =>
                {
                    connection1 = await listener.AcceptConnectionAsync();
                    stream1     = await connection1.AcceptInboundStreamAsync();
                    Assert.Equal(1, await stream1.ReadAsync(buffer));
                }),
                    Task.Run(async() =>
                {
                    try
                    {
                        connection2 = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions()
                        {
                            DefaultStreamErrorCode      = QuicTestBase.DefaultStreamErrorCodeClient,
                            DefaultCloseErrorCode       = QuicTestBase.DefaultCloseErrorCodeClient,
                            RemoteEndPoint              = listener.LocalEndPoint,
                            ClientAuthenticationOptions = GetSslClientAuthenticationOptions()
                        });
                        stream2 = await connection2.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
                        // OpenBidirectionalStream only allocates ID. We will force stream opening
                        // by Writing there and receiving data on the other side.
                        await stream2.WriteAsync(buffer);
                    }
                    catch (Exception ex)
                    {
                        _output?.WriteLine($"Failed to {ex.Message}");
                        throw;
                    }
                }));

                // No need to keep the listener once we have connected connection and streams
                await listener.DisposeAsync();

                var result = new StreamPairWithOtherDisposables(stream1, stream2);
                result.Disposables.Add(connection1);
                result.Disposables.Add(connection2);

                return(result);
            }
            catch
            {
                if (stream1 is not null)
                {
                    await stream1.DisposeAsync();
                }
                if (stream2 is not null)
                {
                    await stream2.DisposeAsync();
                }
                if (connection1 is not null)
                {
                    await connection1.DisposeAsync();
                }
                if (connection2 is not null)
                {
                    await connection2.DisposeAsync();
                }
                throw;
            }
        }
Пример #23
0
        public async Task LargeDataSentAndReceived()
        {
            byte[]    data           = Enumerable.Range(0, 64 * 1024).Select(x => (byte)x).ToArray();
            const int NumberOfWrites = 256;       // total sent = 16M

            using QuicListener listener = CreateQuicListener();

            for (int j = 0; j < 100; j++)
            {
                Task listenTask = Task.Run(async() =>
                {
                    using QuicConnection connection = await listener.AcceptConnectionAsync();
                    await using QuicStream stream   = await connection.AcceptStreamAsync();
                    byte[] buffer = new byte[data.Length];

                    for (int i = 0; i < NumberOfWrites; i++)
                    {
                        int totalBytesRead = 0;
                        while (totalBytesRead < data.Length)
                        {
                            int bytesRead = await stream.ReadAsync(buffer.AsMemory(totalBytesRead));
                            Assert.NotEqual(0, bytesRead);
                            totalBytesRead += bytesRead;
                        }

                        Assert.Equal(data.Length, totalBytesRead);
                        Assert.True(data.AsSpan().SequenceEqual(buffer));
                    }

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

                    await stream.WriteAsync(Memory <byte> .Empty, 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();
                    byte[] buffer = new byte[data.Length];

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

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

                    for (int i = 0; i < NumberOfWrites; i++)
                    {
                        int totalBytesRead = 0;
                        while (totalBytesRead < data.Length)
                        {
                            int bytesRead = await stream.ReadAsync(buffer.AsMemory(totalBytesRead));
                            Assert.NotEqual(0, bytesRead);
                            totalBytesRead += bytesRead;
                        }

                        Assert.Equal(data.Length, totalBytesRead);
                        Assert.True(data.AsSpan().SequenceEqual(buffer));
                    }

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

                await(new[] { listenTask, clientTask }).WhenAllOrAnyFailed(millisecondsTimeout: 1000000);
            }
        }
Пример #24
0
        public async Task CloseAsync_WithOpenStream_LocalAndPeerStreamsFailWithQuicOperationAbortedException(int writesBeforeClose)
        {
            using var sync = new SemaphoreSlim(0);

            await RunClientServer(
                async clientConnection =>
            {
                await using QuicStream clientStream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
                await DoWrites(clientStream, writesBeforeClose);

                // Wait for peer to receive data
                await sync.WaitAsync();

                await clientConnection.CloseAsync(ExpectedErrorCode);

                await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async() => await clientStream.ReadAsync(new byte[1]));
                await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async() => await clientStream.WriteAsync(new byte[1]));
            },
                async serverConnection =>
            {
                await using QuicStream serverStream = await serverConnection.AcceptInboundStreamAsync();
                await DoReads(serverStream, writesBeforeClose);

                sync.Release();

                // Since the peer did the abort, we should receive the abort error code in the exception.
                QuicException ex;
                ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async() => await serverStream.ReadAsync(new byte[1]));
                Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
                ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async() => await serverStream.WriteAsync(new byte[1]));
                Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
            });
        }
Пример #25
0
        public async Task WriteTests(int[][] writes, WriteType writeType)
        {
            await RunClientServer(
                async clientConnection =>
            {
                await using QuicStream stream = clientConnection.OpenUnidirectionalStream();

                foreach (int[] bufferLengths in writes)
                {
                    switch (writeType)
                    {
                    case WriteType.SingleBuffer:
                        foreach (int bufferLength in bufferLengths)
                        {
                            await stream.WriteAsync(new byte[bufferLength]);
                        }
                        break;

                    case WriteType.GatheredBuffers:
                        var buffers = bufferLengths
                                      .Select(bufferLength => new ReadOnlyMemory <byte>(new byte[bufferLength]))
                                      .ToArray();
                        await stream.WriteAsync(buffers);
                        break;

                    case WriteType.GatheredSequence:
                        var firstSegment          = new BufferSegment(new byte[bufferLengths[0]]);
                        BufferSegment lastSegment = firstSegment;

                        foreach (int bufferLength in bufferLengths.Skip(1))
                        {
                            lastSegment = lastSegment.Append(new byte[bufferLength]);
                        }

                        var buffer = new ReadOnlySequence <byte>(firstSegment, 0, lastSegment, lastSegment.Memory.Length);
                        await stream.WriteAsync(buffer);
                        break;

                    default:
                        Debug.Fail("Unknown write type.");
                        break;
                    }
                }

                stream.Shutdown();
                await stream.ShutdownCompleted();
            },
                async serverConnection =>
            {
                await using QuicStream stream = await serverConnection.AcceptStreamAsync();

                var buffer        = new byte[4096];
                int receivedBytes = 0, totalBytes = 0;

                while ((receivedBytes = await stream.ReadAsync(buffer)) != 0)
                {
                    totalBytes += receivedBytes;
                }

                int expectedTotalBytes = writes.SelectMany(x => x).Sum();
                Assert.Equal(expectedTotalBytes, totalBytes);

                stream.Shutdown();
                await stream.ShutdownCompleted();
            });
        }
Пример #26
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);
        }
Пример #27
0
        public async Task Dispose_WithoutClose_ConnectionClosesWithDefault(int writesBeforeClose)
        {
            QuicListenerOptions listenerOptions = CreateQuicListenerOptions();

            using var sync = new SemaphoreSlim(0);

            await RunClientServer(
                async clientConnection =>
            {
                using QuicStream clientStream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
                await DoWrites(clientStream, writesBeforeClose);

                // Wait for peer to receive data
                await sync.WaitAsync();

                await clientConnection.DisposeAsync();

                await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async() => await clientStream.ReadAsync(new byte[1]));
                await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async() => await clientStream.WriteAsync(new byte[1]));
            },
                async serverConnection =>
            {
                using QuicStream serverStream = await serverConnection.AcceptInboundStreamAsync();
                await DoReads(serverStream, writesBeforeClose);

                sync.Release();

                // Since the peer did the abort, we should receive the abort error code in the exception.
                QuicException ex;
                ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async() => await serverStream.ReadAsync(new byte[1]));
                Assert.Equal(DefaultCloseErrorCodeClient, ex.ApplicationErrorCode);
                ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async() => await serverStream.WriteAsync(new byte[1]));
                Assert.Equal(DefaultCloseErrorCodeClient, ex.ApplicationErrorCode);
            }, listenerOptions : listenerOptions);
        }
Пример #28
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);
            }
        }
Пример #29
0
        public async Task Dispose_WithOpenLocalStream_LocalStreamFailsWithQuicOperationAbortedException(int writesBeforeClose)
        {
            if (IsMockProvider)
            {
                return;
            }

            // Set a short idle timeout so that after we dispose the connection, the peer will discover the connection is dead before too long.
            QuicListenerOptions listenerOptions = CreateQuicListenerOptions();

            listenerOptions.IdleTimeout = TimeSpan.FromSeconds(1);

            using var sync = new SemaphoreSlim(0);

            await RunClientServer(
                async clientConnection =>
            {
                using QuicStream clientStream = await clientConnection.OpenBidirectionalStreamAsync();
                await DoWrites(clientStream, writesBeforeClose);

                // Wait for peer to receive data
                await sync.WaitAsync();

                clientConnection.Dispose();

                await Assert.ThrowsAsync <QuicOperationAbortedException>(async() => await clientStream.ReadAsync(new byte[1]));
                await Assert.ThrowsAsync <QuicOperationAbortedException>(async() => await clientStream.WriteAsync(new byte[1]));
            },
                async serverConnection =>
            {
                using QuicStream serverStream = await serverConnection.AcceptStreamAsync();
                await DoReads(serverStream, writesBeforeClose);

                sync.Release();

                // The client has done an abortive shutdown of the connection, which means we are not notified that the connection has closed.
                // But the connection idle timeout should kick in and eventually we will get exceptions.
                await Assert.ThrowsAsync <QuicConnectionAbortedException>(async() => await serverStream.ReadAsync(new byte[1]));
                await Assert.ThrowsAsync <QuicConnectionAbortedException>(async() => await serverStream.WriteAsync(new byte[1]));
            }, listenerOptions : listenerOptions);
        }
Пример #30
0
        public override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // Wait for an available stream (based on QUIC MAX_STREAMS) if there isn't one available yet.

            TaskCompletionSourceWithCancellation <bool> waitForAvailableStreamTcs = null;

            lock (SyncObj)
            {
                long remaining = _requestStreamsRemaining;

                if (remaining > 0)
                {
                    _requestStreamsRemaining = remaining - 1;
                }
                else
                {
                    waitForAvailableStreamTcs = new TaskCompletionSourceWithCancellation <bool>();
                    _waitingRequests.Enqueue(waitForAvailableStreamTcs);
                }
            }

            if (waitForAvailableStreamTcs != null)
            {
                await waitForAvailableStreamTcs.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false);
            }

            // Allocate an active request

            QuicStream         quicStream    = null;
            Http3RequestStream requestStream = null;

            try
            {
                lock (SyncObj)
                {
                    if (_connection != null)
                    {
                        quicStream    = _connection.OpenBidirectionalStream();
                        requestStream = new Http3RequestStream(request, this, quicStream);
                        _activeRequests.Add(quicStream, requestStream);
                    }
                }

                if (quicStream == null)
                {
                    throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnSameOrNextProxy);
                }

                // 0-byte write to force QUIC to allocate a stream ID.
                await quicStream.WriteAsync(Array.Empty <byte>(), cancellationToken).ConfigureAwait(false);

                requestStream.StreamId = quicStream.StreamId;

                bool goAway;
                lock (SyncObj)
                {
                    goAway = _lastProcessedStreamId != -1 && requestStream.StreamId > _lastProcessedStreamId;
                }

                if (goAway)
                {
                    throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnSameOrNextProxy);
                }

                Task <HttpResponseMessage> responseTask = requestStream.SendAsync(cancellationToken);

                // null out requestStream to avoid disposing in finally block. It is now in charge of disposing itself.
                requestStream = null;

                return(await responseTask.ConfigureAwait(false));
            }
            catch (QuicConnectionAbortedException ex)
            {
                // This will happen if we aborted _connection somewhere.
                Abort(ex);
                throw new HttpRequestException(SR.Format(SR.net_http_http3_connection_error, ex.ErrorCode), ex, RequestRetryType.RetryOnSameOrNextProxy);
            }
            finally
            {
                requestStream?.Dispose();
            }
        }