Skip to content

Commit

Permalink
More codes doc'd and code cleanup (#2082)
Browse files Browse the repository at this point in the history
* More codes doc'd, code clean up

* Update license

* Random is static

* License header

* More codes doc'd, code clean up
  • Loading branch information
David R. Williamson committed Jul 16, 2021
1 parent 22feac7 commit 58c4c3f
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 188 deletions.
169 changes: 77 additions & 92 deletions common/src/service/ExceptionHandlingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,97 +17,88 @@ namespace Microsoft.Azure.Devices
{
internal class ExceptionHandlingHelper
{
public static IDictionary<HttpStatusCode, Func<HttpResponseMessage, Task<Exception>>> GetDefaultErrorMapping()
private static readonly IReadOnlyDictionary<HttpStatusCode, Func<HttpResponseMessage, Task<Exception>>> s_mappings =
new Dictionary<HttpStatusCode, Func<HttpResponseMessage, Task<Exception>>>
{
var mappings = new Dictionary<HttpStatusCode, Func<HttpResponseMessage, Task<Exception>>>
{
{
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<HttpStatusCode, Func<HttpResponseMessage, Task<Exception>>> GetDefaultErrorMapping() =>
s_mappings;

public static Task<string> GetExceptionMessageAsync(HttpResponseMessage response)
{
return response.Content.ReadAsStringAsync();
}

/// <summary>
/// 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.
/// </summary>
/// <param name="response">The http response message</param>
/// <returns>The fully qualified error code, or the response status code if no error code was provided.</returns>
/// <param name="response">The HTTP response message</param>
/// <returns>The fully-qualified error code, or the response status code, if no error code was provided.</returns>
public static async Task<ErrorCode> GetExceptionCodeAsync(HttpResponseMessage response)
{
// First we will attempt to retrieve the error code from the response content.
Expand All @@ -121,22 +112,16 @@ public static async Task<ErrorCode> 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<IoTHubExceptionResult>(responseContentStr);
Dictionary<string, string> messageFields = JsonConvert
.DeserializeObject<Dictionary<string, string>>(responseContent.Message);
IoTHubExceptionResult responseContent = JsonConvert.DeserializeObject<IoTHubExceptionResult>(responseContentStr);
Dictionary<string, string> messageFields = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseContent.Message);

if (messageFields != null
&& messageFields.TryGetValue(CommonConstants.ErrorCode, out string errorCodeObj))
{
errorCode = Convert.ToInt32(errorCodeObj, CultureInfo.InvariantCulture);
}
else
{
return ErrorCode.InvalidErrorCode;
errorCodeValue = Convert.ToInt32(errorCodeObj, CultureInfo.InvariantCulture);
}
}
catch (JsonReaderException ex)
Expand All @@ -152,15 +137,15 @@ public static async Task<ErrorCode> 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;
}

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;
Expand Down
5 changes: 2 additions & 3 deletions common/src/service/HttpClientHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ internal sealed class HttpClientHelper : IHttpClientHelper
public HttpClientHelper(
Uri baseAddress,
IAuthorizationHeaderProvider authenticationHeaderProvider,
IDictionary<HttpStatusCode, Func<HttpResponseMessage, Task<Exception>>> defaultErrorMapping,
IReadOnlyDictionary<HttpStatusCode, Func<HttpResponseMessage, Task<Exception>>> defaultErrorMapping,
TimeSpan timeout,
IWebProxy customHttpProxy,
int connectionLeaseTimeoutMilliseconds)
{
_baseAddress = baseAddress;
_authenticationHeaderProvider = authenticationHeaderProvider;
_defaultErrorMapping = new ReadOnlyDictionary<HttpStatusCode, Func<HttpResponseMessage, Task<Exception>>>(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
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion iothub/device/src/Common/Exceptions/ServerBusyException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ namespace Microsoft.Azure.Devices.Client.Exceptions
/// The exception that is thrown when the IoT Hub is busy.
/// </summary>
/// <remarks>
/// 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 <see cref="ExponentialBackoff"/> retry strategy.
/// 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 <see cref="ExponentialBackoff"/> retry strategy.
/// </remarks>
[Serializable]
public sealed class ServerBusyException : IotHubException
Expand Down
5 changes: 4 additions & 1 deletion iothub/device/src/Common/Exceptions/ServerErrorException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ namespace Microsoft.Azure.Devices.Client.Exceptions
/// The exception that is thrown when the IoT Hub returned an internal service error.
/// </summary>
/// <remarks>
/// This exception typically means the IoT Hub service has encountered an unexpected error and is usually transient. Please review the <see href="https://docs.microsoft.com/azure/iot-hub/iot-hub-troubleshoot-error-500xxx-internal-errors">500xxx Internal errors</see> guide for more information. The best course of action is to retry your operation after some time. By default, the SDK will utilize the <see cref="ExponentialBackoff"/> retry strategy.
/// This exception typically means the IoT Hub service has encountered an unexpected error and is usually transient.
/// Please review the <see href="https://docs.microsoft.com/azure/iot-hub/iot-hub-troubleshoot-error-500xxx-internal-errors">500xxx Internal errors</see>
/// guide for more information. The best course of action is to retry your operation after some time. By default,
/// the SDK will utilize the <see cref="ExponentialBackoff"/> retry strategy.
/// </remarks>
[Serializable]
public sealed class ServerErrorException : IotHubException
Expand Down
2 changes: 1 addition & 1 deletion iothub/device/src/Edge/TrustBundleProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
20 changes: 15 additions & 5 deletions iothub/device/src/HsmAuthentication/HttpHsmSignatureProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -69,7 +72,8 @@ public async Task<string> 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);
}
Expand All @@ -91,10 +95,16 @@ public async Task<string> SignAsync(string moduleId, string generationId, string
}
}

private async Task<SignResponse> SignAsyncWithRetryAsync(HttpHsmClient hsmHttpClient, string moduleId, string generationId, SignRequest signRequest)
private async Task<SignResponse> 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;
}

Expand Down
5 changes: 3 additions & 2 deletions iothub/device/src/RetryPolicies/ExponentialBackoff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -10,7 +11,7 @@ namespace Microsoft.Azure.Devices.Client
/// </summary>
public class ExponentialBackoff : IRetryPolicy
{
private readonly TransientFaultHandling.ExponentialBackoff _exponentialBackoffRetryStrategy;
private readonly ExponentialBackoffRetryStrategy _exponentialBackoffRetryStrategy;

/// <summary>
/// Creates an instance of ExponentialBackoff.
Expand All @@ -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);
}

/// <summary>
Expand Down
Loading

0 comments on commit 58c4c3f

Please sign in to comment.