diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs index 51d2b23fce26c..abdb2649a3e30 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs @@ -372,5 +372,103 @@ await Task.WhenAll( await connection.DisposeAsync(); }).WaitAsync(TimeSpan.FromSeconds(5))); } + + [Fact] + public async Task Listener_AlpnNarrowingDown_Success() + { + using CancellationTokenSource testTimeoutCts = new CancellationTokenSource(PassingTestTimeout); + CancellationToken timeoutToken = testTimeoutCts.Token; + + var listenerOptions = new QuicListenerOptions() + { + ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), + ApplicationProtocols = new List() + { + new SslApplicationProtocol("foo"), + new SslApplicationProtocol("bar"), + new SslApplicationProtocol("test"), + }, + ConnectionOptionsCallback = (_, _, _) => + { + // Narrowing down alpn list to more specific. + var options = CreateQuicServerOptions(); + options.ServerAuthenticationOptions.ApplicationProtocols = new() + { + new SslApplicationProtocol("bar"), + new SslApplicationProtocol("test"), + }; + return ValueTask.FromResult(options); + } + }; + await using QuicListener listener = await CreateQuicListener(listenerOptions); + + // Successful connection with bar ALPN + QuicClientConnectionOptions clientOptions1 = CreateQuicClientOptions(listener.LocalEndPoint); + clientOptions1.ClientAuthenticationOptions.ApplicationProtocols = new() + { + new SslApplicationProtocol("foo"), + new SslApplicationProtocol("bar"), + }; + ValueTask connectTask1 = CreateQuicConnection(clientOptions1); + await using QuicConnection serverConnection1 = await listener.AcceptConnectionAsync().AsTask().WaitAsync(timeoutToken); + await using QuicConnection clientConnection1 = await connectTask1.AsTask().WaitAsync(timeoutToken); + + Assert.Equal(new SslApplicationProtocol("bar"), clientConnection1.NegotiatedApplicationProtocol); + + // Successful connection with test ALPN + QuicClientConnectionOptions clientOptions2 = CreateQuicClientOptions(listener.LocalEndPoint); + clientOptions2.ClientAuthenticationOptions.ApplicationProtocols = new() + { + new SslApplicationProtocol("foo"), + new SslApplicationProtocol("test"), + }; + ValueTask connectTask2 = CreateQuicConnection(clientOptions2); + await using QuicConnection serverConnection2 = await listener.AcceptConnectionAsync().AsTask().WaitAsync(timeoutToken); + await using QuicConnection clientConnection2 = await connectTask2.AsTask().WaitAsync(timeoutToken); + + Assert.Equal(new SslApplicationProtocol("test"), clientConnection2.NegotiatedApplicationProtocol); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/86701")] + [Theory] + [InlineData("foo")] + [InlineData("not_existing")] + public async Task Listener_AlpnNarrowingDown_Failure(string alpn) + { + using CancellationTokenSource testTimeoutCts = new CancellationTokenSource(PassingTestTimeout); + CancellationToken timeoutToken = testTimeoutCts.Token; + + var listenerOptions = new QuicListenerOptions() + { + ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), + ApplicationProtocols = new List() + { + new SslApplicationProtocol("foo"), + new SslApplicationProtocol("bar"), + new SslApplicationProtocol("test"), + }, + ConnectionOptionsCallback = (_, _, _) => + { + // Narrowing down alpn list to more specific. + var options = CreateQuicServerOptions(); + options.ServerAuthenticationOptions.ApplicationProtocols = new() + { + new SslApplicationProtocol("bar"), + new SslApplicationProtocol("test"), + }; + return ValueTask.FromResult(options); + } + }; + await using QuicListener listener = await CreateQuicListener(listenerOptions); + + QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); + clientOptions.ClientAuthenticationOptions.ApplicationProtocols = new() + { + new SslApplicationProtocol(alpn), + }; + ValueTask connectTask = CreateQuicConnection(clientOptions); + await Assert.ThrowsAsync(() => listener.AcceptConnectionAsync().AsTask().WaitAsync(timeoutToken)); + await Assert.ThrowsAsync(() => connectTask.AsTask().WaitAsync(timeoutToken)); + } } }