Ejemplo n.º 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);
        }
Ejemplo n.º 2
0
        private static async Task SendAndReceiveEOFAsync(QuicStream s1, QuicStream s2)
        {
            byte[] readBuffer = new byte[1];

            s1.ShutdownWrite();

            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);
        }
Ejemplo n.º 3
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));
                        }
                    }
                }));
            }
        }
Ejemplo n.º 4
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;
        }
Ejemplo n.º 5
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);
        }
Ejemplo n.º 6
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);
        }
Ejemplo n.º 7
0
        private static async Task <QuicStream> OpenAndUseStreamAsync(QuicConnection c)
        {
            QuicStream s = await c.OpenBidirectionalStreamAsync();

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

            return(s);
        }
Ejemplo n.º 8
0
        private static async Task DoReads(QuicStream reader, int readCount)
        {
            for (int i = 0; i < readCount; i++)
            {
                int bytesRead = await reader.ReadAsync(new byte[1]);

                Assert.Equal(1, bytesRead);
            }
        }
Ejemplo n.º 9
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);
        }
Ejemplo n.º 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));
            }
        }
Ejemplo n.º 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);
        }
Ejemplo n.º 12
0
        private static async Task ReceiveDataAsync(ReadOnlyMemory <byte> data, QuicStream s)
        {
            Memory <byte> readBuffer = new byte[data.Length];

            int bytesRead = 0;

            while (bytesRead < data.Length)
            {
                bytesRead += await s.ReadAsync(readBuffer.Slice(bytesRead));
            }

            Assert.True(data.Span.SequenceEqual(readBuffer.Span));
        }
Ejemplo n.º 13
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);
            }
        }
Ejemplo n.º 14
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));
            });
        }
Ejemplo n.º 15
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);
        }
Ejemplo n.º 16
0
        private async Task ProcessReceives()
        {
            var input = Input;

            while (true)
            {
                var buffer        = Input.GetMemory(MinAllocBufferSize);
                var bytesReceived = await _stream.ReadAsync(buffer);

                if (bytesReceived == 0)
                {
                    // Read completed.
                    break;
                }

                input.Advance(bytesReceived);

                var flushTask = input.FlushAsync();

                var paused = !flushTask.IsCompleted;

                if (paused)
                {
                    _log.StreamPause(ConnectionId);
                }

                var result = await flushTask;

                if (paused)
                {
                    _log.StreamResume(ConnectionId);
                }

                if (result.IsCompleted || result.IsCanceled)
                {
                    // Pipe consumer is shut down, do we stop writing
                    break;
                }
            }
        }
Ejemplo n.º 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);
            }
        }
Ejemplo n.º 18
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);
            });
        }
Ejemplo n.º 19
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);
            });
        }
Ejemplo n.º 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);
        }
Ejemplo n.º 21
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);
        }
