Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpClient: Adds detection of DNS changes through use of SocketsHttpHandler for .NET 6 and above #3762

Merged
merged 39 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d461613
initial commit
NaluTripician Mar 14, 2023
9ae170d
removed unneeded usings
NaluTripician Mar 14, 2023
6040c54
added validation callback, still needs tests
NaluTripician Mar 15, 2023
5562f00
nits + fixes
NaluTripician Mar 15, 2023
1644165
added additional test
NaluTripician Mar 16, 2023
f3245e7
test change
NaluTripician Mar 16, 2023
8bc9f23
removed unneeded Dispose calls
NaluTripician Mar 16, 2023
db022f7
removed unnneed dispose calls
NaluTripician Mar 16, 2023
6f188cf
requested changes
NaluTripician Mar 17, 2023
65c8eb9
added pooledConnectionLifetime as client option
NaluTripician Mar 17, 2023
da8e381
nit
NaluTripician Mar 17, 2023
6f5a290
Update Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs
NaluTripician Mar 17, 2023
8127fab
Update Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
NaluTripician Mar 17, 2023
9675ccf
suggested changes
NaluTripician Mar 17, 2023
fb54c24
remove test, reorder usings
NaluTripician Mar 17, 2023
237dd77
updated contracts
NaluTripician Mar 20, 2023
5c56bee
removed all non XXXAPI.json changes from UpdateContracts run
NaluTripician Mar 20, 2023
81c6d9d
Merge branch 'master' into users/nalutripician/EnsureDNSChanges
NaluTripician Mar 23, 2023
8dbf95e
Merge branch 'master' into users/nalutripician/EnsureDNSChanges
NaluTripician Mar 30, 2023
f1698ed
removed public surface, added random timespan
NaluTripician Mar 30, 2023
5b8fa7f
Merge branch 'users/nalutripician/EnsureDNSChanges' of https://github…
NaluTripician Mar 30, 2023
0e75424
removed change from unrelated file
NaluTripician Mar 30, 2023
814b2ef
Update Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs
NaluTripician Mar 30, 2023
39b8da8
added thread safe random method
NaluTripician Mar 30, 2023
04984cf
added thread safe random method
NaluTripician Mar 30, 2023
7f52f15
Merge branch 'master' into users/nalutripician/EnsureDNSChanges
NaluTripician Mar 30, 2023
e762fc0
nit
NaluTripician Mar 31, 2023
c02c5ce
Merge branch 'users/nalutripician/EnsureDNSChanges' of https://github…
NaluTripician Mar 31, 2023
2f7348a
fixed merge mistake
NaluTripician Apr 3, 2023
254c13e
Merge branch 'master' into users/nalutripician/EnsureDNSChanges
NaluTripician Apr 4, 2023
d55ecf0
added reflection failsafe/error tracing
NaluTripician Apr 4, 2023
dc0758f
nits
NaluTripician Apr 4, 2023
ce5db56
added back removed if
NaluTripician Apr 4, 2023
3cb66e0
fixed formatting
NaluTripician Apr 4, 2023
51f3be2
Merge branch 'master' into users/nalutripician/EnsureDNSChanges
ealsur Apr 4, 2023
fd74417
Merge branch 'master' into users/nalutripician/EnsureDNSChanges
ealsur Apr 4, 2023
efd660a
Merge branch 'master' into users/nalutripician/EnsureDNSChanges
ealsur Apr 6, 2023
e6f76c3
Merge branch 'master' into users/nalutripician/EnsureDNSChanges
NaluTripician Apr 7, 2023
af5afcc
changed random method, fixed serverCertificateCustomValidation
NaluTripician Apr 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 62 additions & 18 deletions Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Security;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Resource.CosmosExceptions;
using Microsoft.Azure.Cosmos.Tracing;
using Microsoft.Azure.Cosmos.Tracing.TraceData;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Collections;
Expand Down Expand Up @@ -101,29 +100,74 @@ public static CosmosHttpClient CreateWithConnectionPolicy(

public static HttpMessageHandler CreateHttpClientHandler(int gatewayModeMaxConnectionLimit, IWebProxy webProxy, Func<X509Certificate2, X509Chain, SslPolicyErrors, bool> serverCertificateCustomValidationCallback)
{
HttpClientHandler httpClientHandler = new HttpClientHandler();

// Proxy is only set by users and can cause not supported exception on some platforms
if (webProxy != null)
{
httpClientHandler.Proxy = webProxy;
}

// https://docs.microsoft.com/en-us/archive/blogs/timomta/controlling-the-number-of-outgoing-connections-from-httpclient-net-core-or-full-framework
HttpClientHandler httpClientHandler;
NaluTripician marked this conversation as resolved.
Show resolved Hide resolved
try
{
httpClientHandler.MaxConnectionsPerServer = gatewayModeMaxConnectionLimit;
if (serverCertificateCustomValidationCallback != null)
Type socketHandlerType = Type.GetType("System.Net.Http.SocketsHttpHandler, System.Net.Http");
ealsur marked this conversation as resolved.
Show resolved Hide resolved

object socketHttpHandler = Activator.CreateInstance(socketHandlerType);

PropertyInfo pooledConnectionLifetimeInfo = socketHandlerType.GetProperty("PooledConnectionLifetime");
pooledConnectionLifetimeInfo.SetValue(socketHttpHandler, TimeSpan.FromMinutes(5), null);

// Proxy is only set by users and can cause not supported exception on some platforms
if (webProxy != null)
{
httpClientHandler.ServerCertificateCustomValidationCallback = (_, certificate2, x509Chain, sslPolicyErrors) => serverCertificateCustomValidationCallback(certificate2, x509Chain, sslPolicyErrors);
PropertyInfo webProxyInfo = socketHandlerType.GetProperty("Proxy");
webProxyInfo.SetValue(socketHttpHandler, webProxy, null);
}

// https://docs.microsoft.com/en-us/archive/blogs/timomta/controlling-the-number-of-outgoing-connections-from-httpclient-net-core-or-full-framework
try
{
PropertyInfo maxConnectionsPerSercerInfo = socketHandlerType.GetProperty("MaxConnectionsPerServer");
maxConnectionsPerSercerInfo.SetValue(socketHttpHandler, gatewayModeMaxConnectionLimit, null);

if (serverCertificateCustomValidationCallback != null)
{
Func<HttpRequestMessage, X509Certificate2, X509Chain, SslPolicyErrors, bool> customCertificateValidationCallback =
(_, certificate2, x509Chain, sslPolicyErrors) => serverCertificateCustomValidationCallback(certificate2, x509Chain, sslPolicyErrors);

PropertyInfo serverCertificateCustomValidationCallbackInfo = socketHandlerType.GetProperty("ServerCertificateCustomValidationCallback");
ealsur marked this conversation as resolved.
Show resolved Hide resolved
serverCertificateCustomValidationCallbackInfo.SetValue(socketHttpHandler,
customCertificateValidationCallback,
null);
}
}
// MaxConnectionsPerServer is not supported on some platforms.
catch (PlatformNotSupportedException)
{
}

return (HttpMessageHandler)socketHttpHandler;

}
// MaxConnectionsPerServer is not supported on some platforms.
catch (PlatformNotSupportedException)
catch
ealsur marked this conversation as resolved.
Show resolved Hide resolved
{
}
httpClientHandler = new HttpClientHandler();

// Proxy is only set by users and can cause not supported exception on some platforms
if (webProxy != null)
{
httpClientHandler.Proxy = webProxy;
}

// https://docs.microsoft.com/en-us/archive/blogs/timomta/controlling-the-number-of-outgoing-connections-from-httpclient-net-core-or-full-framework
try
{
httpClientHandler.MaxConnectionsPerServer = gatewayModeMaxConnectionLimit;
if (serverCertificateCustomValidationCallback != null)
{
httpClientHandler.ServerCertificateCustomValidationCallback = (_, certificate2, x509Chain, sslPolicyErrors) => serverCertificateCustomValidationCallback(certificate2, x509Chain, sslPolicyErrors);
}
}
// MaxConnectionsPerServer is not supported on some platforms.
catch (PlatformNotSupportedException)
{
}

return httpClientHandler;
return httpClientHandler;
}
}

