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)); }
public override ValueTask <ConnectionContext> ConnectAsync(IFeatureCollection?features = null, CancellationToken cancellationToken = default) { QuicStream quicStream; var streamDirectionFeature = features?.Get <IStreamDirectionFeature>(); if (streamDirectionFeature != null) { if (streamDirectionFeature.CanRead) { quicStream = _connection.OpenBidirectionalStream(); } else { quicStream = _connection.OpenUnidirectionalStream(); } } else { quicStream = _connection.OpenBidirectionalStream(); } // Only a handful of control streams are created by the server and they last for the // lifetime of the connection. No value in pooling them. QuicStreamContext?context = new QuicStreamContext(this, _context); context.Initialize(quicStream); context.Start(); QuicLog.ConnectedStream(_log, context); return(new ValueTask <ConnectionContext>(context)); }
public ValueTask <ConnectionContext> StartBidirectionalStreamAsync() { var stream = _connection.OpenBidirectionalStream(); var context = new QuicStreamContext(stream, this, _context); context.Start(); return(new ValueTask <ConnectionContext>(context)); }
public async Task ClientCertificate_Required_Sent_Populated() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory, clientCertificateRequired : true); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); var testCert = TestResources.GetTestCertificate(); options.ClientAuthenticationOptions.ClientCertificates = new X509CertificateCollection { testCert }; // Act using var quicConnection = new QuicConnection(options); await quicConnection.ConnectAsync().DefaultTimeout(); var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Server waits for stream from client var serverStreamTask = serverConnection.AcceptAsync().DefaultTimeout(); // Client creates stream using var clientStream = quicConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData).DefaultTimeout(); // Server finishes accepting var serverStream = await serverStreamTask.DefaultTimeout(); // Assert AssertTlsConnectionFeature(serverConnection.Features, testCert); AssertTlsConnectionFeature(serverStream.Features, testCert);
public async Task StreamPool_StreamAbortedOnClientAndServer_NotPooled() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); var testHeartbeatFeature = new TestHeartbeatFeature(); serverConnection.Features.Set <IConnectionHeartbeatFeature>(testHeartbeatFeature); // Act & Assert var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(0, quicConnectionContext.StreamPool.Count); var clientStream = clientConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData).DefaultTimeout(); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); clientStream.AbortWrite((long)Http3ErrorCode.InternalError); // Receive abort form client. var serverEx = await Assert.ThrowsAsync <ConnectionResetException>(() => serverStream.Transport.Input.ReadAsync().AsTask()).DefaultTimeout(); Assert.Equal("Stream aborted by peer (258).", serverEx.Message); Assert.Equal((long)Http3ErrorCode.InternalError, ((QuicStreamAbortedException)serverEx.InnerException).ErrorCode); serverStream.Features.Get <IProtocolErrorCodeFeature>().Error = (long)Http3ErrorCode.RequestRejected; serverStream.Abort(new ConnectionAbortedException("Test message.")); // Complete server. await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); var buffer = new byte[1024]; var clientEx = await Assert.ThrowsAsync <QuicStreamAbortedException>(() => clientStream.ReadAsync(buffer).AsTask()).DefaultTimeout(); Assert.Equal((long)Http3ErrorCode.RequestRejected, clientEx.ErrorCode); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); await quicStreamContext.DisposeAsync(); Assert.Equal(0, quicConnectionContext.StreamPool.Count); }
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 static async Task <QuicStreamContext> CreateAndCompleteBidirectionalStreamGracefully(QuicConnection clientConnection, MultiplexedConnectionContext serverConnection) { var clientStream = clientConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData, endStream : true).DefaultTimeout(); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); // Input should be completed. readResult = await serverStream.Transport.Input.ReadAsync(); Assert.True(readResult.IsCompleted); // Complete reading and writing. await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); Assert.True(quicStreamContext.CanWrite); Assert.True(quicStreamContext.CanRead); await quicStreamContext.DisposeAsync(); quicStreamContext.Dispose(); return(quicStreamContext); }
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 QuicServerConnection( QuicConnection clientConnection, QuicServerTransport quicServerTransport, IPEndPoint lspHookedLocalEP, bool isLspHooked) { if (clientConnection == null) { throw new ArgumentNullException(nameof(clientConnection)); } if (quicServerTransport == null) { throw new ArgumentNullException(nameof(quicServerTransport)); } if (!clientConnection.Connected) { throw new ArgumentException($"Client has already disconnected"); } this.server = quicServerTransport; this.connection = clientConnection; this.stream = clientConnection.OpenBidirectionalStream(); this.thread = new ThreadManager(QuicServerConnectionReceiveLoop, Unblock); this.buffer = new BytesBuffer(); this.localEndPoint = clientConnection.LocalEndPoint as IPEndPoint; this.remoteEndPoint = clientConnection.RemoteEndPoint as IPEndPoint; }
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); }
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 <QuicStream> OpenAndUseStreamAsync(QuicConnection c) { QuicStream s = c.OpenBidirectionalStream(); // This will pend await s.ReadAsync(new byte[1]); return(s); }
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); }
public async Task AcceptAsync_ClientStartsAndStopsBidirectionStream_ServerAccepts() { // 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 = serverConnection.AcceptAsync(); await using var clientStream = quicConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData); await using var serverStream = await acceptTask.DefaultTimeout(); await serverStream.Transport.Output.WriteAsync(TestData); // Assert Assert.NotNull(serverStream); Assert.False(serverStream.ConnectionClosed.IsCancellationRequested); var closedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); serverStream.ConnectionClosed.Register(() => closedTcs.SetResult()); // Read data from client. var read = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, read.Buffer.ToArray()); serverStream.Transport.Input.AdvanceTo(read.Buffer.End); // Read data from server. var data = await clientStream.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, data); // Shutdown from client. clientStream.Shutdown(); // Get shutdown from client. read = await serverStream.Transport.Input.ReadAsync().DefaultTimeout(); Assert.True(read.IsCompleted); await serverStream.Transport.Output.CompleteAsync(); await closedTcs.Task.DefaultTimeout(); }
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() => { try { 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); } 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 StreamPool_StreamAbortedOnServerAfterComplete_NotPooled() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); var testHeartbeatFeature = new TestHeartbeatFeature(); serverConnection.Features.Set <IConnectionHeartbeatFeature>(testHeartbeatFeature); // Act & Assert var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); Assert.Equal(0, quicConnectionContext.StreamPool.Count); var clientStream = clientConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData, endStream : true).DefaultTimeout(); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); // Input should be completed. readResult = await serverStream.Transport.Input.ReadAsync(); Assert.True(readResult.IsCompleted); // Complete reading and writing. await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); serverStream.Abort(new ConnectionAbortedException("Test message")); await quicStreamContext.DisposeAsync(); Assert.Equal(0, quicConnectionContext.StreamPool.Count); }
public async Task StreamAbortFeature_AbortWrite_ClientReceivesAbort() { // 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 await using var clientStream = quicConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData).DefaultTimeout(); await using var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); var serverReadTask = serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).AsTask(); var streamAbortFeature = serverStream.Features.Get <IStreamAbortFeature>(); streamAbortFeature.AbortRead((long)Http3ErrorCode.InternalError, new ConnectionAbortedException("Test reason")); // Assert // Server writes data await serverStream.Transport.Output.WriteAsync(TestData).DefaultTimeout(); // Server completes its output. await serverStream.Transport.Output.CompleteAsync().DefaultTimeout(); // Client successfully reads data to end var data = await clientStream.ReadUntilEndAsync().DefaultTimeout(); Assert.Equal(TestData, data); // Client errors when writing var clientEx = await Assert.ThrowsAsync <QuicStreamAbortedException>(() => clientStream.WriteAsync(data).AsTask()).DefaultTimeout(); Assert.Equal((long)Http3ErrorCode.InternalError, clientEx.ErrorCode); // Server errors when reading var serverEx = await Assert.ThrowsAsync <ConnectionAbortedException>(() => serverReadTask).DefaultTimeout(); Assert.Equal("Test reason", serverEx.Message); }
public async Task GetStreamIdWithoutStartWorks() { using QuicListener listener = CreateQuicListener(); using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); ValueTask clientTask = clientConnection.ConnectAsync(); using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await clientTask; using QuicStream clientStream = clientConnection.OpenBidirectionalStream(); Assert.Equal(0, clientStream.StreamId); }
public async Task BidirectionalStream_ReadAborted_NotPooled() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var clientStream = clientConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData).DefaultTimeout(); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); await clientStream.WriteAsync(TestData).DefaultTimeout(); // Complete writing. await serverStream.Transport.Output.CompleteAsync(); // Abort read-side of the stream and then complete pipe. // This simulates what Kestrel does when a request finishes without // reading the request body to the end. serverStream.Features.Get <IStreamAbortFeature>().AbortRead((long)Http3ErrorCode.NoError, new ConnectionAbortedException("Test message.")); await serverStream.Transport.Input.CompleteAsync(); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); Assert.True(quicStreamContext.CanWrite); Assert.True(quicStreamContext.CanRead); await quicStreamContext.DisposeAsync(); quicStreamContext.Dispose(); var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); // Assert Assert.Equal(0, quicConnectionContext.StreamPool.Count); }
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); }
public async Task BidirectionalStream_ClientAbortedAfterDisposeCalled_NotPooled() { // Arrange await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); // Act var clientStream = clientConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData).DefaultTimeout(); var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); // Server sends a large response that will make it wait to complete sends. await serverStream.Transport.Output.WriteAsync(new byte[1024 * 1024 * 32]).DefaultTimeout(); // Complete reading and writing. await serverStream.Transport.Input.CompleteAsync(); await serverStream.Transport.Output.CompleteAsync(); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Server starts disposing var disposeTask = quicStreamContext.DisposeAsync(); // Client aborts while server is draining clientStream.AbortRead((long)Http3ErrorCode.RequestCancelled); clientStream.AbortWrite((long)Http3ErrorCode.RequestCancelled); // Server finishes disposing await disposeTask.DefaultTimeout(); quicStreamContext.Dispose(); var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection); // Assert Assert.Equal(0, quicConnectionContext.StreamPool.Count); }
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 async Task BidirectionalStream_ClientAbortWrite_ServerReceivesAbort() { // 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 await using var clientStream = quicConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData).DefaultTimeout(); await using var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); var closedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); serverStream.ConnectionClosed.Register(() => closedTcs.SetResult()); clientStream.AbortWrite((long)Http3ErrorCode.InternalError); // Receive abort from client. var ex = await Assert.ThrowsAsync <ConnectionResetException>(() => serverStream.Transport.Input.ReadAsync().AsTask()).DefaultTimeout(); // Server completes its output. await serverStream.Transport.Output.CompleteAsync(); // Assert Assert.Equal((long)Http3ErrorCode.InternalError, ((QuicStreamAbortedException)ex.InnerException).ErrorCode); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); Assert.Equal((long)Http3ErrorCode.InternalError, quicStreamContext.Error); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); await closedTcs.Task.DefaultTimeout(); }
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 AcceptAsync_CancellationThenAccept_AcceptStreamAfterCancellation() { // Arrange var connectionClosedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory); // Act var acceptTask = connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout(); var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint); using var clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); await clientConnection.ConnectAsync().DefaultTimeout(); await using var serverConnection = await acceptTask.DefaultTimeout(); // Wait for stream and then cancel var cts = new CancellationTokenSource(); var acceptStreamTask = serverConnection.AcceptAsync(cts.Token); cts.Cancel(); var serverStream = await acceptStreamTask.DefaultTimeout(); Assert.Null(serverStream); // Wait for stream after cancellation acceptStreamTask = serverConnection.AcceptAsync(); await using var clientStream = clientConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData); // Assert serverStream = await acceptStreamTask.DefaultTimeout(); Assert.NotNull(serverStream); var read = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); Assert.Equal(TestData, read.Buffer.ToArray()); serverStream.Transport.Input.AdvanceTo(read.Buffer.End); }
public async Task BidirectionalStream_ServerReadsDataAndCompletes_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.AcceptAsync().DefaultTimeout(); // Act await using var clientStream = quicConnection.OpenBidirectionalStream(); await clientStream.WriteAsync(TestData, endStream : true).DefaultTimeout(); await using var serverStream = await serverConnection.AcceptAsync().DefaultTimeout(); var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout(); serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End); // Input should be completed. readResult = await serverStream.Transport.Input.ReadAsync(); // Complete output. await serverStream.Transport.Output.CompleteAsync(); // Assert Assert.True(readResult.IsCompleted); var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream); // Both send and receive loops have exited. await quicStreamContext._processingTask.DefaultTimeout(); Assert.True(quicStreamContext.CanWrite); Assert.True(quicStreamContext.CanRead); Assert.Contains(LogMessages, m => m.Message.Contains("send loop completed gracefully")); }
public async Task QuicStream() { using var connection = new QuicConnection(new IPEndPoint(IPAddress.Loopback, QuicPort), new SslClientAuthenticationOptions() { TargetHost = "localhost" }); await connection.ConnectAsync(); await using var stream = connection.OpenBidirectionalStream(); BinaryPrimitives.WriteInt32LittleEndian(_sendBuffer, DataLength); await stream.WriteAsync(_sendBuffer, 0, 4); await stream.FlushAsync(); var read = 0; while (read < DataLength) { read += await stream.ReadAsync(_recvBuffer); } }
/// <summary> /// Runs a client against a `msquicsample -server ...` server from the msquic repo /// </summary> private static async Task MsQuicSampleClient() { // port 4567 is hardcoded in msquicsample executable var serverAddress = IPEndPoint.Parse("127.0.0.1:4567"); using var connection = new QuicConnection(serverAddress, new SslClientAuthenticationOptions { ApplicationProtocols = new List <SslApplicationProtocol> { // make sure we report the same protocol new SslApplicationProtocol("sample") } }); await connection.ConnectAsync(); await using var stream = connection.OpenBidirectionalStream(); var buffer = new byte[1024]; new Random().NextBytes(buffer); await stream.WriteAsync(buffer); await stream.ShutdownWriteCompleted(); var totalRead = 0; int read; do { read = await stream.ReadAsync(buffer.AsMemory(totalRead)); totalRead += read; } while (read > 0); Console.WriteLine($"Received: {BitConverter.ToString(buffer, 0, totalRead)}"); }
private Task <HttpResponseMessage> SendWithoutWaitingAsync(HttpRequestMessage request, CancellationToken cancellationToken) { QuicStream quicStream = null; Http3RequestStream stream; try { // TODO: do less work in this lock, try to get rid of goto. lock (SyncObj) { if (_connection == null) { goto retryRequest; } quicStream = _connection.OpenBidirectionalStream(); if (_lastProcessedStreamId != -1 && quicStream.StreamId > _lastProcessedStreamId) { goto retryRequest; } stream = new Http3RequestStream(request, this, quicStream); _activeRequests.Add(quicStream.StreamId, stream); } return(stream.SendAsync(cancellationToken)); } catch (QuicConnectionAbortedException ex) { // This will happen if we aborted _connection somewhere. Abort(ex); } retryRequest: // We lost a race between GOAWAY/abort and our pool sending the request to this connection. quicStream?.Dispose(); return(Task.FromException <HttpResponseMessage>(new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnSameOrNextProxy))); }
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); } }