1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 namespace System.ServiceModel.Channels
7 using System.Collections;
8 using System.Collections.Specialized;
9 using System.Globalization;
10 using System.Reflection;
12 using System.Runtime.Diagnostics;
13 using System.Runtime.Remoting;
14 using System.Runtime.Remoting.Messaging;
15 using System.Runtime.Remoting.Proxies;
16 using System.Security;
17 using System.ServiceModel.Description;
18 using System.ServiceModel.Diagnostics;
19 using System.ServiceModel.Dispatcher;
20 using System.Threading.Tasks;
22 [Fx.Tag.SecurityNote(Critical = "Accesses a variety of LinkDemanded classes/methods (especially RealProxy)." +
23 "Caller should protect access to the ServiceChannelProxy instance after construction.")]
24 #pragma warning disable 618 // have not moved to the v4 security model yet
25 [SecurityCritical(SecurityCriticalScope.Everything)]
26 #pragma warning restore 618
27 sealed class ServiceChannelProxy : RealProxy, IRemotingTypeInfo
29 const String activityIdSlotName = "E2ETrace.ActivityID";
32 ServiceChannel serviceChannel;
33 MbrObject objectWrapper;
34 ImmutableClientRuntime proxyRuntime;
35 MethodDataCache methodDataCache;
37 internal ServiceChannelProxy(Type interfaceType, Type proxiedType, MessageDirection direction, ServiceChannel serviceChannel)
40 if (!MessageDirectionHelper.IsDefined(direction))
41 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("direction"));
43 this.interfaceType = interfaceType;
44 this.proxiedType = proxiedType;
45 this.serviceChannel = serviceChannel;
46 this.proxyRuntime = serviceChannel.ClientRuntime.GetRuntime();
47 this.methodDataCache = new MethodDataCache();
49 this.objectWrapper = new MbrObject(this, proxiedType);
52 //Workaround is to set the activityid in remoting call's LogicalCallContext
53 static LogicalCallContext SetActivityIdInLogicalCallContext(LogicalCallContext logicalCallContext)
55 if (TraceUtility.ActivityTracing)
57 logicalCallContext.SetData(activityIdSlotName, DiagnosticTraceBase.ActivityId);
60 return logicalCallContext;
63 IMethodReturnMessage CreateReturnMessage(object ret, object[] returnArgs, IMethodCallMessage methodCall)
65 if (returnArgs != null)
67 return CreateReturnMessage(ret, returnArgs, returnArgs.Length, SetActivityIdInLogicalCallContext(methodCall.LogicalCallContext), methodCall);
71 return new SingleReturnMessage(ret, methodCall);
75 IMethodReturnMessage CreateReturnMessage(object ret, object[] outArgs, int outArgsCount, LogicalCallContext callCtx, IMethodCallMessage mcm)
77 return new ReturnMessage(ret, outArgs, outArgsCount, callCtx, mcm);
80 IMethodReturnMessage CreateReturnMessage(Exception e, IMethodCallMessage mcm)
82 return new ReturnMessage(e, mcm);
85 MethodData GetMethodData(IMethodCallMessage methodCall)
87 MethodBase method = methodCall.MethodBase;
89 MethodData methodData;
90 if (methodDataCache.TryGetMethodData(method, out methodData))
95 bool canCacheMessageData;
97 Type declaringType = method.DeclaringType;
98 if (declaringType == typeof(object))
100 MethodType methodType;
101 if (methodCall.MethodBase == typeof(object).GetMethod("GetType"))
103 methodType = MethodType.GetType;
107 methodType = MethodType.Object;
109 canCacheMessageData = true;
110 methodData = new MethodData(method, methodType);
112 else if (declaringType.IsInstanceOfType(this.serviceChannel))
114 canCacheMessageData = true;
115 methodData = new MethodData(method, MethodType.Channel);
119 ProxyOperationRuntime operation = this.proxyRuntime.GetOperation(method, methodCall.Args, out canCacheMessageData);
121 if (operation == null)
123 if (this.serviceChannel.Factory != null)
124 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SFxMethodNotSupported1, method.Name)));
126 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SFxMethodNotSupportedOnCallback1, method.Name)));
129 MethodType methodType;
131 if (operation.IsTaskCall(methodCall))
133 methodType = MethodType.TaskService;
135 else if (operation.IsSyncCall(methodCall))
137 methodType = MethodType.Service;
139 else if (operation.IsBeginCall(methodCall))
141 methodType = MethodType.BeginService;
145 methodType = MethodType.EndService;
148 methodData = new MethodData(method, methodType, operation);
151 if (canCacheMessageData)
153 methodDataCache.SetMethodData(methodData);
159 internal ServiceChannel GetServiceChannel()
161 return this.serviceChannel;
164 public override IMessage Invoke(IMessage message)
168 IMethodCallMessage methodCall = message as IMethodCallMessage;
170 if (methodCall == null)
171 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxExpectedIMethodCallMessage)));
173 MethodData methodData = GetMethodData(methodCall);
175 switch (methodData.MethodType)
177 case MethodType.Service:
178 return InvokeService(methodCall, methodData.Operation);
179 case MethodType.BeginService:
180 return InvokeBeginService(methodCall, methodData.Operation);
181 case MethodType.EndService:
182 return InvokeEndService(methodCall, methodData.Operation);
183 case MethodType.TaskService:
184 return InvokeTaskService(methodCall, methodData.Operation);
185 case MethodType.Channel:
186 return InvokeChannel(methodCall);
187 case MethodType.GetType:
188 return InvokeGetType(methodCall);
189 case MethodType.Object:
190 return InvokeObject(methodCall);
192 Fx.Assert("Invalid proxy method type");
193 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid proxy method type")));
196 #pragma warning suppress 56500 // covered by FxCOP
203 return CreateReturnMessage(e, message as IMethodCallMessage);
207 static class TaskCreator
209 static readonly Func<ServiceChannel, ProxyOperationRuntime, object[], AsyncCallback, object, IAsyncResult> beginCallDelegate = ServiceChannel.BeginCall;
210 static readonly Hashtable createGenericTaskDelegateCache = new Hashtable(); // using Hashtable because it allows for lock-free reads
211 static readonly MethodInfo createGenericTaskMI = typeof(TaskCreator).GetMethod("CreateGenericTask", new Type[] { typeof(ServiceChannel), typeof(ProxyOperationRuntime), typeof(object[]) });
213 static Func<ServiceChannel, ProxyOperationRuntime, object[], Task> GetOrCreateTaskDelegate(Type taskResultType)
215 Func<ServiceChannel, ProxyOperationRuntime, object[], Task> createTaskDelegate = createGenericTaskDelegateCache[taskResultType] as Func<ServiceChannel, ProxyOperationRuntime, object[], Task>;
216 if (createTaskDelegate != null)
218 return createTaskDelegate;
221 lock (createGenericTaskDelegateCache)
223 createTaskDelegate = createGenericTaskDelegateCache[taskResultType] as Func<ServiceChannel, ProxyOperationRuntime, object[], Task>;
224 if (createTaskDelegate != null)
226 return createTaskDelegate;
229 MethodInfo methodInfo = createGenericTaskMI.MakeGenericMethod(taskResultType);
230 createTaskDelegate = Delegate.CreateDelegate(typeof(Func<ServiceChannel, ProxyOperationRuntime, object[], Task>), methodInfo) as Func<ServiceChannel, ProxyOperationRuntime, object[], Task>;
231 createGenericTaskDelegateCache[taskResultType] = createTaskDelegate;
234 return createTaskDelegate;
237 public static Task CreateTask(ServiceChannel channel, IMethodCallMessage methodCall, ProxyOperationRuntime operation)
239 if (operation.TaskTResult == ServiceReflector.VoidType)
241 return TaskCreator.CreateTask(channel, operation, methodCall.InArgs);
243 return TaskCreator.CreateGenericTask(channel, operation, methodCall.InArgs);
246 static Task CreateGenericTask(ServiceChannel channel, ProxyOperationRuntime operation, object[] inputParameters)
248 Func<ServiceChannel, ProxyOperationRuntime, object[], Task> createTaskDelegate = GetOrCreateTaskDelegate(operation.TaskTResult);
249 return createTaskDelegate(channel, operation, inputParameters);
252 static Task CreateTask(ServiceChannel channel, ProxyOperationRuntime operation, object[] inputParameters)
254 Action<IAsyncResult> endCallDelegate = (asyncResult) =>
256 Fx.Assert(asyncResult != null, "'asyncResult' MUST NOT be NULL.");
257 OperationContext originalOperationContext = OperationContext.Current;
258 OperationContext.Current = asyncResult.AsyncState as OperationContext;
261 channel.EndCall(operation.Action, ProxyOperationRuntime.EmptyArray, asyncResult);
265 OperationContext.Current = originalOperationContext;
269 return Task.Factory.FromAsync(beginCallDelegate, endCallDelegate, channel, operation, inputParameters, OperationContext.Current);
272 public static Task<T> CreateGenericTask<T>(ServiceChannel channel, ProxyOperationRuntime operation, object[] inputParameters)
274 Func<IAsyncResult, T> endCallDelegate = (asyncResult) =>
276 OperationContext originalOperationContext = OperationContext.Current;
277 OperationContext.Current = asyncResult.AsyncState as OperationContext;
280 return (T)channel.EndCall(operation.Action, ProxyOperationRuntime.EmptyArray, asyncResult);
284 OperationContext.Current = originalOperationContext;
288 return Task<T>.Factory.FromAsync<ServiceChannel, ProxyOperationRuntime, object[]>(beginCallDelegate, endCallDelegate, channel, operation, inputParameters, OperationContext.Current);
292 IMessage InvokeTaskService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
294 Task task = TaskCreator.CreateTask(this.serviceChannel, methodCall, operation);
295 return CreateReturnMessage(task, null, methodCall);
298 IMethodReturnMessage InvokeChannel(IMethodCallMessage methodCall)
300 string activityName = null;
301 ActivityType activityType = ActivityType.Unknown;
302 if (DiagnosticUtility.ShouldUseActivity)
304 if (ServiceModelActivity.Current == null ||
305 ServiceModelActivity.Current.ActivityType != ActivityType.Close)
307 MethodData methodData = this.GetMethodData(methodCall);
308 if (methodData.MethodBase.DeclaringType == typeof(System.ServiceModel.ICommunicationObject)
309 && methodData.MethodBase.Name.Equals("Close", StringComparison.Ordinal))
311 activityName = SR.GetString(SR.ActivityClose, this.serviceChannel.GetType().FullName);
312 activityType = ActivityType.Close;
317 using (ServiceModelActivity activity = string.IsNullOrEmpty(activityName) ? null : ServiceModelActivity.CreateBoundedActivity())
319 if (DiagnosticUtility.ShouldUseActivity)
321 ServiceModelActivity.Start(activity, activityName, activityType);
323 return ExecuteMessage(this.serviceChannel, methodCall);
327 IMethodReturnMessage InvokeGetType(IMethodCallMessage methodCall)
329 return CreateReturnMessage(proxiedType, null, 0, SetActivityIdInLogicalCallContext(methodCall.LogicalCallContext), methodCall);
332 IMethodReturnMessage InvokeObject(IMethodCallMessage methodCall)
334 return RemotingServices.ExecuteMessage(this.objectWrapper, methodCall);
337 IMethodReturnMessage InvokeBeginService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
339 AsyncCallback callback;
341 object[] ins = operation.MapAsyncBeginInputs(methodCall, out callback, out asyncState);
342 object ret = this.serviceChannel.BeginCall(operation.Action, operation.IsOneWay, operation, ins, callback, asyncState);
343 return CreateReturnMessage(ret, null, methodCall);
346 IMethodReturnMessage InvokeEndService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
350 operation.MapAsyncEndInputs(methodCall, out result, out outs);
351 object ret = this.serviceChannel.EndCall(operation.Action, outs, result);
352 object[] returnArgs = operation.MapAsyncOutputs(methodCall, outs, ref ret);
353 return CreateReturnMessage(ret, returnArgs, methodCall);
356 IMethodReturnMessage InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
359 object[] ins = operation.MapSyncInputs(methodCall, out outs);
360 object ret = this.serviceChannel.Call(operation.Action, operation.IsOneWay, operation, ins, outs);
361 object[] returnArgs = operation.MapSyncOutputs(methodCall, outs, ref ret);
362 return CreateReturnMessage(ret, returnArgs, methodCall);
365 IMethodReturnMessage ExecuteMessage(object target, IMethodCallMessage methodCall)
367 MethodBase targetMethod = methodCall.MethodBase;
369 object[] args = methodCall.Args;
370 object returnValue = null;
373 returnValue = targetMethod.Invoke(target, args);
375 catch (TargetInvocationException e)
377 return CreateReturnMessage(e.InnerException, methodCall);
380 return CreateReturnMessage(returnValue,
387 bool IRemotingTypeInfo.CanCastTo(Type toType, object o)
389 return toType.IsAssignableFrom(proxiedType) || serviceChannel.CanCastTo(toType);
392 string IRemotingTypeInfo.TypeName
394 get { return proxiedType.FullName; }
398 class MethodDataCache
400 MethodData[] methodDatas;
402 public MethodDataCache()
404 this.methodDatas = new MethodData[4];
412 public bool TryGetMethodData(MethodBase method, out MethodData methodData)
416 MethodData[] methodDatas = this.methodDatas;
417 int index = FindMethod(methodDatas, method);
420 methodData = methodDatas[index];
425 methodData = new MethodData();
431 static int FindMethod(MethodData[] methodDatas, MethodBase methodToFind)
433 for (int i = 0; i < methodDatas.Length; i++)
435 MethodBase method = methodDatas[i].MethodBase;
440 if (method == methodToFind)
448 public void SetMethodData(MethodData methodData)
452 int index = FindMethod(this.methodDatas, methodData.MethodBase);
455 for (int i = 0; i < this.methodDatas.Length; i++)
457 if (methodDatas[i].MethodBase == null)
459 methodDatas[i] = methodData;
463 MethodData[] newMethodDatas = new MethodData[methodDatas.Length * 2];
464 Array.Copy(methodDatas, newMethodDatas, methodDatas.Length);
465 newMethodDatas[methodDatas.Length] = methodData;
466 this.methodDatas = newMethodDatas;
485 MethodBase methodBase;
486 MethodType methodType;
487 ProxyOperationRuntime operation;
489 public MethodData(MethodBase methodBase, MethodType methodType)
490 : this(methodBase, methodType, null)
494 public MethodData(MethodBase methodBase, MethodType methodType, ProxyOperationRuntime operation)
496 this.methodBase = methodBase;
497 this.methodType = methodType;
498 this.operation = operation;
501 public MethodBase MethodBase
503 get { return methodBase; }
506 public MethodType MethodType
508 get { return methodType; }
511 public ProxyOperationRuntime Operation
513 get { return operation; }
517 class MbrObject : MarshalByRefObject
522 internal MbrObject(RealProxy proxy, Type targetType)
525 this.targetType = targetType;
528 public override bool Equals(object obj)
530 return Object.ReferenceEquals(obj, this.proxy.GetTransparentProxy());
533 public override string ToString()
535 return this.targetType.ToString();
538 public override int GetHashCode()
540 return this.proxy.GetHashCode();
544 class SingleReturnMessage : IMethodReturnMessage
546 IMethodCallMessage methodCall;
548 PropertyDictionary properties;
550 public SingleReturnMessage(object ret, IMethodCallMessage methodCall)
553 this.methodCall = methodCall;
554 this.properties = new PropertyDictionary();
564 get { return EmptyArray.Instance; }
567 public Exception Exception
572 public bool HasVarArgs
574 get { return methodCall.HasVarArgs; }
577 public LogicalCallContext LogicalCallContext
579 get { return SetActivityIdInLogicalCallContext(methodCall.LogicalCallContext); }
582 public MethodBase MethodBase
584 get { return methodCall.MethodBase; }
587 public string MethodName
589 get { return methodCall.MethodName; }
592 public object MethodSignature
594 get { return methodCall.MethodSignature; }
597 public object[] OutArgs
599 get { return EmptyArray.Instance; }
602 public int OutArgCount
607 public IDictionary Properties
609 get { return properties; }
612 public object ReturnValue
617 public string TypeName
619 get { return methodCall.TypeName; }
627 public object GetArg(int index)
629 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index"));
632 public string GetArgName(int index)
634 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index"));
637 public object GetOutArg(int index)
639 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index"));
642 public string GetOutArgName(int index)
644 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("index"));
647 class PropertyDictionary : IDictionary
649 ListDictionary properties;
651 public object this[object key]
653 get { return Properties[key]; }
654 set { Properties[key] = value; }
659 get { return Properties.Count; }
662 public bool IsFixedSize
664 get { return false; }
667 public bool IsReadOnly
669 get { return false; }
672 public bool IsSynchronized
674 get { return false; }
677 public ICollection Keys
679 get { return Properties.Keys; }
682 ListDictionary Properties
686 if (properties == null)
688 properties = new ListDictionary();
694 public ICollection Values
696 get { return Properties.Values; }
699 public object SyncRoot
704 public void Add(object key, object value)
706 Properties.Add(key, value);
714 public bool Contains(object key)
716 return Properties.Contains(key);
719 public void CopyTo(Array array, int index)
721 Properties.CopyTo(array, index);
724 public IDictionaryEnumerator GetEnumerator()
726 if (properties == null)
728 return EmptyEnumerator.Instance;
732 return properties.GetEnumerator();
736 IEnumerator IEnumerable.GetEnumerator()
738 return ((IEnumerable)Properties).GetEnumerator();
741 public void Remove(object key)
743 Properties.Remove(key);
746 class EmptyEnumerator : IDictionaryEnumerator
748 static EmptyEnumerator instance = new EmptyEnumerator();
754 public static EmptyEnumerator Instance
756 get { return instance; }
759 public bool MoveNext()
764 public Object Current
768 #pragma warning suppress 56503 // [....], IEnumerator guidelines, Current throws exception before calling MoveNext
769 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxDictionaryIsEmpty)));
781 #pragma warning suppress 56503 // [....], IEnumerator guidelines, Current throws exception before calling MoveNext
782 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxDictionaryIsEmpty)));
790 #pragma warning suppress 56503 // [....], IEnumerator guidelines, Current throws exception before calling MoveNext
791 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxDictionaryIsEmpty)));
795 public DictionaryEntry Entry
799 #pragma warning suppress 56503 // [....], IEnumerator guidelines, Current throws exception before calling MoveNext
800 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxDictionaryIsEmpty)));