Ejemplo n.º 22
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);
            }
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Reads the server's control stream.
        /// </summary>
        private async Task ProcessServerControlStreamAsync(QuicStream stream, ArrayBuffer buffer)
        {
            using (buffer)
            {
                // Read the first frame of the control stream. Per spec:
                // A SETTINGS frame MUST be sent as the first frame of each control stream.

                (Http3FrameType? frameType, long payloadLength) = await ReadFrameEnvelopeAsync().ConfigureAwait(false);

                if (frameType == null)
                {
                    // Connection closed prematurely, expected SETTINGS frame.
                    throw new Http3ConnectionException(Http3ErrorCode.ClosedCriticalStream);
                }

                if (frameType != Http3FrameType.Settings)
                {
                    throw new Http3ConnectionException(Http3ErrorCode.MissingSettings);
                }

                await ProcessSettingsFrameAsync(payloadLength).ConfigureAwait(false);

                // Read subsequent frames.

                while (true)
                {
                    (frameType, payloadLength) = await ReadFrameEnvelopeAsync().ConfigureAwait(false);

                    switch (frameType)
                    {
                    case Http3FrameType.GoAway:
                        await ProcessGoAwayFameAsync(payloadLength).ConfigureAwait(false);

                        break;

                    case Http3FrameType.Settings:
                        // If an endpoint receives a second SETTINGS frame on the control stream, the endpoint MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
                        throw new Http3ConnectionException(Http3ErrorCode.UnexpectedFrame);

                    case Http3FrameType.Headers:
                    case Http3FrameType.Data:
                    case Http3FrameType.MaxPushId:
                    case Http3FrameType.DuplicatePush:
                        // Servers should not send these frames to a control stream.
                        throw new Http3ConnectionException(Http3ErrorCode.UnexpectedFrame);

                    case Http3FrameType.PushPromise:
                    case Http3FrameType.CancelPush:
                        // Because we haven't sent any MAX_PUSH_ID frame, it is invalid to receive any push-related frames as they will all reference a too-large ID.
                        throw new Http3ConnectionException(Http3ErrorCode.IdError);

                    case null:
                        // End of stream reached. If we're shutting down, stop looping. Otherwise, this is an error (this stream should not be closed for life of connection).
                        bool shuttingDown;
                        lock (SyncObj)
                        {
                            shuttingDown = ShuttingDown;
                        }
                        if (!shuttingDown)
                        {
                            throw new Http3ConnectionException(Http3ErrorCode.ClosedCriticalStream);
                        }
                        return;

                    default:
                        await SkipUnknownPayloadAsync(frameType.GetValueOrDefault(), payloadLength).ConfigureAwait(false);

                        break;
                    }
                }
            }

            async ValueTask <(Http3FrameType?frameType, long payloadLength)> ReadFrameEnvelopeAsync()
            {
                long frameType, payloadLength;
                int  bytesRead;

                while (!Http3Frame.TryReadIntegerPair(buffer.ActiveSpan, out frameType, out payloadLength, out bytesRead))
                {
                    buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength * 2);
                    bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                    if (bytesRead != 0)
                    {
                        buffer.Commit(bytesRead);
                    }
                    else if (buffer.ActiveLength == 0)
                    {
                        // End of stream.
                        return(null, 0);
                    }
                    else
                    {
                        // Our buffer has partial frame data in it but not enough to complete the read: bail out.
                        throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                    }
                }

                buffer.Discard(bytesRead);

                return((Http3FrameType)frameType, payloadLength);
            }

            async ValueTask ProcessSettingsFrameAsync(long settingsPayloadLength)
            {
                while (settingsPayloadLength != 0)
                {
                    long settingId, settingValue;
                    int  bytesRead;

                    while (!Http3Frame.TryReadIntegerPair(buffer.ActiveSpan, out settingId, out settingValue, out bytesRead))
                    {
                        buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength * 2);
                        bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                        if (bytesRead != 0)
                        {
                            buffer.Commit(bytesRead);
                        }
                        else
                        {
                            // Our buffer has partial frame data in it but not enough to complete the read: bail out.
                            throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                        }
                    }

                    settingsPayloadLength -= bytesRead;

                    if (settingsPayloadLength < 0)
                    {
                        // An integer was encoded past the payload length.
                        // A frame payload that contains additional bytes after the identified fields or a frame payload that terminates before the end of the identified fields MUST be treated as a connection error of type H3_FRAME_ERROR.
                        throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                    }

                    buffer.Discard(bytesRead);

                    // Only support this single setting. Skip others.
                    if (settingId == (long)Http3SettingType.MaxHeaderListSize)
                    {
                        _maximumHeadersLength = (int)Math.Min(settingValue, int.MaxValue);
                    }
                }
            }

            async ValueTask ProcessGoAwayFameAsync(long goawayPayloadLength)
            {
                long lastStreamId;
                int  bytesRead;

                while (!VariableLengthIntegerHelper.TryRead(buffer.AvailableSpan, out lastStreamId, out bytesRead))
                {
                    buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength);
                    bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                    if (bytesRead != 0)
                    {
                        buffer.Commit(bytesRead);
                    }
                    else
                    {
                        // Our buffer has partial frame data in it but not enough to complete the read: bail out.
                        throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                    }
                }

                buffer.Discard(bytesRead);
                if (bytesRead != goawayPayloadLength)
                {
                    // Frame contains unknown extra data after the integer.
                    throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                }

                OnServerGoAway(lastStreamId);
            }

            async ValueTask SkipUnknownPayloadAsync(Http3FrameType frameType, long payloadLength)
            {
                while (payloadLength != 0)
                {
                    if (buffer.ActiveLength == 0)
                    {
                        int bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                        if (bytesRead != 0)
                        {
                            buffer.Commit(bytesRead);
                        }
                        else
                        {
                            // Our buffer has partial frame data in it but not enough to complete the read: bail out.
                            throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                        }
                    }

                    long readLength = Math.Min(payloadLength, buffer.ActiveLength);
                    buffer.Discard((int)readLength);
                    payloadLength -= readLength;
                }
            }
        }