private static HttpMessageHandler CreateHttpMessageHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public async Task ValidateExceptionOnInitTask()
Assert.IsFalse(object.ReferenceEquals(ex, cosmosException1));
Assert.IsTrue(httpCallCount > 0);
}

//Test cleanup
cosmosClient.Dispose();
ealsur marked this conversation as resolved.
Show resolved Hide resolved
}

[TestMethod]
Expand Down Expand Up @@ -154,6 +157,9 @@ public async Task InitTaskThreadSafe()
this.TaskStartedCount = 0;
httpCallCount = 0;
}

//Test cleanup
cosmosClient.Dispose();
}


Expand Down Expand Up @@ -287,6 +293,9 @@ public async Task ValidateAzureKeyCredentialDirectModeUpdateAsync()
masterKeyCredential.Update(authKey);
await TestCommon.DeleteDatabaseAsync(client, client.GetDatabase(databaseName));
}

//Test cleanup
client.Dispose();
}
}

Expand Down Expand Up @@ -338,6 +347,9 @@ public async Task ResourceResponseStreamingTest()
{
Console.WriteLine("Expected exception while deserializing Resource: " + ex.Message);
}

//Test cleanup
client.Dispose();
ealsur marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -382,6 +394,9 @@ public async Task TestHeadersPassedinByClient()
ulong capability = ulong.Parse(sdkSupportedCapability);

