public async Task WaitForAvailableBidirectionStreamsAsyncWorks() { using QuicListener listener = CreateQuicListener(maxBidirectionalStreams: 1); using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); Task <QuicConnection> serverTask = listener.AcceptConnectionAsync().AsTask(); await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); using QuicConnection serverConnection = serverTask.Result; // No stream opened yet, should return immediately. Assert.True(clientConnection.WaitForAvailableBidirectionalStreamsAsync().IsCompletedSuccessfully); // Open one stream, should wait till it closes. QuicStream stream = clientConnection.OpenBidirectionalStream(); ValueTask waitTask = clientConnection.WaitForAvailableBidirectionalStreamsAsync(); Assert.False(waitTask.IsCompleted); Assert.Throws <QuicException>(() => clientConnection.OpenBidirectionalStream()); // Close the streams, the waitTask should finish as a result. stream.Dispose(); QuicStream newStream = await serverConnection.AcceptStreamAsync(); newStream.Dispose(); await waitTask.AsTask().WaitAsync(TimeSpan.FromSeconds(10)); }
/// <summary> /// Accepts unidirectional streams (control, QPack, ...) from the server. /// </summary> private async Task AcceptStreamsAsync() { try { while (true) { ValueTask <QuicStream> streamTask; lock (SyncObj) { if (ShuttingDown) { return; } // No cancellation token is needed here; we expect the operation to cancel itself when _connection is disposed. streamTask = _connection.AcceptStreamAsync(CancellationToken.None); } QuicStream stream = await streamTask.ConfigureAwait(false); // This process is cleaned up when _connection is disposed, and errors are observed via Abort(). _ = ProcessServerStreamAsync(stream); } } catch (Exception ex) { Abort(ex); } }
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() { 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)); } } })); } }
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; }
public async Task CallDifferentWriteMethodsWorks() { using QuicConnection clientConnection = CreateQuicConnection(DefaultListener.ListenEndPoint); ValueTask clientTask = clientConnection.ConnectAsync(); using QuicConnection serverConnection = await DefaultListener.AcceptConnectionAsync(); await clientTask; ReadOnlyMemory <byte> helloWorld = Encoding.ASCII.GetBytes("Hello world!"); ReadOnlySequence <byte> ros = CreateReadOnlySequenceFromBytes(helloWorld.ToArray()); Assert.False(ros.IsSingleSegment); using QuicStream clientStream = clientConnection.OpenBidirectionalStream(); ValueTask writeTask = clientStream.WriteAsync(ros); using QuicStream serverStream = await serverConnection.AcceptStreamAsync(); await writeTask; byte[] memory = new byte[24]; int res = await serverStream.ReadAsync(memory); Assert.Equal(12, res); ReadOnlyMemory <ReadOnlyMemory <byte> > romrom = new ReadOnlyMemory <ReadOnlyMemory <byte> >(new ReadOnlyMemory <byte>[] { helloWorld, helloWorld }); await clientStream.WriteAsync(romrom); res = await serverStream.ReadAsync(memory); Assert.Equal(24, res); }
public override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default) { try { var stream = await _connection.AcceptStreamAsync(cancellationToken); var context = new QuicStreamContext(stream, this, _context); context.Start(); return(context); } catch (QuicConnectionAbortedException ex) { // Shutdown initiated by peer, abortive. // TODO cancel CTS here? _log.LogDebug($"Accept loop ended with exception: {ex.Message}"); } catch (QuicOperationAbortedException) { // Shutdown initiated by us // Allow for graceful closure. } return(null); }
private static async Task ServerConnectionTask(QuicConnection connection, CancellationToken cancellationToken) { try { while (true) { var stream = await connection.AcceptStreamAsync(cancellationToken).ConfigureAwait(false); if (!stream.CanRead || !stream.CanWrite) { await connection.CloseAsync(1, cancellationToken).ConfigureAwait(false); return; } _ = Helpers.Dispatch(() => ServerQuicStreamTask(connection, stream)); } } catch (QuicConnectionAbortedException e) when(e.ErrorCode == 0) { // ignore successful closing } catch (OperationCanceledException) { } finally { await connection.CloseAsync(1).ConfigureAwait(false); connection.Dispose(); } }
private Task EnsureControlStreamAcceptedAsync() { if (_inboundControlStream != null) { return(Task.CompletedTask); } return(EnsureControlStreamAcceptedInternalAsync()); async Task EnsureControlStreamAcceptedInternalAsync() { Http3LoopbackStream controlStream; while (true) { QuicStream quicStream = await _connection.AcceptStreamAsync().ConfigureAwait(false); if (!quicStream.CanWrite) { // control stream accepted controlStream = new Http3LoopbackStream(quicStream); break; } // control streams are unidirectional, so this must be a request stream // keep it for later and wait for another stream _delayedStreams.Enqueue(quicStream); } long?streamType = await controlStream.ReadIntegerAsync(); Assert.Equal(Http3LoopbackStream.ControlStream, streamType); List <(long settingId, long settingValue)> settings = await controlStream.ReadSettingsAsync(); (long settingId, long settingValue) = Assert.Single(settings); Assert.Equal(Http3LoopbackStream.MaxHeaderListSize, settingId); MaxHeaderListSize = settingValue; _inboundControlStream = controlStream; } }
public async Task <Http3LoopbackStream> AcceptStreamAsync() { QuicStream quicStream = await _connection.AcceptStreamAsync().ConfigureAwait(false); var stream = new Http3LoopbackStream(quicStream); _openStreams.Add(checked ((int)quicStream.StreamId), stream); _currentStream = stream; return(stream); }
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); }
public async Task ServerToClientUnidirectionalStream_ServerAborts_ClientGetsAbort() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var quicConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await quicConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAsync().DefaultTimeout(); // Act var features = new FeatureCollection(); features.Set <IStreamDirectionFeature>(new DefaultStreamDirectionFeature(canRead: false, canWrite: true)); var serverStream = await serverConnection.ConnectAsync(features).DefaultTimeout(); await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout(); await using var clientStream = await quicConnection.AcceptStreamAsync(); var data = new List <byte>(); var buffer = new byte[1024]; var readCount = 0; while ((readCount = await clientStream.ReadAsync(buffer).DefaultTimeout()) != -1) { data.AddRange(buffer.AsMemory(0, readCount).ToArray()); if (data.Count == TestData.Length) { break; } } Assert.Equal(TestData, data); ((IProtocolErrorCodeFeature)serverStream).Error = (long)Http3ErrorCode.InternalError; serverStream.Abort(new ConnectionAbortedException("Test message")); // TODO - client isn't getting abort? readCount = await clientStream.ReadAsync(buffer).DefaultTimeout(); // Assert Assert.Equal(0, readCount); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Assert.True(quicStreamContext.CanWrite); Assert.False(quicStreamContext.CanRead); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); }
protected override async Task <StreamPair> CreateConnectedStreamsAsync() { QuicImplementationProvider provider = Provider; var protocol = new SslApplicationProtocol("quictest"); var listener = new QuicListener( provider, new IPEndPoint(IPAddress.Loopback, 0), new SslServerAuthenticationOptions { ApplicationProtocols = new List <SslApplicationProtocol> { protocol } }); listener.Start(); QuicConnection connection1 = null, connection2 = null; QuicStream stream1 = null, stream2 = null; await WhenAllOrAnyFailed( Task.Run(async() => { connection1 = await listener.AcceptConnectionAsync(); stream1 = await connection1.AcceptStreamAsync(); }), Task.Run(async() => { connection2 = new QuicConnection( provider, listener.ListenEndPoint, new SslClientAuthenticationOptions() { ApplicationProtocols = new List <SslApplicationProtocol>() { protocol } }); await connection2.ConnectAsync(); stream2 = connection2.OpenBidirectionalStream(); })); var result = new StreamPairWithOtherDisposables(stream1, stream2); result.Disposables.Add(connection1); result.Disposables.Add(connection2); result.Disposables.Add(listener); return(result); }
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); }
public override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default) { try { var stream = await _connection.AcceptStreamAsync(cancellationToken); return(new QuicStreamContext(stream, this, _context)); } catch (QuicException ex) { // Accept on graceful close throws an aborted exception rather than returning null. _log.LogDebug($"Accept loop ended with exception: {ex.Message}"); } return(null); }
public async Task AcceptAsync_ServerStartsAndStopsUnidirectionStream_ClientAccepts() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var quicConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await quicConnection.ConnectAsync().DefaultTimeout(); var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var acceptTask = quicConnection.AcceptStreamAsync(); await using var serverStream = await serverConnection.ConnectAsync(); await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout(); await using var clientStream = await acceptTask.DefaultTimeout(); // Assert Assert.NotNull(clientStream); // Read data from server. var data = new List <byte>(); var buffer = new byte[1024]; var readCount = 0; while ((readCount = await clientStream.ReadAsync(buffer).DefaultTimeout()) != -1) { data.AddRange(buffer.AsMemory(0, readCount).ToArray()); if (data.Count == TestData.Length) { break; } } Assert.Equal(TestData, data); // Complete server. await serverStream.Transport.Output.CompleteAsync().DefaultTimeout(); // Receive complete in client. readCount = await clientStream.ReadAsync(buffer).DefaultTimeout(); Assert.Equal(0, readCount); }
public async Task ServerToClientUnidirectionalStream_ServerAborts_ClientGetsAbort() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var quicConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await quicConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var features = new FeatureCollection(); features.Set <IStreamDirectionFeature>(new DefaultStreamDirectionFeature(canRead: false, canWrite: true)); var serverStream = await serverConnection.ConnectAsync(features).DefaultTimeout(); await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout(); await using var clientStream = await quicConnection.AcceptStreamAsync(); var data = await clientStream.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, data); Logger.LogInformation("Server aborting stream"); ((IProtocolErrorCodeFeature)serverStream).Error = (long)Http3ErrorCode.InternalError; serverStream.Abort(new ConnectionAbortedException("Test message")); var ex = await Assert.ThrowsAsync <QuicStreamAbortedException>(() => clientStream.ReadAsync(new byte[1024]).AsTask()).DefaultTimeout(); // Assert Assert.Equal((long)Http3ErrorCode.InternalError, ex.ErrorCode); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Assert.True(quicStreamContext.CanWrite); Assert.False(quicStreamContext.CanRead); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); Assert.Contains(TestSink.Writes, m => m.Message.Contains(@"shutting down writes because: ""Test message"".")); }
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 AbortiveConnectionFromClient() { using QuicConnection clientConnection = CreateQuicConnection(DefaultListener.ListenEndPoint); ValueTask clientTask = clientConnection.ConnectAsync(); using QuicConnection serverConnection = await DefaultListener.AcceptConnectionAsync(); await clientTask; // Close connection on client, verifying server connection is aborted. await clientConnection.CloseAsync(); QuicStream stream = await serverConnection.AcceptStreamAsync(); // Providers are alaways wrapped right now by a QuicStream. All fields are null here. // TODO make sure this returns null. Assert.Throws <NullReferenceException>(() => stream.CanRead); }
public async Task ServerToClientUnidirectionalStream_ServerWritesDataAndCompletes_GracefullyClosed() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var quicConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await quicConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var features = new FeatureCollection(); features.Set <IStreamDirectionFeature>(new DefaultStreamDirectionFeature(canRead: false, canWrite: true)); var serverStream = await serverConnection.ConnectAsync(features).DefaultTimeout(); await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout(); await using var clientStream = await quicConnection.AcceptStreamAsync(); var data = await clientStream.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, data); await serverStream.Transport.Output.CompleteAsync(); var readCount = await clientStream.ReadAsync(new byte[1024]).DefaultTimeout(); // Assert Assert.Equal(0, readCount); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Assert.True(quicStreamContext.CanWrite); Assert.False(quicStreamContext.CanRead); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); Assert.Contains(TestSink.Writes, m => m.Message.Contains(@"shutting down writes because: ""The QUIC transport's send loop completed gracefully."".")); }
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 void Start(bool isBlocking, IPEndPoint requiredLocalEP) { ValidateListenerDisposed(); QuicConnection serverConnection = AsyncUtil.RunSync <QuicConnection>(() => this.listener.AcceptConnectionAsync().AsTask()); QuicStream serverStream = AsyncUtil.RunSync <QuicStream>(() => serverConnection.AcceptStreamAsync().AsTask()); if (this.lspHooked) { IPEndPoint actualListenedLocalEP = this.listener.ListenEndPoint as IPEndPoint; LspConsole.Instance.InterceptTraffic(this.server.SocketConfig.Type, isBlocking, requiredLocalEP, actualListenedLocalEP); } this.lspHookedLocalEP = requiredLocalEP; this.thread.Start(); }
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 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 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 override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default) { try { var stream = await _connection.AcceptStreamAsync(cancellationToken); QuicStreamContext?context = null; // Only use pool for bidirectional streams. Just a handful of unidirecitonal // streams are created for a connection and they live for the lifetime of the connection. if (stream.CanRead && stream.CanWrite) { lock (_poolLock) { StreamPool.TryPop(out context); } } if (context == null) { context = new QuicStreamContext(this, _context); } else { context.ResetFeatureCollection(); context.ResetItems(); } context.Initialize(stream); context.Start(); QuicLog.AcceptedStream(_log, context); return(context); } catch (QuicConnectionAbortedException ex) { // Shutdown initiated by peer, abortive. _error = ex.ErrorCode; QuicLog.ConnectionAborted(_log, this, ex.ErrorCode, ex); ThreadPool.UnsafeQueueUserWorkItem(state => { state.CancelConnectionClosedToken(); }, this, preferLocal: false); // Throw error so consumer sees the connection is aborted by peer. throw new ConnectionResetException(ex.Message, ex); } catch (QuicOperationAbortedException ex) { lock (_shutdownLock) { // This error should only happen when shutdown has been initiated by the server. // If there is no abort reason and we have this error then the connection is in an // unexpected state. Abort connection and throw reason error. if (_abortReason == null) { Abort(new ConnectionAbortedException("Unexpected error when accepting stream.", ex)); } _abortReason !.Throw(); } } catch (OperationCanceledException) { Debug.Assert(cancellationToken.IsCancellationRequested, "Error requires cancellation is requested."); lock (_shutdownLock) { // Connection has been aborted. Throw reason exception. _abortReason?.Throw(); } } catch (Exception ex) { Debug.Fail($"Unexpected exception in {nameof(QuicConnectionContext)}.{nameof(AcceptAsync)}: {ex}"); throw; } // Return null for graceful closure or cancellation. return(null); }
public async Task SetListenerTimeoutWorksWithSmallTimeout() { var quicOptions = new QuicListenerOptions(); quicOptions.IdleTimeout = TimeSpan.FromSeconds(1); quicOptions.ServerAuthenticationOptions = GetSslServerAuthenticationOptions(); quicOptions.ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0); using QuicListener listener = new QuicListener(QuicImplementationProviders.MsQuic, quicOptions); QuicClientConnectionOptions options = new QuicClientConnectionOptions() { RemoteEndPoint = listener.ListenEndPoint, ClientAuthenticationOptions = GetSslClientAuthenticationOptions(), }; using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); Task <QuicConnection> serverTask = listener.AcceptConnectionAsync().AsTask(); await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); using QuicConnection serverConnection = serverTask.Result; await Assert.ThrowsAsync <QuicOperationAbortedException>(async() => await serverConnection.AcceptStreamAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(100))); }
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 SetListenerTimeoutWorksWithSmallTimeout() { var quicOptions = new QuicListenerOptions(); quicOptions.IdleTimeout = TimeSpan.FromSeconds(10); quicOptions.ServerAuthenticationOptions = GetSslServerAuthenticationOptions(); quicOptions.ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0); using QuicListener listener = new QuicListener(QuicImplementationProviders.MsQuic, quicOptions); QuicClientConnectionOptions options = new QuicClientConnectionOptions() { RemoteEndPoint = listener.ListenEndPoint, ClientAuthenticationOptions = GetSslClientAuthenticationOptions(), }; using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); ValueTask clientTask = clientConnection.ConnectAsync(); using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await clientTask; await Assert.ThrowsAsync <QuicOperationAbortedException>(async() => await serverConnection.AcceptStreamAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(100))); }