PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/referencesource/System.ServiceModel.Routing/System/ServiceModel/Routing/ProcessRequestAsyncResult.cs

https://github.com/kumpera/mono
C# | 281 lines | 231 code | 29 blank | 21 comment | 50 complexity | e6e6bc0d63e5072faeb9660ba60fc483 MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, Unlicense
  1. //----------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //----------------------------------------------------------------
  4. namespace System.ServiceModel.Routing
  5. {
  6. using System;
  7. using System.Configuration;
  8. using System.Runtime;
  9. using System.ServiceModel.Channels;
  10. using System.ServiceModel.Dispatcher;
  11. using System.ServiceModel.Security;
  12. using System.Threading;
  13. using System.Transactions;
  14. //using System.Security.Principal;
  15. class ProcessRequestAsyncResult<TContract> : TransactedAsyncResult
  16. {
  17. static AsyncCompletion operationCallback = new AsyncCompletion(OperationCallback);
  18. RoutingService service;
  19. IRoutingClient currentClient;
  20. MessageRpc messageRpc;
  21. Message replyMessage;
  22. bool allCompletedSync;
  23. bool abortedRetry;
  24. public ProcessRequestAsyncResult(RoutingService service, Message message, AsyncCallback callback, object state)
  25. : base(callback, state)
  26. {
  27. this.allCompletedSync = true;
  28. this.service = service;
  29. this.messageRpc = new MessageRpc(message, OperationContext.Current, service.ChannelExtension.ImpersonationRequired);
  30. if (TD.RoutingServiceProcessingMessageIsEnabled())
  31. {
  32. TD.RoutingServiceProcessingMessage(this.messageRpc.EventTraceActivity, this.messageRpc.UniqueID,
  33. message.Headers.Action, this.messageRpc.OperationContext.EndpointDispatcher.EndpointAddress.Uri.ToString(), messageRpc.Transaction != null ? "True" : "False");
  34. }
  35. try
  36. {
  37. EndpointNameMessageFilter.Set(this.messageRpc.Message.Properties, service.ChannelExtension.EndpointName);
  38. this.messageRpc.RouteToSingleEndpoint<TContract>(this.service.RoutingConfig);
  39. }
  40. catch (MultipleFilterMatchesException matchesException)
  41. {
  42. // Wrap this exception with one that is more meaningful to users of RoutingService:
  43. throw FxTrace.Exception.AsError(new ConfigurationErrorsException(SR.ReqReplyMulticastNotSupported(this.messageRpc.OperationContext.Channel.LocalAddress), matchesException));
  44. }
  45. while (this.StartProcessing())
  46. {
  47. }
  48. }
  49. bool StartProcessing()
  50. {
  51. bool callAgain = false;
  52. SendOperation sendOperation = this.messageRpc.Operations[0];
  53. this.currentClient = this.service.GetOrCreateClient<TContract>(sendOperation.CurrentEndpoint, this.messageRpc.Impersonating);
  54. if (TD.RoutingServiceTransmittingMessageIsEnabled())
  55. {
  56. TD.RoutingServiceTransmittingMessage(this.messageRpc.EventTraceActivity, this.messageRpc.UniqueID, "0", this.currentClient.Key.ToString());
  57. }
  58. try
  59. {
  60. if (messageRpc.Transaction != null && sendOperation.HasAlternate)
  61. {
  62. throw FxTrace.Exception.AsError(new ConfigurationErrorsException(SR.ErrorHandlingNotSupportedReqReplyTxn(this.messageRpc.OperationContext.Channel.LocalAddress)));
  63. }
  64. // We always work on cloned message when there are backup endpoints to handle exception cases
  65. Message message;
  66. if (sendOperation.AlternateEndpointCount > 0)
  67. {
  68. message = messageRpc.CreateBuffer().CreateMessage();
  69. }
  70. else
  71. {
  72. message = messageRpc.Message;
  73. }
  74. sendOperation.PrepareMessage(message);
  75. IAsyncResult result = null;
  76. using (this.PrepareTransactionalCall(messageRpc.Transaction))
  77. {
  78. IDisposable impersonationContext = null;
  79. try
  80. {
  81. //Perform the assignment in a finally block so it won't be interrupted asynchronously
  82. try { }
  83. finally
  84. {
  85. impersonationContext = messageRpc.PrepareCall();
  86. }
  87. result = this.currentClient.BeginOperation(message, messageRpc.Transaction, this.PrepareAsyncCompletion(operationCallback), this);
  88. }
  89. finally
  90. {
  91. if (impersonationContext != null)
  92. {
  93. impersonationContext.Dispose();
  94. }
  95. }
  96. }
  97. if (this.CheckSyncContinue(result))
  98. {
  99. if (this.OperationComplete(result))
  100. {
  101. this.Complete(this.allCompletedSync);
  102. }
  103. else
  104. {
  105. callAgain = true;
  106. }
  107. }
  108. }
  109. catch (Exception exception)
  110. {
  111. if (Fx.IsFatal(exception))
  112. {
  113. throw;
  114. }
  115. if (!this.HandleClientOperationFailure(exception))
  116. {
  117. throw;
  118. }
  119. callAgain = true;
  120. }
  121. return callAgain;
  122. }
  123. static bool OperationCallback(IAsyncResult result)
  124. {
  125. ProcessRequestAsyncResult<TContract> thisPtr = (ProcessRequestAsyncResult<TContract>)result.AsyncState;
  126. FxTrace.Trace.SetAndTraceTransfer(thisPtr.service.ChannelExtension.ActivityID, true);
  127. thisPtr.allCompletedSync = false;
  128. try
  129. {
  130. if (thisPtr.OperationComplete(result))
  131. {
  132. return true;
  133. }
  134. }
  135. catch (Exception exception)
  136. {
  137. if (Fx.IsFatal(exception))
  138. {
  139. throw;
  140. }
  141. if (!thisPtr.HandleClientOperationFailure(exception))
  142. {
  143. throw;
  144. }
  145. }
  146. while (thisPtr.StartProcessing())
  147. {
  148. }
  149. return false;
  150. }
  151. // Returns true if we're all done and can complete this AsyncResult now
  152. bool OperationComplete(IAsyncResult result)
  153. {
  154. bool completeSelf = false;
  155. Message responseMsg = this.currentClient.EndOperation(result);
  156. if (TD.RoutingServiceTransmitSucceededIsEnabled())
  157. {
  158. TD.RoutingServiceTransmitSucceeded(this.messageRpc.EventTraceActivity, this.messageRpc.UniqueID, "0", this.currentClient.Key.ToString());
  159. }
  160. if (responseMsg == null || !responseMsg.IsFault)
  161. {
  162. if (TD.RoutingServiceSendingResponseIsEnabled())
  163. {
  164. string action = (responseMsg != null) ? responseMsg.Headers.Action : string.Empty;
  165. TD.RoutingServiceSendingResponse(this.messageRpc.EventTraceActivity, action);
  166. }
  167. }
  168. else
  169. {
  170. if (TD.RoutingServiceSendingFaultResponseIsEnabled()) { TD.RoutingServiceSendingFaultResponse(this.messageRpc.EventTraceActivity, responseMsg.Headers.Action); }
  171. }
  172. this.replyMessage = responseMsg;
  173. completeSelf = true;
  174. if (TD.RoutingServiceCompletingTwoWayIsEnabled()) { TD.RoutingServiceCompletingTwoWay(this.messageRpc.EventTraceActivity); }
  175. return completeSelf;
  176. }
  177. internal static Message End(IAsyncResult result)
  178. {
  179. ProcessRequestAsyncResult<TContract> processRequest = AsyncResult.End<ProcessRequestAsyncResult<TContract>>(result);
  180. return processRequest.replyMessage;
  181. }
  182. bool HandleClientOperationFailure(Exception exception)
  183. {
  184. SendOperation sendOperation = this.messageRpc.Operations[0];
  185. if (TD.RoutingServiceTransmitFailedIsEnabled()) { TD.RoutingServiceTransmitFailed(this.messageRpc.EventTraceActivity, sendOperation.CurrentEndpoint.ToString(), exception); }
  186. if (!(exception is CommunicationException || exception is TimeoutException))
  187. {
  188. //We only move to backup for CommunicationExceptions and TimeoutExceptions
  189. return false;
  190. }
  191. if ((exception is CommunicationObjectAbortedException || exception is CommunicationObjectFaultedException) &&
  192. !this.service.ChannelExtension.HasSession)
  193. {
  194. // Messages on a non sessionful channel share outbound connections and can
  195. // fail due to other messages failing on the same channel
  196. if (messageRpc.Transaction == null && !this.abortedRetry)
  197. {
  198. //No session and non transactional, retry the message 1 time (before moving to backup)
  199. this.abortedRetry = true;
  200. return true;
  201. }
  202. }
  203. else if (exception is EndpointNotFoundException)
  204. {
  205. // The channel may not fault for this exception for bindings other than netTcpBinding
  206. // We abort the channel in that case. We proactively clean up so that we don't have to cleanup later
  207. SessionChannels sessionChannels = this.service.GetSessionChannels(this.messageRpc.Impersonating);
  208. if (sessionChannels != null)
  209. {
  210. sessionChannels.AbortChannel(sendOperation.CurrentEndpoint);
  211. }
  212. }
  213. else if (exception is MessageSecurityException)
  214. {
  215. // The service may have been stopped and restarted without the routing service knowledge.
  216. // When we try to use a cached channel to the service, the channel can fault due to this exception
  217. // The faulted channel gets cleaned up and we retry one more time only when service has backup
  218. // If there is no backup, we do not retry since we do not create a buffered message to prevent performance degradation
  219. if (!this.abortedRetry && (sendOperation.AlternateEndpointCount > 0))
  220. {
  221. this.abortedRetry = true;
  222. return true;
  223. }
  224. }
  225. else if (exception is ProtocolException)
  226. {
  227. // This exception may happen when the current cached channel was closed due to end service recycles.
  228. // We abort the channel in this case and clean it up from the session.
  229. // We will then retry the request one more time only. In retried request, it will create a new channel because the cached channel has been cleaned up.
  230. if (!this.abortedRetry)
  231. {
  232. SessionChannels sessionChannels = this.service.GetSessionChannels(this.messageRpc.Impersonating);
  233. if (sessionChannels != null)
  234. {
  235. this.abortedRetry = true;
  236. sessionChannels.AbortChannel(sendOperation.CurrentEndpoint);
  237. return true;
  238. }
  239. }
  240. }
  241. if (sendOperation.TryMoveToAlternate(exception))
  242. {
  243. if (TD.RoutingServiceMovedToBackupIsEnabled())
  244. {
  245. TD.RoutingServiceMovedToBackup(this.messageRpc.EventTraceActivity, messageRpc.UniqueID, "0", sendOperation.CurrentEndpoint.ToString());
  246. }
  247. return true;
  248. }
  249. return false;
  250. }
  251. }
  252. }