public async Task CallDifferentWriteMethodsWorks() { using QuicConnection clientConnection = CreateQuicConnection(DefaultListener.ListenEndPoint); ValueTask clientTask = clientConnection.ConnectAsync(); using QuicConnection serverConnection = await DefaultListener.AcceptConnectionAsync(); await clientTask; ReadOnlyMemory <byte> helloWorld = Encoding.ASCII.GetBytes("Hello world!"); ReadOnlySequence <byte> ros = CreateReadOnlySequenceFromBytes(helloWorld.ToArray()); Assert.False(ros.IsSingleSegment); using QuicStream clientStream = clientConnection.OpenBidirectionalStream(); ValueTask writeTask = clientStream.WriteAsync(ros); using QuicStream serverStream = await serverConnection.AcceptStreamAsync(); await writeTask; byte[] memory = new byte[24]; int res = await serverStream.ReadAsync(memory); Assert.Equal(12, res); ReadOnlyMemory <ReadOnlyMemory <byte> > romrom = new ReadOnlyMemory <ReadOnlyMemory <byte> >(new ReadOnlyMemory <byte>[] { helloWorld, helloWorld }); await clientStream.WriteAsync(romrom); res = await serverStream.ReadAsync(memory); Assert.Equal(24, res); }
public 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)); } } })); } }
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); }
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(); } ); }
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; }
private static async Task DoWrites(QuicStream writer, int writeCount) { for (int i = 0; i < writeCount; i++) { await writer.WriteAsync(new byte[1]); } }
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; } } }
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); }
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); } }
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)); } }
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); }
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); }
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); }
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); } }
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)); }); }
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); }
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); } }
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); }); }
private static async Task SendAndReceiveDataAsync(ReadOnlyMemory <byte> data, QuicStream s1, QuicStream s2) { await s1.WriteAsync(data); await ReceiveDataAsync(data, s2); }
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); }
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; } }
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); } }
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); }); }
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 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); }
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); }
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); } }
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); }
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(); } }