From 9a871bbdde007ddfd54934dc6bd72f48a43ef3c9 Mon Sep 17 00:00:00 2001 From: "David R. Williamson" Date: Tue, 7 Feb 2023 11:47:55 -0800 Subject: [PATCH] Update IoT hub service client init --- e2e/test/helpers/TestDevice.cs | 10 ++- iothub/service/src/ClientApiVersionHelper.cs | 4 +- iothub/service/src/IotHubServiceClient.cs | 69 +++++++++---------- .../src/JsonSerializerSettingsInitializer.cs | 8 +-- .../service/src/ProvisioningServiceClient.cs | 9 +-- 5 files changed, 44 insertions(+), 56 deletions(-) diff --git a/e2e/test/helpers/TestDevice.cs b/e2e/test/helpers/TestDevice.cs index 09ce6ea1da..adfe06d60d 100644 --- a/e2e/test/helpers/TestDevice.cs +++ b/e2e/test/helpers/TestDevice.cs @@ -37,6 +37,7 @@ public sealed class TestDevice : IDisposable private static readonly IIotHubServiceRetryPolicy s_getRetryPolicy = new HubServiceTestRetryPolicy(s_getRetryableStatusCodes); private X509Certificate2 _authCertificate; + private static readonly IotHubServiceClient _client = new(TestConfiguration.IotHub.ConnectionString); private TestDevice(Device device, IAuthenticationMethod authenticationMethod) { @@ -72,7 +73,6 @@ private static async Task CreateDeviceAsync(TestDeviceType type, str string deviceName = "E2E_" + prefix + Guid.NewGuid(); // Delete existing devices named this way and create a new one. - using var serviceClient = new IotHubServiceClient(TestConfiguration.IotHub.ConnectionString); VerboseTestLogger.WriteLine($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}."); IAuthenticationMethod auth = null; @@ -102,7 +102,7 @@ await RetryOperationHelper .RunWithHubServiceRetryAsync( async () => { - device = await serviceClient.Devices.CreateAsync(requestDevice).ConfigureAwait(false); + device = await _client.Devices.CreateAsync(requestDevice).ConfigureAwait(false); }, s_createRetryPolicy, CancellationToken.None) @@ -113,7 +113,7 @@ await RetryOperationHelper .RunWithHubServiceRetryAsync( async () => { - await serviceClient.Devices.GetAsync(requestDevice.Id).ConfigureAwait(false); + await _client.Devices.GetAsync(requestDevice.Id).ConfigureAwait(false); }, s_getRetryPolicy, CancellationToken.None) @@ -184,13 +184,11 @@ public IotHubDeviceClient CreateDeviceClient(IotHubClientOptions options = defau public async Task RemoveDeviceAsync() { - using var serviceClient = new IotHubServiceClient(TestConfiguration.IotHub.ConnectionString); - await RetryOperationHelper .RunWithHubServiceRetryAsync( async () => { - await serviceClient.Devices.DeleteAsync(Id).ConfigureAwait(false); + await _client.Devices.DeleteAsync(Id).ConfigureAwait(false); }, s_getRetryPolicy, CancellationToken.None) diff --git a/iothub/service/src/ClientApiVersionHelper.cs b/iothub/service/src/ClientApiVersionHelper.cs index 589280a636..89159ca508 100644 --- a/iothub/service/src/ClientApiVersionHelper.cs +++ b/iothub/service/src/ClientApiVersionHelper.cs @@ -9,11 +9,11 @@ namespace Microsoft.Azure.Devices internal static class ClientApiVersionHelper { private const string ApiVersionQueryPrefix = "api-version="; - private const string ApiVersionDefault = "2021-04-12"; + internal const string ApiVersionDefault = "2021-04-12"; /// /// The API version used in all service requests. /// - public const string ApiVersionQueryString = ApiVersionQueryPrefix + ApiVersionDefault; + internal const string ApiVersionQueryString = ApiVersionQueryPrefix + ApiVersionDefault; } } diff --git a/iothub/service/src/IotHubServiceClient.cs b/iothub/service/src/IotHubServiceClient.cs index 7c9016bd54..d45ea5f253 100644 --- a/iothub/service/src/IotHubServiceClient.cs +++ b/iothub/service/src/IotHubServiceClient.cs @@ -11,16 +11,18 @@ namespace Microsoft.Azure.Devices { /// - /// The client for making service requests to IoT hub. This client contains subclients for the various feature sets - /// within IoT hub including managing device/module identities, getting/setting twin for device/modules, invoking - /// direct methods on devices/modules, and more. + /// The client for making service requests to IoT hub. /// /// - /// This client is but users are not responsible for disposing subclients within this client. + /// This client contains subclients for the various feature sets within IoT hub including managing device/module + /// identities, getting/setting twin for device/modules, invoking direct methods on devices/modules, and more. /// - /// This client creates a lifetime long instance of that is tied to the URI of the + /// This client is , which will dispose the subclients. + /// + /// + /// This client creates a lifetime-long instance of that is tied to the URI of the /// IoT hub specified and configured with any proxy settings provided. - /// For that reason, the instances are not static and an application using this client + /// For that reason, the HttpClient instances are not static and an application using this client /// should create and save it for all use. Repeated creation may cause /// socket exhaustion. /// @@ -33,7 +35,7 @@ public class IotHubServiceClient : IDisposable private readonly HttpRequestMessageFactory _httpRequestMessageFactory; private readonly IIotHubServiceRetryPolicy _retryPolicy; private readonly RetryHandler _retryHandler; - private const string ApiVersion = "2021-04-12"; + private readonly IotHubServiceClientOptions _clientOptions; /// /// Creates an instance of this class. Provided for unit testing purposes only. @@ -42,6 +44,13 @@ protected IotHubServiceClient() { } + private IotHubServiceClient(IotHubServiceClientOptions options) + { + _clientOptions = options?.Clone() ?? new(); + _retryPolicy = _clientOptions.RetryPolicy ?? new IotHubServiceNoRetry(); + _retryHandler = new RetryHandler(_retryPolicy); + } + /// /// Create an instance of this class that authenticates service requests using an IoT hub connection string. /// @@ -50,23 +59,19 @@ protected IotHubServiceClient() /// Thrown when the provided connection string is null. /// Thrown when the provided connection string is empty or whitespace. public IotHubServiceClient(string connectionString, IotHubServiceClientOptions options = default) + : this(options) { Argument.AssertNotNullOrWhiteSpace(connectionString, nameof(connectionString)); - IotHubServiceClientOptions clientOptions = options != null - ? options.Clone() - : new(); IotHubConnectionString iotHubConnectionString = IotHubConnectionStringParser.Parse(connectionString); _credentialProvider = iotHubConnectionString; _hostName = iotHubConnectionString.HostName; - _httpClient = HttpClientFactory.Create(_hostName, clientOptions); + _httpClient = HttpClientFactory.Create(_hostName, _clientOptions); _httpRequestMessageFactory = new HttpRequestMessageFactory( new UriBuilder(HttpClientFactory.HttpsEndpointPrefix, _hostName).Uri, - ApiVersion); - _retryPolicy = clientOptions.RetryPolicy ?? new IotHubServiceNoRetry(); - _retryHandler = new RetryHandler(_retryPolicy); + ClientApiVersionHelper.ApiVersionDefault); - InitializeSubclients(clientOptions); + InitializeSubclients(); } /// @@ -85,24 +90,19 @@ public IotHubServiceClient(string connectionString, IotHubServiceClientOptions o /// Thrown when the provided or is null. /// Thrown when the provided is empty or whitespace. public IotHubServiceClient(string hostName, TokenCredential credential, IotHubServiceClientOptions options = default) + : this(options) { Argument.AssertNotNullOrWhiteSpace(hostName, nameof(hostName)); Argument.AssertNotNull(credential, nameof(credential)); - IotHubServiceClientOptions clientOptions = options != null - ? options.Clone() - : new(); - _credentialProvider = new IotHubTokenCredentialProperties(hostName, credential); _hostName = hostName; - _httpClient = HttpClientFactory.Create(_hostName, clientOptions); + _httpClient = HttpClientFactory.Create(_hostName, _clientOptions); _httpRequestMessageFactory = new HttpRequestMessageFactory( new UriBuilder(HttpClientFactory.HttpsEndpointPrefix, _hostName).Uri, - ApiVersion); - _retryPolicy = clientOptions.RetryPolicy ?? new IotHubServiceNoRetry(); - _retryHandler = new RetryHandler(_retryPolicy); + ClientApiVersionHelper.ApiVersionDefault); - InitializeSubclients(clientOptions); + InitializeSubclients(); } /// @@ -120,24 +120,19 @@ public IotHubServiceClient(string hostName, TokenCredential credential, IotHubSe /// Thrown when the provided or is null. /// Thrown when the provided is empty or whitespace. public IotHubServiceClient(string hostName, AzureSasCredential credential, IotHubServiceClientOptions options = default) + : this(options) { Argument.AssertNotNullOrWhiteSpace(hostName, nameof(hostName)); Argument.AssertNotNull(credential, nameof(credential)); - IotHubServiceClientOptions clientOptions = options != null - ? options.Clone() - : new(); - _credentialProvider = new IotHubSasCredentialProperties(hostName, credential); _hostName = hostName; - _httpClient = HttpClientFactory.Create(_hostName, clientOptions); + _httpClient = HttpClientFactory.Create(_hostName, _clientOptions); _httpRequestMessageFactory = new HttpRequestMessageFactory( new UriBuilder(HttpClientFactory.HttpsEndpointPrefix, _hostName).Uri, - ApiVersion); - _retryPolicy = clientOptions.RetryPolicy ?? new IotHubServiceNoRetry(); - _retryHandler = new RetryHandler(_retryPolicy); + ClientApiVersionHelper.ApiVersionDefault); - InitializeSubclients(clientOptions); + InitializeSubclients(); } /// @@ -219,7 +214,7 @@ public void Dispose() GC.SuppressFinalize(this); } - private void InitializeSubclients(IotHubServiceClientOptions options) + private void InitializeSubclients() { Devices = new DevicesClient(_hostName, _credentialProvider, _httpClient, _httpRequestMessageFactory, _retryHandler); Modules = new ModulesClient(_hostName, _credentialProvider, _httpClient, _httpRequestMessageFactory, _retryHandler); @@ -229,10 +224,10 @@ private void InitializeSubclients(IotHubServiceClientOptions options) DirectMethods = new DirectMethodsClient(_hostName, _credentialProvider, _httpClient, _httpRequestMessageFactory, _retryHandler); DigitalTwins = new DigitalTwinsClient(_hostName, _credentialProvider, _httpClient, _httpRequestMessageFactory, _retryHandler); Twins = new TwinsClient(_hostName, _credentialProvider, _httpClient, _httpRequestMessageFactory, _retryHandler); - Messages = new MessagesClient(_hostName, _credentialProvider, _httpClient, _httpRequestMessageFactory, options, _retryHandler); + Messages = new MessagesClient(_hostName, _credentialProvider, _httpClient, _httpRequestMessageFactory, _clientOptions, _retryHandler); - MessageFeedback = new MessageFeedbackProcessorClient(_hostName, _credentialProvider, options, _retryHandler); - FileUploadNotifications = new FileUploadNotificationProcessorClient(_hostName, _credentialProvider, options, _retryHandler); + MessageFeedback = new MessageFeedbackProcessorClient(_hostName, _credentialProvider, _clientOptions, _retryHandler); + FileUploadNotifications = new FileUploadNotificationProcessorClient(_hostName, _credentialProvider, _clientOptions, _retryHandler); // Specify the JsonSerializerSettings for subclients JsonConvert.DefaultSettings = JsonSerializerSettingsInitializer.GetJsonSerializerSettingsDelegate(); diff --git a/iothub/service/src/JsonSerializerSettingsInitializer.cs b/iothub/service/src/JsonSerializerSettingsInitializer.cs index eea5f5ea0c..ac9a3f4ef8 100644 --- a/iothub/service/src/JsonSerializerSettingsInitializer.cs +++ b/iothub/service/src/JsonSerializerSettingsInitializer.cs @@ -9,7 +9,7 @@ namespace Microsoft.Azure.Devices /// /// A class to initialize JsonSerializerSettings which can be applied to the project. /// - public static class JsonSerializerSettingsInitializer + internal static class JsonSerializerSettingsInitializer { /// /// A static instance of JsonSerializerSettings which sets DateParseHandling to None. @@ -19,7 +19,7 @@ public static class JsonSerializerSettingsInitializer /// strings to a date type, which drops trailing zeros in the microseconds date portion. By /// specifying DateParseHandling with None, the original string will be read as-is. /// - public static readonly JsonSerializerSettings Settings = new() + private static readonly JsonSerializerSettings s_settings = new() { DateParseHandling = DateParseHandling.None }; @@ -27,9 +27,9 @@ public static class JsonSerializerSettingsInitializer /// /// Returns JsonSerializerSettings Func delegate /// - public static Func GetJsonSerializerSettingsDelegate() + internal static Func GetJsonSerializerSettingsDelegate() { - return () => Settings; + return () => s_settings; } } } diff --git a/provisioning/service/src/ProvisioningServiceClient.cs b/provisioning/service/src/ProvisioningServiceClient.cs index d30c84341b..ba40a98f77 100644 --- a/provisioning/service/src/ProvisioningServiceClient.cs +++ b/provisioning/service/src/ProvisioningServiceClient.cs @@ -36,9 +36,7 @@ public class ProvisioningServiceClient : IDisposable /// Thrown if an error occurs when communicating with device provisioning service. public ProvisioningServiceClient(string connectionString, ProvisioningServiceClientOptions options = default) { - ProvisioningServiceClientOptions clientOptions = options != null - ? options.Clone() - : new(); + ProvisioningServiceClientOptions clientOptions = options?.Clone() ?? new(); Argument.AssertNotNullOrWhiteSpace(connectionString, nameof(connectionString)); @@ -80,10 +78,7 @@ public ProvisioningServiceClient(string connectionString, ProvisioningServiceCli /// public void Dispose() { - if (_contractApiHttp != null) - { - _contractApiHttp.Dispose(); - } + _contractApiHttp?.Dispose(); GC.SuppressFinalize(this); } }