| File: System\ServiceModel\Dispatcher\DispatchOperationRuntime.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.Runtime; using System.ServiceModel.Channels; using System.ServiceModel.Diagnostics; namespace System.ServiceModel.Dispatcher { internal class DispatchOperationRuntime { private static AsyncCallback s_invokeCallback = Fx.ThunkCallback(DispatchOperationRuntime.InvokeCallback); private readonly bool _isSessionOpenNotificationEnabled; private readonly bool _deserializeRequest; private readonly bool _serializeReply; private readonly bool _disposeParameters; internal DispatchOperationRuntime(DispatchOperation operation, ImmutableDispatchRuntime parent) { if (operation == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(operation)); } if (operation.Invoker == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.RuntimeRequiresInvoker0)); } _disposeParameters = ((operation.AutoDisposeParameters) && (!operation.HasNoDisposableParameters)); Parent = parent ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parent)); ParameterInspectors = EmptyArray<IParameterInspector>.ToArray(operation.ParameterInspectors); FaultFormatter = operation.FaultFormatter; _deserializeRequest = operation.DeserializeRequest; _serializeReply = operation.SerializeReply; Formatter = operation.Formatter; Invoker = operation.Invoker; IsTerminating = operation.IsTerminating; _isSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled; Action = operation.Action; Name = operation.Name; ReplyAction = operation.ReplyAction; IsOneWay = operation.IsOneWay; if (Formatter == null && (_deserializeRequest || _serializeReply)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.DispatchRuntimeRequiresFormatter0, Name))); } } internal string Action { get; } internal bool DisposeParameters { get { return _disposeParameters; } } internal IDispatchFaultFormatter FaultFormatter { get; } internal IDispatchMessageFormatter Formatter { get; } internal IOperationInvoker Invoker { get; } internal bool IsOneWay { get; } internal bool IsTerminating { get; } internal string Name { get; } internal IParameterInspector[] ParameterInspectors { get; } internal ImmutableDispatchRuntime Parent { get; } internal string ReplyAction { get; } private void DeserializeInputs(ref MessageRpc rpc) { bool success = false; try { rpc.InputParameters = Invoker.AllocateInputs(); // If the field is true, then this operation is to be invoked at the time the service // channel is opened. The incoming message is created at ChannelHandler level with no // content, so we don't need to deserialize the message. if (!_isSessionOpenNotificationEnabled) { if (_deserializeRequest) { if (WcfEventSource.Instance.DispatchFormatterDeserializeRequestStartIsEnabled()) { WcfEventSource.Instance.DispatchFormatterDeserializeRequestStart(rpc.EventTraceActivity); } Formatter.DeserializeRequest(rpc.Request, rpc.InputParameters); if (WcfEventSource.Instance.DispatchFormatterDeserializeRequestStopIsEnabled()) { WcfEventSource.Instance.DispatchFormatterDeserializeRequestStop(rpc.EventTraceActivity); } } else { rpc.InputParameters[0] = rpc.Request; } } success = true; } finally { rpc.DidDeserializeRequestBody = (rpc.Request.State != MessageState.Created); if (!success && MessageLogger.LoggingEnabled) { MessageLogger.LogMessage(ref rpc.Request, MessageLoggingSource.Malformed); } } } private void InspectInputs(ref MessageRpc rpc) { if (ParameterInspectors.Length > 0) { InspectInputsCore(ref rpc); } } private void InspectInputsCore(ref MessageRpc rpc) { for (int i = 0; i < ParameterInspectors.Length; i++) { IParameterInspector inspector = ParameterInspectors[i]; rpc.Correlation[i] = inspector.BeforeCall(Name, rpc.InputParameters); if (WcfEventSource.Instance.ParameterInspectorBeforeCallInvokedIsEnabled()) { WcfEventSource.Instance.ParameterInspectorBeforeCallInvoked(rpc.EventTraceActivity, ParameterInspectors[i].GetType().FullName); } } } private void InspectOutputs(ref MessageRpc rpc) { if (ParameterInspectors.Length > 0) { InspectOutputsCore(ref rpc); } } private void InspectOutputsCore(ref MessageRpc rpc) { for (int i = ParameterInspectors.Length - 1; i >= 0; i--) { IParameterInspector inspector = ParameterInspectors[i]; inspector.AfterCall(Name, rpc.OutputParameters, rpc.ReturnParameter, rpc.Correlation[i]); if (WcfEventSource.Instance.ParameterInspectorAfterCallInvokedIsEnabled()) { WcfEventSource.Instance.ParameterInspectorAfterCallInvoked(rpc.EventTraceActivity, ParameterInspectors[i].GetType().FullName); } } } internal void InvokeBegin(ref MessageRpc rpc) { if (rpc.Error == null) { object target = rpc.Instance; DeserializeInputs(ref rpc); InspectInputs(ref rpc); ValidateMustUnderstand(ref rpc); IAsyncResult result; bool isBeginSuccessful = false; IResumeMessageRpc resumeRpc = rpc.Pause(); try { result = Invoker.InvokeBegin(target, rpc.InputParameters, s_invokeCallback, resumeRpc); isBeginSuccessful = true; } finally { if (!isBeginSuccessful) { rpc.UnPause(); } } if (result == null) { throw TraceUtility.ThrowHelperError(new ArgumentNullException("IOperationInvoker.BeginDispatch"), rpc.Request); } if (result.CompletedSynchronously) { // if the async call completed synchronously, then the responsibility to call // ProcessMessage{6,7,Cleanup} still remains on this thread rpc.UnPause(); rpc.AsyncResult = result; } } } private static void InvokeCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } IResumeMessageRpc resume = result.AsyncState as IResumeMessageRpc; if (resume == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SRP.SFxInvalidAsyncResultState0); } resume.SignalConditionalResume(result); } internal void InvokeEnd(ref MessageRpc rpc) { if ((rpc.Error == null)) { rpc.ReturnParameter = Invoker.InvokeEnd(rpc.Instance, out rpc.OutputParameters, rpc.AsyncResult); InspectOutputs(ref rpc); SerializeOutputs(ref rpc); } } private void SerializeOutputs(ref MessageRpc rpc) { if (!IsOneWay && Parent.EnableFaults) { Message reply; if (_serializeReply) { if (WcfEventSource.Instance.DispatchFormatterSerializeReplyStartIsEnabled()) { WcfEventSource.Instance.DispatchFormatterSerializeReplyStart(rpc.EventTraceActivity); } reply = Formatter.SerializeReply(rpc.RequestVersion, rpc.OutputParameters, rpc.ReturnParameter); if (WcfEventSource.Instance.DispatchFormatterSerializeReplyStopIsEnabled()) { WcfEventSource.Instance.DispatchFormatterSerializeReplyStop(rpc.EventTraceActivity); } if (reply == null) { string message = SRP.Format(SRP.SFxNullReplyFromFormatter2, Formatter.GetType().ToString(), (Name ?? "")); ErrorBehavior.ThrowAndCatch(new InvalidOperationException(message)); } } else { if ((rpc.ReturnParameter == null) && (rpc.OperationContext.RequestContext != null)) { string message = SRP.Format(SRP.SFxDispatchRuntimeMessageCannotBeNull, Name); ErrorBehavior.ThrowAndCatch(new InvalidOperationException(message)); } reply = (Message)rpc.ReturnParameter; if ((reply != null) && (!ProxyOperationRuntime.IsValidAction(reply, ReplyAction))) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.SFxInvalidReplyAction, Name, reply.Headers.Action ?? "{NULL}", ReplyAction))); } } if (DiagnosticUtility.ShouldUseActivity && rpc.Activity != null && reply != null) { TraceUtility.SetActivity(reply, rpc.Activity); if (TraceUtility.ShouldPropagateActivity) { TraceUtility.AddActivityHeader(reply); } } else if (TraceUtility.ShouldPropagateActivity && reply != null && rpc.ResponseActivityId != Guid.Empty) { ActivityIdHeader header = new ActivityIdHeader(rpc.ResponseActivityId); header.AddTo(reply); } //rely on the property set during the message receive to correlate the trace if (TraceUtility.MessageFlowTracingOnly) { //Guard against MEX scenarios where the message is closed by now if (null != rpc.OperationContext.IncomingMessage && MessageState.Closed != rpc.OperationContext.IncomingMessage.State) { FxTrace.Trace.SetAndTraceTransfer(TraceUtility.GetReceivedActivityId(rpc.OperationContext), true); } else { if (rpc.ResponseActivityId != Guid.Empty) { FxTrace.Trace.SetAndTraceTransfer(rpc.ResponseActivityId, true); } } } if (MessageLogger.LoggingEnabled && null != reply) { MessageLogger.LogMessage(ref reply, MessageLoggingSource.ServiceLevelSendReply | MessageLoggingSource.LastChance); } rpc.Reply = reply; } } private void ValidateMustUnderstand(ref MessageRpc rpc) { if (Parent.ValidateMustUnderstand) { rpc.NotUnderstoodHeaders = rpc.Request.Headers.GetHeadersNotUnderstood(); if (rpc.NotUnderstoodHeaders != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new MustUnderstandSoapException(rpc.NotUnderstoodHeaders, rpc.Request.Version.Envelope)); } } } } }