diff --git a/common/src/service/ExceptionHandlingHelper.cs b/common/src/service/ExceptionHandlingHelper.cs index 947659eaad..f2943ddb11 100644 --- a/common/src/service/ExceptionHandlingHelper.cs +++ b/common/src/service/ExceptionHandlingHelper.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -17,86 +18,77 @@ namespace Microsoft.Azure.Devices { internal class ExceptionHandlingHelper { - public static IDictionary>> GetDefaultErrorMapping() + private static readonly IReadOnlyDictionary>> s_mappings = + new Dictionary>> { - var mappings = new Dictionary>> { - { - HttpStatusCode.NoContent, - async (response) => new DeviceNotFoundException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { - HttpStatusCode.NotFound, - async (response) => new DeviceNotFoundException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { - HttpStatusCode.Conflict, - async (response) => new DeviceAlreadyExistsException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { HttpStatusCode.BadRequest, async (response) => new ArgumentException( - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) }, - - { - HttpStatusCode.Unauthorized, - async (response) => new UnauthorizedException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { - HttpStatusCode.Forbidden, - async (response) => new QuotaExceededException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { - HttpStatusCode.PreconditionFailed, - async (response) => new DeviceMessageLockLostException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { - HttpStatusCode.RequestEntityTooLarge, - async (response) => new MessageTooLargeException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { - HttpStatusCode.InternalServerError, - async (response) => new ServerErrorException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { - HttpStatusCode.ServiceUnavailable, - async (response) => new ServerBusyException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - }, - - { - (HttpStatusCode)429, - async (response) => new ThrottlingException( - code: await GetExceptionCodeAsync(response).ConfigureAwait(false), - message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) - } - }; + HttpStatusCode.NoContent, + async (response) => new DeviceNotFoundException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.NotFound, + async (response) => new DeviceNotFoundException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.Conflict, + async (response) => new DeviceAlreadyExistsException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.BadRequest, async (response) => new ArgumentException( + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.Unauthorized, + async (response) => new UnauthorizedException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.Forbidden, + async (response) => new QuotaExceededException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.PreconditionFailed, + async (response) => new DeviceMessageLockLostException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.RequestEntityTooLarge, + async (response) => new MessageTooLargeException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.InternalServerError, + async (response) => new ServerErrorException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + HttpStatusCode.ServiceUnavailable, + async (response) => new ServerBusyException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + }, + { + (HttpStatusCode)429, + async (response) => new ThrottlingException( + code: await GetExceptionCodeAsync(response).ConfigureAwait(false), + message: await GetExceptionMessageAsync(response).ConfigureAwait(false)) + } + }; - return mappings; - } + public static IReadOnlyDictionary>> GetDefaultErrorMapping() => + s_mappings; public static Task GetExceptionMessageAsync(HttpResponseMessage response) { @@ -104,10 +96,10 @@ public static Task GetExceptionMessageAsync(HttpResponseMessage response } /// - /// Get the fully qualified error code from the http response message, if exists + /// Get the fully-qualified error code from the HTTP response message, if exists. /// - /// The http response message - /// The fully qualified error code, or the response status code if no error code was provided. + /// The HTTP response message + /// The fully-qualified error code, or the response status code, if no error code was provided. public static async Task GetExceptionCodeAsync(HttpResponseMessage response) { // First we will attempt to retrieve the error code from the response content. @@ -121,22 +113,67 @@ public static async Task GetExceptionCodeAsync(HttpResponseMessage re // to 'error code' enum mapping, the SDK will check if both values are a match. If so, the SDK will populate the exception with the proper Code. In the case where // there is a mismatch between the error code and the description, the SDK returns ErrorCode.InvalidErrorCode and log a warning. - int errorCode; + int errorCodeValue = (int)ErrorCode.InvalidErrorCode; try { - IoTHubExceptionResult responseContent = JsonConvert - .DeserializeObject(responseContentStr); - Dictionary messageFields = JsonConvert - .DeserializeObject>(responseContent.Message); + IoTHubExceptionResult responseContent = JsonConvert.DeserializeObject(responseContentStr); - if (messageFields != null - && messageFields.TryGetValue(CommonConstants.ErrorCode, out string errorCodeObj)) + try { - errorCode = Convert.ToInt32(errorCodeObj, CultureInfo.InvariantCulture); + Dictionary messageFields = JsonConvert.DeserializeObject>(responseContent.Message); + + if (messageFields != null + && messageFields.TryGetValue(CommonConstants.ErrorCode, out string errorCodeObj)) + { + // The result of TryParse is not being tracked since errorCodeValue has already been initialized to a default value of InvalidErrorCode. + _ = int.TryParse(errorCodeObj, NumberStyles.Any, CultureInfo.InvariantCulture, out errorCodeValue); + } } - else + catch (JsonReaderException ex) { - return ErrorCode.InvalidErrorCode; + if (Logging.IsEnabled) + Logging.Error(null, $"Failed to deserialize error message into a dictionary: {ex}. Message body: '{responseContentStr}.'"); + + // In some scenarios, the error response string is a ';' delimited string with the service-returned error code. + const char errorFieldsDelimiter = ';'; + string[] messageFields = responseContent.Message?.Split(errorFieldsDelimiter); + + if (messageFields != null) + { + foreach (string messageField in messageFields) + { +#if NET451 || NET472 || NETSTANDARD2_0 + if (messageField.IndexOf(CommonConstants.ErrorCode, StringComparison.OrdinalIgnoreCase) >= 0) +#else + if (messageField.Contains(CommonConstants.ErrorCode, StringComparison.OrdinalIgnoreCase)) +#endif + { + const char errorCodeDelimiter = ':'; + +#if NET451 || NET472 || NETSTANDARD2_0 + if (messageField.IndexOf(errorCodeDelimiter) >= 0) +#else + if (messageField.Contains(errorCodeDelimiter)) +#endif + { + string[] errorCodeFields = messageField.Split(errorCodeDelimiter); + if (Enum.TryParse(errorCodeFields[1], out ErrorCode errorCode)) + { + errorCodeValue = (int)errorCode; + } + } + } + break; + } + } + else + { + if (Logging.IsEnabled) + Logging.Error(null, $"Failed to deserialize error message into a dictionary and could not parse ';' delimited string either: {ex}." + + $" Message body: '{responseContentStr}.'"); + + return ErrorCode.InvalidErrorCode; + } } } catch (JsonReaderException ex) @@ -152,7 +189,7 @@ public static async Task GetExceptionCodeAsync(HttpResponseMessage re if (headerErrorCodeString != null && Enum.TryParse(headerErrorCodeString, out ErrorCode headerErrorCode)) { - if ((int)headerErrorCode == errorCode) + if ((int)headerErrorCode == errorCodeValue) { // We have a match. Therefore, return the proper error code. return headerErrorCode; @@ -160,7 +197,7 @@ public static async Task GetExceptionCodeAsync(HttpResponseMessage re if (Logging.IsEnabled) Logging.Error(null, $"There is a mismatch between the error code retrieved from the response content and the response header." + - $"Content error code: {errorCode}. Header error code description: {(int)headerErrorCode}."); + $"Content error code: {errorCodeValue}. Header error code description: {(int)headerErrorCode}."); } return ErrorCode.InvalidErrorCode; diff --git a/common/src/service/HttpClientHelper.cs b/common/src/service/HttpClientHelper.cs index fb17cebcce..a3ac7fccb3 100644 --- a/common/src/service/HttpClientHelper.cs +++ b/common/src/service/HttpClientHelper.cs @@ -45,14 +45,14 @@ internal sealed class HttpClientHelper : IHttpClientHelper public HttpClientHelper( Uri baseAddress, IAuthorizationHeaderProvider authenticationHeaderProvider, - IDictionary>> defaultErrorMapping, + IReadOnlyDictionary>> defaultErrorMapping, TimeSpan timeout, IWebProxy customHttpProxy, int connectionLeaseTimeoutMilliseconds) { _baseAddress = baseAddress; _authenticationHeaderProvider = authenticationHeaderProvider; - _defaultErrorMapping = new ReadOnlyDictionary>>(defaultErrorMapping); + _defaultErrorMapping = defaultErrorMapping; _defaultOperationTimeout = timeout; // We need two types of HttpClients, one with our default operation timeout, and one without. The one without will rely on @@ -924,7 +924,6 @@ internal static HttpMessageHandler CreateDefaultHttpMessageHandler(IWebProxy web #endif #pragma warning restore CA2000 // Dispose objects before losing scope - if (webProxy != DefaultWebProxySettings.Instance) { httpMessageHandler.UseProxy = webProxy != null; diff --git a/e2e/test/prerequisites/readme.md b/e2e/test/prerequisites/readme.md index 91d3e9aea3..edfa8ad4fe 100644 --- a/e2e/test/prerequisites/readme.md +++ b/e2e/test/prerequisites/readme.md @@ -43,7 +43,7 @@ docker run -d --restart unless-stopped --name azure-iot-tpmsim -p 127.0.0.1:2321 Alternatives: -- Stand-alone executable for Windows: https://www.microsoft.com/en-us/download/details.aspx?id=52507 +- Stand-alone executable for Windows: https://www.microsoft.com/download/details.aspx?id=52507 ### Proxy Server diff --git a/iothub/device/src/Common/ErrorCode.cs b/iothub/device/src/Common/ErrorCode.cs deleted file mode 100644 index c7fd41a90b..0000000000 --- a/iothub/device/src/Common/ErrorCode.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace Microsoft.Azure.Devices.Client.Errors -{ - /// - /// Unique code for each instance of DeviceGateway exception that identifies the error condition that caused the failure. - /// - /// - /// These error codes will allow us to do automatic analysis and aggregation of error responses sent from resource provider and frontend. - /// - public enum ErrorCode - { -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - None = -1, - OrchestrationStateInvalid = 100, - OrchestrationRunningOnIotHub = 101, - IotHubNotFoundInDatabase = 102, - NoMatchingResourcePoolFound = 103, - ResourcePoolNotFound = 104, - NoMatchingResourceFound = 105, - MultipleMatchingResourcesFound = 106, - GarbageCollectionFailed = 107, - IotHubUpdateFailed = 108, - InvalidEventHubAccessRight = 109, - - /// - /// Bad Request - /// - AuthorizationRulesExceededQuota = 200, - - InvalidIotHubName = 201, - InvalidOperationId = 202, - IotHubNameNotAvailable = 203, - SystemPropertiesNotAllowed = 204, - - /// - /// Internal Error - /// - IotHubActivationFailed = 300, - - IotHubDeletionFailed = 301, - IotHubExportFailed = 302, - IotHubsExportFailed = 303, - IotHubImportFailed = 304, - IotHubsImportFailed = 305, - WinFabApplicationUpgradeFailed = 306, - WinFabClusterUpgradeFailed = 307, - IotHubInvalidStateTransition = 308, - IotHubStateTransitionNotDefined = 309, - IotHubInvalidProperties = 310, - - /// - /// Not found - /// - KeyNameNotFound = 400, - - /// - /// Internal Warning Range 1000-1299 - /// - WinFabApplicationCleanupNotAttempted = 1000 - -#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - } -} diff --git a/iothub/device/src/Common/Exceptions/DeviceMaximumQueueDepthExceededException.cs b/iothub/device/src/Common/Exceptions/DeviceMaximumQueueDepthExceededException.cs index 88b693b4ad..ed80d08fb0 100644 --- a/iothub/device/src/Common/Exceptions/DeviceMaximumQueueDepthExceededException.cs +++ b/iothub/device/src/Common/Exceptions/DeviceMaximumQueueDepthExceededException.cs @@ -8,7 +8,9 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown when an attempt to enqueue a message fails because the message queue for the device is already full. + /// This exception actually corresponds to IoTHubQuotaExceeded. For more information on what causes this error + /// and steps to resolve, see . + /// The exception type has not been changed to avoid breaking changes but the inner exception has the correct exception type. /// [Serializable] public sealed class DeviceMaximumQueueDepthExceededException : IotHubException diff --git a/iothub/device/src/Common/Exceptions/DeviceMessageLockLostException.cs b/iothub/device/src/Common/Exceptions/DeviceMessageLockLostException.cs index e296539033..c2c0d80a08 100644 --- a/iothub/device/src/Common/Exceptions/DeviceMessageLockLostException.cs +++ b/iothub/device/src/Common/Exceptions/DeviceMessageLockLostException.cs @@ -8,10 +8,15 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown when an attempt to communicate with a device fails because the lock token was lost (if the connection is lost and regained for example). This timeout has the same effect as if the message was abandonned. + /// This exception is thrown when attempting to reject/abandon/complete a cloud-to-device message with a lock + /// token that has already expired. The lock token expires after the lock timeout set by the service, or if your + /// client connection was lost and regained while receiving the message but before you could reject/abandon/complete it. /// /// - /// An abandoned message will be re-enqueued in the per-device queue, and the instance will receive it again. A rejected message will be deleted from the queue and not received again by the device. + /// An abandoned message will be re-enqueued in the per-device/module queue, and the instance will receive it again. + /// A rejected message will be deleted from the queue and not received again by the device. + /// For more information on the cause for this error and how to resolve, see . + /// For more information on cloud-to-device message lifecycle, see . /// [Serializable] public class DeviceMessageLockLostException : IotHubException diff --git a/iothub/device/src/Common/Exceptions/DeviceNotFoundException.cs b/iothub/device/src/Common/Exceptions/DeviceNotFoundException.cs index 32cf197bd0..270f5f8e6a 100644 --- a/iothub/device/src/Common/Exceptions/DeviceNotFoundException.cs +++ b/iothub/device/src/Common/Exceptions/DeviceNotFoundException.cs @@ -8,7 +8,13 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown when an attempt to communicate with a device fails because the given device identifier cannot be found. + /// The exception is thrown when the device is disabled and will be used to set the status to device disabled in the + /// connection status handler. This exception also corresponds to the following error codes on operation responses: + /// + /// AmqpErrorCode.NotFound + /// HttpStatusCode.NotFound + /// HttpStatusCode.NoContent + /// /// [Serializable] public sealed class DeviceNotFoundException : IotHubException diff --git a/iothub/device/src/Common/Exceptions/ExceptionHandlingHelper.cs b/iothub/device/src/Common/Exceptions/ExceptionHandlingHelper.cs index 46a984c2fe..aceb772cef 100644 --- a/iothub/device/src/Common/Exceptions/ExceptionHandlingHelper.cs +++ b/iothub/device/src/Common/Exceptions/ExceptionHandlingHelper.cs @@ -24,7 +24,7 @@ public static IDictionary new MessageTooLargeException(await GetExceptionMessageAsync(response).ConfigureAwait(false))); mappings.Add(HttpStatusCode.InternalServerError, async (response) => new ServerErrorException(await GetExceptionMessageAsync(response).ConfigureAwait(false))); mappings.Add(HttpStatusCode.ServiceUnavailable, async (response) => new ServerBusyException(await GetExceptionMessageAsync(response).ConfigureAwait(false))); - mappings.Add((System.Net.HttpStatusCode)429, async (response) => new IotHubThrottledException(await GetExceptionMessageAsync(response).ConfigureAwait(false), null)); + mappings.Add((HttpStatusCode)429, async (response) => new IotHubThrottledException(await GetExceptionMessageAsync(response).ConfigureAwait(false), null)); return mappings; } diff --git a/iothub/device/src/Common/Exceptions/IotHubCommunicationException.cs b/iothub/device/src/Common/Exceptions/IotHubCommunicationException.cs index 23a445b09b..165433891e 100644 --- a/iothub/device/src/Common/Exceptions/IotHubCommunicationException.cs +++ b/iothub/device/src/Common/Exceptions/IotHubCommunicationException.cs @@ -7,8 +7,16 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown when an attempt to communicate with the IoT Hub service fails. + /// This exception is thrown when an attempt to communicate with the IoT hub service fails due to transient + /// network errors after exhausting all the retries based on the retry policy set on the client or + /// due to operation timeouts. /// + /// + /// By default, the SDK indefinitely retries dropped connections, unless the retry policy is overridden. + /// For more information on the SDK's retry policy and how to override it, see . + /// When the exception is thrown due to operation timeouts, the inner exception will have OperationCanceledException. + /// Retrying operations failed due to timeouts could resolve the error. + /// [Serializable] public sealed class IotHubCommunicationException : IotHubException { diff --git a/iothub/device/src/Common/Exceptions/IotHubSuspendedException.cs b/iothub/device/src/Common/Exceptions/IotHubSuspendedException.cs index 92ef6d7450..9016aff6e6 100644 --- a/iothub/device/src/Common/Exceptions/IotHubSuspendedException.cs +++ b/iothub/device/src/Common/Exceptions/IotHubSuspendedException.cs @@ -8,7 +8,8 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown when the IoT Hub has been suspended. + /// This exception is thrown when the IoT hub has been suspended. This is likely due to exceeding Azure + /// spending limits. To resolve the error, check the Azure bill and ensure there are enough credits. /// [Serializable] public class IotHubSuspendedException : IotHubException diff --git a/iothub/device/src/Common/Exceptions/IotHubThrottledException.cs b/iothub/device/src/Common/Exceptions/IotHubThrottledException.cs index 3de839542d..2e4a4ad7a2 100644 --- a/iothub/device/src/Common/Exceptions/IotHubThrottledException.cs +++ b/iothub/device/src/Common/Exceptions/IotHubThrottledException.cs @@ -8,8 +8,12 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown when the service requires exponential back-off because it has exceeded the maximum number of allowed active requests. + /// This exception is thrown when the requests to the IoT hub exceed the limits based on the tier of the hub. + /// Retrying with exponential back-off could resolve this error. /// + /// + /// For information on the IoT hub quotas and throttling, see . + /// [Serializable] public sealed class IotHubThrottledException : IotHubException { diff --git a/iothub/device/src/Common/Exceptions/MessageTooLargeException.cs b/iothub/device/src/Common/Exceptions/MessageTooLargeException.cs index 2ee5d1a167..964ae011c2 100644 --- a/iothub/device/src/Common/Exceptions/MessageTooLargeException.cs +++ b/iothub/device/src/Common/Exceptions/MessageTooLargeException.cs @@ -10,6 +10,9 @@ namespace Microsoft.Azure.Devices.Client.Exceptions /// /// The exception that is thrown when an attempt to send a message fails because the length of the message exceeds the maximum size allowed. /// + /// + /// When the message is too large for IoT Hub you will receive this exception. You should attempt to reduce your message size and send again. For more information on message sizes, see IoT Hub quotas and throttling | Other limits + /// [Serializable] public sealed class MessageTooLargeException : IotHubException { diff --git a/iothub/device/src/Common/Exceptions/QuotaExceededException.cs b/iothub/device/src/Common/Exceptions/QuotaExceededException.cs index 6b2755b8b5..f32923ddac 100644 --- a/iothub/device/src/Common/Exceptions/QuotaExceededException.cs +++ b/iothub/device/src/Common/Exceptions/QuotaExceededException.cs @@ -7,8 +7,11 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown when an attempt to add a device fails because the maximum number of registered devices has been reached. + /// The exception that is thrown by the device client when the daily message quota for the IoT hub is exceeded. /// + /// + /// To resolve this exception please review the Troubleshoot Quota Exceeded guide. + /// [Serializable] public sealed class QuotaExceededException : IotHubException { diff --git a/iothub/device/src/Common/Exceptions/ServerBusyException.cs b/iothub/device/src/Common/Exceptions/ServerBusyException.cs index 97ea195aee..a4b2a2aee5 100644 --- a/iothub/device/src/Common/Exceptions/ServerBusyException.cs +++ b/iothub/device/src/Common/Exceptions/ServerBusyException.cs @@ -9,6 +9,10 @@ namespace Microsoft.Azure.Devices.Client.Exceptions /// /// The exception that is thrown when the IoT Hub is busy. /// + /// + /// This exception typically means the service is unavailable due to high load or an unexpected error and is usually transient. + /// The best course of action is to retry your operation after some time. By default, the SDK will utilize the retry strategy. + /// [Serializable] public sealed class ServerBusyException : IotHubException { diff --git a/iothub/device/src/Common/Exceptions/ServerErrorException.cs b/iothub/device/src/Common/Exceptions/ServerErrorException.cs index 3b907a940d..02ca731e78 100644 --- a/iothub/device/src/Common/Exceptions/ServerErrorException.cs +++ b/iothub/device/src/Common/Exceptions/ServerErrorException.cs @@ -6,8 +6,14 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown when the IoT Hub returned an error code. + /// The exception that is thrown when the IoT Hub returned an internal service error. /// + /// + /// This exception typically means the IoT Hub service has encountered an unexpected error and is usually transient. + /// Please review the 500xxx Internal errors + /// guide for more information. The best course of action is to retry your operation after some time. By default, + /// the SDK will utilize the retry strategy. + /// [Serializable] public sealed class ServerErrorException : IotHubException { diff --git a/iothub/device/src/Common/Exceptions/UnauthorizedException.cs b/iothub/device/src/Common/Exceptions/UnauthorizedException.cs index 1de938b09f..c0e20172c7 100644 --- a/iothub/device/src/Common/Exceptions/UnauthorizedException.cs +++ b/iothub/device/src/Common/Exceptions/UnauthorizedException.cs @@ -7,8 +7,11 @@ namespace Microsoft.Azure.Devices.Client.Exceptions { /// - /// The exception that is thrown if the current operation was not authorized. + /// The exception that is thrown when there is an authorization error. /// + /// + /// This exception means the client is not authorized to use the specified IoT Hub. Please review the 401003 IoTHubUnauthorized guide for more information. + /// [Serializable] public sealed class UnauthorizedException : IotHubException { diff --git a/iothub/device/src/Edge/TrustBundleProvider.cs b/iothub/device/src/Edge/TrustBundleProvider.cs index 949b214559..d7bf525a03 100644 --- a/iothub/device/src/Edge/TrustBundleProvider.cs +++ b/iothub/device/src/Edge/TrustBundleProvider.cs @@ -17,7 +17,7 @@ internal class TrustBundleProvider : ITrustBundleProvider private static readonly ITransientErrorDetectionStrategy s_transientErrorDetectionStrategy = new ErrorDetectionStrategy(); private static readonly RetryStrategy s_transientRetryStrategy = - new TransientFaultHandling.ExponentialBackoff( + new ExponentialBackoffRetryStrategy( retryCount: 3, minBackoff: TimeSpan.FromSeconds(2), maxBackoff: TimeSpan.FromSeconds(30), diff --git a/iothub/device/src/HsmAuthentication/HttpHsmSignatureProvider.cs b/iothub/device/src/HsmAuthentication/HttpHsmSignatureProvider.cs index 98d1c2d27b..64ad119b95 100644 --- a/iothub/device/src/HsmAuthentication/HttpHsmSignatureProvider.cs +++ b/iothub/device/src/HsmAuthentication/HttpHsmSignatureProvider.cs @@ -25,8 +25,11 @@ internal class HttpHsmSignatureProvider : ISignatureProvider private static readonly ITransientErrorDetectionStrategy s_transientErrorDetectionStrategy = new ErrorDetectionStrategy(); - private static readonly RetryStrategy s_transientRetryStrategy = - new TransientFaultHandling.ExponentialBackoff(retryCount: 3, minBackoff: TimeSpan.FromSeconds(2), maxBackoff: TimeSpan.FromSeconds(30), deltaBackoff: TimeSpan.FromSeconds(3)); + private static readonly RetryStrategy s_transientRetryStrategy = new ExponentialBackoffRetryStrategy( + retryCount: 3, + minBackoff: TimeSpan.FromSeconds(2), + maxBackoff: TimeSpan.FromSeconds(30), + deltaBackoff: TimeSpan.FromSeconds(3)); public HttpHsmSignatureProvider(string providerUri, string apiVersion) { @@ -69,7 +72,8 @@ public async Task SignAsync(string moduleId, string generationId, string BaseUrl = HttpClientHelper.GetBaseUrl(_providerUri) }; - SignResponse response = await SignAsyncWithRetryAsync(hsmHttpClient, moduleId, generationId, signRequest).ConfigureAwait(false); + SignResponse response = await SignAsyncWithRetryAsync(hsmHttpClient, moduleId, generationId, signRequest) + .ConfigureAwait(false); return Convert.ToBase64String(response.Digest); } @@ -91,10 +95,16 @@ public async Task SignAsync(string moduleId, string generationId, string } } - private async Task SignAsyncWithRetryAsync(HttpHsmClient hsmHttpClient, string moduleId, string generationId, SignRequest signRequest) + private async Task SignAsyncWithRetryAsync( + HttpHsmClient hsmHttpClient, + string moduleId, + string generationId, + SignRequest signRequest) { var transientRetryPolicy = new RetryPolicy(s_transientErrorDetectionStrategy, s_transientRetryStrategy); - SignResponse response = await transientRetryPolicy.ExecuteAsync(() => hsmHttpClient.SignAsync(_apiVersion, moduleId, generationId, signRequest)).ConfigureAwait(false); + SignResponse response = await transientRetryPolicy + .ExecuteAsync(() => hsmHttpClient.SignAsync(_apiVersion, moduleId, generationId, signRequest)) + .ConfigureAwait(false); return response; } diff --git a/iothub/device/src/RetryPolicies/ExponentialBackoff.cs b/iothub/device/src/RetryPolicies/ExponentialBackoff.cs index dd1d7fc333..e46d149962 100644 --- a/iothub/device/src/RetryPolicies/ExponentialBackoff.cs +++ b/iothub/device/src/RetryPolicies/ExponentialBackoff.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using Microsoft.Azure.Devices.Client.TransientFaultHandling; namespace Microsoft.Azure.Devices.Client { @@ -10,7 +11,7 @@ namespace Microsoft.Azure.Devices.Client /// public class ExponentialBackoff : IRetryPolicy { - private readonly TransientFaultHandling.ExponentialBackoff _exponentialBackoffRetryStrategy; + private readonly ExponentialBackoffRetryStrategy _exponentialBackoffRetryStrategy; /// /// Creates an instance of ExponentialBackoff. @@ -22,7 +23,7 @@ public class ExponentialBackoff : IRetryPolicy public ExponentialBackoff(int retryCount, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) { - _exponentialBackoffRetryStrategy = new TransientFaultHandling.ExponentialBackoff(retryCount, minBackoff, maxBackoff, deltaBackoff); + _exponentialBackoffRetryStrategy = new ExponentialBackoffRetryStrategy(retryCount, minBackoff, maxBackoff, deltaBackoff); } /// diff --git a/iothub/device/src/TransientFaultHandling/ExponentialBackoff.cs b/iothub/device/src/TransientFaultHandling/ExponentialBackoffRetryStrategy.cs similarity index 66% rename from iothub/device/src/TransientFaultHandling/ExponentialBackoff.cs rename to iothub/device/src/TransientFaultHandling/ExponentialBackoffRetryStrategy.cs index 0813e4c20c..b76c505610 100644 --- a/iothub/device/src/TransientFaultHandling/ExponentialBackoff.cs +++ b/iothub/device/src/TransientFaultHandling/ExponentialBackoffRetryStrategy.cs @@ -1,72 +1,76 @@ -//Copyright(c) Microsoft.All rights reserved. -//Microsoft would like to thank its contributors, a list -//of whom are at http://aka.ms/entlib-contributors +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// Microsoft would like to thank its contributors, a list of whom are at http://aka.ms/entlib-contributors using System; -//Licensed under the Apache License, Version 2.0 (the "License"); you -//may not use this file except in compliance with the License. You may -//obtain a copy of the License at +// Source licensed under the Apache License, Version 2.0 (the "License"); you +// may not use this file except in compliance with the License. You may +// obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -//implied. See the License for the specific language governing permissions -//and limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing permissions +// and limitations under the License. // THIS FILE HAS BEEN MODIFIED FROM ITS ORIGINAL FORM. // Change Log: // 9/1/2017 jasminel Renamed namespace to Microsoft.Azure.Devices.Client.TransientFaultHandling and modified access modifier to internal. +// 7/12/2021 drwill Renamed class from ExponentialBackoff to ExponentialBackoffRetryStrategy to avoid naming internal conflict. namespace Microsoft.Azure.Devices.Client.TransientFaultHandling { /// /// A retry strategy with back-off parameters for calculating the exponential delay between retries. /// - internal class ExponentialBackoff : RetryStrategy + internal class ExponentialBackoffRetryStrategy : RetryStrategy { - private readonly int _retryCount; + private static readonly Random s_random = new Random(); + private readonly int _retryCount; private readonly TimeSpan _minBackoff; - private readonly TimeSpan _maxBackoff; - private readonly TimeSpan _deltaBackoff; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ExponentialBackoff() : this(DefaultClientRetryCount, DefaultMinBackoff, DefaultMaxBackoff, DefaultClientBackoff) + public ExponentialBackoffRetryStrategy() + : this(DefaultClientRetryCount, DefaultMinBackoff, DefaultMaxBackoff, DefaultClientBackoff) { } /// - /// Initializes a new instance of the class with the specified retry settings. + /// Initializes a new instance of the class with the specified retry settings. /// /// The maximum number of retry attempts. /// The minimum back-off time /// The maximum back-off time. /// The value that will be used to calculate a random delta in the exponential delay between retries. - public ExponentialBackoff(int retryCount, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) : this(null, retryCount, minBackoff, maxBackoff, deltaBackoff, RetryStrategy.DefaultFirstFastRetry) + public ExponentialBackoffRetryStrategy(int retryCount, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) + : this(null, retryCount, minBackoff, maxBackoff, deltaBackoff, RetryStrategy.DefaultFirstFastRetry) { } /// - /// Initializes a new instance of the class with the specified name and retry settings. + /// Initializes a new instance of the class with the specified name and retry settings. /// /// The name of the retry strategy. /// The maximum number of retry attempts. /// The minimum back-off time /// The maximum back-off time. /// The value that will be used to calculate a random delta in the exponential delay between retries. - public ExponentialBackoff(string name, int retryCount, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) : this(name, retryCount, minBackoff, maxBackoff, deltaBackoff, RetryStrategy.DefaultFirstFastRetry) + public ExponentialBackoffRetryStrategy(string name, int retryCount, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) + : this(name, retryCount, minBackoff, maxBackoff, deltaBackoff, RetryStrategy.DefaultFirstFastRetry) { } /// - /// Initializes a new instance of the class with the specified name, retry settings, and fast retry option. + /// Initializes a new instance of the class with the specified name, retry settings, and fast retry option. /// /// The name of the retry strategy. /// The maximum number of retry attempts. @@ -74,7 +78,8 @@ public ExponentialBackoff(string name, int retryCount, TimeSpan minBackoff, Time /// The maximum back-off time. /// The value that will be used to calculate a random delta in the exponential delay between retries. /// true to immediately retry in the first attempt; otherwise, false. The subsequent retries will remain subject to the configured retry interval. - public ExponentialBackoff(string name, int retryCount, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff, bool firstFastRetry) : base(name, firstFastRetry) + public ExponentialBackoffRetryStrategy(string name, int retryCount, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff, bool firstFastRetry) + : base(name, firstFastRetry) { Guard.ArgumentNotNegativeValue(retryCount, "retryCount"); Guard.ArgumentNotNegativeValue(minBackoff.Ticks, "minBackoff"); @@ -97,11 +102,9 @@ public override ShouldRetry GetShouldRetry() { if (currentRetryCount < _retryCount) { - Random random = new Random(); - double exponentialInterval = (Math.Pow(2.0, currentRetryCount) - 1.0) - * random.Next( + * s_random.Next( (int)_deltaBackoff.TotalMilliseconds * 8 / 10, (int)_deltaBackoff.TotalMilliseconds * 12 / 10) + _minBackoff.TotalMilliseconds; diff --git a/iothub/device/src/TransientFaultHandling/RetryPolicy.cs b/iothub/device/src/TransientFaultHandling/RetryPolicy.cs index 5e46d23f65..2131a7cbec 100644 --- a/iothub/device/src/TransientFaultHandling/RetryPolicy.cs +++ b/iothub/device/src/TransientFaultHandling/RetryPolicy.cs @@ -151,7 +151,7 @@ public RetryPolicy( TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) - : this(errorDetectionStrategy, new ExponentialBackoff(retryCount, minBackoff, maxBackoff, deltaBackoff)) + : this(errorDetectionStrategy, new ExponentialBackoffRetryStrategy(retryCount, minBackoff, maxBackoff, deltaBackoff)) { } diff --git a/iothub/device/src/TransientFaultHandling/RetryStrategy.cs b/iothub/device/src/TransientFaultHandling/RetryStrategy.cs index c18c5c4eef..a73ef0779e 100644 --- a/iothub/device/src/TransientFaultHandling/RetryStrategy.cs +++ b/iothub/device/src/TransientFaultHandling/RetryStrategy.cs @@ -1,24 +1,26 @@ -//Copyright(c) Microsoft.All rights reserved. -//Microsoft would like to thank its contributors, a list -//of whom are at http://aka.ms/entlib-contributors +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// Microsoft would like to thank its contributors, a list of whom are at http://aka.ms/entlib-contributors using System; -//Licensed under the Apache License, Version 2.0 (the "License"); you -//may not use this file except in compliance with the License. You may -//obtain a copy of the License at +// Source licensed under the Apache License, Version 2.0 (the "License"); you +// may not use this file except in compliance with the License. You may +// obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -//implied. See the License for the specific language governing permissions -//and limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing permissions +// and limitations under the License. // THIS FILE HAS BEEN MODIFIED FROM ITS ORIGINAL FORM. // Change Log: // 9/1/2017 jasminel Renamed namespace to Microsoft.Azure.Devices.Client.TransientFaultHandling and modified access modifier to internal. +// 7/12/2021 drwill Changed property+backing field to auto-property. namespace Microsoft.Azure.Devices.Client.TransientFaultHandling { @@ -63,73 +65,57 @@ internal abstract class RetryStrategy /// public const bool DefaultFirstFastRetry = true; - private static readonly RetryStrategy s_noRetry = new FixedInterval(0, DefaultRetryInterval); - - private static readonly RetryStrategy s_defaultFixed = new FixedInterval(DefaultClientRetryCount, DefaultRetryInterval); - - private static readonly RetryStrategy s_defaultProgressive = new Incremental(DefaultClientRetryCount, DefaultRetryInterval, DefaultRetryIncrement); - - private static readonly RetryStrategy s_defaultExponential = new ExponentialBackoff(DefaultClientRetryCount, DefaultMinBackoff, DefaultMaxBackoff, DefaultClientBackoff); + /// + /// Initializes a new instance of the class. + /// + /// The name of the retry strategy. + /// + /// True to immediately retry in the first attempt; otherwise, false. + /// The subsequent retries will remain subject to the configured retry interval. + /// + protected RetryStrategy(string name, bool firstFastRetry) + { + Name = name; + FastFirstRetry = firstFastRetry; + } /// /// Returns a default policy that performs no retries, but invokes the action only once. /// - public static RetryStrategy NoRetry => s_noRetry; + public static RetryStrategy NoRetry { get; } = new FixedInterval(0, DefaultRetryInterval); /// - /// Returns a default policy that implements a fixed retry interval configured with the and parameters. - /// The default retry policy treats all caught exceptions as transient errors. + /// Returns a default policy that implements a fixed retry interval configured with the + /// and parameters. The default retry policy treats all caught exceptions as transient errors. /// - public static RetryStrategy DefaultFixed => s_defaultFixed; + public static RetryStrategy DefaultFixed { get; } = new FixedInterval(DefaultClientRetryCount, DefaultRetryInterval); /// - /// Returns a default policy that implements a progressive retry interval configured with the - /// , - /// , + /// Returns a default policy that implements a progressive retry interval configured with the + /// , + /// , /// and parameters. /// The default retry policy treats all caught exceptions as transient errors. /// - public static RetryStrategy DefaultProgressive => s_defaultProgressive; + public static RetryStrategy DefaultProgressive { get; } = new Incremental(DefaultClientRetryCount, DefaultRetryInterval, DefaultRetryIncrement); /// - /// Returns a default policy that implements a random exponential retry interval configured with the - /// , - /// , - /// , - /// and parameters. + /// Returns a default policy that implements a random exponential retry interval configured with the , + /// , , and parameters. /// The default retry policy treats all caught exceptions as transient errors. /// - public static RetryStrategy DefaultExponential => s_defaultExponential; + public static RetryStrategy DefaultExponential { get; } = new ExponentialBackoffRetryStrategy(DefaultClientRetryCount, DefaultMinBackoff, DefaultMaxBackoff, DefaultClientBackoff); /// /// Gets or sets a value indicating whether the first retry attempt will be made immediately, /// whereas subsequent retries will remain subject to the retry interval. /// - public bool FastFirstRetry - { - get; - set; - } + public bool FastFirstRetry { get; set; } /// /// Gets the name of the retry strategy. /// - public string Name - { - get; - private set; - } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the retry strategy. - /// true to immediately retry in the first attempt; otherwise, false. The subsequent retries will remain subject to the configured retry interval. - protected RetryStrategy(string name, bool firstFastRetry) - { - Name = name; - FastFirstRetry = firstFastRetry; - } + public string Name { get; private set; } /// /// Returns the corresponding ShouldRetry delegate. diff --git a/iothub/device/src/Transport/AmqpIot/AmqpIotErrorAdapter.cs b/iothub/device/src/Transport/AmqpIot/AmqpIotErrorAdapter.cs index ee524c278e..aa4a9b982b 100644 --- a/iothub/device/src/Transport/AmqpIot/AmqpIotErrorAdapter.cs +++ b/iothub/device/src/Transport/AmqpIot/AmqpIotErrorAdapter.cs @@ -26,7 +26,6 @@ internal static class AmqpIotErrorAdapter public static readonly AmqpSymbol ArgumentError = AmqpIotConstants.Vendor + ":argument-error"; public static readonly AmqpSymbol ArgumentOutOfRangeError = AmqpIotConstants.Vendor + ":argument-out-of-range"; public static readonly AmqpSymbol DeviceContainerThrottled = AmqpIotConstants.Vendor + ":device-container-throttled"; - public static readonly AmqpSymbol PartitionNotFound = AmqpIotConstants.Vendor + ":partition-not-found"; public static readonly AmqpSymbol IotHubSuspended = AmqpIotConstants.Vendor + ":iot-hub-suspended"; public static Exception GetExceptionFromOutcome(Outcome outcome) @@ -240,8 +239,8 @@ public static Exception ToIotHubClientContract(Error error) else if (error.Condition.Equals(AmqpErrorCode.ResourceLimitExceeded)) { // Note: The DeviceMaximumQueueDepthExceededException is not supposed to be thrown here as it is being mapped to the incorrect error code - // Error code 403004 is only applicable to C2D (Service client); see https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-troubleshoot-error-403004-devicemaximumqueuedepthexceeded - // Error code 403002 is applicable to D2C (Device client); see https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-troubleshoot-error-403002-iothubquotaexceeded + // Error code 403004 is only applicable to C2D (Service client); see https://docs.microsoft.com/azure/iot-hub/iot-hub-troubleshoot-error-403004-devicemaximumqueuedepthexceeded + // Error code 403002 is applicable to D2C (Device client); see https://docs.microsoft.com/azure/iot-hub/iot-hub-troubleshoot-error-403002-iothubquotaexceeded // We have opted not to change the exception type thrown here since it will be a breaking change, alternatively, we are adding the correct exception type // as the inner exception. retException = new DeviceMaximumQueueDepthExceededException( diff --git a/iothub/device/tests/ExponentialBackoffTests.cs b/iothub/device/tests/ExponentialBackoffTests.cs index 8abb193ef5..9d4d756cea 100644 --- a/iothub/device/tests/ExponentialBackoffTests.cs +++ b/iothub/device/tests/ExponentialBackoffTests.cs @@ -16,7 +16,11 @@ public class ExponentialBackoffTests [TestCategory("Unit")] public void ExponentialBackoffDoesNotUnderflow() { - var exponentialBackoff = new TransientFaultHandling.ExponentialBackoff(MAX_RETRY_ATTEMPTS, RetryStrategy.DefaultMinBackoff, RetryStrategy.DefaultMaxBackoff, RetryStrategy.DefaultClientBackoff); + var exponentialBackoff = new ExponentialBackoffRetryStrategy( + MAX_RETRY_ATTEMPTS, + RetryStrategy.DefaultMinBackoff, + RetryStrategy.DefaultMaxBackoff, + RetryStrategy.DefaultClientBackoff); ShouldRetry shouldRetry = exponentialBackoff.GetShouldRetry(); for (int i = 1; i < MAX_RETRY_ATTEMPTS; i++) { diff --git a/iothub/service/src/Common/Data/AmqpErrorMapper.cs b/iothub/service/src/Common/Data/AmqpErrorMapper.cs index b49a00c947..edf1a537b7 100644 --- a/iothub/service/src/Common/Data/AmqpErrorMapper.cs +++ b/iothub/service/src/Common/Data/AmqpErrorMapper.cs @@ -10,187 +10,6 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { internal static class AmqpErrorMapper { - private const int MaxSizeInInfoMap = 32 * 1024; - - public static Tuple GenerateError(Exception ex) - { - if (ex is DeviceNotFoundException deviceNotFoundException) - { - return Tuple.Create(AmqpErrorCode.NotFound.ToString(), deviceNotFoundException.Message, deviceNotFoundException.TrackingId); - } - - if (ex is DeviceAlreadyExistsException deviceAlreadyExistsException) - { - return Tuple.Create(IotHubAmqpErrorCode.DeviceAlreadyExists.ToString(), deviceAlreadyExistsException.Message, deviceAlreadyExistsException.TrackingId); - } - - if (ex is IotHubThrottledException deviceContainerThrottledException) - { - return Tuple.Create(IotHubAmqpErrorCode.DeviceContainerThrottled.ToString(), deviceContainerThrottledException.Message, deviceContainerThrottledException.TrackingId); - } - - if (ex is QuotaExceededException quotaExceededException) - { - return Tuple.Create(IotHubAmqpErrorCode.QuotaExceeded.ToString(), quotaExceededException.Message, quotaExceededException.TrackingId); - } - - if (ex is DeviceMessageLockLostException messageLockLostException) - { - return Tuple.Create(IotHubAmqpErrorCode.MessageLockLostError.ToString(), messageLockLostException.Message, messageLockLostException.TrackingId); - } - - if (ex is MessageTooLargeException deviceMessageTooLargeException) - { - return Tuple.Create(AmqpErrorCode.MessageSizeExceeded.ToString(), deviceMessageTooLargeException.Message, deviceMessageTooLargeException.TrackingId); - } - - if (ex is DeviceMaximumQueueDepthExceededException queueDepthExceededException) - { - return Tuple.Create(AmqpErrorCode.ResourceLimitExceeded.ToString(), queueDepthExceededException.Message, queueDepthExceededException.TrackingId); - } - - if (ex is PreconditionFailedException preconditionFailedException) - { - return Tuple.Create(IotHubAmqpErrorCode.PreconditionFailed.ToString(), preconditionFailedException.Message, preconditionFailedException.TrackingId); - } - - if (ex is IotHubSuspendedException iotHubSuspendedException) - { - return Tuple.Create(IotHubAmqpErrorCode.IotHubSuspended.ToString(), iotHubSuspendedException.Message, iotHubSuspendedException.TrackingId); - } - - return Tuple.Create(AmqpErrorCode.InternalError.ToString(), ex.ToStringSlim(), (string)null); - } - - public static AmqpException ToAmqpException(Exception exception) - { - return ToAmqpException(exception, false); - } - - public static AmqpException ToAmqpException(Exception exception, bool includeStackTrace) - { - Error amqpError = ToAmqpError(exception, includeStackTrace); - return new AmqpException(amqpError); - } - - public static Error ToAmqpError(Exception exception) - { - return ToAmqpError(exception, false); - } - - public static Error ToAmqpError(Exception exception, bool includeStackTrace) - { - if (exception == null) - { - throw new ArgumentNullException(nameof(exception)); - } - - var error = new Error - { - Description = exception.Message - }; - - if (exception is AmqpException) - { - var amqpException = (AmqpException)exception; - error.Condition = amqpException.Error.Condition; - error.Info = amqpException.Error.Info; - } - else if (exception is UnauthorizedAccessException || exception is UnauthorizedException) - { - error.Condition = AmqpErrorCode.UnauthorizedAccess; - } - else if (exception is NotSupportedException) - { - error.Condition = AmqpErrorCode.NotImplemented; - } - else if (exception is DeviceNotFoundException) - { - error.Condition = AmqpErrorCode.NotFound; - } - else if (exception is IotHubNotFoundException) - { - error.Condition = IotHubAmqpErrorCode.IotHubNotFoundError; - } - else if (exception is DeviceMessageLockLostException) - { - error.Condition = IotHubAmqpErrorCode.MessageLockLostError; - } - else if (exception is MessageTooLargeException) - { - error.Condition = AmqpErrorCode.MessageSizeExceeded; - } - else if (exception is DeviceMaximumQueueDepthExceededException) - { - error.Condition = AmqpErrorCode.ResourceLimitExceeded; - } - else if (exception is TimeoutException) - { - error.Condition = IotHubAmqpErrorCode.TimeoutError; - } - else if (exception is InvalidOperationException) - { - error.Condition = AmqpErrorCode.NotAllowed; - } - else if (exception is ArgumentOutOfRangeException) - { - error.Condition = IotHubAmqpErrorCode.ArgumentOutOfRangeError; - } - else if (exception is ArgumentException) - { - error.Condition = IotHubAmqpErrorCode.ArgumentError; - } - else if (exception is PreconditionFailedException) - { - error.Condition = IotHubAmqpErrorCode.PreconditionFailed; - } - else if (exception is IotHubSuspendedException) - { - error.Condition = IotHubAmqpErrorCode.IotHubSuspended; - } - else if (exception is QuotaExceededException) - { - error.Condition = IotHubAmqpErrorCode.QuotaExceeded; - } - else if (exception is TimeoutException) - { - error.Condition = IotHubAmqpErrorCode.TimeoutError; - } - else - { - error.Condition = AmqpErrorCode.InternalError; - error.Description = error.Description; - } - // we will always need this to add trackingId - if (error.Info == null) - { - error.Info = new Fields(); - } - - string stackTrace; - if (includeStackTrace && !string.IsNullOrEmpty(stackTrace = exception.StackTrace)) - { - if (stackTrace.Length > MaxSizeInInfoMap) - { - stackTrace = stackTrace.Substring(0, MaxSizeInInfoMap); - } - - // error.Info came from AmqpException then it contains StackTraceName already. - if (!error.Info.TryGetValue(IotHubAmqpProperty.StackTraceName, out string _)) - { - error.Info.Add(IotHubAmqpProperty.StackTraceName, stackTrace); - } - } - - error.Info.TryGetValue(IotHubAmqpProperty.TrackingId, out string trackingId); -#pragma warning disable CS0618 // Type or member is obsolete only for external dependency. - trackingId = TrackingHelper.CheckAndAddGatewayIdToTrackingId(trackingId); -#pragma warning restore CS0618 // Type or member is obsolete only for external dependency. - error.Info[IotHubAmqpProperty.TrackingId] = trackingId; - - return error; - } - public static Exception GetExceptionFromOutcome(Outcome outcome) { Exception retException; diff --git a/iothub/service/src/Common/Exceptions/DeviceMessageLockLostException.cs b/iothub/service/src/Common/Exceptions/DeviceMessageLockLostException.cs index 21817881eb..7f2e3d6da6 100644 --- a/iothub/service/src/Common/Exceptions/DeviceMessageLockLostException.cs +++ b/iothub/service/src/Common/Exceptions/DeviceMessageLockLostException.cs @@ -7,9 +7,8 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// The exception that is thrown when an attempt to communicate with a device fails - /// because the lock token was lost (if the connection is lost and regained for example). - /// This timeout has the same effect as if the message was abandoned. + /// This exception is not directly returned by the service for ServiceClient operations. However, the status code + /// HttpStatusCode.PreconditionFailed is converted to this exception. /// [Serializable] public class DeviceMessageLockLostException : IotHubException diff --git a/iothub/service/src/Common/Exceptions/ErrorCode.cs b/iothub/service/src/Common/Exceptions/ErrorCode.cs index 5d5947da15..4b1be0838c 100644 --- a/iothub/service/src/Common/Exceptions/ErrorCode.cs +++ b/iothub/service/src/Common/Exceptions/ErrorCode.cs @@ -1,128 +1,345 @@ // 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.ComponentModel; + namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// Error Codes for common IoT hub exceptions. + /// Error codes for common IoT hub response errors. /// public enum ErrorCode { -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + /// + /// Used when the error code returned by the hub is unrecognized. If encountered, please report the issue so it can be added here. + /// InvalidErrorCode = 0, // BadRequest - 400 + + /// + /// The API version used by the SDK is not supported by the IoT hub endpoint used in this connection. + /// + /// Usually this would mean that the region of the hub doesn't yet support the API version. One should + /// consider downgrading to a previous version of the SDK that uses an older API version, or use a hub + /// in a region that supports it. + /// + /// InvalidProtocolVersion = 400001, + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] DeviceInvalidResultCount = 400002, + + /// + /// The client has requested an operation that the hub recognizes as invalid. Check the error message + /// for more information about what is invalid. + /// + // Note: although infrequent, this does appear in logs for "Amqp Message.Properties.To must contain the device identifier". + // and perhaps other cases. InvalidOperation = 400003, + + /// + /// Something in the request payload is invalid. Check the error message for more information about what + /// is invalid. + /// + // Note: one example found in logs is for invalid characters in a twin property name. ArgumentInvalid = 400004, + + /// + /// Something in the payload is unexpectedly null. Check the error message for more information about what is invalid. + /// + // Note: an example suggested is null method payloads, but our client converts null to a JSON null, which is allowed. ArgumentNull = 400005, + + /// + /// Returned by the service if a JSON object provided by this library cannot be parsed, for instance, if the JSON provided for + /// is invalid. + /// IotHubFormatError = 400006, + + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] DeviceStorageEntitySerializationError = 400007, + + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] BlobContainerValidationError = 400008, + + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] ImportWarningExistsError = 400009, + + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] InvalidSchemaVersion = 400010, + + /// + /// A devices with the same Id was present multiple times in the input request for bulk device registry operations. + /// + /// For more information on bulk registry operations, see . + /// + /// DeviceDefinedMultipleTimes = 400011, + + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] DeserializationError = 400012, + + /// + /// An error was encountered processing bulk registry operations. + /// + /// As this error is in the 4xx HTTP status code range, the service would have detected a problem with the job + /// request or user input. + /// + /// BulkRegistryOperationFailure = 400013, + + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] CannotRegisterModuleToModule = 400301, // Unauthorized - 401 + + /// + /// The error is internal to IoT hub and is likely transient. + /// + [Obsolete("This error does should not be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] IotHubNotFound = 401001, + /// + /// The SAS token has expired or IoT hub couldn't authenticate the authentication header, rule, or key. + /// For more information, see . + /// IotHubUnauthorizedAccess = 401002, /// - /// The SAS token has expired or IoT hub couldn't authenticate the authentication header, rule, or key. + /// Unused error code. Service does not return it and neither does the SDK. + /// Replaced by /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] IotHubUnauthorized = 401003, // Forbidden - 403 + + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] IotHubSuspended = 403001, /// - /// The daily message quota for the IoT hub is exceeded. + /// Total number of messages on the hub exceeded the allocated quota. + /// + /// Increase units for this hub to increase the quota. + /// For more information on quota, please refer to . + /// /// IotHubQuotaExceeded = 403002, + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] JobQuotaExceeded = 403003, /// - /// The underlying cause is that the number of messages enqueued for the device exceeds the queue limit (50). - /// The most likely reason that you're running into this limit is because you're using HTTPS to receive the message, - /// which leads to continuous polling using ReceiveAsync, resulting in IoT hub throttling the request. + /// The underlying cause is that the number of cloud-to-device messages enqueued for the device exceeds the queue limit. + /// + /// You will need to receive and complete/reject the messages from the device-side before you can enqueue any additional messages. + /// If you want to discard the currently enqueued messages, you can + /// purge your device message queue. + /// For more information on cloud-to-device message operations, see . + /// /// DeviceMaximumQueueDepthExceeded = 403004, + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] IotHubMaxCbsTokenExceeded = 403005, // NotFound - 404 /// - /// The operation failed because the device cannot be found by IoT Hub. The device is either not registered or disabled. + /// The operation failed because the device cannot be found by IoT hub. + /// + /// The device is either not registered or disabled. May be thrown by operations such as + /// . + /// /// DeviceNotFound = 404001, + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] JobNotFound = 404002, - PartitionNotFound = 404003, + + /// + /// The error is internal to IoT hub and is likely transient. + /// + /// For more information, see 503003 PartitionNotFound. + /// + /// + [Obsolete("This error does should not be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] + PartitionNotFound = 503003, + + /// + /// The operation failed because the module cannot be found by IoT hub. + /// + /// The module is either not registered or disabled. May be thrown by operations such as + /// . + /// + /// ModuleNotFound = 404010, // Conflict - 409 /// /// There's already a device with the same device Id in the IoT hub. + /// + /// This can be returned on calling + /// with a device that already exists in the IoT hub. + /// /// DeviceAlreadyExists = 409001, + /// + /// The operation failed because it attempted to add a module to a device when that device already has a module registered to it with the same Id. This issue can be + /// fixed by removing the existing module from the device first with . This error code is only returned from + /// methods like . + /// ModuleAlreadyExistsOnDevice = 409301, - // PreconditionFailed - 412 - PreconditionFailed = 412001, + /// + /// The ETag in the request does not match the ETag of the existing resource, as per RFC7232. + /// + /// The ETag is a mechanism for protecting against the race conditions of multiple clients updating the same resource and overwriting each other. + /// In order to get the up-to-date ETag for a twin, see or + /// . + /// + /// + PreconditionFailed = 412001, // PreconditionFailed - 412 /// + /// If the device tries to complete the message after the lock timeout expires, IoT hub throws this exception. + /// /// When a device receives a cloud-to-device message from the queue (for example, using ReceiveAsync()) /// the message is locked by IoT hub for a lock timeout duration of one minute. - /// If the device tries to complete the message after the lock timeout expires, IoT hub throws this exception. + /// /// + [Obsolete("This error should not be returned to a service application. This is relevant only for a device application.")] + [EditorBrowsable(EditorBrowsableState.Never)] DeviceMessageLockLost = 412002, // RequestEntityTooLarge - 413 + + /// + /// When the message is too large for IoT hub you will receive this error.' + /// + /// You should attempt to reduce your message size and send again. + /// For more information on message sizes, see IoT hub quotas and throttling | Other limits + /// + /// MessageTooLarge = 413001, + /// + /// Too many devices were included in the bulk operation. + /// + /// Check the response for details. + /// For more information, see . + /// + /// TooManyDevices = 413002, + + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] TooManyModulesOnDevice = 413003, // Throttling Exception /// /// IoT hub throttling limits have been exceeded for the requested operation. - /// For more information, + /// For more information, IoT hub quotas and throttling. /// ThrottlingException = 429001, + /// + /// IoT hub throttling limits have been exceeded for the requested operation. + /// + /// For more information, see IoT hub quotas and throttling. + /// + /// ThrottleBacklogLimitExceeded = 429002, - InvalidThrottleParameter = 429003, + + /// + /// IoT hub ran into a server side issue when attempting to throttle. + /// + /// For more information, see 500xxx Internal errors. + /// + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] + InvalidThrottleParameter = 500009, // InternalServerError - 500 /// /// IoT hub ran into a server side issue. + /// /// There can be a number of causes for a 500xxx error response. In all cases, the issue is most likely transient. - /// IoT hub nodes can occasionally experience transient faults. When your device tries to connect to a node that is - /// having issues, you receive this error. To mitigate 500xxx errors, issue a retry from the device. + /// IoT hub nodes can occasionally experience transient faults. When your application tries to connect to a node that is + /// having issues, you receive this error. To mitigate 500xxx errors, issue a retry from your application. + /// /// ServerError = 500001, + /// + /// Unused error code. Service does not return it and neither does the SDK. + /// + [Obsolete("This error does not appear to be returned by the service.")] + [EditorBrowsable(EditorBrowsableState.Never)] JobCancelled = 500002, // ServiceUnavailable /// - /// IoT hub encountered an internal error. + /// IoT hub is currently unable to process the request. This is a transient, retryable error. /// ServiceUnavailable = 503001, - -#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member } } diff --git a/iothub/service/src/Common/Exceptions/IotHubAmqpErrorCode.cs b/iothub/service/src/Common/Exceptions/IotHubAmqpErrorCode.cs index 419567a58d..1ef9db4f6d 100644 --- a/iothub/service/src/Common/Exceptions/IotHubAmqpErrorCode.cs +++ b/iothub/service/src/Common/Exceptions/IotHubAmqpErrorCode.cs @@ -26,7 +26,6 @@ internal static class IotHubAmqpErrorCode public static readonly AmqpSymbol DeviceAlreadyExists = AmqpConstants.Vendor + ":device-already-exists"; public static readonly AmqpSymbol DeviceContainerThrottled = AmqpConstants.Vendor + ":device-container-throttled"; public static readonly AmqpSymbol QuotaExceeded = AmqpConstants.Vendor + ":quota-exceeded"; - public static readonly AmqpSymbol PartitionNotFound = AmqpConstants.Vendor + ":partition-not-found"; public static readonly AmqpSymbol PreconditionFailed = AmqpConstants.Vendor + ":precondition-failed"; public static readonly AmqpSymbol IotHubSuspended = AmqpConstants.Vendor + ":iot-hub-suspended"; } diff --git a/iothub/service/src/Common/Exceptions/IotHubCommunicationException.cs b/iothub/service/src/Common/Exceptions/IotHubCommunicationException.cs index 82874f6dd4..8b6c88de2f 100644 --- a/iothub/service/src/Common/Exceptions/IotHubCommunicationException.cs +++ b/iothub/service/src/Common/Exceptions/IotHubCommunicationException.cs @@ -7,7 +7,8 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// The exception that is thrown when an attempt to communicate with the IoT Hub fails. + /// This exception is thrown when an attempt to communicate with the IoT hub service fails due to transient + /// network issues or operation timeouts. Retrying failed operations could resolve the error. /// [Serializable] public sealed class IotHubCommunicationException : IotHubException diff --git a/iothub/service/src/Common/Exceptions/IotHubSuspendedException.cs b/iothub/service/src/Common/Exceptions/IotHubSuspendedException.cs index 190f79b886..43fb7c0b29 100644 --- a/iothub/service/src/Common/Exceptions/IotHubSuspendedException.cs +++ b/iothub/service/src/Common/Exceptions/IotHubSuspendedException.cs @@ -7,7 +7,8 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// The exception that is thrown when a request is made against an IoT Hub that has been suspended. + /// This exception is thrown when the IoT hub has been suspended. This is likely due to exceeding Azure + /// spending limits. To resolve the error, check the Azure bill and ensure there are enough credits. /// [Serializable] public class IotHubSuspendedException : IotHubException diff --git a/iothub/service/src/Common/Exceptions/IotHubThrottledException.cs b/iothub/service/src/Common/Exceptions/IotHubThrottledException.cs index 91642d653c..5f4b90def0 100644 --- a/iothub/service/src/Common/Exceptions/IotHubThrottledException.cs +++ b/iothub/service/src/Common/Exceptions/IotHubThrottledException.cs @@ -7,8 +7,12 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// The exception that is thrown when the rate of incoming requests exceeds the throttling limit set by IoT Hub. + /// This exception is thrown when the requests to the IoT hub exceed the limits based on the tier of the hub. + /// Retrying with exponential back-off could resolve this error. /// + /// + /// For information on the IoT hub quotas and throttling, see . + /// [Serializable] public sealed class IotHubThrottledException : IotHubException { diff --git a/iothub/service/src/Common/Exceptions/MessageTooLargeException.cs b/iothub/service/src/Common/Exceptions/MessageTooLargeException.cs index 9f61742049..6d3ebcdf42 100644 --- a/iothub/service/src/Common/Exceptions/MessageTooLargeException.cs +++ b/iothub/service/src/Common/Exceptions/MessageTooLargeException.cs @@ -7,8 +7,11 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// The exception that is thrown when a message is sent to IoT Hub that exceeds the maximum allowed bytes in size. + /// The exception that is thrown when an attempt to send a message fails because the length of the message exceeds the maximum size allowed. /// + /// + /// When the message is too large for IoT Hub you will receive this exception. You should attempt to reduce your message size and send again. For more information on message sizes, see IoT Hub quotas and throttling | Other limits + /// [Serializable] public sealed class MessageTooLargeException : IotHubException { diff --git a/iothub/service/src/Common/Exceptions/QuotaExceededException.cs b/iothub/service/src/Common/Exceptions/QuotaExceededException.cs index 8b005c8407..09032c2172 100644 --- a/iothub/service/src/Common/Exceptions/QuotaExceededException.cs +++ b/iothub/service/src/Common/Exceptions/QuotaExceededException.cs @@ -7,8 +7,11 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// The exception that is thrown when the allocated quota set by IoT Hub is exceeded. + /// The exception that is thrown by the service client when the daily message quota for the IoT hub is exceeded. /// + /// + /// To resolve this exception please review the Troubleshoot Quota Exceeded guide. + /// [Serializable] public sealed class QuotaExceededException : IotHubException { diff --git a/iothub/service/src/Common/Exceptions/ServerBusyException.cs b/iothub/service/src/Common/Exceptions/ServerBusyException.cs index 5f06accc1c..cdbcf0c4bb 100644 --- a/iothub/service/src/Common/Exceptions/ServerBusyException.cs +++ b/iothub/service/src/Common/Exceptions/ServerBusyException.cs @@ -7,9 +7,11 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// The exception that is thrown when IoT Hub is busy with previous requests. - /// Callers should wait a while and retry the operation. + /// The exception that is thrown when the IoT Hub is busy. /// + /// + /// This exception typically means the service is unavailable due to high load or an unexpected error and is usually transient. The best course of action is to retry your operation after some time. + /// [Serializable] public sealed class ServerBusyException : IotHubException { diff --git a/iothub/service/src/Common/Exceptions/ServerErrorException.cs b/iothub/service/src/Common/Exceptions/ServerErrorException.cs index 5ce7c011eb..4cf3c31e1f 100644 --- a/iothub/service/src/Common/Exceptions/ServerErrorException.cs +++ b/iothub/service/src/Common/Exceptions/ServerErrorException.cs @@ -7,8 +7,13 @@ namespace Microsoft.Azure.Devices.Common.Exceptions { /// - /// The exception that is thrown when IoT Hub encounters an error while processing a request. + /// The exception that is thrown when the IoT Hub returned an internal service error. /// + /// + /// This exception typically means the IoT Hub service has encountered an unexpected error and is usually transient. + /// Please review the 500xxx Internal errors + /// guide for more information. The best course of action is to retry your operation after some time. + /// [Serializable] public sealed class ServerErrorException : IotHubException { diff --git a/iothub/service/src/Common/Exceptions/UnauthorizedException.cs b/iothub/service/src/Common/Exceptions/UnauthorizedException.cs index 6eba26f540..6afd0aa45d 100644 --- a/iothub/service/src/Common/Exceptions/UnauthorizedException.cs +++ b/iothub/service/src/Common/Exceptions/UnauthorizedException.cs @@ -9,6 +9,9 @@ namespace Microsoft.Azure.Devices.Common.Exceptions /// /// The exception that is thrown when there is an authorization error. /// + /// + /// This exception means the client is not authorized to use the specified IoT Hub. Please review the 401003 IoTHubUnauthorized guide for more information. + /// [Serializable] public sealed class UnauthorizedException : IotHubException { diff --git a/iothub/service/src/Common/TrackingHelper.cs b/iothub/service/src/Common/TrackingHelper.cs index cbc4e77d4b..e6f6c5f875 100644 --- a/iothub/service/src/Common/TrackingHelper.cs +++ b/iothub/service/src/Common/TrackingHelper.cs @@ -220,10 +220,6 @@ public static ErrorCode GetErrorCodeFromAmqpError(Error ex) { return ErrorCode.DeviceNotFound; } - if (ex.Condition.Equals(IotHubAmqpErrorCode.MessageLockLostError)) - { - return ErrorCode.DeviceMessageLockLost; - } if (ex.Condition.Equals(IotHubAmqpErrorCode.IotHubSuspended)) { return ErrorCode.IotHubSuspended; diff --git a/iothub/service/src/JobClient/HttpJobClient.cs b/iothub/service/src/JobClient/HttpJobClient.cs index 63fe9f3683..88a1d2e0ab 100644 --- a/iothub/service/src/JobClient/HttpJobClient.cs +++ b/iothub/service/src/JobClient/HttpJobClient.cs @@ -8,10 +8,9 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.Azure.Devices.Shared; using Microsoft.Azure.Devices.Common; using Microsoft.Azure.Devices.Common.Exceptions; -using System.Diagnostics.CodeAnalysis; +using Microsoft.Azure.Devices.Shared; namespace Microsoft.Azure.Devices { diff --git a/iothub/service/src/JobStatus.cs b/iothub/service/src/JobStatus.cs index aacc7140e5..11a46cbd6b 100644 --- a/iothub/service/src/JobStatus.cs +++ b/iothub/service/src/JobStatus.cs @@ -56,7 +56,7 @@ public enum JobStatus Scheduled, /// - /// Indicates that a Job is in the queue for execution (synonym for enqueued to be depricated) + /// Indicates that a Job is in the queue for execution (synonym for enqueued to be deprecated) /// [EnumMember(Value = "queued")] Queued