Line data Source code
1 : // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 :
3 : namespace Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling
4 : {
5 : using System;
6 : using Microsoft.IdentityModel.Tokens;
7 : using System.Linq;
8 : using System.Net;
9 : using System.Net.Sockets;
10 : using System.Security;
11 : using System.ServiceModel;
12 : using System.Text.RegularExpressions;
13 : using Microsoft.Azure.ServiceBus;
14 :
15 : /// <summary>
16 : /// Provides the transient error detection logic that can recognize transient faults when dealing with Windows Azure Service Bus.
17 : /// </summary>
18 : public class ServiceBusTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
19 1 : {
20 : /// <summary>
21 : /// Provides a compiled regular expression used for extracting the error code from the message.
22 : /// </summary>
23 : private static readonly Regex acsErrorCodeRegex = new Regex(@"Error:Code:(\d+):SubCode:(\w\d+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
24 : private static readonly int[] httpStatusCodes = new[] { (int)HttpStatusCode.InternalServerError, (int)HttpStatusCode.GatewayTimeout, (int)HttpStatusCode.ServiceUnavailable, (int)HttpStatusCode.RequestTimeout };
25 : private static readonly WebExceptionStatus[] webExceptionStatus = new[] { WebExceptionStatus.ConnectionClosed, WebExceptionStatus.Timeout, WebExceptionStatus.RequestCanceled };
26 :
27 : /// <summary>
28 : /// Determines whether the specified exception represents a transient failure that can be compensated by a retry.
29 : /// </summary>
30 : /// <param name="ex">The exception object to be verified.</param>
31 : /// <returns>true if the specified exception is considered transient; otherwise, false.</returns>
32 1 : public bool IsTransient(Exception ex)
33 : {
34 : return ex != null && (CheckIsTransient(ex) || (ex.InnerException != null && CheckIsTransient(ex.InnerException)));
35 : }
36 :
37 : // SecuritySafeCritical because it references MessagingException
38 : [SecuritySafeCritical]
39 : private static bool CheckIsTransient(Exception ex)
40 : {
41 : var messagingException = ex as ServiceBusException;
42 : if (messagingException != null)
43 : {
44 : // The IsTransient property already covers the following scenarios:
45 : //if (ex is MessageLockLostException) return false;
46 : //if (ex is MessagingEntityAlreadyExistsException) return false;
47 : //if (ex is MessagingEntityNotFoundException) return false;
48 : //if (ex is MessagingCommunicationException) return true;
49 : //if (ex is ServerBusyException) return true;
50 : return messagingException.IsTransient;
51 : }
52 :
53 : if (ex is FaultException) return false;
54 :
55 : if (ex is CommunicationObjectFaultedException) return false;
56 :
57 : if (ex is TimeoutException) return true;
58 :
59 : var webException = ex as WebException;
60 : if (webException != null)
61 : {
62 : if (webExceptionStatus.Contains(webException.Status)) return true;
63 :
64 : if (webException.Status == WebExceptionStatus.ProtocolError)
65 : {
66 : var response = webException.Response as HttpWebResponse;
67 : if (response != null && httpStatusCodes.Contains((int)response.StatusCode)) return true;
68 : }
69 : }
70 :
71 : if (ex is SecurityTokenException) return true;
72 :
73 : if (ex is ServerTooBusyException) return true;
74 :
75 : if (ex is ProtocolException) return true;
76 :
77 : // This exception may occur when a listener and a consumer are being
78 : // initialized out of sync (e.g. consumer is reaching to a listener that
79 : // is still in the process of setting up the Service Host).
80 : if (ex is EndpointNotFoundException) return true;
81 :
82 : if (ex is CommunicationException) return true;
83 :
84 : var socketFault = ex as SocketException;
85 : if (socketFault != null)
86 : {
87 : return socketFault.SocketErrorCode == SocketError.TimedOut;
88 : }
89 :
90 : if (ex is UnauthorizedAccessException)
91 : {
92 : // Need to provide some resilience against the following fault that was seen a few times:
93 : // System.UnauthorizedAccessException: The token provider was unable to provide a security token while accessing 'https://xxx.accesscontrol.windows.net/WRAPv0.9/'.
94 : // Token provider returned message: 'Error:Code:500:SubCode:T9002:Detail:An internal network error occured. Please try again.'.
95 : // System.IdentityModel.Tokens.SecurityTokenException: The token provider was unable to provide a security token while accessing 'https://xxx.accesscontrol.windows.net/WRAPv0.9/'.
96 : // Token provider returned message: 'Error:Code:500:SubCode:T9002:Detail:An internal network error occured. Please try again.'.
97 : // System.Net.WebException: The remote server returned an error: (500) Internal Server Error.
98 : var match = acsErrorCodeRegex.Match(ex.Message);
99 : var errorCode = 0;
100 :
101 : if (match.Success && match.Groups.Count > 1 && int.TryParse(match.Groups[1].Value, out errorCode))
102 : {
103 : return httpStatusCodes.Contains(errorCode);
104 : }
105 : }
106 :
107 : return false;
108 : }
109 : }
110 : }
|