| File: System\ServiceModel\Dispatcher\ProxyOperationRuntime.cs | Web Access |
| Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Reflection; using System.Runtime; using System.ServiceModel.Channels; using System.ServiceModel.Description; namespace System.ServiceModel.Dispatcher { internal class ProxyOperationRuntime { private readonly IClientMessageFormatter _formatter; private readonly bool _isSessionOpenNotificationEnabled; private readonly IParameterInspector[] _parameterInspectors; private readonly ImmutableClientRuntime _parent; private bool _deserializeReply; private string _replyAction; private MethodInfo _beginMethod; private MethodInfo _syncMethod; private MethodInfo _taskMethod; private ParameterInfo[] _inParams; private ParameterInfo[] _outParams; private ParameterInfo[] _endOutParams; private ParameterInfo _returnParam; internal ProxyOperationRuntime(ClientOperation operation, ImmutableClientRuntime parent) { if (operation == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(operation)); } _parent = parent ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parent)); _formatter = operation.Formatter; IsInitiating = operation.IsInitiating; IsOneWay = operation.IsOneWay; IsTerminating = operation.IsTerminating; _isSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled; Name = operation.Name; _parameterInspectors = EmptyArray<IParameterInspector>.ToArray(operation.ParameterInspectors); FaultFormatter = operation.FaultFormatter; SerializeRequest = operation.SerializeRequest; _deserializeReply = operation.DeserializeReply; Action = operation.Action; _replyAction = operation.ReplyAction; _beginMethod = operation.BeginMethod; _syncMethod = operation.SyncMethod; _taskMethod = operation.TaskMethod; TaskTResult = operation.TaskTResult; if (_beginMethod != null) { _inParams = ServiceReflector.GetInputParameters(_beginMethod, true); if (_syncMethod != null) { _outParams = ServiceReflector.GetOutputParameters(_syncMethod, false); } else { _outParams = Array.Empty<ParameterInfo>(); } _endOutParams = ServiceReflector.GetOutputParameters(operation.EndMethod, true); _returnParam = operation.EndMethod.ReturnParameter; } else if (_syncMethod != null) { _inParams = ServiceReflector.GetInputParameters(_syncMethod, false); _outParams = ServiceReflector.GetOutputParameters(_syncMethod, false); _returnParam = _syncMethod.ReturnParameter; } if (_formatter == null && (SerializeRequest || _deserializeReply)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.ClientRuntimeRequiresFormatter0, Name))); } } internal string Action { get; } internal IClientFaultFormatter FaultFormatter { get; } internal bool IsInitiating { get; } internal bool IsOneWay { get; } internal bool IsTerminating { get; } internal bool IsSessionOpenNotificationEnabled { get { return _isSessionOpenNotificationEnabled; } } internal string Name { get; } internal ImmutableClientRuntime Parent { get { return _parent; } } internal string ReplyAction { get { return _replyAction; } } internal bool DeserializeReply { get { return _deserializeReply; } } internal bool SerializeRequest { get; } internal Type TaskTResult { get; set; } internal void AfterReply(ref ProxyRpc rpc) { if (!IsOneWay) { Message reply = rpc.Reply; if (_deserializeReply) { if (WcfEventSource.Instance.ClientFormatterDeserializeReplyStartIsEnabled()) { WcfEventSource.Instance.ClientFormatterDeserializeReplyStart(rpc.EventTraceActivity); } rpc.ReturnValue = _formatter.DeserializeReply(reply, rpc.OutputParameters); if (WcfEventSource.Instance.ClientFormatterDeserializeReplyStopIsEnabled()) { WcfEventSource.Instance.ClientFormatterDeserializeReplyStop(rpc.EventTraceActivity); } } else { rpc.ReturnValue = reply; } int offset = _parent.ParameterInspectorCorrelationOffset; try { for (int i = _parameterInspectors.Length - 1; i >= 0; i--) { _parameterInspectors[i].AfterCall(Name, rpc.OutputParameters, rpc.ReturnValue, rpc.Correlation[offset + i]); if (WcfEventSource.Instance.ClientParameterInspectorAfterCallInvokedIsEnabled()) { WcfEventSource.Instance.ClientParameterInspectorAfterCallInvoked(rpc.EventTraceActivity, _parameterInspectors[i].GetType().FullName); } } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e)) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); } if (_parent.ValidateMustUnderstand) { Collection<MessageHeaderInfo> headersNotUnderstood = reply.Headers.GetHeadersNotUnderstood(); if (headersNotUnderstood != null && headersNotUnderstood.Count > 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SRP.Format(SRP.SFxHeaderNotUnderstood, headersNotUnderstood[0].Name, headersNotUnderstood[0].Namespace))); } } } } internal void BeforeRequest(ref ProxyRpc rpc) { int offset = _parent.ParameterInspectorCorrelationOffset; try { for (int i = 0; i < _parameterInspectors.Length; i++) { rpc.Correlation[offset + i] = _parameterInspectors[i].BeforeCall(Name, rpc.InputParameters); if (WcfEventSource.Instance.ClientParameterInspectorBeforeCallInvokedIsEnabled()) { WcfEventSource.Instance.ClientParameterInspectorBeforeCallInvoked(rpc.EventTraceActivity, _parameterInspectors[i].GetType().FullName); } } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (ErrorBehavior.ShouldRethrowClientSideExceptionAsIs(e)) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); } if (SerializeRequest) { if (WcfEventSource.Instance.ClientFormatterSerializeRequestStartIsEnabled()) { WcfEventSource.Instance.ClientFormatterSerializeRequestStart(rpc.EventTraceActivity); } rpc.Request = _formatter.SerializeRequest(rpc.MessageVersion, rpc.InputParameters); if (WcfEventSource.Instance.ClientFormatterSerializeRequestStopIsEnabled()) { WcfEventSource.Instance.ClientFormatterSerializeRequestStop(rpc.EventTraceActivity); } } else { if (rpc.InputParameters[0] == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.SFxProxyRuntimeMessageCannotBeNull, Name))); } rpc.Request = (Message)rpc.InputParameters[0]; if (!IsValidAction(rpc.Request, Action)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.SFxInvalidRequestAction, Name, rpc.Request.Headers.Action ?? "{NULL}", Action))); } } } internal static object GetDefaultParameterValue(Type parameterType) { return (parameterType.IsValueType() && parameterType != typeof(void)) ? Activator.CreateInstance(parameterType) : null; } internal bool IsSyncCall(MethodCall methodCall) { if (_syncMethod == null) { return false; } Contract.Assert(methodCall != null); Contract.Assert(methodCall.MethodBase != null); return methodCall.MethodBase.MethodHandle.Value.Equals(_syncMethod.MethodHandle.Value); } internal bool IsBeginCall(MethodCall methodCall) { if (_beginMethod == null) { return false; } Contract.Assert(methodCall != null); Contract.Assert(methodCall.MethodBase != null); return methodCall.MethodBase.MethodHandle.Value.Equals(_beginMethod.MethodHandle.Value); } internal bool IsTaskCall(MethodCall methodCall) { if (_taskMethod == null) { return false; } Contract.Assert(methodCall != null); Contract.Assert(methodCall.MethodBase != null); return methodCall.MethodBase.MethodHandle.Value.Equals(_taskMethod.MethodHandle.Value); } internal object[] MapSyncInputs(MethodCall methodCall, out object[] outs) { if (_outParams.Length == 0) { outs = Array.Empty<object>(); } else { outs = new object[_outParams.Length]; } if (_inParams.Length == 0) { return Array.Empty<object>(); } return methodCall.InArgs; } internal object[] MapAsyncBeginInputs(MethodCall methodCall, out AsyncCallback callback, out object asyncState) { object[] ins; if (_inParams.Length == 0) { ins = Array.Empty<object>(); } else { ins = new object[_inParams.Length]; } object[] args = methodCall.Args; for (int i = 0; i < ins.Length; i++) { ins[i] = args[_inParams[i].Position]; } callback = args[methodCall.Args.Length - 2] as AsyncCallback; asyncState = args[methodCall.Args.Length - 1]; return ins; } internal void MapAsyncEndInputs(MethodCall methodCall, out IAsyncResult result, out object[] outs) { outs = new object[_endOutParams.Length]; result = methodCall.Args[methodCall.Args.Length - 1] as IAsyncResult; } internal object[] MapSyncOutputs(MethodCall methodCall, object[] outs, ref object ret) { return MapOutputs(_outParams, methodCall, outs, ref ret); } internal object[] MapAsyncOutputs(MethodCall methodCall, object[] outs, ref object ret) { return MapOutputs(_endOutParams, methodCall, outs, ref ret); } private object[] MapOutputs(ParameterInfo[] parameters, MethodCall methodCall, object[] outs, ref object ret) { if (ret == null && _returnParam != null) { ret = GetDefaultParameterValue(TypeLoader.GetParameterType(_returnParam)); } if (parameters.Length == 0) { return null; } object[] args = methodCall.Args; for (int i = 0; i < parameters.Length; i++) { if (outs[i] == null) { // the RealProxy infrastructure requires a default value for value types args[parameters[i].Position] = GetDefaultParameterValue(TypeLoader.GetParameterType(parameters[i])); } else { args[parameters[i].Position] = outs[i]; } } return args; } static internal bool IsValidAction(Message message, string action) { if (message == null) { return false; } if (message.IsFault) { return true; } if (action == MessageHeaders.WildcardAction) { return true; } return (String.CompareOrdinal(message.Headers.Action, action) == 0); } } }