Skip to content

Commit

Permalink
Consistent ConfigureAwait on Loopback, RemoteExecutor.DisposeAsync (d…
Browse files Browse the repository at this point in the history
…otnet#102699)

* Add ConfigureAwait(false) to various place

* Add RemoteExecutor.DisposeAsync and use it on System.Net.Mail

* Add RemoteExecutor.DisposeAsync and use it on System.Net.Http

* Add RemoteExecutor.DisposeAsync and use it on System.Net.Primitives

* Add RemoteExecutor.DisposeAsync and use it on System.Net.Quic

* Add RemoteExecutor.DisposeAsync and use it on System.Net.Requests

* Add RemoteExecutor.DisposeAsync and use it on System.Net.Security

* Add RemoteExecutor.DisposeAsync and use it on System.Net.Sockets

* Add RemoteExecutor.DisposeAsync and use it on Common System.Net
  • Loading branch information
liveans authored Jun 3, 2024
1 parent 48f260e commit 17bd1da
Show file tree
Hide file tree
Showing 55 changed files with 437 additions and 384 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public override async ValueTask DisposeAsync()
// Dispose the connection
// If we already waited for graceful shutdown from the client, then the connection is already closed and this will simply release the handle.
// If not, then this will silently abort the connection.
await _connection.DisposeAsync();
await _connection.DisposeAsync().ConfigureAwait(false);

// Dispose control streams so that we release their handles too.
if (_inboundControlStream is not null)
Expand All @@ -92,12 +92,12 @@ public override async ValueTask DisposeAsync()

public async ValueTask<Http3LoopbackStream> OpenUnidirectionalStreamAsync()
{
return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional));
return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional).ConfigureAwait(false));
}

public async ValueTask<Http3LoopbackStream> OpenBidirectionalStreamAsync()
{
return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional));
return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional).ConfigureAwait(false));
}

public static int GetRequestId(QuicStream stream)
Expand Down Expand Up @@ -141,10 +141,10 @@ async Task EnsureControlStreamAcceptedInternalAsync()
_delayedStreams.Enqueue(quicStream);
}

long? streamType = await controlStream.ReadIntegerAsync();
long? streamType = await controlStream.ReadIntegerAsync().ConfigureAwait(false);
Assert.Equal(Http3LoopbackStream.ControlStream, streamType);

List<(long settingId, long settingValue)> settings = await controlStream.ReadSettingsAsync();
List<(long settingId, long settingValue)> settings = await controlStream.ReadSettingsAsync().ConfigureAwait(false);
(long settingId, long settingValue) = Assert.Single(settings);

Assert.Equal(Http3LoopbackStream.MaxHeaderListSize, settingId);
Expand Down Expand Up @@ -177,17 +177,17 @@ public async Task<Http3LoopbackStream> AcceptRequestStreamAsync()

public async Task<(Http3LoopbackStream clientControlStream, Http3LoopbackStream requestStream)> AcceptControlAndRequestStreamAsync()
{
Http3LoopbackStream requestStream = await AcceptRequestStreamAsync();
Http3LoopbackStream requestStream = await AcceptRequestStreamAsync().ConfigureAwait(false);
Http3LoopbackStream controlStream = _inboundControlStream;

return (controlStream, requestStream);
}

public async Task EstablishControlStreamAsync(SettingsEntry[] settingsEntries)
{
_outboundControlStream = await OpenUnidirectionalStreamAsync();
await _outboundControlStream.SendUnidirectionalStreamTypeAsync(Http3LoopbackStream.ControlStream);
await _outboundControlStream.SendSettingsFrameAsync(settingsEntries);
_outboundControlStream = await OpenUnidirectionalStreamAsync().ConfigureAwait(false);
await _outboundControlStream.SendUnidirectionalStreamTypeAsync(Http3LoopbackStream.ControlStream).ConfigureAwait(false);
await _outboundControlStream.SendSettingsFrameAsync(settingsEntries).ConfigureAwait(false);
}

