diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Asynchrony.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Asynchrony.cs index 8762b4dbfa9348..276ca98ce001d5 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Asynchrony.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Asynchrony.cs @@ -31,6 +31,11 @@ public async Task ResponseHeadersRead_SynchronizationContextNotUsedByHandler(boo return; } + if (UseVersion.Major != 1) + { + return; + } + await Task.Run(async delegate // escape xunit's sync ctx { await LoopbackServer.CreateClientAndServerAsync(uri => diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index 862ac35498719a..ae9dfb1024b68b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -225,7 +225,12 @@ public async ValueTask SetupAsync(CancellationToken cancellationToken) // Processing the incoming frames before sending the client preface and SETTINGS is necessary when using a NamedPipe as a transport. // If the preface and SETTINGS coming from the server are not read first the below WriteAsync and the ProcessIncomingFramesAsync fall into a deadlock. - _ = ProcessIncomingFramesAsync(); + // Avoid capturing the initial request's ExecutionContext for the entire lifetime of the new connection. + using (ExecutionContext.SuppressFlow()) + { + _ = ProcessIncomingFramesAsync(); + } + await _stream.WriteAsync(_outgoingBuffer.ActiveMemory, cancellationToken).ConfigureAwait(false); _rttEstimator.OnInitialSettingsSent(); _outgoingBuffer.ClearAndReturnBuffer(); @@ -249,7 +254,11 @@ public async ValueTask SetupAsync(CancellationToken cancellationToken) throw new IOException(SR.net_http_http2_connection_not_established, e); } - _ = ProcessOutgoingFramesAsync(); + // Avoid capturing the initial request's ExecutionContext for the entire lifetime of the new connection. + using (ExecutionContext.SuppressFlow()) + { + _ = ProcessOutgoingFramesAsync(); + } } private void Shutdown() 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 379c97d5c70754..fbf3e40d5cb216 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 @@ -95,11 +95,15 @@ public void InitQuicConnection(QuicConnection connection, Activity? connectionSe _connection = connection; - // Errors are observed via Abort(). - _sendSettingsTask = SendSettingsAsync(); + // Avoid capturing the initial request's ExecutionContext for the entire lifetime of the new connection. + using (ExecutionContext.SuppressFlow()) + { + // Errors are observed via Abort(). + _sendSettingsTask = SendSettingsAsync(); - // This process is cleaned up when _connection is disposed, and errors are observed via Abort(). - _ = AcceptStreamsAsync(); + // This process is cleaned up when _connection is disposed, and errors are observed via Abort(). + _ = AcceptStreamsAsync(); + } } /// diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 7a82f848e78a95..70c4c72760a59b 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -28,10 +28,9 @@ namespace System.Net.Http.Functional.Tests { using Configuration = System.Net.Test.Common.Configuration; - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] - public sealed class SocketsHttpHandler_HttpClientHandler_Asynchrony_Test : HttpClientHandler_Asynchrony_Test + public sealed class SocketsHttpHandler_HttpClientHandler_Asynchrony_Test_Http11 : SocketsHttpHandler_HttpClientHandler_Asynchrony_Test { - public SocketsHttpHandler_HttpClientHandler_Asynchrony_Test(ITestOutputHelper output) : base(output) { } + public SocketsHttpHandler_HttpClientHandler_Asynchrony_Test_Http11(ITestOutputHelper output) : base(output) { } [OuterLoop("Relies on finalization")] [Fact] @@ -98,6 +97,25 @@ static async Task MakeARequestWithoutDisposingTheHandlerAsync() requestCompleted.SetResult(); } } + } + + public sealed class SocketsHttpHandler_HttpClientHandler_Asynchrony_Test_Http2 : SocketsHttpHandler_HttpClientHandler_Asynchrony_Test + { + public SocketsHttpHandler_HttpClientHandler_Asynchrony_Test_Http2(ITestOutputHelper output) : base(output) { } + protected override Version UseVersion => HttpVersion.Version20; + } + + [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] + public sealed class SocketsHttpHandler_HttpClientHandler_Asynchrony_Test_Http3 : SocketsHttpHandler_HttpClientHandler_Asynchrony_Test + { + public SocketsHttpHandler_HttpClientHandler_Asynchrony_Test_Http3(ITestOutputHelper output) : base(output) { } + protected override Version UseVersion => HttpVersion.Version30; + } + + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public abstract class SocketsHttpHandler_HttpClientHandler_Asynchrony_Test : HttpClientHandler_Asynchrony_Test + { + public SocketsHttpHandler_HttpClientHandler_Asynchrony_Test(ITestOutputHelper output) : base(output) { } [Fact] public async Task ReadAheadTaskOnConnectionReuse_ExceptionsAreObserved() @@ -191,7 +209,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync( public async Task ExecutionContext_HttpConnectionLifetimeDoesntKeepContextAlive() { var clientCompleted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - await LoopbackServer.CreateClientAndServerAsync(async uri => + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { try { @@ -217,7 +235,9 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => { await server.AcceptConnectionAsync(async connection => { - await connection.ReadRequestHeaderAndSendResponseAsync(); + await connection.ReadRequestDataAsync(); + await connection.SendResponseAsync(); + await clientCompleted.Task; }); }); @@ -240,7 +260,7 @@ private static (Task completedOnFinalized, Task getRequest) MakeHttpRequestWithT return (tcs.Task, t); } - private sealed class SetOnFinalized + protected sealed class SetOnFinalized { public readonly TaskCompletionSource CompletedWhenFinalized = new(TaskCreationOptions.RunContinuationsAsynchronously);