From be0a5a524f6d4495132469ab65d8edee6b98a3a0 Mon Sep 17 00:00:00 2001 From: Tim Taylor Date: Thu, 13 Apr 2023 16:32:00 -0700 Subject: [PATCH] Fix file upload HTTP client not handling DNS changes (#3266) By setting the connection lease timeout to 5 minutes (defaults to infinite), HTTP operations will perform DNS lookups once every 5 minutes. This allows a client to continue performing file upload operations even if the service changes DNS due to failover --- .../src/Transport/Http/HttpClientHelper.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/iothub/device/src/Transport/Http/HttpClientHelper.cs b/iothub/device/src/Transport/Http/HttpClientHelper.cs index de1e0e0800..dae097807b 100644 --- a/iothub/device/src/Transport/Http/HttpClientHelper.cs +++ b/iothub/device/src/Transport/Http/HttpClientHelper.cs @@ -43,6 +43,22 @@ internal sealed class HttpClientHelper : IHttpClientHelper private readonly ProductInfo _productInfo; private readonly bool _isClientPrimaryTransportHandler; + // 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 + private const int DefaultMaxConnectionsPerServer = 50; + + // How long, in milliseconds, a given cached TCP connection created by this client's HTTP layer will live before being closed. + // If this value is set to any negative value, the connection lease will be infinite. If this value is set to 0, then the TCP connection will close after + // each HTTP request and a new TCP connection will be opened upon the next request. + // + // By closing cached TCP connections and opening a new one upon the next request, the underlying HTTP client has a chance to do a DNS lookup + // to validate that it will send the requests to the correct IP address. While it is atypical for a given IoT hub to change its IP address, it does + // happen when a given IoT hub fails over into a different region. + // + // This default value is consistent with the default value used in Azure.Core + // https://github.com/Azure/azure-sdk-for-net/blob/7e3cf643977591e9041f4c628fd4d28237398e0b/sdk/core/Azure.Core/src/Pipeline/ServicePointHelpers.cs#L29 + private static readonly TimeSpan s_defaultConnectionLeaseTimeout = TimeSpan.FromMinutes(5); + public HttpClientHelper( Uri baseAddress, IAuthorizationProvider authenticationHeaderProvider, @@ -59,6 +75,9 @@ public HttpClientHelper( _authenticationHeaderProvider = authenticationHeaderProvider; _defaultErrorMapping = new ReadOnlyDictionary>>(defaultErrorMapping); + ServicePoint servicePoint = ServicePointManager.FindServicePoint(_baseAddress); + servicePoint.ConnectionLeaseTimeout = s_defaultConnectionLeaseTimeout.Milliseconds; + #if NET451 TlsVersions.Instance.SetLegacyAcceptableVersions(); @@ -105,6 +124,8 @@ public HttpClientHelper( _httpClientHandler.Proxy = proxy; } + _httpClientHandler.MaxConnectionsPerServer = DefaultMaxConnectionsPerServer; + _httpClientObj = new HttpClient(_httpClientHandler); #endif @@ -594,5 +615,5 @@ private static async Task ReadAsAsync(HttpContent content, CancellationTok } #endif - } + } }