public async Task DisposeCurrentStream()
Expand All @@ -213,7 +213,7 @@ public override async Task<HttpRequestData> ReadRequestDataAsync(bool readBody =

public override async Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "", bool isFinal = true)
{
await _currentStream.SendResponseAsync(statusCode, headers, content, isFinal);
await _currentStream.SendResponseAsync(statusCode, headers, content, isFinal).ConfigureAwait(false);
if (isFinal)
{
await DisposeCurrentStream().ConfigureAwait(false);
Expand All @@ -222,7 +222,7 @@ public override async Task SendResponseAsync(HttpStatusCode statusCode = HttpSta

public override async Task SendResponseBodyAsync(byte[] content, bool isFinal = true)
{
await _currentStream.SendResponseBodyAsync(content, isFinal);
await _currentStream.SendResponseBodyAsync(content, isFinal).ConfigureAwait(false);
if (isFinal)
{
await DisposeCurrentStream().ConfigureAwait(false);
Expand All @@ -249,11 +249,11 @@ public override async Task<HttpRequestData> HandleRequestAsync(HttpStatusCode st
// So, send a GOAWAY frame now so the client won't inadvertantly try to reuse the connection.
// Note that in HTTP3 (unlike HTTP2) there is no strict ordering between the GOAWAY and the response below;
// so the client may race in processing them and we need to handle this.
await _outboundControlStream.SendGoAwayFrameAsync(stream.StreamId + 4);
await _outboundControlStream.SendGoAwayFrameAsync(stream.StreamId + 4).ConfigureAwait(false);

await stream.SendResponseAsync(statusCode, headers, content).ConfigureAwait(false);

await WaitForClientDisconnectAsync();
await WaitForClientDisconnectAsync().ConfigureAwait(false);

return request;
}
Expand All @@ -263,7 +263,7 @@ public async Task ShutdownAsync(bool failCurrentRequest = false)
try
{
long firstInvalidStreamId = failCurrentRequest ? _currentStreamId : _currentStreamId + 4;
await _outboundControlStream.SendGoAwayFrameAsync(firstInvalidStreamId);
await _outboundControlStream.SendGoAwayFrameAsync(firstInvalidStreamId).ConfigureAwait(false);
}
catch (QuicException abortException) when (abortException.QuicError == QuicError.ConnectionAborted && abortException.ApplicationErrorCode == H3_NO_ERROR)
{
Expand All @@ -283,7 +283,7 @@ public async Task ShutdownAsync(bool failCurrentRequest = false)
return;
}

await WaitForClientDisconnectAsync();
await WaitForClientDisconnectAsync().ConfigureAwait(false);
}

// Wait for the client to close the connection, e.g. after we send a GOAWAY, or after the HttpClient is disposed.
Expand Down Expand Up @@ -315,10 +315,10 @@ public async Task WaitForClientDisconnectAsync(bool refuseNewRequests = true)

// The client's control stream should throw QuicConnectionAbortedException, indicating that it was
// aborted because the connection was closed (and was not explicitly closed or aborted prior to the connection being closed)
QuicException ex = await Assert.ThrowsAsync<QuicException>(async () => await _inboundControlStream.ReadFrameAsync());
QuicException ex = await Assert.ThrowsAsync<QuicException>(async () => await _inboundControlStream.ReadFrameAsync().ConfigureAwait(false));
Assert.Equal(QuicError.ConnectionAborted, ex.QuicError);

await CloseAsync(H3_NO_ERROR);
await CloseAsync(H3_NO_ERROR).ConfigureAwait(false);
}

public override async Task WaitForCancellationAsync(bool ignoreIncomingData = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.IO;
using System.Net.Quic;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;

Expand Down Expand Up @@ -71,13 +70,13 @@ private async Task<Http3LoopbackConnection> EstablishHttp3ConnectionAsync(params
QuicConnection con = await _listener.AcceptConnectionAsync().ConfigureAwait(false);
Http3LoopbackConnection connection = new Http3LoopbackConnection(con);

await connection.EstablishControlStreamAsync(settingsEntries);
await connection.EstablishControlStreamAsync(settingsEntries).ConfigureAwait(false);
return connection;
}

public override async Task<GenericLoopbackConnection> EstablishGenericConnectionAsync()
{
return await EstablishHttp3ConnectionAsync();
return await EstablishHttp3ConnectionAsync().ConfigureAwait(false);
}

public Task<Http3LoopbackConnection> EstablishConnectionAsync(params SettingsEntry[] settingsEntries)
Expand All @@ -89,12 +88,12 @@ public override async Task AcceptConnectionAsync(Func<GenericLoopbackConnection,
{
await using Http3LoopbackConnection con = await EstablishHttp3ConnectionAsync().ConfigureAwait(false);
await funcAsync(con).ConfigureAwait(false);
await con.ShutdownAsync();
await con.ShutdownAsync().ConfigureAwait(false);
}

public override async Task<HttpRequestData> HandleRequestAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "")
{
await using Http3LoopbackConnection con = (Http3LoopbackConnection)await EstablishGenericConnectionAsync().ConfigureAwait(false);
await using Http3LoopbackConnection con = await EstablishHttp3ConnectionAsync().ConfigureAwait(false);
return await con.HandleRequestAsync(statusCode, headers, content).ConfigureAwait(false);
}
}
Expand All @@ -113,7 +112,7 @@ public override GenericLoopbackServer CreateServer(GenericLoopbackOptions option
public override async Task CreateServerAsync(Func<GenericLoopbackServer, Uri, Task> funcAsync, int millisecondsTimeout = LoopbackServerTimeoutMilliseconds, GenericLoopbackOptions options = null)
{
using GenericLoopbackServer server = CreateServer(options);
await funcAsync(server, server.Address).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout));
await funcAsync(server, server.Address).WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout)).ConfigureAwait(false);
}

