Skip to content

Commit

Permalink
More codes doc'd, code clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
David R. Williamson committed Jul 12, 2021
1 parent df9b34a commit 369cd2b
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 181 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
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 369cd2b

Please sign in to comment.