diff --git a/src/libraries/Common/src/System/Net/Logging/NetEventSource.Common.cs b/src/libraries/Common/src/System/Net/Logging/NetEventSource.Common.cs index 8dadcacb9be91..ca60b6b0ad52e 100644 --- a/src/libraries/Common/src/System/Net/Logging/NetEventSource.Common.cs +++ b/src/libraries/Common/src/System/Net/Logging/NetEventSource.Common.cs @@ -77,7 +77,8 @@ public static void Info(object? thisOrContextObject, object? message, [CallerMem [Event(InfoEventId, Level = EventLevel.Informational, Keywords = Keywords.Default)] private void Info(string thisOrContextObject, string? memberName, string? message) { - Debug.Assert(IsEnabled()); + //Debug.Assert(IsEnabled()); + if (!IsEnabled()) return; WriteEvent(InfoEventId, thisOrContextObject, memberName ?? MissingMember, message); } #endregion @@ -102,7 +103,8 @@ public static void Error(object? thisOrContextObject, object message, [CallerMem [Event(ErrorEventId, Level = EventLevel.Error, Keywords = Keywords.Default)] private void ErrorMessage(string thisOrContextObject, string? memberName, string? message) { - Debug.Assert(IsEnabled()); + //Debug.Assert(IsEnabled()); + if (!IsEnabled()) return; WriteEvent(ErrorEventId, thisOrContextObject, memberName ?? MissingMember, message); } #endregion diff --git a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs index 66c64e3e8a578..065d2ae9bfab0 100644 --- a/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/GenericLoopbackServer.cs @@ -188,6 +188,7 @@ public class GenericLoopbackOptions #if !NETSTANDARD2_0 && !NETFRAMEWORK public SslStreamCertificateContext? CertificateContext { get; set; } #endif + public Xunit.Abstractions.ITestOutputHelper? TestOutputHelper { get; set; } } public struct HttpHeaderData diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs index 5635b753a5f6c..a85e06974fcbe 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs @@ -52,9 +52,12 @@ public sealed class Http3LoopbackConnection : GenericLoopbackConnection public Http3LoopbackStream OutboundControlStream => _outboundControlStream ?? throw new Exception("Control stream has not been opened yet"); public Http3LoopbackStream InboundControlStream => _inboundControlStream ?? throw new Exception("Inbound control stream has not been accepted yet"); - public Http3LoopbackConnection(QuicConnection connection) + private Xunit.Abstractions.ITestOutputHelper? _output; + + public Http3LoopbackConnection(QuicConnection connection, Xunit.Abstractions.ITestOutputHelper? output) { _connection = connection; + _output = output; } public long MaxHeaderListSize { get; private set; } = -1; @@ -92,12 +95,12 @@ public override async ValueTask DisposeAsync() public async ValueTask OpenUnidirectionalStreamAsync() { - return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional)); + return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional), _output); } public async ValueTask OpenBidirectionalStreamAsync() { - return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional)); + return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional), _output); } public static int GetRequestId(QuicStream stream) @@ -124,15 +127,15 @@ private Task EnsureControlStreamAcceptedAsync() async Task EnsureControlStreamAcceptedInternalAsync() { Http3LoopbackStream controlStream; - while (true) { + _output?.WriteLine($"{this} {_connection} Accepting a new stream"); QuicStream quicStream = await _connection.AcceptInboundStreamAsync().ConfigureAwait(false); - + _output?.WriteLine($"{this} {quicStream} Accepted a stream, CanWrite = {quicStream.CanWrite}"); if (!quicStream.CanWrite) { // control stream accepted - controlStream = new Http3LoopbackStream(quicStream); + controlStream = new Http3LoopbackStream(quicStream, _output); break; } @@ -140,7 +143,7 @@ async Task EnsureControlStreamAcceptedInternalAsync() // keep it for later and wait for another stream _delayedStreams.Enqueue(quicStream); } - + _output?.WriteLine($"{this} {_connection} All streams has been caught, delayed streams count = {_delayedStreams.Count}"); long? streamType = await controlStream.ReadIntegerAsync(); Assert.Equal(Http3LoopbackStream.ControlStream, streamType); @@ -157,6 +160,7 @@ async Task EnsureControlStreamAcceptedInternalAsync() // This will automatically handle the control stream, including validating its contents public async Task AcceptRequestStreamAsync() { + _output?.WriteLine($"{this} {_connection} Accepting control stream."); await EnsureControlStreamAcceptedAsync().ConfigureAwait(false); if (!_delayedStreams.TryDequeue(out QuicStream quicStream)) @@ -164,7 +168,7 @@ public async Task AcceptRequestStreamAsync() quicStream = await _connection.AcceptInboundStreamAsync().ConfigureAwait(false); } - var stream = new Http3LoopbackStream(quicStream); + var stream = new Http3LoopbackStream(quicStream, _output); Assert.True(quicStream.CanWrite, "Expected writeable stream."); @@ -185,9 +189,11 @@ public async Task AcceptRequestStreamAsync() public async Task EstablishControlStreamAsync(SettingsEntry[] settingsEntries) { - _outboundControlStream = await OpenUnidirectionalStreamAsync(); - await _outboundControlStream.SendUnidirectionalStreamTypeAsync(Http3LoopbackStream.ControlStream); - await _outboundControlStream.SendSettingsFrameAsync(settingsEntries); + _output?.WriteLine($"{this} Establishing control stream"); + _outboundControlStream = await OpenUnidirectionalStreamAsync().ConfigureAwait(false); + _output?.WriteLine($"{this} {_outboundControlStream.Stream} Stream opened."); + await _outboundControlStream.SendUnidirectionalStreamTypeAsync(Http3LoopbackStream.ControlStream).ConfigureAwait(false); + await _outboundControlStream.SendSettingsFrameAsync(settingsEntries).ConfigureAwait(false); } public async Task DisposeCurrentStream() @@ -241,6 +247,7 @@ public override Task SendPartialResponseHeadersAsync(HttpStatusCode statusCode = public override async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") { + _output?.WriteLine($"{this} {_connection} HandleRequestAsync triggered."); Http3LoopbackStream stream = await AcceptRequestStreamAsync().ConfigureAwait(false); HttpRequestData request = await stream.ReadRequestDataAsync().ConfigureAwait(false); diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs index 63368b49c129f..0f7e0160b3227 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs @@ -16,6 +16,7 @@ public sealed class Http3LoopbackServer : GenericLoopbackServer { private X509Certificate2 _cert; private QuicListener _listener; + private Xunit.Abstractions.ITestOutputHelper? _output; public override Uri Address => new Uri($"https://{_listener.LocalEndPoint}/"); @@ -58,6 +59,8 @@ public Http3LoopbackServer(Http3Options options = null) ValueTask valueTask = QuicListener.ListenAsync(listenerOptions); Debug.Assert(valueTask.IsCompleted); _listener = valueTask.Result; + _output = options.TestOutputHelper; + _output?.WriteLine($"{this} Http3LoopbackServer created."); } public override void Dispose() @@ -68,16 +71,19 @@ public override void Dispose() private async Task EstablishHttp3ConnectionAsync(params SettingsEntry[] settingsEntries) { + _output?.WriteLine($"{this} Accepting connection"); QuicConnection con = await _listener.AcceptConnectionAsync().ConfigureAwait(false); - Http3LoopbackConnection connection = new Http3LoopbackConnection(con); + _output?.WriteLine($"{this} Connection accepted: {con}"); + Http3LoopbackConnection connection = new Http3LoopbackConnection(con, _output); - await connection.EstablishControlStreamAsync(settingsEntries); + await connection.EstablishControlStreamAsync(settingsEntries).ConfigureAwait(false); + _output?.WriteLine($"{this} {con} Control stream established"); return connection; } public override async Task EstablishGenericConnectionAsync() { - return await EstablishHttp3ConnectionAsync(); + return await EstablishHttp3ConnectionAsync().ConfigureAwait(false); } public Task EstablishConnectionAsync(params SettingsEntry[] settingsEntries) @@ -94,7 +100,9 @@ public override async Task AcceptConnectionAsync(Func HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") { - await using Http3LoopbackConnection con = (Http3LoopbackConnection)await EstablishGenericConnectionAsync().ConfigureAwait(false); + _output?.WriteLine("Establishing HTTP/3 connection."); + await using Http3LoopbackConnection con = await EstablishHttp3ConnectionAsync().ConfigureAwait(false); + _output?.WriteLine($"{con} HTTP/3 Connection established successfully!"); return await con.HandleRequestAsync(statusCode, headers, content).ConfigureAwait(false); } } @@ -138,6 +146,7 @@ private static Http3Options CreateOptions(GenericLoopbackOptions options) http3Options.Certificate = options.Certificate; http3Options.SslProtocols = options.SslProtocols; http3Options.ListenBacklog = options.ListenBacklog; + http3Options.TestOutputHelper = options.TestOutputHelper; } return http3Options; } diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs index d8d0f9a7a7caf..829d41dae7907 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs @@ -34,13 +34,19 @@ public sealed class Http3LoopbackStream : IAsyncDisposable public bool CanRead => _stream.CanRead; public bool CanWrite => _stream.CanWrite; - public Http3LoopbackStream(QuicStream stream) + private Xunit.Abstractions.ITestOutputHelper? _output; + + public Http3LoopbackStream(QuicStream stream, Xunit.Abstractions.ITestOutputHelper? output) { _stream = stream; + _output = output; + _output?.WriteLine($"Created stream {stream}, Readable: {stream.CanRead}, Writable: {stream.CanWrite}"); } public ValueTask DisposeAsync() => _stream.DisposeAsync(); + public QuicStream Stream => _stream; + public long StreamId => _stream.Id; public async Task HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList headers = null, string content = "") @@ -162,7 +168,9 @@ private async Task SendFrameHeaderAsync(long frameType, int payloadLength) public async Task SendFrameAsync(long frameType, ReadOnlyMemory framePayload) { await SendFrameHeaderAsync(frameType, framePayload.Length).ConfigureAwait(false); + _output?.WriteLine($"Sent {frameType} header"); await _stream.WriteAsync(framePayload).ConfigureAwait(false); + _output?.WriteLine($"Sent {framePayload} - Length: {framePayload.Length} frame"); } static int EncodeHttpInteger(long longToEncode, Span buffer) @@ -431,9 +439,11 @@ public void Abort(long errorCode) public async Task<(long? frameType, byte[] payload)> ReadFrameAsync() { long? frameType = await ReadIntegerAsync().ConfigureAwait(false); + _output?.WriteLine($"Read frame type: {frameType}"); if (frameType == null) return (null, null); long? payloadLength = await ReadIntegerAsync().ConfigureAwait(false); + _output?.WriteLine($"Read payload length: {payloadLength}"); if (payloadLength == null) throw new Exception("Unable to read frame; unexpected end of stream."); byte[] payload = new byte[checked((int)payloadLength)]; @@ -442,11 +452,13 @@ public void Abort(long errorCode) while (totalBytesRead != payloadLength) { int bytesRead = await _stream.ReadAsync(payload.AsMemory(totalBytesRead)).ConfigureAwait(false); + _output?.WriteLine($"Read {bytesRead} bytes of payload"); if (bytesRead == 0) throw new Exception("Unable to read frame; unexpected end of stream."); totalBytesRead += bytesRead; } + _output?.WriteLine($"Read frame payload: {payload}"); return (frameType, payload); } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs index 6ecd7261ca2d9..b2f03bfbc930e 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cookies.cs @@ -44,7 +44,7 @@ private static CookieContainer CreateSingleCookieContainer(Uri uri, string cooki private static string GetCookieHeaderValue(string cookieName, string cookieValue) => $"{cookieName}={cookieValue}"; [Fact] - public virtual async Task GetAsync_DefaultCoookieContainer_NoCookieSent() + public async Task GetAsync_DefaultCoookieContainer_NoCookieSent() { await LoopbackServerFactory.CreateClientAndServerAsync( async uri => @@ -216,15 +216,10 @@ private string GetCookieValue(HttpRequestData request) return cookieHeaderValue; } - [ConditionalFact] + [Fact] [SkipOnPlatform(TestPlatforms.Browser, "CookieContainer is not supported on Browser")] public async Task GetAsync_SetCookieContainerAndCookieHeader_BothCookiesSent() { - if (UseVersion == HttpVersion30) - { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/101377"); - } - await LoopbackServerFactory.CreateServerAsync(async (server, url) => { HttpClientHandler handler = CreateHttpClientHandler(); @@ -318,7 +313,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async url => using (HttpClient client = CreateHttpClient(handler)) { client.DefaultRequestHeaders.ConnectionClose = true; // to avoid issues with connection pooling - await client.GetAsync(url1); + await client.GetAsync(url1); } }, async server => 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 0fe06a4670ef9..1dec0141427de 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs @@ -14,6 +14,7 @@ using Microsoft.DotNet.XUnitExtensions; using Xunit; using Xunit.Abstractions; +using TestUtilities; namespace System.Net.Http.Functional.Tests { @@ -56,6 +57,24 @@ public void ValidValue_SetGet_Roundtrips(int validValue) [Fact] public async Task SetAfterUse_Throws() { + AsyncLocal asyncLocal = new(); + asyncLocal.Value = new(); + + TestEventListener? listener = null; + if (UseVersion == HttpVersion30) + { + listener = new TestEventListener(e => + { + if (asyncLocal.Value is not null) + { + lock (_output) + { + _output.WriteLine($"[SetAfterUse]{e}"); + } + } + }, TestEventListener.NetworkingEvents); + } + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { using HttpClientHandler handler = CreateHttpClientHandler(); @@ -65,7 +84,8 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => (await client.GetStreamAsync(uri)).Dispose(); Assert.Throws(() => handler.MaxResponseHeadersLength = 1); }, - server => server.AcceptConnectionSendResponseAndCloseAsync()); + server => server.AcceptConnectionSendResponseAndCloseAsync(), options: new() { TestOutputHelper = _output }); + listener?.Dispose(); } [Theory] @@ -73,6 +93,24 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => [InlineData(15)] public async Task LargeSingleHeader_ThrowsException(int maxResponseHeadersLength) { + AsyncLocal asyncLocal = new(); + asyncLocal.Value = new(); + + TestEventListener? listener = null; + if (UseVersion == HttpVersion30) + { + listener = new TestEventListener(e => + { + if (asyncLocal.Value is not null) + { + lock (_output) + { + _output.WriteLine($"[LargeSingleHeader_ThrowsException]{e}"); + } + } + }, TestEventListener.NetworkingEvents); + } + using HttpClientHandler handler = CreateHttpClientHandler(); handler.MaxResponseHeadersLength = maxResponseHeadersLength; @@ -88,6 +126,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => }, async server => { + _output.WriteLine($"Listening on {server.Address}"); try { await server.HandleRequestAsync(headers: new[] { new HttpHeaderData("Foo", new string('a', handler.MaxResponseHeadersLength * 1024)) }); @@ -97,7 +136,9 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => #if !WINHTTPHANDLER_TEST catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted && ex.ApplicationErrorCode == Http3ExcessiveLoad) {} #endif - }); + }, options: new() { TestOutputHelper = _output }); + + listener?.Dispose(); } [Theory] diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 31d8d9f154fc4..a8c6460c46c52 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -18,6 +18,7 @@ using Microsoft.DotNet.XUnitExtensions; using Xunit; using Xunit.Abstractions; +using TestUtilities; namespace System.Net.Http.Functional.Tests { @@ -270,7 +271,7 @@ await LoopbackServer.CreateClientAndServerAsync(async proxyUri => public static IEnumerable SecureAndNonSecure_IPBasedUri_MemberData() => from address in new[] { IPAddress.Loopback, IPAddress.IPv6Loopback } from useSsl in BoolValues - // we could not create SslStream in browser, [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] + // we could not create SslStream in browser, [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] where PlatformDetection.IsNotBrowser || !useSsl select new object[] { address, useSsl }; @@ -883,8 +884,8 @@ await LoopbackServer.CreateClientAndServerAsync(async url => "\r\n" + "5\r\n" + "hello" + // missing \r\n terminator - //"5\r\n" + - //"world" + // missing \r\n terminator + //"5\r\n" + + //"world" + // missing \r\n terminator "0\r\n" + "\r\n")); } @@ -989,7 +990,7 @@ await connection.WriteStringAsync( }); } - [ConditionalTheory] + [Theory] [InlineData(true, true, true)] [InlineData(true, true, false)] [InlineData(true, false, false)] @@ -999,11 +1000,6 @@ await connection.WriteStringAsync( [ActiveIssue("https://github.com/dotnet/runtime/issues/65429", typeof(PlatformDetection), nameof(PlatformDetection.IsNodeJS))] public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked, bool enableWasmStreaming, bool slowChunks) { - if (UseVersion == HttpVersion30) - { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/91757"); - } - if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) { return; @@ -1021,6 +1017,26 @@ public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(boo return; } + AsyncLocal asyncLocal = new(); + asyncLocal.Value = new(); + + TestEventListener? listener = null; + if (UseVersion == HttpVersion30) + { + listener = new TestEventListener(e => + { + if (asyncLocal.Value is not null) + { + lock (_output) + { + _output.WriteLine($"[ReadAsStreamAsync]{e}"); + } + } + }, TestEventListener.NetworkingEvents); + } + + _output.WriteLine("Starting ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream test"); + var tcs = new TaskCompletionSource(); await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { @@ -1035,9 +1051,13 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => } } - using (var client = new HttpMessageInvoker(CreateHttpClientHandler())) - using (HttpResponseMessage response = await client.SendAsync(TestAsync, request, CancellationToken.None)) + using var client = new HttpMessageInvoker(CreateHttpClientHandler()); + _output.WriteLine("[ReadAsStreamAsync] Before SendAsync on test code"); + Task responseTask = client.SendAsync(TestAsync, request, CancellationToken.None); + _output.WriteLine("[ReadAsStreamAsync] After SendAsync on test code"); + using (HttpResponseMessage response = await responseTask.ConfigureAwait(false)) { + _output.WriteLine("[ReadAsStreamAsync] After await of responseTask on test code"); using (Stream responseStream = await response.Content.ReadAsStreamAsync(TestAsync)) { Assert.Same(responseStream, await response.Content.ReadAsStreamAsync(TestAsync)); @@ -1103,7 +1123,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => if (PlatformDetection.IsBrowser) { #if !NETFRAMEWORK - if (slowChunks) + if(slowChunks) { Assert.Equal(1, await responseStream.ReadAsync(new Memory(buffer2))); Assert.Equal((byte)'h', buffer2[0]); @@ -1213,7 +1233,7 @@ await server.AcceptConnectionAsync(async connection => { case true: await connection.SendResponseAsync(HttpStatusCode.OK, headers: new HttpHeaderData[] { new HttpHeaderData("Transfer-Encoding", "chunked") }, isFinal: false); - if (PlatformDetection.IsBrowser && slowChunks) + if(PlatformDetection.IsBrowser && slowChunks) { await connection.SendResponseBodyAsync("1\r\nh\r\n", false); await tcs.Task; @@ -1228,17 +1248,18 @@ await server.AcceptConnectionAsync(async connection => break; case false: - await connection.SendResponseAsync(HttpStatusCode.OK, headers: new HttpHeaderData[] { new HttpHeaderData("Content-Length", "11") }, content: "hello world"); + await connection.SendResponseAsync(HttpStatusCode.OK, headers: new HttpHeaderData[] { new HttpHeaderData("Content-Length", "11")}, content: "hello world"); break; case null: // This inject Content-Length header with null value to hint Loopback code to not include one automatically. - await connection.SendResponseAsync(HttpStatusCode.OK, headers: new HttpHeaderData[] { new HttpHeaderData("Content-Length", null) }, isFinal: false); + await connection.SendResponseAsync(HttpStatusCode.OK, headers: new HttpHeaderData[] { new HttpHeaderData("Content-Length", null)}, isFinal: false); await connection.SendResponseBodyAsync("hello world"); break; } }); - }); + }, options: new() { TestOutputHelper = _output }); + listener?.Dispose(); } [Fact] @@ -1469,10 +1490,10 @@ await LoopbackServerFactory.CreateServerAsync(async (server3, url3) => Task serverTask3 = server3.AcceptConnectionAsync(async connection3 => { await connection3.ReadRequestDataAsync(); - await connection3.SendResponseAsync(HttpStatusCode.OK, new HttpHeaderData[] { new HttpHeaderData("Content-Length", "20") }, isFinal: false); - await connection3.SendResponseBodyAsync("1234567890", isFinal: false); + await connection3.SendResponseAsync(HttpStatusCode.OK, new HttpHeaderData[] { new HttpHeaderData("Content-Length", "20") }, isFinal : false); + await connection3.SendResponseBodyAsync("1234567890", isFinal : false); await unblockServers.Task; - await connection3.SendResponseBodyAsync("1234567890", isFinal: true); + await connection3.SendResponseBodyAsync("1234567890", isFinal : true); }); // Make three requests @@ -1546,7 +1567,7 @@ public async Task GetAsync_UnicodeHostName_SuccessStatusCodeInResponse() } } - #region Post Methods Tests +#region Post Methods Tests [Fact] [SkipOnPlatform(TestPlatforms.Browser, "ExpectContinue not supported on Browser")] @@ -1590,13 +1611,13 @@ await server.AcceptConnectionAsync(async connection => public static IEnumerable Interim1xxStatusCode() { - yield return new object[] { (HttpStatusCode)100 }; // 100 Continue. + yield return new object[] { (HttpStatusCode) 100 }; // 100 Continue. // 101 SwitchingProtocols will be treated as a final status code. - yield return new object[] { (HttpStatusCode)102 }; // 102 Processing. - yield return new object[] { (HttpStatusCode)103 }; // 103 EarlyHints. - yield return new object[] { (HttpStatusCode)150 }; - yield return new object[] { (HttpStatusCode)180 }; - yield return new object[] { (HttpStatusCode)199 }; + yield return new object[] { (HttpStatusCode) 102 }; // 102 Processing. + yield return new object[] { (HttpStatusCode) 103 }; // 103 EarlyHints. + yield return new object[] { (HttpStatusCode) 150 }; + yield return new object[] { (HttpStatusCode) 180 }; + yield return new object[] { (HttpStatusCode) 199 }; } [Theory] @@ -1657,7 +1678,7 @@ await server.AcceptConnectionAsync(async connection => new HttpHeaderData("Content-type", "text/xml"), new HttpHeaderData("Set-Cookie", SetCookieIgnored1)}, isFinal: false); - await connection.SendResponseAsync(responseStatusCode, headers: new HttpHeaderData[] { + await connection.SendResponseAsync(responseStatusCode, headers: new HttpHeaderData[] { new HttpHeaderData("Cookie", "ignore_cookie=choco2"), new HttpHeaderData("Content-type", "text/plain"), new HttpHeaderData("Set-Cookie", SetCookieIgnored2)}, isFinal: false); @@ -1761,7 +1782,7 @@ await server.AcceptConnectionAsync(async connection => { await connection.ReadRequestDataAsync(readBody: false); // Send multiple 100-Continue responses. - for (int count = 0; count < 4; count++) + for (int count = 0 ; count < 4; count++) { await connection.SendResponseAsync(HttpStatusCode.Continue, isFinal: false); } @@ -1865,7 +1886,7 @@ await server.AcceptConnectionAsync(async connection => { await connection.ReadRequestDataAsync(readBody: false); - await connection.SendResponseAsync(HttpStatusCode.OK, headers: new HttpHeaderData[] { new HttpHeaderData("Content-Length", $"{ResponseString.Length}") }, isFinal: false); + await connection.SendResponseAsync(HttpStatusCode.OK, headers: new HttpHeaderData[] {new HttpHeaderData("Content-Length", $"{ResponseString.Length}")}, isFinal : false); byte[] body = await connection.ReadRequestBodyAsync(); Assert.Equal(RequestString, Encoding.ASCII.GetString(body)); @@ -2146,7 +2167,7 @@ await LoopbackServerFactory.CreateServerAsync(async (server, rootUrl) => } }); } - #endregion +#endregion [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowserDomSupported))] public async Task GetAsync_InvalidUrl_ExpectedExceptionThrown() diff --git a/src/libraries/Common/tests/TestUtilities/TestEventListener.cs b/src/libraries/Common/tests/TestUtilities/TestEventListener.cs index 8cb70ee3cbd8c..a2b70be5874b5 100644 --- a/src/libraries/Common/tests/TestUtilities/TestEventListener.cs +++ b/src/libraries/Common/tests/TestUtilities/TestEventListener.cs @@ -36,7 +36,7 @@ public sealed class TestEventListener : EventListener "Private.InternalDiagnostics.System.Net.Mail", "Private.InternalDiagnostics.System.Net.NetworkInformation", "Private.InternalDiagnostics.System.Net.Primitives", - "Private.InternalDiagnostics.System.Net.Requests", + "Private.InternalDiagnostics.System.Net.Requests" }; private readonly Action _writeFunc; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs index 75b9d9b1f8a96..7fb59c417e279 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs @@ -308,10 +308,20 @@ private void OnServerGoAway(long firstRejectedStreamId) // Stop sending requests to this connection. _pool.InvalidateHttp3Connection(this); + if (NetEventSource.Log.IsEnabled()) + { + Trace($"OnServerGoAway - Just before entering lock zone."); + } + var streamsToGoAway = new List(); lock (SyncObj) { + if (NetEventSource.Log.IsEnabled()) + { + Trace($"OnServerGoAway - After entering lock zone."); + } + if (_firstRejectedStreamId != -1 && firstRejectedStreamId > _firstRejectedStreamId) { // Server can send multiple GOAWAY frames. @@ -334,7 +344,17 @@ private void OnServerGoAway(long firstRejectedStreamId) } } + if (NetEventSource.Log.IsEnabled()) + { + Trace($"OnServerGoAway - Before CheckForShutdown"); + } + CheckForShutdown(); + + if (NetEventSource.Log.IsEnabled()) + { + Trace($"OnServerGoAway - After CheckForShutdown"); + } } // GOAWAY each stream outside of the lock, so they can acquire the lock to remove themselves from _activeRequests. diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs index d6a53a33e2342..cd0b346178017 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AltSvc.cs @@ -7,8 +7,9 @@ using Xunit.Abstractions; using System.Net.Test.Common; using System.Net.Quic; - +using TestUtilities; using Microsoft.DotNet.XUnitExtensions; +using System.Threading; namespace System.Net.Http.Functional.Tests { @@ -29,15 +30,27 @@ private HttpClient CreateHttpClient(Version version) return client; } - [ConditionalTheory] + [Theory] [MemberData(nameof(AltSvcHeaderUpgradeVersions))] public async Task AltSvc_Header_Upgrade_Success(Version fromVersion, bool overrideHost) { - if (UseVersion == HttpVersion30 && fromVersion == HttpVersion.Version11 && overrideHost) + AsyncLocal asyncLocal = new(); + asyncLocal.Value = new(); + + TestEventListener? listener = null; + if (UseVersion == HttpVersion30) { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/91757"); + listener = new TestEventListener(e => + { + if (asyncLocal.Value is not null) + { + lock (_output) + { + _output.WriteLine($"[AltSvc_Header_Upgrade_Success]{e}"); + } + } + }, TestEventListener.NetworkingEvents); } - // The test makes a request to a HTTP/1 or HTTP/2 server first, which supplies an Alt-Svc header pointing to the second server. using GenericLoopbackServer firstServer = fromVersion.Major switch @@ -61,12 +74,13 @@ public async Task AltSvc_Header_Upgrade_Success(Version fromVersion, bool overri new HttpHeaderData("Alt-Svc", $"h3=\"{(overrideHost ? secondServer.Address.IdnHost : null)}:{secondServer.Address.Port}\"") }); - await new[] { firstResponseTask, serverTask }.WhenAllOrAnyFailed(30_000); + await new[] { firstResponseTask, serverTask }.WhenAllOrAnyFailed(30_000).ConfigureAwait(false); using HttpResponseMessage firstResponse = firstResponseTask.Result; Assert.True(firstResponse.IsSuccessStatusCode); - await AltSvc_Upgrade_Success(firstServer, secondServer, client); + await AltSvc_Upgrade_Success(firstServer, secondServer, client).ConfigureAwait(false); + listener?.Dispose(); } public static TheoryData AltSvcHeaderUpgradeVersions => @@ -78,34 +92,47 @@ public async Task AltSvc_Header_Upgrade_Success(Version fromVersion, bool overri { HttpVersion.Version20, false } }; - [ConditionalFact] + [Fact] public async Task AltSvc_ConnectionFrame_UpgradeFrom20_Success() { + AsyncLocal asyncLocal = new(); + asyncLocal.Value = new(); + + TestEventListener? listener = null; if (UseVersion == HttpVersion30) { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/101376"); + listener = new TestEventListener(e => + { + if (asyncLocal.Value is not null) + { + lock (_output) + { + _output.WriteLine($"[AltSvc_ConnectionFrame_UpgradeFrom20_Success]{e}"); + } + } + }, TestEventListener.NetworkingEvents); } - using Http2LoopbackServer firstServer = Http2LoopbackServer.CreateServer(); - using Http3LoopbackServer secondServer = CreateHttp3LoopbackServer(); + using Http3LoopbackServer secondServer = CreateHttp3LoopbackServer(options: new() { TestOutputHelper = _output }); using HttpClient client = CreateHttpClient(HttpVersion.Version20); Task firstResponseTask = client.GetAsync(firstServer.Address); Task serverTask = Task.Run(async () => { - await using Http2LoopbackConnection connection = await firstServer.EstablishConnectionAsync(); + await using Http2LoopbackConnection connection = await firstServer.EstablishConnectionAsync().ConfigureAwait(false); - int streamId = await connection.ReadRequestHeaderAsync(); - await connection.WriteFrameAsync(new AltSvcFrame($"https://{firstServer.Address.IdnHost}:{firstServer.Address.Port}", $"h3=\"{secondServer.Address.IdnHost}:{secondServer.Address.Port}\"", streamId: 0)); - await connection.SendDefaultResponseAsync(streamId); + int streamId = await connection.ReadRequestHeaderAsync().ConfigureAwait(false); + await connection.WriteFrameAsync(new AltSvcFrame($"https://{firstServer.Address.IdnHost}:{firstServer.Address.Port}", $"h3=\"{secondServer.Address.IdnHost}:{secondServer.Address.Port}\"", streamId: 0)).ConfigureAwait(false); + await connection.SendDefaultResponseAsync(streamId).ConfigureAwait(false); }); - await new[] { firstResponseTask, serverTask }.WhenAllOrAnyFailed(30_000); + await new[] { firstResponseTask, serverTask }.WhenAllOrAnyFailed(60_000).ConfigureAwait(false); // Allow to fail due to hang and QuicException HttpResponseMessage firstResponse = firstResponseTask.Result; Assert.True(firstResponse.IsSuccessStatusCode); - await AltSvc_Upgrade_Success(firstServer, secondServer, client); + await AltSvc_Upgrade_Success(firstServer, secondServer, client).ConfigureAwait(false); + listener?.Dispose(); } [Fact] @@ -118,28 +145,45 @@ public async Task AltSvc_ResponseFrame_UpgradeFrom20_Success() Task firstResponseTask = client.GetAsync(firstServer.Address); Task serverTask = Task.Run(async () => { - await using Http2LoopbackConnection connection = await firstServer.EstablishConnectionAsync(); + await using Http2LoopbackConnection connection = await firstServer.EstablishConnectionAsync().ConfigureAwait(false); - int streamId = await connection.ReadRequestHeaderAsync(); - await connection.SendDefaultResponseHeadersAsync(streamId); - await connection.WriteFrameAsync(new AltSvcFrame("", $"h3=\"{secondServer.Address.IdnHost}:{secondServer.Address.Port}\"", streamId)); - await connection.SendResponseDataAsync(streamId, Array.Empty(), true); + int streamId = await connection.ReadRequestHeaderAsync().ConfigureAwait(false); + await connection.SendDefaultResponseHeadersAsync(streamId).ConfigureAwait(false); + await connection.WriteFrameAsync(new AltSvcFrame("", $"h3=\"{secondServer.Address.IdnHost}:{secondServer.Address.Port}\"", streamId)).ConfigureAwait(false); + await connection.SendResponseDataAsync(streamId, Array.Empty(), true).ConfigureAwait(false); }); - await new[] { firstResponseTask, serverTask }.WhenAllOrAnyFailed(30_000); + await new[] { firstResponseTask, serverTask }.WhenAllOrAnyFailed(60_000).ConfigureAwait(false); HttpResponseMessage firstResponse = firstResponseTask.Result; Assert.True(firstResponse.IsSuccessStatusCode); - await AltSvc_Upgrade_Success(firstServer, secondServer, client); + await AltSvc_Upgrade_Success(firstServer, secondServer, client).ConfigureAwait(false); } private async Task AltSvc_Upgrade_Success(GenericLoopbackServer firstServer, Http3LoopbackServer secondServer, HttpClient client) { + AsyncLocal asyncLocal = new(); + asyncLocal.Value = new(); + + TestEventListener? listener = null; + if (UseVersion == HttpVersion30) + { + listener = new TestEventListener(e => + { + if (asyncLocal.Value is not null) + { + lock (_output) + { + _output.WriteLine($"[AltSvc_Upgrade_Success]{e}"); + } + } + }, TestEventListener.NetworkingEvents); + } Task secondResponseTask = client.GetAsync(firstServer.Address); Task secondRequestTask = secondServer.AcceptConnectionSendResponseAndCloseAsync(); - await new[] { (Task)secondResponseTask, secondRequestTask }.WhenAllOrAnyFailed(30_000); + await new[] { (Task)secondResponseTask, secondRequestTask }.WhenAllOrAnyFailed(60_000).ConfigureAwait(false); HttpRequestData secondRequest = secondRequestTask.Result; using HttpResponseMessage secondResponse = secondResponseTask.Result; @@ -147,6 +191,7 @@ private async Task AltSvc_Upgrade_Success(GenericLoopbackServer firstServer, Htt string altUsed = secondRequest.GetSingleHeaderValue("Alt-Used"); Assert.Equal($"{secondServer.Address.IdnHost}:{secondServer.Address.Port}", altUsed); Assert.True(secondResponse.IsSuccessStatusCode); + listener?.Dispose(); } } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Finalization.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Finalization.cs index 49989acffb36c..634ca89f32708 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Finalization.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Finalization.cs @@ -21,7 +21,7 @@ private static Task GetAndDropResponse(HttpClient client, Uri url) return Task.Run(async () => { // Get the response stream, but don't dispose it or return it. Just drop it. - await client.GetStreamAsync(url); + _ = await client.GetStreamAsync(url).ConfigureAwait(false); }); } @@ -33,7 +33,7 @@ public async Task IncompleteResponseStream_ResponseDropped_CancelsRequestToServe bool stopGCs = false; await LoopbackServerFactory.CreateClientAndServerAsync(async url => { - await GetAndDropResponse(client, url); + await GetAndDropResponse(client, url).ConfigureAwait(false); while (!Volatile.Read(ref stopGCs)) { @@ -46,15 +46,15 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async url => { try { - HttpRequestData data = await connection.ReadRequestDataAsync(readBody: false); - await connection.SendResponseHeadersAsync(headers: new HttpHeaderData[] { new HttpHeaderData("SomeHeaderName", "AndValue") }); - await connection.WaitForCancellationAsync(); + HttpRequestData data = await connection.ReadRequestDataAsync(readBody: false).ConfigureAwait(false); + await connection.SendResponseHeadersAsync(headers: new HttpHeaderData[] { new HttpHeaderData("SomeHeaderName", "AndValue") }).ConfigureAwait(false); + await connection.WaitForCancellationAsync().ConfigureAwait(false); } finally { Volatile.Write(ref stopGCs, true); } - })); + })).ConfigureAwait(false); } } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs index cbd74f8188a20..48d72d4888013 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs @@ -13,8 +13,6 @@ using Xunit; using Xunit.Abstractions; -using Microsoft.DotNet.XUnitExtensions; - namespace System.Net.Http.Functional.Tests { using Configuration = System.Net.Test.Common.Configuration; @@ -289,17 +287,12 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => }); } - [ConditionalTheory] + [Theory] [InlineData("Thu, 01 Dec 1994 16:00:00 GMT", true)] [InlineData("-1", false)] [InlineData("0", false)] public async Task SendAsync_Expires_Success(string value, bool isValid) { - if (UseVersion == HttpVersion30) - { - throw new SkipTestException("https://github.com/dotnet/runtime/issues/91757"); - } - await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { using (HttpClient client = CreateHttpClient()) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index f91fc2dda1dd3..66029ecd1c0e8 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -1633,10 +1633,7 @@ public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http2(ITest protected override Version UseVersion => HttpVersion.Version20; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/91757")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/101015")] public sealed class SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http3 : SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength { public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http3(ITestOutputHelper output) : base(output) { } @@ -4028,7 +4025,6 @@ public SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http2(ITestOutputH protected override Version UseVersion => HttpVersion.Version20; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandlerTest_HttpClientHandlerTest_Http3 : HttpClientHandlerTest { @@ -4036,19 +4032,13 @@ public SocketsHttpHandlerTest_HttpClientHandlerTest_Http3(ITestOutputHelper outp protected override Version UseVersion => HttpVersion.Version30; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandlerTest_Cookies_Http3 : HttpClientHandlerTest_Cookies { public SocketsHttpHandlerTest_Cookies_Http3(ITestOutputHelper output) : base(output) { } protected override Version UseVersion => HttpVersion.Version30; - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/91757")] - public override Task GetAsync_DefaultCoookieContainer_NoCookieSent() { return null!; } } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandlerTest_HttpClientHandlerTest_Headers_Http3 : HttpClientHandlerTest_Headers { @@ -4056,7 +4046,6 @@ public SocketsHttpHandlerTest_HttpClientHandlerTest_Headers_Http3(ITestOutputHel protected override Version UseVersion => HttpVersion.Version30; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http3 : SocketsHttpHandler_Cancellation_Test { @@ -4064,7 +4053,6 @@ public SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http3(ITestOutputH protected override Version UseVersion => HttpVersion.Version30; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_AltSvc_Test_Http3 : HttpClientHandler_AltSvc_Test { @@ -4072,7 +4060,6 @@ public SocketsHttpHandler_HttpClientHandler_AltSvc_Test_Http3(ITestOutputHelper protected override Version UseVersion => HttpVersion.Version30; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpClientHandler_Finalization_Http3 : HttpClientHandler_Finalization_Test { @@ -4237,7 +4224,6 @@ public SocketsHttpHandler_RequestContentLengthMismatchTest_Http2(ITestOutputHelp protected override Version UseVersion => HttpVersion.Version20; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_RequestContentLengthMismatchTest_Http3 : SocketsHttpHandler_RequestContentLengthMismatchTest { @@ -4337,7 +4323,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync( }; policy.ExtraStore.AddRange(caCerts); - policy.CustomTrustStore.Add(caCerts[caCerts.Count - 1]); + policy.CustomTrustStore.Add(caCerts[caCerts.Count -1]); socketsHandler.SslOptions = new SslClientAuthenticationOptions() { CertificateChainPolicy = policy }; using HttpClient client = CreateHttpClient(handler); @@ -4414,7 +4400,6 @@ public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2(ITestOutputHelpe protected override Version UseVersion => HttpVersion.Version20; } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3 : SocketsHttpHandler_SecurityTest { @@ -4500,7 +4485,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => options: new GenericLoopbackOptions() { UseSsl = true }); } - + } public sealed class SocketsHttpHandler_HttpRequestErrorTest_Http11 : SocketsHttpHandler_HttpRequestErrorTest @@ -4543,7 +4528,6 @@ await Http11LoopbackServerFactory.Singleton.CreateClientAndServerAsync(async uri } } - [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] public sealed class SocketsHttpHandler_HttpRequestErrorTest_Http30 : SocketsHttpHandler_HttpRequestErrorTest { diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs index d5962dfba4d09..e97e4baaf8208 100644 --- a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs +++ b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/Program.cs @@ -16,8 +16,8 @@ using System.Net.Quic; using Microsoft.Quic; -[assembly: SupportedOSPlatform("windows")] -[assembly: SupportedOSPlatform("linux")] +[assembly:SupportedOSPlatform("windows")] +[assembly:SupportedOSPlatform("linux")] namespace HttpStress { @@ -186,6 +186,24 @@ private static async Task Run(Configuration config) Console.WriteLine("Query Parameters: " + config.MaxParameters); Console.WriteLine(); + if (config.HttpVersion == HttpVersion.Version30 && IsQuicSupported) + { + unsafe + { + // If the system gets overloaded, MsQuic has a tendency to drop incoming connections, see https://github.com/dotnet/runtime/issues/55979. + // So in case we're running H/3 stress test, we're using the same hack as for System.Net.Quic tests, which increases the time limit for pending operations in MsQuic thread pool. + object msQuicApiInstance = msQuicApiType.GetProperty("Api", BindingFlags.NonPublic | BindingFlags.Static)!.GetGetMethod(true)!.Invoke(null, Array.Empty())!; + QUIC_API_TABLE* apiTable = (QUIC_API_TABLE*)(Pointer.Unbox(msQuicApiType.GetProperty("ApiTable")!.GetGetMethod()!.Invoke(msQuicApiInstance, Array.Empty())!)); + QUIC_SETTINGS settings = default(QUIC_SETTINGS); + settings.IsSet.MaxWorkerQueueDelayUs = 1; + settings.MaxWorkerQueueDelayUs = 2_500_000u; // 2.5s, 10x the default + if (MsQuic.StatusFailed(apiTable->SetParam(null, MsQuic.QUIC_PARAM_GLOBAL_SETTINGS, (uint)sizeof(QUIC_SETTINGS), (byte*)&settings))) + { + Console.WriteLine($"Unable to set MsQuic MaxWorkerQueueDelayUs."); + } + } + } + StressServer? server = null; if (config.RunMode.HasFlag(RunMode.server)) { 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 28c326b7b65fb..829c279969c2e 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 @@ -62,7 +62,7 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) internal static string? NotSupportedReason { get; } // workaround for https://github.com/microsoft/msquic/issues/4132 - internal static bool SupportsAsyncCertValidation => Version >= new Version(2, 3, 5); + internal static bool SupportsAsyncCertValidation => Version >= new Version(2, 4, 0); internal static bool UsesSChannelBackend { get; } 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 3316fc8050d33..d7c8869029d88 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 @@ -468,10 +468,24 @@ public async ValueTask AcceptInboundStreamAsync(CancellationToken ca throw new InvalidOperationException(SR.net_quic_accept_not_allowed); } + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, $"{this} Waiting for incoming stream"); + } + GCHandle keepObject = GCHandle.Alloc(this); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, $"{this} GC Handle allocated, will read from acceptQueue"); + } try { - return await _acceptQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); + QuicStream stream = await _acceptQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, $"{this} Accepted incoming stream {stream}"); + } + return stream; } catch (ChannelClosedException ex) when (ex.InnerException is not null) { @@ -544,6 +558,11 @@ private unsafe int HandleEventConnected(ref CONNECTED_DATA data) } private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATED_BY_TRANSPORT_DATA data) { + if (NetEventSource.Log.IsEnabled() && data.Status == QUIC_STATUS_CONNECTION_IDLE) // Idle + { + NetEventSource.Info($"{this} Connection Idle: {LocalEndPoint} - {RemoteEndPoint}"); + } + Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetExceptionForMsQuicStatus(data.Status, (long)data.ErrorCode)); _connectedTcs.TrySetException(exception); _connectionCloseTcs.TrySetException(exception); @@ -600,6 +619,10 @@ private unsafe int HandleEventPeerStreamStarted(ref PEER_STREAM_STARTED_DATA dat stream.Dispose(); return QUIC_STATUS_SUCCESS; } + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, $"{this} Stream has been enqueued to the _acceptQueue {stream}"); + } data.Flags |= QUIC_STREAM_OPEN_FLAGS.DELAY_ID_FC_UPDATES; return QUIC_STATUS_SUCCESS; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs index 249f757151913..edd61e1dfab21 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs @@ -238,12 +238,24 @@ private async void StartConnectionHandshake(QuicConnection connection, SslClient handshakeTimeout = options.HandshakeTimeout; linkedCts.CancelAfter(handshakeTimeout); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, $"{connection} Finishing handshake with options: {options}"); + } await connection.FinishHandshakeAsync(options, clientHello.ServerName, cancellationToken).ConfigureAwait(false); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, $"{connection} Finished handshake with options: {options}"); + } if (!_acceptQueue.Writer.TryWrite(connection)) { // Channel has been closed, dispose the connection as it'll never be handed out. await connection.DisposeAsync().ConfigureAwait(false); } + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, $"{connection} has been enqueued to the _acceptQueue"); + } } catch (OperationCanceledException) when (connection.ConnectionShutdownToken.IsCancellationRequested) { @@ -343,6 +355,10 @@ private unsafe int HandleEventNewConnection(ref NEW_CONNECTION_DATA data) SslClientHelloInfo clientHello = new SslClientHelloInfo(data.Info->ServerNameLength > 0 ? Marshal.PtrToStringUTF8((IntPtr)data.Info->ServerName, data.Info->ServerNameLength) : "", SslProtocols.Tls13); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, $"{connection} Starting the handshake process for connection."); + } // Kicks off the rest of the handshake in the background, the process itself will enqueue the result in the accept queue. StartConnectionHandshake(connection, clientHello); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 41ac5e41da24d..b2042adffe33b 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -357,8 +357,10 @@ public async Task UntrustedClientCertificateFails() } } + static bool SupportsAsyncCertValidation => QuicTestCollection.MsQuicVersion >= new Version(2, 4); + [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/99074")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/99074", typeof(MsQuicTests), nameof(SupportsAsyncCertValidation))] public async Task CertificateCallbackThrowPropagates() { using CancellationTokenSource cts = new CancellationTokenSource(PassingTestTimeout); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs index f8dd160acb00b..5125c72e0ca8d 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs @@ -15,7 +15,7 @@ namespace System.Net.Quic.Tests; -[CollectionDefinition(nameof(QuicTestCollection), DisableParallelization = true)] +[CollectionDefinition(nameof(QuicTestCollection))] public unsafe class QuicTestCollection : ICollectionFixture, IDisposable { public static bool IsSupported => QuicListener.IsSupported && QuicConnection.IsSupported;