public override Task<GenericLoopbackConnection> CreateConnectionAsync(SocketWrapper socket, Stream stream, GenericLoopbackOptions options = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private async Task SendPartialHeadersFrameAsync(HttpStatusCode? statusCode, IEnu
{
Memory<byte> payload = ConstructHeadersPayload(statusCode, headers);

await SendFrameHeaderAsync(HeadersFrame, payload.Length);
await SendFrameHeaderAsync(HeadersFrame, payload.Length).ConfigureAwait(false);

// Slice off final byte so the payload is not complete
payload = payload.Slice(0, payload.Length - 1);
Expand All @@ -144,7 +144,7 @@ public async Task SendGoAwayFrameAsync(long firstInvalidStreamId)
int bytesWritten = 0;

bytesWritten += EncodeHttpInteger(firstInvalidStreamId, buffer);
await SendFrameAsync(GoAwayFrame, buffer.AsMemory(0, bytesWritten));
await SendFrameAsync(GoAwayFrame, buffer.AsMemory(0, bytesWritten)).ConfigureAwait(false);
}

private async Task SendFrameHeaderAsync(long frameType, int payloadLength)
Expand Down Expand Up @@ -367,11 +367,11 @@ async Task WaitForReadCancellation()
{
if (ignoreIncomingData)
{
await DrainResponseData();
await DrainResponseData().ConfigureAwait(false);
}
else
{
int bytesRead = await _stream.ReadAsync(new byte[1]);
int bytesRead = await _stream.ReadAsync(new byte[1]).ConfigureAwait(false);
if (bytesRead != 0)
{
throw new Exception($"Unexpected data received while waiting for client cancllation.");
Expand All @@ -388,15 +388,15 @@ async Task WaitForWriteCancellation()
{
try
{
await _stream.WritesClosed;
await _stream.WritesClosed.ConfigureAwait(false);
}
catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted && ex.ApplicationErrorCode == Http3LoopbackConnection.H3_REQUEST_CANCELLED)
{
writeCanceled = true;
}
}

await Task.WhenAll(WaitForReadCancellation(), WaitForWriteCancellation());
await Task.WhenAll(WaitForReadCancellation(), WaitForWriteCancellation()).ConfigureAwait(false);

if (!readCanceled && !writeCanceled)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ public async Task ProxySetViaEnvironmentVariable_DefaultProxyCredentialsUsed(boo
const string ExpectedPassword = "rightpassword";
LoopbackServer.Options options = new LoopbackServer.Options { IsProxy = true, Username = ExpectedUsername, Password = ExpectedPassword };

await LoopbackServer.CreateClientAndServerAsync(uri => Task.Run(() =>
await LoopbackServer.CreateClientAndServerAsync(uri => Task.Run(async () =>
{
var psi = new ProcessStartInfo();
psi.Environment.Add("http_proxy", $"http://{uri.Host}:{uri.Port}");
RemoteExecutor.Invoke(async (useProxyString, useVersionString, uriString) =>
await RemoteExecutor.Invoke(async (useProxyString, useVersionString, uriString) =>
{
using (HttpClientHandler handler = CreateHttpClientHandler(useVersionString))
using (HttpClient client = CreateHttpClient(handler, useVersionString))
Expand All @@ -111,7 +111,7 @@ await LoopbackServer.CreateClientAndServerAsync(uri => Task.Run(() =>
}, useProxy.ToString(), UseVersion.ToString(),
// If proxy is used , the url does not matter. We set it to be different to avoid confusion.
useProxy ? Configuration.Http.RemoteEchoServer.ToString() : uri.ToString(),
new RemoteInvokeOptions { StartInfo = psi }).Dispose();
new RemoteInvokeOptions { StartInfo = psi }).DisposeAsync();
}),
server => server.AcceptConnectionAsync(async connection =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ public async Task PostAsync_Post_ChannelBinding_ConfiguredCorrectly()

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[PlatformSpecific(TestPlatforms.Linux)]
public void HttpClientUsesSslCertEnvironmentVariables()
public async Task HttpClientUsesSslCertEnvironmentVariables()
{
// We set SSL_CERT_DIR and SSL_CERT_FILE to empty locations.
// The HttpClient should fail to validate the server certificate.
Expand All @@ -395,7 +395,7 @@ public void HttpClientUsesSslCertEnvironmentVariables()
File.WriteAllText(sslCertFile, "");
psi.Environment.Add("SSL_CERT_FILE", sslCertFile);

RemoteExecutor.Invoke(async (useVersionString, allowAllCertificatesString) =>
await RemoteExecutor.Invoke(async (useVersionString, allowAllCertificatesString) =>
{
const string Url = "https://www.microsoft.com";
var version = Version.Parse(useVersionString);
Expand All @@ -405,7 +405,7 @@ public void HttpClientUsesSslCertEnvironmentVariables()
using HttpClient client = CreateHttpClient(handler, useVersionString);
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(Url));
}, UseVersion.ToString(), AllowAllCertificates.ToString(), new RemoteInvokeOptions { StartInfo = psi }).Dispose();
}, UseVersion.ToString(), AllowAllCertificates.ToString(), new RemoteInvokeOptions { StartInfo = psi }).DisposeAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Sockets;
#if !NETFRAMEWORK
using System.Net.Quic;
#endif
Expand Down
15 changes: 15 additions & 0 deletions src/libraries/Common/tests/System/Net/RemoteExecutorExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Microsoft.DotNet.RemoteExecutor;

namespace Microsoft.DotNet.RemoteExecutor;

internal static class RemoteExecutorExtensions
{
public static async ValueTask DisposeAsync(this RemoteInvokeHandle handle)
{
await Task.Run(handle.Dispose);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
Link="Common\System\Net\EventSourceTestLogging.cs" />
<Compile Include="$(CommonTestPath)System\Net\HttpsTestServer.cs"
Link="Common\System\Net\HttpsTestServer.cs" />
<Compile Include="$(CommonTestPath)System\Net\RemoteExecutorExtensions.cs"
Link="Common\System\Net\RemoteExecutorExtensions.cs" />
<Compile Include="$(CommonTestPath)System\Net\RemoteServerQuery.cs"
Link="Common\System\Net\RemoteServerQuery.cs" />
<Compile Include="$(CommonTestPath)System\Net\VerboseTestLogging.cs"
Expand Down
Loading

0 comments on commit 17bd1da

Please sign in to comment.