2 using System.Collections.Generic;
4 using System.ServiceModel.Channels;
5 using System.ServiceModel;
6 using System.Reflection;
7 using System.Threading;
9 namespace System.ServiceModel.Dispatcher
11 internal class OperationInvokerHandler : BaseRequestProcessorHandler
13 IDuplexChannel duplex;
15 public OperationInvokerHandler (IChannel channel)
17 duplex = channel as IDuplexChannel;
20 protected override bool ProcessRequest (MessageProcessingContext mrc)
22 RequestContext rc = mrc.RequestContext;
23 DispatchRuntime dispatchRuntime = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
24 DispatchOperation operation = GetOperation (mrc.IncomingMessage, dispatchRuntime);
25 mrc.Operation = operation;
27 DoProcessRequest (mrc);
28 if (!mrc.Operation.IsOneWay)
30 } catch (TargetInvocationException ex) {
31 mrc.ReplyMessage = BuildExceptionMessage (mrc, ex.InnerException,
32 dispatchRuntime.ChannelDispatcher.IncludeExceptionDetailInFaults);
33 if (!mrc.Operation.IsOneWay)
35 ProcessCustomErrorHandlers (mrc, ex);
40 void DoProcessRequest (MessageProcessingContext mrc)
42 DispatchOperation operation = mrc.Operation;
43 Message req = mrc.IncomingMessage;
44 object instance = mrc.InstanceContext.GetServiceInstance(req);
45 object [] parameters, outParams;
46 BuildInvokeParams (mrc, out parameters);
48 if (operation.Invoker.IsSynchronous) {
49 object result = operation.Invoker.Invoke (instance, parameters, out outParams);
50 HandleInvokeResult (mrc, outParams, result);
52 AsyncCallback callback = delegate {};
53 // FIXME: the original code passed null callback
54 // and null state, which is very wrong :(
55 // It is still wrong to pass dummy callback, but
56 // wrong code without obvious issues is better
57 // than code with an obvious issue.
58 var ar = operation.Invoker.InvokeBegin (instance, parameters, callback, null);
59 object result = operation.Invoker.InvokeEnd (instance, out outParams, ar);
60 HandleInvokeResult (mrc, outParams, result);
64 void Reply (MessageProcessingContext mrc, bool useTimeout)
67 mrc.Reply (duplex, useTimeout);
69 mrc.Reply (useTimeout);
72 DispatchOperation GetOperation (Message input, DispatchRuntime dispatchRuntime)
74 if (dispatchRuntime.OperationSelector != null) {
75 string name = dispatchRuntime.OperationSelector.SelectOperation (ref input);
76 foreach (DispatchOperation d in dispatchRuntime.Operations)
80 string action = input.Headers.Action;
81 foreach (DispatchOperation d in dispatchRuntime.Operations)
82 if (d.Action == action)
85 return dispatchRuntime.UnhandledDispatchOperation;
88 void HandleInvokeResult (MessageProcessingContext mrc, object [] outputs, object result)
90 DispatchOperation operation = mrc.Operation;
91 mrc.EventsHandler.AfterInvoke (operation);
93 if (operation.IsOneWay)
97 if (operation.SerializeReply)
98 res = operation.Formatter.SerializeReply (
99 mrc.OperationContext.IncomingMessageVersion, outputs, result);
101 res = (Message) result;
102 res.Headers.CopyHeadersFrom (mrc.OperationContext.OutgoingMessageHeaders);
103 res.Properties.CopyProperties (mrc.OperationContext.OutgoingMessageProperties);
104 if (res.Headers.RelatesTo == null)
105 res.Headers.RelatesTo = mrc.OperationContext.IncomingMessageHeaders.MessageId;
106 mrc.ReplyMessage = res;
109 void BuildInvokeParams (MessageProcessingContext mrc, out object [] parameters)
111 DispatchOperation operation = mrc.Operation;
112 EnsureValid (operation);
114 if (operation.DeserializeRequest) {
115 parameters = operation.Invoker.AllocateInputs ();
116 operation.Formatter.DeserializeRequest (mrc.IncomingMessage, parameters);
118 parameters = new object [] { mrc.IncomingMessage };
120 mrc.EventsHandler.BeforeInvoke (operation);
123 void ProcessCustomErrorHandlers (MessageProcessingContext mrc, Exception ex)
125 var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
126 bool shutdown = false;
127 if (dr.ChannelDispatcher != null) // non-callback channel
128 foreach (var eh in dr.ChannelDispatcher.ErrorHandlers)
129 shutdown |= eh.HandleError (ex);
131 ProcessSessionErrorShutdown (mrc);
134 void ProcessSessionErrorShutdown (MessageProcessingContext mrc)
136 var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
137 var session = mrc.OperationContext.Channel.InputSession;
138 var dcc = mrc.OperationContext.Channel as IDuplexContextChannel;
139 if (session == null || dcc == null)
141 foreach (var h in dr.InputSessionShutdownHandlers)
142 h.ChannelFaulted (dcc);
145 bool IsGenericFaultException (Type type, out Type arg)
147 for (; type != null; type = type.BaseType) {
148 if (!type.IsGenericType)
150 var tdef = type.GetGenericTypeDefinition ();
151 if (!tdef.Equals (typeof (FaultException<>)))
153 arg = type.GetGenericArguments () [0];
161 Message BuildExceptionMessage (MessageProcessingContext mrc, Exception ex, bool includeDetailsInFault)
163 var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
164 var cd = dr.ChannelDispatcher;
166 if (cd != null) // non-callback channel
167 foreach (var eh in cd.ErrorHandlers)
168 eh.ProvideFault (ex, cd.MessageVersion, ref msg);
172 var req = mrc.IncomingMessage;
175 var fe = ex as FaultException;
176 if (fe != null && IsGenericFaultException (fe.GetType (), out gft)) {
177 foreach (var fci in mrc.Operation.FaultContractInfos) {
178 if (fci.Detail == gft)
179 return Message.CreateMessage (req.Version, fe.CreateMessageFault (), fci.Action);
183 // FIXME: set correct name
184 FaultCode fc = new FaultCode (
185 "InternalServiceFault",
186 req.Version.Addressing.Namespace);
189 if (includeDetailsInFault) {
190 return Message.CreateMessage (req.Version, fc, ex.Message, new ExceptionDetail (ex), req.Headers.Action);
194 @"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).";
195 return Message.CreateMessage (req.Version, fc, faultString, req.Headers.Action);
198 void EnsureValid (DispatchOperation operation)
200 if (operation.Invoker == null)
201 throw new InvalidOperationException (String.Format ("DispatchOperation '{0}' for contract '{1}' requires Invoker.", operation.Name, operation.Parent.EndpointDispatcher.ContractName));
202 if ((operation.DeserializeRequest || operation.SerializeReply) && operation.Formatter == null)
203 throw new InvalidOperationException ("The DispatchOperation '" + operation.Name + "' requires Formatter, since DeserializeRequest and SerializeReply are not both false.");