diff --git a/eng/Versions.props b/eng/Versions.props
index bae5f15833b28f..54e52c5474dd85 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -179,7 +179,7 @@
7.0.0-rtm.23315.2
- 2.1.1
+ 2.2.2
7.0.0-alpha.1.22459.1
11.1.0-alpha.1.23115.1
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs
index 63b4a827d6790b..b6552b56e9efba 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs
@@ -18,7 +18,7 @@ internal sealed unsafe partial class MsQuicApi
{
private static readonly Version MinWindowsVersion = new Version(10, 0, 20145, 1000);
- private static readonly Version MinMsQuicVersion = new Version(2, 1);
+ private static readonly Version MinMsQuicVersion = new Version(2, 2, 2);
private static readonly delegate* unmanaged[Cdecl] MsQuicOpenVersion;
private static readonly delegate* unmanaged[Cdecl] MsQuicClose;
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/msquic_generated.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/msquic_generated.cs
index 738c2365f604f7..e81786c5fbcb7d 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/msquic_generated.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/msquic_generated.cs
@@ -129,6 +129,7 @@ internal enum QUIC_STREAM_OPEN_FLAGS
NONE = 0x0000,
UNIDIRECTIONAL = 0x0001,
ZERO_RTT = 0x0002,
+ DELAY_FC_UPDATES = 0x0004,
}
[System.Flags]
diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
index 83073bcfab5022..91386d957c5438 100644
--- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
+++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
@@ -500,6 +500,8 @@ private unsafe int HandleEventPeerStreamStarted(ref PEER_STREAM_STARTED_DATA dat
stream.Dispose();
return QUIC_STATUS_SUCCESS;
}
+
+ data.Flags |= QUIC_STREAM_OPEN_FLAGS.DELAY_FC_UPDATES;
return QUIC_STATUS_SUCCESS;
}
private unsafe int HandleEventPeerCertificateReceived(ref PEER_CERTIFICATE_RECEIVED_DATA data)
diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
index c032f4f8a14ea3..322f47a9e8425e 100644
--- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
+++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
@@ -584,6 +584,32 @@ ValueTask OpenStreamAsync(QuicConnection connection) => unidirection
await serverConnection.DisposeAsync();
}
+ [Fact]
+ public async Task OpenStreamAsync_BlocksUntilAvailable_PeerClosesWritingUnidirectional()
+ {
+ QuicListenerOptions listenerOptions = new QuicListenerOptions()
+ {
+ ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0),
+ ApplicationProtocols = new List() { ApplicationProtocol },
+ ConnectionOptionsCallback = (_, _, _) =>
+ {
+ var serverOptions = CreateQuicServerOptions();
+ serverOptions.MaxInboundBidirectionalStreams = 1;
+ serverOptions.MaxInboundUnidirectionalStreams = 1;
+ return ValueTask.FromResult(serverOptions);
+ }
+ };
+ (QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(null, listenerOptions);
+
+ // Open one stream, second call should block
+ await using var stream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional);
+ await stream.WriteAsync(new byte[64*1024], completeWrites: true);
+ await Assert.ThrowsAsync(() => clientConnection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional).AsTask().WaitAsync(TimeSpan.FromSeconds(1)));
+
+ await clientConnection.DisposeAsync();
+ await serverConnection.DisposeAsync();
+ }
+
[Theory]
[InlineData(false)]
[InlineData(true)]
@@ -622,11 +648,24 @@ ValueTask OpenStreamAsync(QuicConnection connection, CancellationTok
// Close the streams, the waitTask should finish as a result.
await stream.DisposeAsync();
- QuicStream newStream = await serverConnection.AcceptInboundStreamAsync();
- await newStream.DisposeAsync();
+ // Drain all server streams.
+ while (true)
+ {
+ using var acceptCts = new CancellationTokenSource(TimeSpan.FromSeconds(0.5));
+ try
+ {
+ QuicStream serverStream = await serverConnection.AcceptInboundStreamAsync(acceptCts.Token);
+ await serverStream.DisposeAsync();
+ }
+ catch (OperationCanceledException)
+ {
+ // Token expired, no more streams in the server queue, exit the loop.
+ break;
+ }
+ }
// next call should work as intended
- newStream = await OpenStreamAsync(clientConnection).AsTask().WaitAsync(TimeSpan.FromSeconds(10));
+ var newStream = await OpenStreamAsync(clientConnection).AsTask().WaitAsync(TimeSpan.FromSeconds(10));
await newStream.DisposeAsync();
await clientConnection.DisposeAsync();
diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj
index 62b1be2da62e95..18a4a8a0f8a521 100644
--- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj
+++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj
@@ -4,6 +4,7 @@
true
$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix
true
+ CA2252