diff --git a/azureiot.sln b/azureiot.sln index 0b45546187..f5a83728d4 100644 --- a/azureiot.sln +++ b/azureiot.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32328.378 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iothub", "iothub", "{537FA828-124D-4C70-9FC1-89301D9E776D}" EndProject diff --git a/e2e/test/iothub/FileUploadE2ETests.cs b/e2e/test/iothub/FileUploadE2ETests.cs index d461b0788b..f0fdbaade0 100644 --- a/e2e/test/iothub/FileUploadE2ETests.cs +++ b/e2e/test/iothub/FileUploadE2ETests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net; using System.Security.Cryptography.X509Certificates; @@ -52,6 +51,14 @@ public async Task FileUpload_GetFileUploadSasUri_Http_x509_NoFileTransportSettin await GetSasUriAsync(Client.TransportType.Http1, smallFileBlobName, true).ConfigureAwait(false); } + [LoggedTestMethod] + [TestCategory("LongRunning")] + public async Task FileUpload_GetFileUploadSasUri_Mqtt_x509_NoFileTransportSettingSpecified() + { + string smallFileBlobName = await GetTestFileNameAsync(FileSizeSmall).ConfigureAwait(false); + await GetSasUriAsync(Client.TransportType.Mqtt, smallFileBlobName, true).ConfigureAwait(false); + } + [LoggedTestMethod] [TestCategory("LongRunning")] [Obsolete] @@ -200,10 +207,14 @@ private async Task UploadFileAsync(Client.TransportType transport, string filena private async Task GetSasUriAsync(Client.TransportType transport, string blobName, bool useX509auth = false) { - using TestDevice testDevice = await TestDevice.GetTestDeviceAsync( - Logger, - _devicePrefix, - useX509auth ? TestDeviceType.X509 : TestDeviceType.Sasl).ConfigureAwait(false); + using TestDevice testDevice = await TestDevice + .GetTestDeviceAsync( + Logger, + _devicePrefix, + useX509auth + ? TestDeviceType.X509 + : TestDeviceType.Sasl) + .ConfigureAwait(false); DeviceClient deviceClient; X509Certificate2 cert = null; diff --git a/iothub/device/src/AuthenticationMethodFactory.cs b/iothub/device/src/AuthenticationMethodFactory.cs index c371040a20..11bb1b46db 100644 --- a/iothub/device/src/AuthenticationMethodFactory.cs +++ b/iothub/device/src/AuthenticationMethodFactory.cs @@ -11,43 +11,48 @@ namespace Microsoft.Azure.Devices.Client /// public sealed class AuthenticationMethodFactory { - internal static IAuthenticationMethod GetAuthenticationMethod(IotHubConnectionStringBuilder iotHubConnectionStringBuilder) + internal static IAuthenticationMethod GetAuthenticationMethod(IotHubConnectionStringBuilder csBuilder) { - if (iotHubConnectionStringBuilder.SharedAccessKeyName != null) + if (csBuilder.SharedAccessKeyName != null) { return new DeviceAuthenticationWithSharedAccessPolicyKey( - iotHubConnectionStringBuilder.DeviceId, iotHubConnectionStringBuilder.SharedAccessKeyName, iotHubConnectionStringBuilder.SharedAccessKey); + csBuilder.DeviceId, + csBuilder.SharedAccessKeyName, + csBuilder.SharedAccessKey); } - else if (iotHubConnectionStringBuilder.SharedAccessKey != null) + else if (csBuilder.SharedAccessKey != null) { - if (iotHubConnectionStringBuilder.ModuleId != null) + if (csBuilder.ModuleId != null) { return new ModuleAuthenticationWithRegistrySymmetricKey( - iotHubConnectionStringBuilder.DeviceId, iotHubConnectionStringBuilder.ModuleId, iotHubConnectionStringBuilder.SharedAccessKey); + csBuilder.DeviceId, + csBuilder.ModuleId, + csBuilder.SharedAccessKey); } else { return new DeviceAuthenticationWithRegistrySymmetricKey( - iotHubConnectionStringBuilder.DeviceId, iotHubConnectionStringBuilder.SharedAccessKey); + csBuilder.DeviceId, + csBuilder.SharedAccessKey); } } - else if (iotHubConnectionStringBuilder.SharedAccessSignature != null) + else if (csBuilder.SharedAccessSignature != null) { - return iotHubConnectionStringBuilder.ModuleId != null + return csBuilder.ModuleId != null ? new ModuleAuthenticationWithToken( - iotHubConnectionStringBuilder.DeviceId, - iotHubConnectionStringBuilder.ModuleId, - iotHubConnectionStringBuilder.SharedAccessSignature) + csBuilder.DeviceId, + csBuilder.ModuleId, + csBuilder.SharedAccessSignature) : (IAuthenticationMethod)new DeviceAuthenticationWithToken( - iotHubConnectionStringBuilder.DeviceId, - iotHubConnectionStringBuilder.SharedAccessSignature); + csBuilder.DeviceId, + csBuilder.SharedAccessSignature); } - else if (iotHubConnectionStringBuilder.UsingX509Cert) + else if (csBuilder.UsingX509Cert) { - return new DeviceAuthenticationWithX509Certificate(iotHubConnectionStringBuilder.DeviceId, iotHubConnectionStringBuilder.Certificate); + return new DeviceAuthenticationWithX509Certificate(csBuilder.DeviceId, csBuilder.Certificate); } - throw new InvalidOperationException("Unsupported Authentication Method {0}".FormatInvariant(iotHubConnectionStringBuilder)); + throw new InvalidOperationException($"Unsupported authentication method in '{csBuilder}'."); } /// diff --git a/iothub/device/src/ClientFactory.cs b/iothub/device/src/ClientFactory.cs index 3ab6d9b710..ef27d6f884 100644 --- a/iothub/device/src/ClientFactory.cs +++ b/iothub/device/src/ClientFactory.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Net; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; using Microsoft.Azure.Devices.Client.Exceptions; @@ -122,12 +123,14 @@ internal static InternalClient Create( throw new ArgumentException("No certificate was found. To use certificate authentication certificate must be present."); } - InternalClient dc = CreateFromConnectionString( + InternalClient internalClient = CreateFromConnectionString( connectionStringBuilder.ToString(), + authenticationMethod, PopulateCertificateInTransportSettings(connectionStringBuilder, transportType), + null, options); - dc.Certificate = connectionStringBuilder.Certificate; + internalClient.Certificate = connectionStringBuilder.Certificate; // Install all the intermediate certificates in the chain if specified. if (connectionStringBuilder.ChainCertificates != null) @@ -143,7 +146,7 @@ internal static InternalClient Create( } } - return dc; + return internalClient; } return CreateFromConnectionString(connectionStringBuilder.ToString(), authenticationMethod, transportType, null, options); @@ -296,7 +299,7 @@ internal static InternalClient CreateFromConnectionString( throw new ArgumentException("Connection string must not contain DeviceId keyvalue parameter", nameof(connectionString)); } - return CreateFromConnectionString(connectionString + ";" + DeviceId + "=" + deviceId, transportType, options); + return CreateFromConnectionString($"{connectionString};{DeviceId}={deviceId}", transportType, options); } /// @@ -306,8 +309,10 @@ internal static InternalClient CreateFromConnectionString( /// Prioritized list of transports and their settings /// The options that allow configuration of the device client instance during initialization. /// InternalClient - internal static InternalClient CreateFromConnectionString(string connectionString, - ITransportSettings[] transportSettings, ClientOptions options = default) + internal static InternalClient CreateFromConnectionString( + string connectionString, + ITransportSettings[] transportSettings, + ClientOptions options = default) { return CreateFromConnectionString(connectionString, null, transportSettings, null, options); } @@ -317,10 +322,14 @@ internal static InternalClient CreateFromConnectionString(string connectionStrin /// /// Connection string for the IoT hub (without DeviceId) /// Id of the device - /// Prioritized list of transportTypes and their settings + /// Prioritized list of transport types and their settings /// The options that allow configuration of the device client instance during initialization. /// InternalClient - internal static InternalClient CreateFromConnectionString(string connectionString, string deviceId, ITransportSettings[] transportSettings, ClientOptions options = default) + internal static InternalClient CreateFromConnectionString( + string connectionString, + string deviceId, + ITransportSettings[] transportSettings, + ClientOptions options = default) { if (connectionString == null) { @@ -337,7 +346,7 @@ internal static InternalClient CreateFromConnectionString(string connectionStrin throw new ArgumentException("Connection string must not contain DeviceId keyvalue parameter", nameof(connectionString)); } - return CreateFromConnectionString(connectionString + ";" + DeviceId + "=" + deviceId, transportSettings, options); + return CreateFromConnectionString($"{connectionString};{DeviceId}={deviceId}", transportSettings, options); } internal static InternalClient CreateFromConnectionString( @@ -430,8 +439,9 @@ internal static InternalClient CreateFromConnectionString( // Clients that derive their authentication method from AuthenticationWithTokenRefresh will need to specify // the token time to live and renewal buffer values through the corresponding AuthenticationWithTokenRefresh - // implementation constructors instead. - if (!(builder.AuthenticationMethod is AuthenticationWithTokenRefresh)) + // implementation constructors instead, and these values are irrelevant for cert-based auth. + if (!(builder.AuthenticationMethod is AuthenticationWithTokenRefresh) + && !(builder.AuthenticationMethod is DeviceAuthenticationWithX509Certificate)) { builder.SasTokenTimeToLive = options?.SasTokenTimeToLive ?? default; builder.SasTokenRenewalBuffer = options?.SasTokenRenewalBuffer ?? default; @@ -501,17 +511,20 @@ internal static InternalClient CreateFromConnectionString( /// private static void EnsureOptionsIsSetup(X509Certificate2 cert, ref ClientOptions options) { - if (options?.FileUploadTransportSettings == null) + if (options == null) { - var fileUploadTransportSettings = new Http1TransportSettings { ClientCertificate = cert }; - if (options == null) - { - options = new ClientOptions { FileUploadTransportSettings = fileUploadTransportSettings }; - } - else - { - options.FileUploadTransportSettings = fileUploadTransportSettings; - } + options = new ClientOptions(); + } + + if (options.FileUploadTransportSettings == null) + { + options.FileUploadTransportSettings = new Http1TransportSettings(); + } + + if (cert != null + && options.FileUploadTransportSettings.ClientCertificate == null) + { + options.FileUploadTransportSettings.ClientCertificate = cert; } } diff --git a/iothub/device/src/ClientOptions.cs b/iothub/device/src/ClientOptions.cs index 8f6fd9cd0e..1953bded96 100644 --- a/iothub/device/src/ClientOptions.cs +++ b/iothub/device/src/ClientOptions.cs @@ -19,9 +19,9 @@ public class ClientOptions /// /// The transport settings to use for all file upload operations, regardless of what protocol the device - /// client is configured with. All file upload operations take place over https. - /// If FileUploadTransportSettings is not provided, then file upload operations will use the client certificates configured - /// in the transport settings set for the non-file upload operations. + /// client is configured with. All file upload operations take place over https. + /// If FileUploadTransportSettings is not provided, then file upload operations will use the same client certificates + /// configured in the transport settings set for client connect. /// public Http1TransportSettings FileUploadTransportSettings { get; set; } = new Http1TransportSettings(); diff --git a/iothub/device/src/InternalClient.cs b/iothub/device/src/InternalClient.cs index 67ec4b8533..28c8f15757 100644 --- a/iothub/device/src/InternalClient.cs +++ b/iothub/device/src/InternalClient.cs @@ -159,8 +159,9 @@ public InternalClient(IotHubConnectionString iotHubConnectionString, ITransportS TlsVersions.Instance.SetLegacyAcceptableVersions(); - IotHubConnectionString = iotHubConnectionString; + _transportSettings = transportSettings; _clientOptions = options; + IotHubConnectionString = iotHubConnectionString; var pipelineContext = new PipelineContext(); pipelineContext.Set(transportSettings); @@ -176,25 +177,17 @@ public InternalClient(IotHubConnectionString iotHubConnectionString, ITransportS IDelegatingHandler innerHandler = pipelineBuilder.Build(pipelineContext); if (Logging.IsEnabled) - { Logging.Associate(this, innerHandler, nameof(InternalClient)); - } InnerHandler = innerHandler; if (Logging.IsEnabled) - { Logging.Associate(this, transportSettings, nameof(InternalClient)); - } - - _transportSettings = transportSettings; _fileUploadHttpTransportHandler = new HttpTransportHandler(pipelineContext, IotHubConnectionString, options.FileUploadTransportSettings); if (Logging.IsEnabled) - { Logging.Exit(this, transportSettings, pipelineBuilder, nameof(InternalClient) + "_ctor"); - } } /// @@ -224,7 +217,6 @@ public int DiagnosticSamplingPercentage /// /// Gets or sets the timeout used in the operation retries. /// - // Codes_SRS_DEVICECLIENT_28_002: [This property shall be defaulted to 240000 (4 minutes).] public uint OperationTimeoutInMilliseconds { get; set; } = DeviceClient.DefaultOperationTimeoutInMilliseconds; /// @@ -242,8 +234,6 @@ public string ProductInfo /// /// Stores the retry strategy used in the operation retries. /// - // Codes_SRS_DEVICECLIENT_28_001: [This property shall be defaulted to the exponential retry strategy with back-off - // parameters for calculating delay in between retries.] [Obsolete("This method has been deprecated. Please use Microsoft.Azure.Devices.Client.SetRetryPolicy(IRetryPolicy retryPolicy) instead.")] public RetryPolicyType RetryPolicy { get; set; } @@ -257,8 +247,6 @@ public string ProductInfo /// Sets the retry policy used in the operation retries. /// /// The retry policy. The default is new ExponentialBackoff(int.MaxValue, TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(10), TimeSpan.FromMilliseconds(100)); - // Codes_SRS_DEVICECLIENT_28_001: [This property shall be defaulted to the exponential retry strategy with back-off - // parameters for calculating delay in between retries.] public void SetRetryPolicy(IRetryPolicy retryPolicy) { RetryDelegatingHandler retryDelegatingHandler = GetDelegateHandler(); @@ -297,7 +285,6 @@ public async Task OpenAsync() { try { - // Codes_SRS_DEVICECLIENT_28_007: [ The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable(authentication, quota exceed) error occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await OpenAsync(cts.Token).ConfigureAwait(false); } @@ -311,7 +298,7 @@ public async Task OpenAsync() /// /// Explicitly open the InternalClient instance. /// - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A token to cancel the operation. public Task OpenAsync(CancellationToken cancellationToken) { try @@ -346,6 +333,7 @@ public async Task CloseAsync() /// /// Close the InternalClient instance /// + /// A token to cancel the operation. public Task CloseAsync(CancellationToken cancellationToken) { try @@ -366,8 +354,6 @@ public Task CloseAsync(CancellationToken cancellationToken) /// The name of the method to associate with the delegate. public void SetConnectionStatusChangesHandler(ConnectionStatusChangesHandler statusChangesHandler) { - // codes_SRS_DEVICECLIENT_28_025: [** `SetConnectionStatusChangesHandler` shall set connectionStatusChangesHandler **]** - // codes_SRS_DEVICECLIENT_28_026: [** `SetConnectionStatusChangesHandler` shall unset connectionStatusChangesHandler if `statusChangesHandler` is null **]** if (Logging.IsEnabled) { Logging.Info(this, statusChangesHandler, nameof(SetConnectionStatusChangesHandler)); @@ -384,9 +370,7 @@ internal void OnConnectionStatusChanged(ConnectionStatus status, ConnectionStatu try { if (Logging.IsEnabled) - { Logging.Enter(this, status, reason, nameof(OnConnectionStatusChanged)); - } if (_connectionStatusChangesHandler != null && (_lastConnectionStatus != status @@ -414,7 +398,6 @@ public async Task CompleteAsync(string lockToken) { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await CompleteAsync(lockToken, cts.Token).ConfigureAwait(false); } @@ -429,11 +412,10 @@ public async Task CompleteAsync(string lockToken) /// Deletes a received message from the device queue /// /// - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A token to cancel the operation. /// The lock identifier for the previously received message public Task CompleteAsync(string lockToken, CancellationToken cancellationToken) { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] if (string.IsNullOrEmpty(lockToken)) { throw new ArgumentNullException(nameof(lockToken)); @@ -461,7 +443,6 @@ public Task CompleteAsync(Message message) throw new ArgumentNullException(nameof(message)); } - // Codes_SRS_DEVICECLIENT_28_015: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] return CompleteAsync(message.LockToken); } @@ -469,7 +450,7 @@ public Task CompleteAsync(Message message) /// Deletes a received message from the device queue /// /// The message to complete - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A token to cancel the operation. /// The previously received message public Task CompleteAsync(Message message, CancellationToken cancellationToken) { @@ -506,7 +487,6 @@ public async Task AbandonAsync(string lockToken) // Exception adaptation for non-CancellationToken public API. throw new TimeoutException("The operation timed out.", ex); } - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] } /// @@ -573,7 +553,6 @@ public async Task RejectAsync(string lockToken) { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await RejectAsync(lockToken, cts.Token).ConfigureAwait(false); } @@ -647,7 +626,6 @@ public async Task SendEventAsync(Message message) { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await SendEventAsync(message, cts.Token).ConfigureAwait(false); } @@ -695,7 +673,6 @@ public async Task SendEventBatchAsync(IEnumerable messages) { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await SendEventBatchAsync(messages, cts.Token).ConfigureAwait(false); } @@ -752,7 +729,6 @@ public async Task SetMethodHandlerAsync(string methodName, MethodCallback method { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await SetMethodHandlerAsync(methodName, methodHandler, userContext, cts.Token).ConfigureAwait(false); } @@ -830,7 +806,6 @@ public async Task SetMethodDefaultHandlerAsync(MethodCallback methodHandler, obj // Exception adaptation for non-CancellationToken public API. throw new TimeoutException("The operation timed out.", ex); } - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] } /// @@ -857,7 +832,6 @@ public async Task SetMethodDefaultHandlerAsync(MethodCallback methodHandler, obj { await HandleMethodEnableAsync(cancellationToken).ConfigureAwait(false); - // codes_SRS_DEVICECLIENT_24_001: [ If the default callback has already been set, it is replaced with the new callback. ] _deviceDefaultMethodCallback = new Tuple(methodHandler, userContext); } else @@ -926,7 +900,6 @@ internal async Task OnMethodCalledAsync(MethodRequestInternal methodRequestInter Logging.Enter(this, methodRequestInternal?.Name, methodRequestInternal, nameof(OnMethodCalledAsync)); } - // codes_SRS_DEVICECLIENT_10_012: [ If the given methodRequestInternal argument is null, fail silently ] if (methodRequestInternal == null) { return; @@ -951,7 +924,6 @@ internal async Task OnMethodCalledAsync(MethodRequestInternal methodRequestInter Logging.Error(this, ex, nameof(OnMethodCalledAsync)); } - // codes_SRS_DEVICECLIENT_28_020: [ If the given methodRequestInternal data is not valid json, respond with status code 400 (BAD REQUEST) ] methodResponseInternal = new MethodResponseInternal(methodRequestInternal.RequestId, (int)MethodResponseStatusCode.BadRequest); await SendMethodResponseAsync(methodResponseInternal, methodRequestInternal.CancellationToken).ConfigureAwait(false); @@ -978,19 +950,14 @@ internal async Task OnMethodCalledAsync(MethodRequestInternal methodRequestInter if (callbackContextPair == null) { - // codes_SRS_DEVICECLIENT_10_013: [ If the given method does not have an associated delegate and no default delegate was registered, respond with status code 501 (METHOD NOT IMPLEMENTED) ] methodResponseInternal = new MethodResponseInternal(methodRequestInternal.RequestId, (int)MethodResponseStatusCode.MethodNotImplemented); } else { try { - // codes_SRS_DEVICECLIENT_10_011: [ The OnMethodCalled shall invoke the specified delegate. ] - // codes_SRS_DEVICECLIENT_24_002: [ The OnMethodCalled shall invoke the default delegate if there is no specified delegate for that method. ] MethodResponse rv = await callbackContextPair.Item1(new MethodRequest(methodRequestInternal.Name, requestData), callbackContextPair.Item2).ConfigureAwait(false); - // codes_SRS_DEVICECLIENT_03_012: [If the MethodResponse does not contain result, the MethodResponseInternal constructor shall be invoked with no results.] - // codes_SRS_DEVICECLIENT_03_013: [Otherwise, the MethodResponseInternal constructor shall be invoked with the result supplied.] methodResponseInternal = rv.Result == null ? new MethodResponseInternal(methodRequestInternal.RequestId, rv.Status) : new MethodResponseInternal(rv.Result, methodRequestInternal.RequestId, rv.Status); @@ -1002,7 +969,6 @@ internal async Task OnMethodCalledAsync(MethodRequestInternal methodRequestInter Logging.Error(this, ex, nameof(OnMethodCalledAsync)); } - // codes_SRS_DEVICECLIENT_28_021: [ If the MethodResponse from the MethodHandler is not valid json, respond with status code 500 (USER CODE EXCEPTION) ] methodResponseInternal = new MethodResponseInternal(methodRequestInternal.RequestId, (int)MethodResponseStatusCode.UserCodeException); } } @@ -1061,7 +1027,6 @@ public async Task SetDesiredPropertyUpdateCallbackAsync(DesiredPropertyUpdateCal { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await SetDesiredPropertyUpdateCallbackAsync(callback, userContext, cts.Token).ConfigureAwait(false); } @@ -1085,13 +1050,8 @@ public async Task SetDesiredPropertyUpdateCallbackAsync(DesiredPropertyUpdateCal /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. public async Task SetDesiredPropertyUpdateCallbackAsync(DesiredPropertyUpdateCallback callback, object userContext, CancellationToken cancellationToken) { - // Codes_SRS_DEVICECLIENT_18_003: `SetDesiredPropertyUpdateCallbackAsync` shall call the transport to register for PATCHes on it's first call. - // Codes_SRS_DEVICECLIENT_18_004: `SetDesiredPropertyUpdateCallbackAsync` shall not call the transport to register for PATCHes on subsequent calls - if (Logging.IsEnabled) - { Logging.Enter(this, callback, userContext, nameof(SetDesiredPropertyUpdateCallbackAsync)); - } // Wait to acquire the _twinSemaphore. This ensures that concurrently invoked SetDesiredPropertyUpdateCallbackAsync calls are invoked in a thread-safe manner. await _twinDesiredPropertySemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -1151,7 +1111,6 @@ public async Task GetTwinAsync() { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); return await GetTwinAsync(cts.Token).ConfigureAwait(false); } @@ -1189,7 +1148,6 @@ public async Task UpdateReportedPropertiesAsync(TwinCollection reportedPropertie { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await UpdateReportedPropertiesAsync(reportedProperties, cts.Token).ConfigureAwait(false); } @@ -1225,7 +1183,6 @@ public Task UpdateReportedPropertiesAsync(TwinCollection reportedProperties, Can } } - // Codes_SRS_DEVICECLIENT_18_005: When a patch is received from the service, the `callback` shall be called. internal void OnReportedStatePatchReceived(TwinCollection patch) { if (_desiredPropertyUpdateCallback == null) @@ -1251,7 +1208,6 @@ public async Task ReceiveAsync() { try { - // Codes_SRS_DEVICECLIENT_28_011: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable (authentication, quota exceed) error occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); return await ReceiveAsync(cts.Token).ConfigureAwait(false); } @@ -1397,9 +1353,7 @@ private Task DisableReceiveMessageAsync(CancellationToken cancellationToken) private async Task OnDeviceMessageReceivedAsync(Message message) { if (Logging.IsEnabled) - { Logging.Enter(this, message, nameof(OnDeviceMessageReceivedAsync)); - } if (message == null) { @@ -1426,9 +1380,7 @@ private async Task OnDeviceMessageReceivedAsync(Message message) } if (Logging.IsEnabled) - { Logging.Exit(this, message, nameof(OnDeviceMessageReceivedAsync)); - } } internal Task GetFileUploadSasUriAsync(FileUploadSasUriRequest request, CancellationToken cancellationToken = default) @@ -1468,9 +1420,7 @@ public Task UploadToBlobAsync(string blobName, Stream source, CancellationToken try { if (Logging.IsEnabled) - { Logging.Enter(this, blobName, source, nameof(UploadToBlobAsync)); - } if (string.IsNullOrEmpty(blobName)) { @@ -1489,8 +1439,7 @@ public Task UploadToBlobAsync(string blobName, Stream source, CancellationToken throw Fx.Exception.Argument(nameof(blobName), "Path segment count cannot exceed 254"); } - return _fileUploadHttpTransportHandler - .UploadToBlobAsync(blobName, source, cancellationToken); + return _fileUploadHttpTransportHandler.UploadToBlobAsync(blobName, source, cancellationToken); } catch (IotHubCommunicationException ex) when (ex.InnerException is OperationCanceledException) { @@ -1500,9 +1449,7 @@ public Task UploadToBlobAsync(string blobName, Stream source, CancellationToken finally { if (Logging.IsEnabled) - { Logging.Exit(this, blobName, nameof(UploadToBlobAsync)); - } } } @@ -1520,7 +1467,6 @@ public async Task SendEventAsync(string outputName, Message message) { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await SendEventAsync(outputName, message, cts.Token).ConfigureAwait(false); } @@ -1543,36 +1489,28 @@ public Task SendEventAsync(string outputName, Message message, CancellationToken try { if (Logging.IsEnabled) - { Logging.Enter(this, outputName, message, nameof(SendEventAsync)); - } ValidateModuleTransportHandler("SendEventAsync for a named output"); - // Codes_SRS_DEVICECLIENT_10_012: [If `outputName` is `null` or empty, an `ArgumentNullException` shall be thrown.] if (string.IsNullOrWhiteSpace(outputName)) { throw new ArgumentNullException(nameof(outputName)); } - // Codes_SRS_DEVICECLIENT_10_013: [If `message` is `null` or empty, an `ArgumentNullException` shall be thrown.] if (message == null) { throw new ArgumentNullException(nameof(message)); } - // Codes_SRS_DEVICECLIENT_10_015: [The `output` property of a given `message` shall be assigned the value `outputName` before submitting each request to the transport layer.] message.SystemProperties.Add(MessageSystemPropertyNames.OutputName, outputName); - // Codes_SRS_DEVICECLIENT_10_011: [The `SendEventAsync` operation shall retry sending `message` until the `BaseClient::RetryStrategy` timespan expires or unrecoverable error(authentication or quota exceed) occurs.] return InnerHandler.SendEventAsync(message, cancellationToken); } finally { if (Logging.IsEnabled) - { Logging.Exit(this, outputName, message, nameof(SendEventAsync)); - } } } @@ -1586,7 +1524,6 @@ public async Task SendEventBatchAsync(string outputName, IEnumerable me { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await SendEventBatchAsync(outputName, messages, cts.Token).ConfigureAwait(false); } @@ -1609,37 +1546,29 @@ public Task SendEventBatchAsync(string outputName, IEnumerable messages try { if (Logging.IsEnabled) - { Logging.Enter(this, outputName, messages, nameof(SendEventBatchAsync)); - } ValidateModuleTransportHandler("SendEventBatchAsync for a named output"); - // Codes_SRS_DEVICECLIENT_10_012: [If `outputName` is `null` or empty, an `ArgumentNullException` shall be thrown.] if (string.IsNullOrWhiteSpace(outputName)) { throw new ArgumentNullException(nameof(outputName)); } var messagesList = messages?.ToList(); - // Codes_SRS_DEVICECLIENT_10_013: [If `message` is `null` or empty, an `ArgumentNullException` shall be thrown] if (messagesList == null || messagesList.Count == 0) { throw new ArgumentNullException(nameof(messages)); } - // Codes_SRS_DEVICECLIENT_10_015: [The `module-output` property of a given `message` shall be assigned the value `outputName` before submitting each request to the transport layer.] messagesList.ForEach(m => m.SystemProperties.Add(MessageSystemPropertyNames.OutputName, outputName)); - // Codes_SRS_DEVICECLIENT_10_014: [The `SendEventBachAsync` operation shall retry sending `messages` until the `BaseClient::RetryStrategy` timespan expires or unrecoverable error(authentication or quota exceed) occurs.] return InnerHandler.SendEventAsync(messagesList, cancellationToken); } finally { if (Logging.IsEnabled) - { Logging.Exit(this, outputName, messages, nameof(SendEventBatchAsync)); - } } } @@ -1657,7 +1586,6 @@ public async Task SetInputMessageHandlerAsync(string inputName, MessageHandler m { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await SetInputMessageHandlerAsync(inputName, messageHandler, userContext, isAnEdgeModule, cts.Token).ConfigureAwait(false); } @@ -1682,9 +1610,7 @@ public async Task SetInputMessageHandlerAsync(string inputName, MessageHandler m public async Task SetInputMessageHandlerAsync(string inputName, MessageHandler messageHandler, object userContext, bool isAnEdgeModule, CancellationToken cancellationToken) { if (Logging.IsEnabled) - { Logging.Enter(this, inputName, messageHandler, userContext, nameof(SetInputMessageHandlerAsync)); - } ValidateModuleTransportHandler("SetInputMessageHandlerAsync for a named output"); @@ -1698,7 +1624,6 @@ public async Task SetInputMessageHandlerAsync(string inputName, MessageHandler m // When using a device module we need to enable the 'deviceBound' message link await EnableEventReceiveAsync(isAnEdgeModule, cancellationToken).ConfigureAwait(false); - // codes_SRS_DEVICECLIENT_33_005: [ It shall lazy-initialize the receiveEventEndpoints property. ] if (_receiveEventEndpoints == null) { _receiveEventEndpoints = new Dictionary>(); @@ -1717,7 +1642,6 @@ public async Task SetInputMessageHandlerAsync(string inputName, MessageHandler m } } - // codes_SRS_DEVICECLIENT_33_004: [ It shall call DisableEventReceiveAsync when the last delegate has been removed. ] await DisableEventReceiveAsync(isAnEdgeModule, cancellationToken).ConfigureAwait(false); } } @@ -1726,9 +1650,7 @@ public async Task SetInputMessageHandlerAsync(string inputName, MessageHandler m _moduleReceiveMessageSemaphore.Release(); if (Logging.IsEnabled) - { Logging.Exit(this, inputName, messageHandler, userContext, nameof(SetInputMessageHandlerAsync)); - } } } @@ -1746,7 +1668,6 @@ public async Task SetMessageHandlerAsync(MessageHandler messageHandler, object u { try { - // Codes_SRS_DEVICECLIENT_28_013: [The asynchronous operation shall retry until time specified in OperationTimeoutInMilliseconds property expire or unrecoverable error(authentication, quota exceed) occurs.] using CancellationTokenSource cts = CancellationTokenSourceFactory(); await SetMessageHandlerAsync(messageHandler, userContext, isAnEdgeModule, cts.Token).ConfigureAwait(false); } @@ -1771,9 +1692,7 @@ public async Task SetMessageHandlerAsync(MessageHandler messageHandler, object u public async Task SetMessageHandlerAsync(MessageHandler messageHandler, object userContext, bool isAnEdgeModule, CancellationToken cancellationToken) { if (Logging.IsEnabled) - { Logging.Enter(this, messageHandler, userContext, nameof(SetMessageHandlerAsync)); - } cancellationToken.ThrowIfCancellationRequested(); await _moduleReceiveMessageSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -1782,14 +1701,12 @@ public async Task SetMessageHandlerAsync(MessageHandler messageHandler, object u { if (messageHandler != null) { - // codes_SRS_DEVICECLIENT_33_003: [ It shall EnableEventReceiveAsync when called for the first time. ] await EnableEventReceiveAsync(isAnEdgeModule, cancellationToken).ConfigureAwait(false); _defaultEventCallback = new Tuple(messageHandler, userContext); } else { _defaultEventCallback = null; - // codes_SRS_DEVICECLIENT_33_004: [ It shall DisableEventReceiveAsync when the last delegate has been removed. ] await DisableEventReceiveAsync(isAnEdgeModule, cancellationToken).ConfigureAwait(false); } } @@ -1798,9 +1715,7 @@ public async Task SetMessageHandlerAsync(MessageHandler messageHandler, object u _moduleReceiveMessageSemaphore.Release(); if (Logging.IsEnabled) - { Logging.Exit(this, messageHandler, userContext, nameof(SetMessageHandlerAsync)); - } } } @@ -1812,11 +1727,8 @@ public async Task SetMessageHandlerAsync(MessageHandler messageHandler, object u internal async Task OnModuleEventMessageReceivedAsync(string input, Message message) { if (Logging.IsEnabled) - { Logging.Enter(this, input, message, nameof(OnModuleEventMessageReceivedAsync)); - } - // codes_SRS_DEVICECLIENT_33_001: [ If the given eventMessageInternal argument is null, fail silently ] if (message == null) { return; @@ -1828,7 +1740,6 @@ internal async Task OnModuleEventMessageReceivedAsync(string input, Message mess await _moduleReceiveMessageSemaphore.WaitAsync().ConfigureAwait(false); try { - // codes_SRS_DEVICECLIENT_33_006: [ The OnReceiveEventMessageCalled shall get the default delegate if a delegate has not been assigned. ] if (_receiveEventEndpoints == null || string.IsNullOrWhiteSpace(input) || !_receiveEventEndpoints.TryGetValue(input, out callback)) @@ -1841,12 +1752,9 @@ internal async Task OnModuleEventMessageReceivedAsync(string input, Message mess _moduleReceiveMessageSemaphore.Release(); } - // codes_SRS_DEVICECLIENT_33_002: [ The OnReceiveEventMessageCalled shall invoke the specified delegate. ] MessageResponse response = await (callback?.Item1?.Invoke(message, callback.Item2) ?? Task.FromResult(MessageResponse.Completed)).ConfigureAwait(false); if (Logging.IsEnabled) - { Logging.Info(this, $"{nameof(MessageResponse)} = {response}", nameof(OnModuleEventMessageReceivedAsync)); - } switch (response) { @@ -1865,9 +1773,7 @@ internal async Task OnModuleEventMessageReceivedAsync(string input, Message mess finally { if (Logging.IsEnabled) - { Logging.Exit(this, input, message, nameof(OnModuleEventMessageReceivedAsync)); - } } } diff --git a/iothub/device/src/IotHubConnectionString.Core.cs b/iothub/device/src/IotHubConnectionString.Core.cs index 543ea47c5f..caeadbe722 100644 --- a/iothub/device/src/IotHubConnectionString.Core.cs +++ b/iothub/device/src/IotHubConnectionString.Core.cs @@ -12,7 +12,7 @@ internal sealed partial class IotHubConnectionString : IAuthorizationProvider { public AuthenticationWithTokenRefresh TokenRefresher { get; private set; } - Task IAuthorizationProvider.GetPasswordAsync() + async Task IAuthorizationProvider.GetPasswordAsync() { try { @@ -24,9 +24,17 @@ Task IAuthorizationProvider.GetPasswordAsync() || TokenRefresher != null, "The token refresher and the shared access signature can't both be null"); - return !string.IsNullOrWhiteSpace(SharedAccessSignature) - ? Task.FromResult(SharedAccessSignature) - : TokenRefresher?.GetTokenAsync(Audience); + if (!string.IsNullOrWhiteSpace(SharedAccessSignature)) + { + return SharedAccessSignature; + } + + if (TokenRefresher != null) + { + return await TokenRefresher.GetTokenAsync(Audience); + } + + return null; } finally { diff --git a/iothub/device/src/IotHubConnectionString.cs b/iothub/device/src/IotHubConnectionString.cs index b41bad43e6..daf1e632fd 100644 --- a/iothub/device/src/IotHubConnectionString.cs +++ b/iothub/device/src/IotHubConnectionString.cs @@ -34,16 +34,12 @@ public IotHubConnectionString(IotHubConnectionStringBuilder builder) AmqpEndpoint = new UriBuilder(CommonConstants.AmqpsScheme, HostName, DefaultSecurePort).Uri; - if (builder.AuthenticationMethod is AuthenticationWithTokenRefresh) + if (builder.AuthenticationMethod is AuthenticationWithTokenRefresh authWithTokenRefresh) { - TokenRefresher = (AuthenticationWithTokenRefresh)builder.AuthenticationMethod; + TokenRefresher = authWithTokenRefresh; if (Logging.IsEnabled) { Logging.Info(this, $"{nameof(IAuthenticationMethod)} is {nameof(AuthenticationWithTokenRefresh)}: {Logging.IdOf(TokenRefresher)}"); - } - - if (Logging.IsEnabled) - { Logging.Associate(this, TokenRefresher, nameof(TokenRefresher)); } @@ -53,29 +49,23 @@ public IotHubConnectionString(IotHubConnectionStringBuilder builder) { if (ModuleId.IsNullOrWhiteSpace()) { - // Since the sdk creates the instance of disposable DeviceAuthenticationWithSakRefresh, the sdk needs to dispose it once the client is disposed. + // Since the SDK creates the instance of disposable DeviceAuthenticationWithSakRefresh, the SDK needs to dispose it once the client is disposed. TokenRefresher = new DeviceAuthenticationWithSakRefresh(DeviceId, this, builder.SasTokenTimeToLive, builder.SasTokenRenewalBuffer, disposeWithClient: true); if (Logging.IsEnabled) - { Logging.Info(this, $"{nameof(IAuthenticationMethod)} is {nameof(DeviceAuthenticationWithSakRefresh)}: {Logging.IdOf(TokenRefresher)}"); - } } else { - // Since the sdk creates the instance of disposable ModuleAuthenticationWithSakRefresh, the sdk needs to dispose it once the client is disposed. + // Since the SDK creates the instance of disposable ModuleAuthenticationWithSakRefresh, the SDK needs to dispose it once the client is disposed. TokenRefresher = new ModuleAuthenticationWithSakRefresh(DeviceId, ModuleId, this, builder.SasTokenTimeToLive, builder.SasTokenRenewalBuffer, disposeWithClient: true); if (Logging.IsEnabled) - { Logging.Info(this, $"{nameof(IAuthenticationMethod)} is {nameof(ModuleAuthenticationWithSakRefresh)}: {Logging.IdOf(TokenRefresher)}"); - } } if (Logging.IsEnabled) - { Logging.Associate(this, TokenRefresher, nameof(TokenRefresher)); - } Debug.Assert(TokenRefresher != null); } diff --git a/iothub/device/src/IotHubConnectionStringBuilder.cs b/iothub/device/src/IotHubConnectionStringBuilder.cs index 181cd642f6..8fd1a34917 100644 --- a/iothub/device/src/IotHubConnectionStringBuilder.cs +++ b/iothub/device/src/IotHubConnectionStringBuilder.cs @@ -116,8 +116,8 @@ internal static IotHubConnectionStringBuilder CreateWithIAuthenticationOverride( if (authenticationMethod == null) { iotHubConnectionStringBuilder.Parse(iotHubConnectionString); - iotHubConnectionStringBuilder.AuthenticationMethod = - AuthenticationMethodFactory.GetAuthenticationMethod(iotHubConnectionStringBuilder); + iotHubConnectionStringBuilder.AuthenticationMethod = AuthenticationMethodFactory.GetAuthenticationMethod( + iotHubConnectionStringBuilder); } else { diff --git a/iothub/device/src/Transport/HttpClientHelper.cs b/iothub/device/src/Transport/HttpClientHelper.cs index 363fb42841..08d41243c4 100644 --- a/iothub/device/src/Transport/HttpClientHelper.cs +++ b/iothub/device/src/Transport/HttpClientHelper.cs @@ -53,13 +53,11 @@ public HttpClientHelper( HttpClientHandler httpClientHandler, ProductInfo productInfo, IWebProxy proxy, - bool isClientPrimaryTransportHandler = false - ) + bool isClientPrimaryTransportHandler = false) { _baseAddress = baseAddress; _authenticationHeaderProvider = authenticationHeaderProvider; - _defaultErrorMapping = - new ReadOnlyDictionary>>(defaultErrorMapping); + _defaultErrorMapping = new ReadOnlyDictionary>>(defaultErrorMapping); #if NET451 TlsVersions.Instance.SetLegacyAcceptableVersions(); @@ -385,17 +383,17 @@ public Task DeleteAsync( CancellationToken cancellationToken) where T : IETagHolder { return ExecuteAsync( - HttpMethod.Delete, - new Uri(_baseAddress, requestUri), - (requestMsg, token) => - { - InsertEtag(requestMsg, entity); - AddCustomHeaders(requestMsg, customHeaders); - return TaskHelpers.CompletedTask; - }, - null, - errorMappingOverrides, - cancellationToken); + HttpMethod.Delete, + new Uri(_baseAddress, requestUri), + (requestMsg, token) => + { + InsertEtag(requestMsg, entity); + AddCustomHeaders(requestMsg, customHeaders); + return TaskHelpers.CompletedTask; + }, + null, + errorMappingOverrides, + cancellationToken); } private Task ExecuteAsync( @@ -434,7 +432,10 @@ private async Task ExecuteAsync( if (!_usingX509ClientCert) { string authHeader = await _authenticationHeaderProvider.GetPasswordAsync().ConfigureAwait(false); - msg.Headers.Add(HttpRequestHeader.Authorization.ToString(), authHeader); + if (!string.IsNullOrWhiteSpace(authHeader)) + { + msg.Headers.Add(HttpRequestHeader.Authorization.ToString(), authHeader); + } } msg.Headers.UserAgent.ParseAdd(_productInfo.ToString(UserAgentFormats.Http));