From 6027e88cbed9ecb16834605f4398095062a46715 Mon Sep 17 00:00:00 2001 From: Tim Taylor Date: Tue, 18 Apr 2023 14:17:58 -0700 Subject: [PATCH] Properly set connection lease timeout regardless of .NET target framework (#3279) Previous fix only worked for .NET framework, not .NET core #3263 --- .../src/Transport/Http/HttpClientHelper.cs | 11 ++-- .../src/Transport/Http/ServicePointHelpers.cs | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 iothub/device/src/Transport/Http/ServicePointHelpers.cs diff --git a/iothub/device/src/Transport/Http/HttpClientHelper.cs b/iothub/device/src/Transport/Http/HttpClientHelper.cs index dae097807b..edf4aef573 100644 --- a/iothub/device/src/Transport/Http/HttpClientHelper.cs +++ b/iothub/device/src/Transport/Http/HttpClientHelper.cs @@ -75,9 +75,6 @@ public HttpClientHelper( _authenticationHeaderProvider = authenticationHeaderProvider; _defaultErrorMapping = new ReadOnlyDictionary>>(defaultErrorMapping); - ServicePoint servicePoint = ServicePointManager.FindServicePoint(_baseAddress); - servicePoint.ConnectionLeaseTimeout = s_defaultConnectionLeaseTimeout.Milliseconds; - #if NET451 TlsVersions.Instance.SetLegacyAcceptableVersions(); @@ -104,8 +101,6 @@ public HttpClientHelper( _httpClientHandler.UseProxy = (proxy != null); _httpClientHandler.Proxy = proxy; } - - _httpClientObj = _httpClientHandler != null ? new HttpClient(_httpClientHandler) : new HttpClient(); #else _httpClientHandler = httpClientHandler ?? new HttpClientHandler(); @@ -123,11 +118,11 @@ public HttpClientHelper( _httpClientHandler.UseProxy = proxy != null; _httpClientHandler.Proxy = proxy; } +#endif - _httpClientHandler.MaxConnectionsPerServer = DefaultMaxConnectionsPerServer; + ServicePointHelpers.SetLimits(_httpClientHandler, _baseAddress); - _httpClientObj = new HttpClient(_httpClientHandler); -#endif + _httpClientObj = _httpClientHandler != null ? new HttpClient(_httpClientHandler) : new HttpClient(); _httpClientObj.BaseAddress = _baseAddress; _httpClientObj.Timeout = timeout; diff --git a/iothub/device/src/Transport/Http/ServicePointHelpers.cs b/iothub/device/src/Transport/Http/ServicePointHelpers.cs new file mode 100644 index 0000000000..d1ff818287 --- /dev/null +++ b/iothub/device/src/Transport/Http/ServicePointHelpers.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Net; +using System.Net.Http; + +namespace Microsoft.Azure.Devices.Client +{ + // This type manages changing HttpClient defaults to more appropriate values + // There are two limits we target: + // - Per Server Connection Limit + // - Keep Alive Connection Timeout + // On .NET Core 2.1+ the HttpClient defaults to using the HttpSocketHandler so we adjust both limits on the client handler + // + // On .NET Standard & NET 4.51+ the HttpClient defaults to using the HttpClientHandler + // and there is no easy way to set Keep Alive Connection Timeout but it's mitigated by setting the service point's connection lease timeout + internal static class ServicePointHelpers + { + // These default values are consistent with Azure.Core default values: + // https://github.com/Azure/azure-sdk-for-net/blob/7e3cf643977591e9041f4c628fd4d28237398e0b/sdk/core/Azure.Core/src/Pipeline/ServicePointHelpers.cs#L28 + internal const int DefaultMaxConnectionsPerServer = 50; + + internal const int DefaultConnectionLeaseTimeout = 300 * 1000; // 5 minutes + + // messageHandler passed in is an HttpClientHandler for .NET Framework and .NET standard, and a SocketsHttpHandler for .NET core + public static void SetLimits(HttpMessageHandler messageHandler, Uri baseUri, int connectionLeaseTimeoutMilliseconds = DefaultConnectionLeaseTimeout) + { + if (messageHandler == null) + { + // no limits can be set if no handler is provided + return; + } + + switch (messageHandler) + { + case HttpClientHandler httpClientHandler: +#if !NET451 + httpClientHandler.MaxConnectionsPerServer = DefaultMaxConnectionsPerServer; +#endif + ServicePoint servicePoint = ServicePointManager.FindServicePoint(baseUri); + servicePoint.ConnectionLeaseTimeout = connectionLeaseTimeoutMilliseconds; + break; +#if NETCOREAPP2_1_OR_GREATER || NET5_0_OR_GREATER + // SocketsHttpHandler is only available in netcore2.1 and onwards + case SocketsHttpHandler socketsHttpHandler: + socketsHttpHandler.MaxConnectionsPerServer = DefaultMaxConnectionsPerServer; + socketsHttpHandler.PooledConnectionLifetime = TimeSpan.FromMilliseconds(connectionLeaseTimeoutMilliseconds); + break; +#endif + } + } + } +}