diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs index 31a0dd7893669a..a23c0dfd2bf55c 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs @@ -118,7 +118,7 @@ public override Task InitializeConnectionAsync() throw new NotImplementedException(); } - private Task EnsureControlStreamAcceptedAsync() + public Task EnsureControlStreamAcceptedAsync() { if (_inboundControlStream != null) { diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs index 8a9cd0dfb51330..519cf42ad1acf6 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs @@ -49,23 +49,45 @@ public void ValidValue_SetGet_Roundtrips(int validValue) [Fact] public async Task SetAfterUse_Throws() { - await LoopbackServerFactory.CreateClientAndServerAsync(async uri => + for (int repeat = 0; repeat <= (UseVersion.Major == 3 ? 50 : 1); repeat++) { - using HttpClientHandler handler = CreateHttpClientHandler(); - using HttpClient client = CreateHttpClient(handler); + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => + { + using HttpClientHandler handler = CreateHttpClientHandler(); + using HttpClient client = CreateHttpClient(handler); + + handler.MaxResponseHeadersLength = 1; + (await client.GetStreamAsync(uri)).Dispose(); + Assert.Throws(() => handler.MaxResponseHeadersLength = 1); + }, + server => server.AcceptConnectionSendResponseAndCloseAsync()); + } + } - handler.MaxResponseHeadersLength = 1; - (await client.GetStreamAsync(uri)).Dispose(); - Assert.Throws(() => handler.MaxResponseHeadersLength = 1); - }, - server => server.AcceptConnectionSendResponseAndCloseAsync()); + public static IEnumerable LargeSingleHeader_ThrowsException_MemberData() + { + object[][] options = new[] + { + new object[] { 1 }, + new object[] { 2 }, + new object[] { 15 }, + }; + + var rng = new Random(); + int count = options.Length * 50; + + for (int i = 0; i < count; i++) + { + yield return options[rng.Next(options.Length)]; + } } [Theory] - [InlineData(1)] - [InlineData(15)] + [MemberData(nameof(LargeSingleHeader_ThrowsException_MemberData))] public async Task LargeSingleHeader_ThrowsException(int maxResponseHeadersLength) { + if (UseVersion.Major != 3) return; + using HttpClientHandler handler = CreateHttpClientHandler(); handler.MaxResponseHeadersLength = maxResponseHeadersLength; @@ -73,7 +95,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { using HttpClient client = CreateHttpClient(handler); - Exception e = await Assert.ThrowsAsync(() => client.GetAsync(uri)); + Exception e = await Assert.ThrowsAsync(() => client.GetAsync(uri)).WaitAsync(TestHelper.PassingTestTimeout); if (!IsWinHttpHandler) { Assert.Contains((handler.MaxResponseHeadersLength * 1024).ToString(), e.ToString()); @@ -81,18 +103,47 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => }, async server => { - await server.HandleRequestAsync(headers: new[] { new HttpHeaderData("Foo", new string('a', handler.MaxResponseHeadersLength * 1024)) }); + HttpHeaderData[] headers = new[] { new HttpHeaderData("Foo", new string('a', handler.MaxResponseHeadersLength * 1024)) }; + + try + { + await server.HandleRequestAsync(headers: headers).WaitAsync(TestHelper.PassingTestTimeout); + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } + public static IEnumerable ThresholdExceeded_ThrowsException_MemberData() + { + object[][] options = new[] + { + new object[] { null, 63 * 1024 }, + new object[] { null, 65 * 1024 }, + new object[] { 1, 100 }, + new object[] { 1, 1024 }, + new object[] { 2, 1100 }, + new object[] { 2, 2048 }, + new object[] { int.MaxValue / 800, 100 * 1024 }, + }; + + var rng = new Random(); + int count = options.Length * 50; + + for (int i = 0; i < count; i++) + { + yield return options[rng.Next(options.Length)]; + } + } + [Theory] - [InlineData(null, 63 * 1024)] - [InlineData(null, 65 * 1024)] - [InlineData(1, 100)] - [InlineData(1, 1024)] - [InlineData(int.MaxValue / 800, 100 * 1024)] // Capped at int.MaxValue + [MemberData(nameof(ThresholdExceeded_ThrowsException_MemberData))] public async Task ThresholdExceeded_ThrowsException(int? maxResponseHeadersLength, int headersLengthEstimate) { + if (UseVersion.Major != 3) return; + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { using HttpClientHandler handler = CreateHttpClientHandler(); @@ -106,11 +157,11 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => if (headersLengthEstimate < handler.MaxResponseHeadersLength * 1024L) { - await client.GetAsync(uri); + await client.GetAsync(uri).WaitAsync(TestHelper.PassingTestTimeout); } else { - Exception e = await Assert.ThrowsAsync(() => client.GetAsync(uri)); + Exception e = await Assert.ThrowsAsync(() => client.GetAsync(uri)).WaitAsync(TestHelper.PassingTestTimeout); if (!IsWinHttpHandler) { Assert.Contains((handler.MaxResponseHeadersLength * 1024).ToString(), e.ToString()); @@ -125,7 +176,14 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => headers.Add(new HttpHeaderData($"Custom-{i}", new string('a', 480))); } - await server.HandleRequestAsync(headers: headers); + try + { + await server.HandleRequestAsync(headers: headers).WaitAsync(TestHelper.PassingTestTimeout); + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index d2bc4873b6ad85..3e03f077092fe9 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -1240,12 +1240,70 @@ public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http2(ITest protected override Version UseVersion => HttpVersion.Version20; } - [ActiveIssue("https://github.com/dotnet/runtime/issues/73930")] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http3 : HttpClientHandler_MaxResponseHeadersLength_Test { public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http3(ITestOutputHelper output) : base(output) { } protected override Version UseVersion => HttpVersion.Version30; + + public static IEnumerable Http3Test_MemberData() + { + object[][] options = new[] + { + new object[] { null }, + new object[] { 1 }, + new object[] { 2 }, + new object[] { 16 }, + new object[] { 64 }, + new object[] { 256 }, + new object[] { 1024 }, + new object[] { 10240 }, + }; + + var rng = new Random(); + int count = options.Length * 50; + + for (int i = 0; i < count; i++) + { + yield return options[rng.Next(options.Length)]; + } + } + + [Theory] + [MemberData(nameof(Http3Test_MemberData))] + public async Task Http3Test(int? maxResponseHeadersLength) + { + var requestCts = new CancellationTokenSource(); + var controlStreamEstablishedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => + { + using HttpClientHandler handler = CreateHttpClientHandler(); + using HttpClient client = CreateHttpClient(handler); + + if (maxResponseHeadersLength.HasValue) + { + handler.MaxResponseHeadersLength = maxResponseHeadersLength.Value; + } + + await Assert.ThrowsAnyAsync(() => client.GetAsync(uri, requestCts.Token)); + + await controlStreamEstablishedTcs.Task.WaitAsync(TestHelper.PassingTestTimeout); + }, + async server => + { + await server.AcceptConnectionAsync(async connection => + { + requestCts.Cancel(); + + var http3Connection = (Http3LoopbackConnection)connection; + + await http3Connection.EnsureControlStreamAcceptedAsync().WaitAsync(TestHelper.PassingTestTimeout); + + controlStreamEstablishedTcs.SetResult(); + }); + }); + } } [SkipOnPlatform(TestPlatforms.Browser, "Socket is not supported on Browser")]