Ejemplo n.º 24
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);
            }
        }
Ejemplo n.º 25
0
        /// <summary>
        /// Reads the server's control stream.
        /// </summary>
        private async Task ProcessServerControlStreamAsync(QuicStream stream, ArrayBuffer buffer)
        {
            (Http3FrameType? frameType, long payloadLength) = await ReadFrameEnvelopeAsync().ConfigureAwait(false);

            if (frameType == null)
            {
                // Connection closed prematurely, expected SETTINGS frame.
                throw new Http3ConnectionException(Http3ErrorCode.ClosedCriticalStream);
            }

            if (frameType != Http3FrameType.Settings)
            {
                // Expected SETTINGS as first frame of control stream.
                throw new Http3ConnectionException(Http3ErrorCode.MissingSettings);
            }

            await ProcessSettingsFrameAsync(payloadLength).ConfigureAwait(false);

            while (true)
            {
                (frameType, payloadLength) = await ReadFrameEnvelopeAsync().ConfigureAwait(false);

                switch (frameType)
                {
                case Http3FrameType.GoAway:
                    await ProcessGoAwayFameAsync(payloadLength).ConfigureAwait(false);

                    break;

                case Http3FrameType.Settings:
                    // Only a single SETTINGS frame is supported.
                    throw new Http3ConnectionException(Http3ErrorCode.UnexpectedFrame);

                case Http3FrameType.Headers:
                case Http3FrameType.Data:
                case Http3FrameType.MaxPushId:
                case Http3FrameType.DuplicatePush:
                    // Servers should not send these frames to a control stream.
                    throw new Http3ConnectionException(Http3ErrorCode.UnexpectedFrame);

                case Http3FrameType.PushPromise:
                case Http3FrameType.CancelPush:
                    // Because we haven't sent any MAX_PUSH_ID frame, it is invalid to receive any push-related frames as they will all reference a too-large ID.
                    throw new Http3ConnectionException(Http3ErrorCode.IdError);

                case null:
                    // End of stream reached. If we're shutting down, stop looping. Otherwise, this is an error (this stream should not be closed for life of connection).
                    bool shuttingDown;
                    lock (SyncObj)
                    {
                        shuttingDown = ShuttingDown;
                    }
                    if (!shuttingDown)
                    {
                        throw new Http3ConnectionException(Http3ErrorCode.ClosedCriticalStream);
                    }
                    return;

                default:
                    await SkipUnknownPayloadAsync(frameType.GetValueOrDefault(), payloadLength).ConfigureAwait(false);

                    break;
                }
            }

            async ValueTask <(Http3FrameType?frameType, long payloadLength)> ReadFrameEnvelopeAsync()
            {
                long frameType, payloadLength;
                int  bytesRead;

                while (!Http3Frame.TryReadIntegerPair(buffer.ActiveSpan, out frameType, out payloadLength, out bytesRead))
                {
                    buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength * 2);
                    bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                    if (bytesRead != 0)
                    {
                        buffer.Commit(bytesRead);
                    }
                    else if (buffer.ActiveLength == 0)
                    {
                        // End of stream.
                        return(null, 0);
                    }
                    else
                    {
                        // Our buffer has partial frame data in it but not enough to complete the read: bail out.
                        throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                    }
                }

                buffer.Discard(bytesRead);

                return((Http3FrameType)frameType, payloadLength);
            }

            async ValueTask ProcessSettingsFrameAsync(long settingsPayloadLength)
            {
                if (settingsPayloadLength > MaximumSettingsPayloadLength)
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        Trace($"Received SETTINGS frame with {settingsPayloadLength} byte payload exceeding the {MaximumSettingsPayloadLength} byte maximum.");
                    }
                    throw new Http3ConnectionException(Http3ErrorCode.ExcessiveLoad);
                }

                while (settingsPayloadLength != 0)
                {
                    long settingId, settingValue;
                    int  bytesRead;

                    while (!Http3Frame.TryReadIntegerPair(buffer.ActiveSpan, out settingId, out settingValue, out bytesRead))
                    {
                        buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength * 2);
                        bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                        if (bytesRead != 0)
                        {
                            buffer.Commit(bytesRead);
                        }
                        else
                        {
                            // Our buffer has partial frame data in it but not enough to complete the read: bail out.
                            throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                        }
                    }

                    settingsPayloadLength -= bytesRead;

                    // Only support this single setting. Skip others.
                    if (settingId == (long)Http3SettingType.MaxHeaderListSize)
                    {
                        _maximumHeadersLength = (int)Math.Min(settingValue, int.MaxValue);
                    }
                }
            }

            async ValueTask ProcessGoAwayFameAsync(long goawayPayloadLength)
            {
                long lastStreamId;
                int  bytesRead;

                while (!VariableLengthIntegerHelper.TryRead(buffer.AvailableSpan, out lastStreamId, out bytesRead))
                {
                    buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength);
                    bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                    if (bytesRead != 0)
                    {
                        buffer.Commit(bytesRead);
                    }
                    else
                    {
                        // Our buffer has partial frame data in it but not enough to complete the read: bail out.
                        throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                    }
                }

                buffer.Discard(bytesRead);
                if (bytesRead != goawayPayloadLength)
                {
                    // Frame contains unknown extra data after the integer.
                    throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                }

                OnServerGoAway(lastStreamId);
            }

            async ValueTask SkipUnknownPayloadAsync(Http3FrameType frameType, long payloadLength)
            {
                if (payloadLength > MaximumUnknownFramePayloadLength)
                {
                    Trace($"Received unknown frame type 0x{(long)frameType:x} with {payloadLength} byte payload exceeding the {MaximumUnknownFramePayloadLength} byte maximum.");
                    throw new Http3ConnectionException(Http3ErrorCode.ExcessiveLoad);
                }

                while (payloadLength != 0)
                {
                    if (buffer.ActiveLength == 0)
                    {
                        int bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                        if (bytesRead != 0)
                        {
                            buffer.Commit(bytesRead);
                        }
                        else
                        {
                            // Our buffer has partial frame data in it but not enough to complete the read: bail out.
                            throw new Http3ConnectionException(Http3ErrorCode.FrameError);
                        }
                    }

                    long readLength = Math.Min(payloadLength, buffer.ActiveLength);
                    buffer.Discard((int)readLength);
                    payloadLength -= readLength;
                }
            }
        }
