1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 
5 namespace System.ServiceModel.Dispatcher
6 {
7     using System;
8     using System.Collections.ObjectModel;
9     using System.Reflection;
10     using System.Runtime;
11     using System.Runtime.Remoting.Messaging;
12     using System.Security;
13     using System.ServiceModel;
14     using System.ServiceModel.Channels;
15     using System.ServiceModel.Description;
16     using System.ServiceModel.Diagnostics;
17     using System.ServiceModel.Diagnostics.Application;
18 
19     class ProxyOperationRuntime
20     {
21         static internal readonly ParameterInfo[] NoParams = new ParameterInfo[0];
22         static internal readonly object[] EmptyArray = new object[0];
23 
24         readonly IClientMessageFormatter formatter;
25         readonly bool isInitiating;
26         readonly bool isOneWay;
27         readonly bool isTerminating;
28         readonly bool isSessionOpenNotificationEnabled;
29         readonly string name;
30         readonly IParameterInspector[] parameterInspectors;
31         readonly IClientFaultFormatter faultFormatter;
32         readonly ImmutableClientRuntime parent;
33         bool serializeRequest;
34         bool deserializeReply;
35         string action;
36         string replyAction;
37 
38         MethodInfo beginMethod;
39         MethodInfo syncMethod;
40         MethodInfo taskMethod;
41         ParameterInfo[] inParams;
42         ParameterInfo[] outParams;
43         ParameterInfo[] endOutParams;
44         ParameterInfo returnParam;
45 
ProxyOperationRuntime(ClientOperation operation, ImmutableClientRuntime parent)46         internal ProxyOperationRuntime(ClientOperation operation, ImmutableClientRuntime parent)
47         {
48             if (operation == null)
49                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operation");
50             if (parent == null)
51                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent");
52 
53             this.parent = parent;
54             this.formatter = operation.Formatter;
55             this.isInitiating = operation.IsInitiating;
56             this.isOneWay = operation.IsOneWay;
57             this.isTerminating = operation.IsTerminating;
58             this.isSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled;
59             this.name = operation.Name;
60             this.parameterInspectors = EmptyArray<IParameterInspector>.ToArray(operation.ParameterInspectors);
61             this.faultFormatter = operation.FaultFormatter;
62             this.serializeRequest = operation.SerializeRequest;
63             this.deserializeReply = operation.DeserializeReply;
64             this.action = operation.Action;
65             this.replyAction = operation.ReplyAction;
66             this.beginMethod = operation.BeginMethod;
67             this.syncMethod = operation.SyncMethod;
68             this.taskMethod = operation.TaskMethod;
69             this.TaskTResult = operation.TaskTResult;
70 
71             if (this.beginMethod != null)
72             {
73                 this.inParams = ServiceReflector.GetInputParameters(this.beginMethod, true);
74                 if (this.syncMethod != null)
75                 {
76                     this.outParams = ServiceReflector.GetOutputParameters(this.syncMethod, false);
77                 }
78                 else
79                 {
80                     this.outParams = NoParams;
81                 }
82                 this.endOutParams = ServiceReflector.GetOutputParameters(operation.EndMethod, true);
83                 this.returnParam = operation.EndMethod.ReturnParameter;
84             }
85             else if (this.syncMethod != null)
86             {
87                 this.inParams = ServiceReflector.GetInputParameters(this.syncMethod, false);
88                 this.outParams = ServiceReflector.GetOutputParameters(this.syncMethod, false);
89                 this.returnParam = this.syncMethod.ReturnParameter;
90             }
91 
92             if (this.formatter == null && (serializeRequest || deserializeReply))
93             {
94                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ClientRuntimeRequiresFormatter0, this.name)));
95             }
96         }
97 
98         internal string Action
99         {
100             get { return this.action; }
101         }
102 
103         internal IClientFaultFormatter FaultFormatter
104         {
105             get { return this.faultFormatter; }
106         }
107 
108         internal bool IsInitiating
109         {
110             get { return this.isInitiating; }
111         }
112 
113         internal bool IsOneWay
114         {
115             get { return this.isOneWay; }
116         }
117 
118         internal bool IsTerminating
119         {
120             get { return this.isTerminating; }
121         }
122 
123         internal bool IsSessionOpenNotificationEnabled
124         {
125             get { return this.isSessionOpenNotificationEnabled; }
126         }
127 
128         internal string Name
129         {
130             get { return this.name; }
131         }
132 
133         internal ImmutableClientRuntime Parent
134         {
135             get { return this.parent; }
136         }
137 
138         internal string ReplyAction
139         {
140             get { return this.replyAction; }
141         }
142 
143         internal bool DeserializeReply
144         {
145             get { return this.deserializeReply; }
146         }
147 
148         internal bool SerializeRequest
149         {
150             get { return this.serializeRequest; }
151         }
152 
153         internal Type TaskTResult
154         {
155             get;
156             set;
157         }
158 
AfterReply(ref ProxyRpc rpc)159         internal void AfterReply(ref ProxyRpc rpc)
160         {
161             if (!this.isOneWay)
162             {
163                 Message reply = rpc.Reply;
164 
165                 if (this.deserializeReply)
166                 {
167                     if (TD.ClientFormatterDeserializeReplyStartIsEnabled())
168                     {
169                         TD.ClientFormatterDeserializeReplyStart(rpc.EventTraceActivity);
170                     }
171 
172                     rpc.ReturnValue = this.formatter.DeserializeReply(reply, rpc.OutputParameters);
173 
174                     if (TD.ClientFormatterDeserializeReplyStopIsEnabled())
175                     {
176                         TD.ClientFormatterDeserializeReplyStop(rpc.EventTraceActivity);
177                     }
178 
179                 }
180                 else
181                 {
182                     rpc.ReturnValue = reply;
183                 }
184 
185                 int offset = this.parent.ParameterInspectorCorrelationOffset;
186                 try
187                 {
188                     for (int i = parameterInspectors.Length - 1; i >= 0; i--)
189                     {
190                         this.parameterInspectors[i].AfterCall(this.name,
191                                                               rpc.OutputParameters,
192                                                               rpc.ReturnValue,
193                                                               rpc.Correlation[offset + i]);
194                         if (TD.ClientParameterInspectorAfterCallInvokedIsEnabled())
195                         {
196                             TD.ClientParameterInspectorAfterCallInvoked(rpc.EventTraceActivity, this.parameterInspectors[i].GetType().FullName);
197                         }
198                     }
199                 }
200                 catch (Exception e)
201                 {
202                     if (Fx.IsFatal(e))
203                     {
204                         throw;
205                     }
206                     if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e))
207                     {
208                         throw;
209                     }
210                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
211                 }
212 
213                 if (parent.ValidateMustUnderstand)
214                 {
215                     Collection<MessageHeaderInfo> headersNotUnderstood = reply.Headers.GetHeadersNotUnderstood();
216                     if (headersNotUnderstood != null && headersNotUnderstood.Count > 0)
217                     {
218                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.SFxHeaderNotUnderstood, headersNotUnderstood[0].Name, headersNotUnderstood[0].Namespace)));
219                     }
220                 }
221             }
222         }
223 
BeforeRequest(ref ProxyRpc rpc)224         internal void BeforeRequest(ref ProxyRpc rpc)
225         {
226             int offset = this.parent.ParameterInspectorCorrelationOffset;
227             try
228             {
229                 for (int i = 0; i < parameterInspectors.Length; i++)
230                 {
231                     rpc.Correlation[offset + i] = this.parameterInspectors[i].BeforeCall(this.name, rpc.InputParameters);
232                     if (TD.ClientParameterInspectorBeforeCallInvokedIsEnabled())
233                     {
234                         TD.ClientParameterInspectorBeforeCallInvoked(rpc.EventTraceActivity, this.parameterInspectors[i].GetType().FullName);
235                     }
236                 }
237             }
238             catch (Exception e)
239             {
240                 if (Fx.IsFatal(e))
241                 {
242                     throw;
243                 }
244                 if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e))
245                 {
246                     throw;
247                 }
248                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
249             }
250 
251             if (this.serializeRequest)
252             {
253                 if (TD.ClientFormatterSerializeRequestStartIsEnabled())
254                 {
255                     TD.ClientFormatterSerializeRequestStart(rpc.EventTraceActivity);
256                 }
257 
258                 rpc.Request = this.formatter.SerializeRequest(rpc.MessageVersion, rpc.InputParameters);
259 
260 
261 
262                 if (TD.ClientFormatterSerializeRequestStopIsEnabled())
263                 {
264                     TD.ClientFormatterSerializeRequestStop(rpc.EventTraceActivity);
265                 }
266             }
267             else
268             {
269                 if (rpc.InputParameters[0] == null)
270                 {
271                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxProxyRuntimeMessageCannotBeNull, this.name)));
272                 }
273 
274                 rpc.Request = (Message)rpc.InputParameters[0];
275                 if (!IsValidAction(rpc.Request, Action))
276                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidRequestAction, this.Name, rpc.Request.Headers.Action ?? "{NULL}", this.Action)));
277             }
278         }
279 
GetDefaultParameterValue(Type parameterType)280         internal static object GetDefaultParameterValue(Type parameterType)
281         {
282             return (parameterType.IsValueType && parameterType != typeof(void)) ? Activator.CreateInstance(parameterType) : null;
283         }
284 
285         [SecurityCritical]
IsSyncCall(IMethodCallMessage methodCall)286         internal bool IsSyncCall(IMethodCallMessage methodCall)
287         {
288             if (this.syncMethod == null)
289             {
290                 return false;
291             }
292 
293             return (methodCall.MethodBase.MethodHandle == this.syncMethod.MethodHandle);
294         }
295 
296         [SecurityCritical]
IsBeginCall(IMethodCallMessage methodCall)297         internal bool IsBeginCall(IMethodCallMessage methodCall)
298         {
299             if (this.beginMethod == null)
300             {
301                 return false;
302             }
303 
304             return (methodCall.MethodBase.MethodHandle == this.beginMethod.MethodHandle);
305         }
306 
307         [SecurityCritical]
IsTaskCall(IMethodCallMessage methodCall)308         internal bool IsTaskCall(IMethodCallMessage methodCall)
309         {
310             if (this.taskMethod == null)
311             {
312                 return false;
313             }
314 
315             return (methodCall.MethodBase.MethodHandle == this.taskMethod.MethodHandle);
316         }
317 
318         [SecurityCritical]
MapSyncInputs(IMethodCallMessage methodCall, out object[] outs)319         internal object[] MapSyncInputs(IMethodCallMessage methodCall, out object[] outs)
320         {
321             if (this.outParams.Length == 0)
322             {
323                 outs = EmptyArray;
324             }
325             else
326             {
327                 outs = new object[this.outParams.Length];
328             }
329             if (this.inParams.Length == 0)
330                 return EmptyArray;
331             return methodCall.InArgs;
332         }
333 
334         [SecurityCritical]
MapAsyncBeginInputs(IMethodCallMessage methodCall, out AsyncCallback callback, out object asyncState)335         internal object[] MapAsyncBeginInputs(IMethodCallMessage methodCall, out AsyncCallback callback, out object asyncState)
336         {
337             object[] ins;
338             if (this.inParams.Length == 0)
339             {
340                 ins = EmptyArray;
341             }
342             else
343             {
344                 ins = new object[this.inParams.Length];
345             }
346 
347             object[] args = methodCall.Args;
348             for (int i = 0; i < ins.Length; i++)
349             {
350                 ins[i] = args[this.inParams[i].Position];
351             }
352 
353             callback = args[methodCall.ArgCount - 2] as AsyncCallback;
354             asyncState = args[methodCall.ArgCount - 1];
355             return ins;
356         }
357 
358         [SecurityCritical]
MapAsyncEndInputs(IMethodCallMessage methodCall, out IAsyncResult result, out object[] outs)359         internal void MapAsyncEndInputs(IMethodCallMessage methodCall, out IAsyncResult result, out object[] outs)
360         {
361             outs = new object[this.endOutParams.Length];
362             result = methodCall.Args[methodCall.ArgCount - 1] as IAsyncResult;
363         }
364 
365         [SecurityCritical]
MapSyncOutputs(IMethodCallMessage methodCall, object[] outs, ref object ret)366         internal object[] MapSyncOutputs(IMethodCallMessage methodCall, object[] outs, ref object ret)
367         {
368             return MapOutputs(this.outParams, methodCall, outs, ref ret);
369         }
370 
371         [SecurityCritical]
MapAsyncOutputs(IMethodCallMessage methodCall, object[] outs, ref object ret)372         internal object[] MapAsyncOutputs(IMethodCallMessage methodCall, object[] outs, ref object ret)
373         {
374             return MapOutputs(this.endOutParams, methodCall, outs, ref ret);
375         }
376 
377         [SecurityCritical]
MapOutputs(ParameterInfo[] parameters, IMethodCallMessage methodCall, object[] outs, ref object ret)378         object[] MapOutputs(ParameterInfo[] parameters, IMethodCallMessage methodCall, object[] outs, ref object ret)
379         {
380             if (ret == null && this.returnParam != null)
381             {
382                 ret = GetDefaultParameterValue(TypeLoader.GetParameterType(this.returnParam));
383             }
384 
385             if (parameters.Length == 0)
386             {
387                 return null;
388             }
389 
390             object[] args = methodCall.Args;
391             for (int i = 0; i < parameters.Length; i++)
392             {
393                 if (outs[i] == null)
394                 {
395                     // the RealProxy infrastructure requires a default value for value types
396                     args[parameters[i].Position] = GetDefaultParameterValue(TypeLoader.GetParameterType(parameters[i]));
397                 }
398                 else
399                 {
400                     args[parameters[i].Position] = outs[i];
401                 }
402             }
403 
404             return args;
405         }
406 
IsValidAction(Message message, string action)407         static internal bool IsValidAction(Message message, string action)
408         {
409             if (message == null)
410             {
411                 return false;
412             }
413 
414             if (message.IsFault)
415             {
416                 return true;
417             }
418 
419             if (action == MessageHeaders.WildcardAction)
420             {
421                 return true;
422             }
423 
424             return (String.CompareOrdinal(message.Headers.Action, action) == 0);
425         }
426     }
427 }
428