OperationInvokerHandler.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.ServiceModel.Channels;
  5. using System.ServiceModel;
  6. using System.Reflection;
  7. using System.Threading;
  8. namespace System.ServiceModel.Dispatcher
  9. {
  10. internal class OperationInvokerHandler : BaseRequestProcessorHandler
  11. {
  12. IDuplexChannel duplex;
  13. public OperationInvokerHandler (IChannel channel)
  14. {
  15. duplex = channel as IDuplexChannel;
  16. }
  17. protected override bool ProcessRequest (MessageProcessingContext mrc)
  18. {
  19. RequestContext rc = mrc.RequestContext;
  20. DispatchRuntime dispatchRuntime = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
  21. DispatchOperation operation = GetOperation (mrc.IncomingMessage, dispatchRuntime);
  22. mrc.Operation = operation;
  23. try {
  24. DoProcessRequest (mrc);
  25. if (!mrc.Operation.IsOneWay)
  26. Reply (mrc, true);
  27. } catch (TargetInvocationException ex) {
  28. mrc.ReplyMessage = BuildExceptionMessage (mrc, ex.InnerException,
  29. dispatchRuntime.ChannelDispatcher.IncludeExceptionDetailInFaults);
  30. if (!mrc.Operation.IsOneWay)
  31. Reply (mrc, true);
  32. ProcessCustomErrorHandlers (mrc, ex);
  33. }
  34. return false;
  35. }
  36. void DoProcessRequest (MessageProcessingContext mrc)
  37. {
  38. DispatchOperation operation = mrc.Operation;
  39. Message req = mrc.IncomingMessage;
  40. object instance = mrc.InstanceContext.GetServiceInstance(req);
  41. object [] parameters, outParams;
  42. BuildInvokeParams (mrc, out parameters);
  43. if (operation.Invoker.IsSynchronous) {
  44. object result = operation.Invoker.Invoke (instance, parameters, out outParams);
  45. HandleInvokeResult (mrc, outParams, result);
  46. } else {
  47. AsyncCallback callback = delegate {};
  48. // FIXME: the original code passed null callback
  49. // and null state, which is very wrong :(
  50. // It is still wrong to pass dummy callback, but
  51. // wrong code without obvious issues is better
  52. // than code with an obvious issue.
  53. var ar = operation.Invoker.InvokeBegin (instance, parameters, callback, null);
  54. object result = operation.Invoker.InvokeEnd (instance, out outParams, ar);
  55. HandleInvokeResult (mrc, outParams, result);
  56. }
  57. }
  58. void Reply (MessageProcessingContext mrc, bool useTimeout)
  59. {
  60. if (duplex != null)
  61. mrc.Reply (duplex, useTimeout);
  62. else
  63. mrc.Reply (useTimeout);
  64. }
  65. DispatchOperation GetOperation (Message input, DispatchRuntime dispatchRuntime)
  66. {
  67. if (dispatchRuntime.OperationSelector != null) {
  68. string name = dispatchRuntime.OperationSelector.SelectOperation (ref input);
  69. foreach (DispatchOperation d in dispatchRuntime.Operations)
  70. if (d.Name == name)
  71. return d;
  72. } else {
  73. string action = input.Headers.Action;
  74. foreach (DispatchOperation d in dispatchRuntime.Operations)
  75. if (d.Action == action)
  76. return d;
  77. }
  78. return dispatchRuntime.UnhandledDispatchOperation;
  79. }
  80. void HandleInvokeResult (MessageProcessingContext mrc, object [] outputs, object result)
  81. {
  82. DispatchOperation operation = mrc.Operation;
  83. mrc.EventsHandler.AfterInvoke (operation);
  84. if (operation.IsOneWay)
  85. return;
  86. Message res = null;
  87. if (operation.SerializeReply)
  88. res = operation.Formatter.SerializeReply (
  89. mrc.OperationContext.IncomingMessageVersion, outputs, result);
  90. else
  91. res = (Message) result;
  92. res.Headers.CopyHeadersFrom (mrc.OperationContext.OutgoingMessageHeaders);
  93. res.Properties.CopyProperties (mrc.OperationContext.OutgoingMessageProperties);
  94. if (res.Headers.RelatesTo == null)
  95. res.Headers.RelatesTo = mrc.OperationContext.IncomingMessageHeaders.MessageId;
  96. mrc.ReplyMessage = res;
  97. }
  98. void BuildInvokeParams (MessageProcessingContext mrc, out object [] parameters)
  99. {
  100. DispatchOperation operation = mrc.Operation;
  101. EnsureValid (operation);
  102. if (operation.DeserializeRequest) {
  103. parameters = operation.Invoker.AllocateInputs ();
  104. operation.Formatter.DeserializeRequest (mrc.IncomingMessage, parameters);
  105. } else
  106. parameters = new object [] { mrc.IncomingMessage };
  107. mrc.EventsHandler.BeforeInvoke (operation);
  108. }
  109. void ProcessCustomErrorHandlers (MessageProcessingContext mrc, Exception ex)
  110. {
  111. var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
  112. bool shutdown = false;
  113. if (dr.ChannelDispatcher != null) // non-callback channel
  114. foreach (var eh in dr.ChannelDispatcher.ErrorHandlers)
  115. shutdown |= eh.HandleError (ex);
  116. if (shutdown)
  117. ProcessSessionErrorShutdown (mrc);
  118. }
  119. void ProcessSessionErrorShutdown (MessageProcessingContext mrc)
  120. {
  121. var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
  122. var session = mrc.OperationContext.Channel.InputSession;
  123. var dcc = mrc.OperationContext.Channel as IDuplexContextChannel;
  124. if (session == null || dcc == null)
  125. return;
  126. foreach (var h in dr.InputSessionShutdownHandlers)
  127. h.ChannelFaulted (dcc);
  128. }
  129. Message BuildExceptionMessage (MessageProcessingContext mrc, Exception ex, bool includeDetailsInFault)
  130. {
  131. var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
  132. var cd = dr.ChannelDispatcher;
  133. Message msg = null;
  134. if (cd != null) // non-callback channel
  135. foreach (var eh in cd.ErrorHandlers)
  136. eh.ProvideFault (ex, cd.MessageVersion, ref msg);
  137. if (msg != null)
  138. return msg;
  139. var req = mrc.IncomingMessage;
  140. var fe = ex as FaultException;
  141. if (fe != null && fe.GetType ().IsGenericType) {
  142. var t = fe.GetType ().GetGenericArguments () [0];
  143. foreach (var fci in mrc.Operation.FaultContractInfos)
  144. if (fci.Detail == t)
  145. return Message.CreateMessage (req.Version, fe.CreateMessageFault (), fci.Action);
  146. }
  147. // FIXME: set correct name
  148. FaultCode fc = new FaultCode (
  149. "InternalServiceFault",
  150. req.Version.Addressing.Namespace);
  151. if (includeDetailsInFault) {
  152. return Message.CreateMessage (req.Version, fc, ex.Message, new ExceptionDetail (ex), req.Headers.Action);
  153. }
  154. string faultString =
  155. @"The server was unable to process the request due to an internal error. The server may be able to return exception details (it depends on the server settings).";
  156. return Message.CreateMessage (req.Version, fc, faultString, req.Headers.Action);
  157. }
  158. void EnsureValid (DispatchOperation operation)
  159. {
  160. if (operation.Invoker == null)
  161. throw new InvalidOperationException (String.Format ("DispatchOperation '{0}' for contract '{1}' requires Invoker.", operation.Name, operation.Parent.EndpointDispatcher.ContractName));
  162. if ((operation.DeserializeRequest || operation.SerializeReply) && operation.Formatter == null)
  163. throw new InvalidOperationException ("The DispatchOperation '" + operation.Name + "' requires Formatter, since DeserializeRequest and SerializeReply are not both false.");
  164. }
  165. }
  166. }