Assert.AreEqual((ulong)SDKSupportedCapabilities.PartitionMerge, capability & (ulong)SDKSupportedCapabilities.PartitionMerge,$" received header value as {sdkSupportedCapability}");

//Test cleanup
cosmosClient.Dispose();
}

[TestMethod]
Expand Down Expand Up @@ -445,6 +460,9 @@ internal async Task TestEtagOnUpsertOperation(bool useGateway, Protocol protocol
{
await client.DeleteDatabaseAsync(db);
}

//Test cleanup
client.Dispose();
}
}

Expand Down Expand Up @@ -487,6 +505,8 @@ public async Task Verify_CertificateCallBackGetsCalled_ForTCP_HTTP()
await database?.DeleteStreamAsync();
}

//Test cleanup
cosmosClient.Dispose();
}

[TestMethod]
Expand Down Expand Up @@ -857,6 +877,9 @@ public async Task HttpClientFactorySmokeTest()
await database.DeleteAsync();
}
}

//Test cleanup
cosmosClient.Dispose();
}

[TestMethod]
Expand All @@ -878,7 +901,7 @@ public async Task HttpClientConnectionLimitTest()
))
{
CosmosHttpClient cosmosHttpClient = cosmosClient.DocumentClient.httpClient;
HttpClientHandler httpClientHandler = (HttpClientHandler)cosmosHttpClient.HttpMessageHandler;
SocketsHttpHandler httpClientHandler = (SocketsHttpHandler)cosmosHttpClient.HttpMessageHandler;
Assert.AreEqual(gatewayConnectionLimit, httpClientHandler.MaxConnectionsPerServer);

Cosmos.Database database = await cosmosClient.CreateDatabaseAsync(Guid.NewGuid().ToString());
Expand All @@ -897,6 +920,7 @@ public async Task HttpClientConnectionLimitTest()

// Clean up the database and container
await database.DeleteAsync();
cosmosClient.Dispose();
}


Expand All @@ -907,6 +931,23 @@ public async Task HttpClientConnectionLimitTest()
$"Before connections: {JsonConvert.SerializeObject(excludeConnections)}; After connections: {JsonConvert.SerializeObject(afterConnections)}");
}

[TestMethod]
public void PooledConnectionLifetimeTest()
{
//Create Cosmos Client
using CosmosClient cosmosClient = new CosmosClient(
accountEndpoint: "https://localhost:8081",
authKeyOrResourceToken: Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())));

//Assert type of message handler
Type socketHandlerType = Type.GetType("System.Net.Http.SocketsHttpHandler, System.Net.Http");
Type clientMessageHandlerType = cosmosClient.ClientContext.DocumentClient.httpClient.HttpMessageHandler.GetType();
Assert.AreEqual(socketHandlerType, clientMessageHandlerType);

//Test cleanup
cosmosClient.Dispose();
NaluTripician marked this conversation as resolved.
Show resolved Hide resolved
}

public static IReadOnlyList<string> GetActiveConnections()
{
string testPid = Process.GetCurrentProcess().Id.ToString();
Expand Down