Ejemplo n.º 26
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);
        }
Ejemplo n.º 27
0
        /// <summary>
        /// Routes a stream to an appropriate stream-type-specific processor
        /// </summary>
        private async Task ProcessServerStreamAsync(QuicStream stream)
        {
            try
            {
                await using (stream.ConfigureAwait(false))
                    using (var buffer = new ArrayBuffer(initialSize: 32, usePool: true))
                    {
                        if (stream.CanWrite)
                        {
                            // Server initiated bidirectional streams are either push streams or extensions, and we support neither.
                            throw new Http3ConnectionException(Http3ErrorCode.StreamCreationError);
                        }

                        int bytesRead;

                        try
                        {
                            bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);
                        }
                        catch (QuicStreamAbortedException)
                        {
                            // Treat identical to receiving 0. See below comment.
                            bytesRead = 0;
                        }

                        if (bytesRead == 0)
                        {
                            // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-unidirectional-streams
                            // A sender can close or reset a unidirectional stream unless otherwise specified. A receiver MUST
                            // tolerate unidirectional streams being closed or reset prior to the reception of the unidirectional
                            // stream header.
                            return;
                        }

                        buffer.Commit(bytesRead);

                        // Stream type is a variable-length integer, but we only check the first byte. There is no known type requiring more than 1 byte.
                        switch (buffer.ActiveSpan[0])
                        {
                        case (byte)Http3StreamType.Control:
                            if (Interlocked.Exchange(ref _haveServerControlStream, 1) != 0)
                            {
                                // A second control stream has been received.
                                throw new Http3ConnectionException(Http3ErrorCode.StreamCreationError);
                            }

                            // Discard the stream type header.
                            buffer.Discard(1);

                            await ProcessServerControlStreamAsync(stream, buffer).ConfigureAwait(false);

                            return;

                        case (byte)Http3StreamType.QPackDecoder:
                            if (Interlocked.Exchange(ref _haveServerQpackDecodeStream, 1) != 0)
                            {
                                // A second QPack decode stream has been received.
                                throw new Http3ConnectionException(Http3ErrorCode.StreamCreationError);
                            }

                            // The stream must not be closed, but we aren't using QPACK right now -- ignore.
                            buffer.Dispose();
                            await stream.CopyToAsync(Stream.Null).ConfigureAwait(false);

                            return;

                        case (byte)Http3StreamType.QPackEncoder:
                            if (Interlocked.Exchange(ref _haveServerQpackEncodeStream, 1) != 0)
                            {
                                // A second QPack encode stream has been received.
                                throw new Http3ConnectionException(Http3ErrorCode.StreamCreationError);
                            }

                            // The stream must not be closed, but we aren't using QPACK right now -- ignore.
                            buffer.Dispose();
                            await stream.CopyToAsync(Stream.Null).ConfigureAwait(false);

                            return;

                        case (byte)Http3StreamType.Push:
                            // We don't support push streams.
                            // Because no maximum push stream ID was negotiated via a MAX_PUSH_ID frame, server should not have sent this. Abort the connection with H3_ID_ERROR.
                            throw new Http3ConnectionException(Http3ErrorCode.IdError);

                        default:
                            // Unknown stream type. Per spec, these must be ignored and aborted but not be considered a connection-level error.

                            if (NetEventSource.Log.IsEnabled())
                            {
                                // Read the rest of the integer, which might be more than 1 byte, so we can log it.

                                long unknownStreamType;
                                while (!VariableLengthIntegerHelper.TryRead(buffer.ActiveSpan, out unknownStreamType, out _))
                                {
                                    buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength);
                                    bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                                    if (bytesRead == 0)
                                    {
                                        unknownStreamType = -1;
                                        break;
                                    }

                                    buffer.Commit(bytesRead);
                                }

                                NetEventSource.Info(this, $"Ignoring server-initiated stream of unknown type {unknownStreamType}.");
                            }

                            stream.AbortWrite((long)Http3ErrorCode.StreamCreationError);
                            return;
                        }
                    }
            }
            catch (Exception ex)
            {
                Abort(ex);
            }
        }
Ejemplo n.º 28
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);
        }
Ejemplo n.º 29
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();
            });
        }
        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;
            }
        }