File: System\ServiceModel\Dispatcher\DispatchOperationRuntime.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.ServiceModel.Dispatcher
{
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.IdentityModel.Configuration;
    using System.IdentityModel.Tokens;
    using System.Reflection;
    using System.Runtime;
    using System.Security;
    using System.Security.Claims;
    using System.Security.Principal;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Diagnostics.Application;
    using System.ServiceModel.Security;
    
    class DispatchOperationRuntime
    {
        static AsyncCallback invokeCallback = Fx.ThunkCallback(new AsyncCallback(DispatchOperationRuntime.InvokeCallback));
        readonly string action;
        readonly ICallContextInitializer[] callContextInitializers;
        readonly IDispatchFaultFormatter faultFormatter;
        readonly IDispatchMessageFormatter formatter;
        readonly ImpersonationOption impersonation;
        readonly IParameterInspector[] inspectors;
        readonly IOperationInvoker invoker;
        readonly bool isTerminating;
        readonly bool isSessionOpenNotificationEnabled;
        readonly bool isSynchronous;
        readonly string name;
        readonly ImmutableDispatchRuntime parent;
        readonly bool releaseInstanceAfterCall;
        readonly bool releaseInstanceBeforeCall;
        readonly string replyAction;
        readonly bool transactionAutoComplete;
        readonly bool transactionRequired;
        readonly bool deserializeRequest;
        readonly bool serializeReply;
        readonly bool isOneWay;
        readonly bool disposeParameters;
        readonly ReceiveContextAcknowledgementMode receiveContextAcknowledgementMode;
        readonly bool bufferedReceiveEnabled;
        readonly bool isInsideTransactedReceiveScope;
 
        internal DispatchOperationRuntime(DispatchOperation operation, ImmutableDispatchRuntime parent)
        {
            if (operation == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operation");
            }
            if (parent == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parent");
            }
            if (operation.Invoker == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.RuntimeRequiresInvoker0)));
            }
 
            this.disposeParameters = ((operation.AutoDisposeParameters) && (!operation.HasNoDisposableParameters));
            this.parent = parent;
            this.callContextInitializers = EmptyArray<ICallContextInitializer>.ToArray(operation.CallContextInitializers);
            this.inspectors = EmptyArray<IParameterInspector>.ToArray(operation.ParameterInspectors);
            this.faultFormatter = operation.FaultFormatter;
            this.impersonation = operation.Impersonation;
            this.deserializeRequest = operation.DeserializeRequest;
            this.serializeReply = operation.SerializeReply;
            this.formatter = operation.Formatter;
            this.invoker = operation.Invoker;
 
            try
            {
                this.isSynchronous = operation.Invoker.IsSynchronous;
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                {
                    throw;
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
            }
            this.isTerminating = operation.IsTerminating;
            this.isSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled;
            this.action = operation.Action;
            this.name = operation.Name;
            this.releaseInstanceAfterCall = operation.ReleaseInstanceAfterCall;
            this.releaseInstanceBeforeCall = operation.ReleaseInstanceBeforeCall;
            this.replyAction = operation.ReplyAction;
            this.isOneWay = operation.IsOneWay;
            this.transactionAutoComplete = operation.TransactionAutoComplete;
            this.transactionRequired = operation.TransactionRequired;
            this.receiveContextAcknowledgementMode = operation.ReceiveContextAcknowledgementMode;
            this.bufferedReceiveEnabled = operation.BufferedReceiveEnabled;
            this.isInsideTransactedReceiveScope = operation.IsInsideTransactedReceiveScope;
 
            if (this.formatter == null && (deserializeRequest || serializeReply))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.DispatchRuntimeRequiresFormatter0, this.name)));
            }
 
            if ((operation.Parent.InstanceProvider == null) && (operation.Parent.Type != null))
            {
                SyncMethodInvoker sync = this.invoker as SyncMethodInvoker;
                if (sync != null)
                {
                    this.ValidateInstanceType(operation.Parent.Type, sync.Method);
                }
 
                AsyncMethodInvoker async = this.invoker as AsyncMethodInvoker;
                if (async != null)
                {
                    this.ValidateInstanceType(operation.Parent.Type, async.BeginMethod);
                    this.ValidateInstanceType(operation.Parent.Type, async.EndMethod);
                }
 
                TaskMethodInvoker task = this.invoker as TaskMethodInvoker;
                if (task != null)
                {
                    this.ValidateInstanceType(operation.Parent.Type, task.TaskMethod);
                }
            }
        }
 
        internal string Action
        {
            get { return this.action; }
        }
 
        internal ICallContextInitializer[] CallContextInitializers
        {
            get { return this.callContextInitializers; }
        }
 
        internal bool DisposeParameters
        {
            get { return this.disposeParameters; }
        }
 
        internal bool HasDefaultUnhandledActionInvoker
        {
            get { return (this.invoker is DispatchRuntime.UnhandledActionInvoker); }
        }
 
        internal bool SerializeReply
        {
            get { return this.serializeReply; }
        }
 
        internal IDispatchFaultFormatter FaultFormatter
        {
            get { return this.faultFormatter; }
        }
 
        internal IDispatchMessageFormatter Formatter
        {
            get { return this.formatter; }
        }
 
        internal ImpersonationOption Impersonation
        {
            get { return this.impersonation; }
        }
 
        internal IOperationInvoker Invoker
        {
            get { return this.invoker; }
        }
 
        internal bool IsSynchronous
        {
            get { return this.isSynchronous; }
        }
 
        internal bool IsOneWay
        {
            get { return this.isOneWay; }
        }
 
        internal bool IsTerminating
        {
            get { return this.isTerminating; }
        }
 
        internal string Name
        {
            get { return this.name; }
        }
 
        internal IParameterInspector[] ParameterInspectors
        {
            get { return this.inspectors; }
        }
 
        internal ImmutableDispatchRuntime Parent
        {
            get { return this.parent; }
        }
 
        internal ReceiveContextAcknowledgementMode ReceiveContextAcknowledgementMode
        {
            get { return this.receiveContextAcknowledgementMode; }
        }
 
        internal bool ReleaseInstanceAfterCall
        {
            get { return this.releaseInstanceAfterCall; }
        }
 
        internal bool ReleaseInstanceBeforeCall
        {
            get { return this.releaseInstanceBeforeCall; }
        }
 
        internal string ReplyAction
        {
            get { return this.replyAction; }
        }
 
        internal bool TransactionAutoComplete
        {
            get { return this.transactionAutoComplete; }
        }
 
        internal bool TransactionRequired
        {
            get { return this.transactionRequired; }
        }
 
        internal bool IsInsideTransactedReceiveScope
        {
            get { return this.isInsideTransactedReceiveScope; }
        }
 
        void DeserializeInputs(ref MessageRpc rpc)
        {
            bool success = false;
            try
            {
                try
                {
                    rpc.InputParameters = this.Invoker.AllocateInputs();
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    if (ErrorBehavior.ShouldRethrowExceptionAsIs(e))
                    {
                        throw;
                    }
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
                }
                try
                {
                    // 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 (!this.isSessionOpenNotificationEnabled)
                    {
                        if (this.deserializeRequest)
                        {
                            if (TD.DispatchFormatterDeserializeRequestStartIsEnabled())
                            {
                                TD.DispatchFormatterDeserializeRequestStart(rpc.EventTraceActivity);
                            }
 
                            bool outputTiming = DS.MessageFormatterIsEnabled();
                            Stopwatch sw = null;
                            if (outputTiming)
                            {
                                sw = Stopwatch.StartNew();
                            }
 
                            this.Formatter.DeserializeRequest(rpc.Request, rpc.InputParameters);
 
                            if (outputTiming)
                            {
                                DS.DispatchMessageFormatterDeserialize(this.Formatter.GetType(), sw.Elapsed);
                            }
 
                            if (TD.DispatchFormatterDeserializeRequestStopIsEnabled())
                            {
                                TD.DispatchFormatterDeserializeRequestStop(rpc.EventTraceActivity);
                            }
                        }
                        else
                        {
                            rpc.InputParameters[0] = rpc.Request;
                        }
                    }
 
                    success = true;
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    if (ErrorBehavior.ShouldRethrowExceptionAsIs(e))
                    {
                        throw;
                    }
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
                }
            }
            finally
            {
                rpc.DidDeserializeRequestBody = (rpc.Request.State != MessageState.Created);
 
                if (!success && MessageLogger.LoggingEnabled)
                {
                    MessageLogger.LogMessage(ref rpc.Request, MessageLoggingSource.Malformed);
                }
            }
        }
 
        void InitializeCallContext(ref MessageRpc rpc)
        {
            if (this.CallContextInitializers.Length > 0)
            {
                InitializeCallContextCore(ref rpc);
            }
        }
 
        void InitializeCallContextCore(ref MessageRpc rpc)
        {
            IClientChannel channel = rpc.Channel.Proxy as IClientChannel;
            int offset = this.Parent.CallContextCorrelationOffset;
 
            try
            {
                for (int i = 0; i < rpc.Operation.CallContextInitializers.Length; i++)
                {
                    ICallContextInitializer initializer = this.CallContextInitializers[i];
                    rpc.Correlation[offset + i] = initializer.BeforeInvoke(rpc.InstanceContext, channel, rpc.Request);
                }
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                {
                    throw;
                }
                if (ErrorBehavior.ShouldRethrowExceptionAsIs(e))
                {
                    throw;
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
            }
        }
 
        void UninitializeCallContext(ref MessageRpc rpc)
        {
            if (this.CallContextInitializers.Length > 0)
            {
                UninitializeCallContextCore(ref rpc);
            }
        }
 
        void UninitializeCallContextCore(ref MessageRpc rpc)
        {
            IClientChannel channel = rpc.Channel.Proxy as IClientChannel;
            int offset = this.Parent.CallContextCorrelationOffset;
 
            try
            {
                for (int i = this.CallContextInitializers.Length - 1; i >= 0; i--)
                {
                    ICallContextInitializer initializer = this.CallContextInitializers[i];
                    initializer.AfterInvoke(rpc.Correlation[offset + i]);
                }
            }
            catch (Exception e)
            {
                // thread-local storage may be corrupt
                DiagnosticUtility.FailFast(string.Format(CultureInfo.InvariantCulture, "ICallContextInitializer.BeforeInvoke threw an exception of type {0}: {1}", e.GetType(), e.Message));
            }
        }
 
        void InspectInputs(ref MessageRpc rpc)
        {
            if (this.ParameterInspectors.Length > 0)
            {
                InspectInputsCore(ref rpc);
            }
        }
 
        void InspectInputsCore(ref MessageRpc rpc)
        {
            int offset = this.Parent.ParameterInspectorCorrelationOffset;
            bool outputTiming = DS.ParameterInspectorIsEnabled();
            Stopwatch sw = null;
            if (outputTiming)
            {
                sw = new Stopwatch();
            }
 
            for (int i = 0; i < this.ParameterInspectors.Length; i++)
            {
                IParameterInspector inspector = this.ParameterInspectors[i];
                if (outputTiming)
                {
                    sw.Restart();
                }
 
                rpc.Correlation[offset + i] = inspector.BeforeCall(this.Name, rpc.InputParameters);
                if (outputTiming)
                {
                    DS.ParameterInspectorBefore(inspector.GetType(), sw.Elapsed);
                }
 
                if (TD.ParameterInspectorBeforeCallInvokedIsEnabled())
                {
                    TD.ParameterInspectorBeforeCallInvoked(rpc.EventTraceActivity, inspector.GetType().FullName);
                }
            }
        }
 
        void InspectOutputs(ref MessageRpc rpc)
        {
            if (this.ParameterInspectors.Length > 0)
            {
                InspectOutputsCore(ref rpc);
            }
        }
 
        void InspectOutputsCore(ref MessageRpc rpc)
        {
            int offset = this.Parent.ParameterInspectorCorrelationOffset;
            bool outputTiming = DS.ParameterInspectorIsEnabled();
            Stopwatch sw = null;
            if (outputTiming)
            {
                sw = new Stopwatch();
            }
 
            for (int i = this.ParameterInspectors.Length - 1; i >= 0; i--)
            {
                IParameterInspector inspector = this.ParameterInspectors[i];
                if (outputTiming)
                {
                    sw.Restart();
                }
 
                inspector.AfterCall(this.Name, rpc.OutputParameters, rpc.ReturnParameter, rpc.Correlation[offset + i]);
                if (outputTiming)
                {
                    DS.ParameterInspectorAfter(inspector.GetType(), sw.Elapsed);
                }
 
                if (TD.ParameterInspectorAfterCallInvokedIsEnabled())
                {
                    TD.ParameterInspectorAfterCallInvoked(rpc.EventTraceActivity, inspector.GetType().FullName);
                }
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical method StartImpersonation.",
            Safe = "Manages the result of impersonation and properly Disposes it.")]
        [DebuggerStepperBoundary]
        [SecuritySafeCritical]
        internal void InvokeBegin(ref MessageRpc rpc)
        {
            if (rpc.Error == null)
            {
                try
                {
                    this.InitializeCallContext(ref rpc);
                    object target = rpc.Instance;
                    this.DeserializeInputs(ref rpc);
                    this.InspectInputs(ref rpc);
 
                    ValidateMustUnderstand(ref rpc);
 
                    IAsyncResult result = null;
                    IDisposable impersonationContext = null;
                    IPrincipal originalPrincipal = null;
                    bool isThreadPrincipalSet = false;
                    bool isConcurrent = this.Parent.IsConcurrent(ref rpc);
 
                    try
                    {
                        if (this.parent.RequireClaimsPrincipalOnOperationContext)
                        {
                            SetClaimsPrincipalToOperationContext(rpc);
                        }
                       
                        if (this.parent.SecurityImpersonation != null)
                        {
                            this.parent.SecurityImpersonation.StartImpersonation(ref rpc, out impersonationContext, out originalPrincipal, out isThreadPrincipalSet);
                        }
                        IManualConcurrencyOperationInvoker manualInvoker = this.Invoker as IManualConcurrencyOperationInvoker;
 
                        if (DS.OperationInvokerIsEnabled())
                        {
                            DS.InvokeOperationStart(this.Invoker.GetType(), Stopwatch.GetTimestamp());
                        }
 
                        if (this.isSynchronous)
                        {
                            if (manualInvoker != null && isConcurrent)
                            {
                                if (this.bufferedReceiveEnabled)
                                {
                                    rpc.OperationContext.IncomingMessageProperties.Add(
                                        BufferedReceiveMessageProperty.Name, new BufferedReceiveMessageProperty(ref rpc));
                                }
                                rpc.ReturnParameter = manualInvoker.Invoke(target, rpc.InputParameters, rpc.InvokeNotification, out rpc.OutputParameters);
                            }
                            else
                            {
                                rpc.ReturnParameter = this.Invoker.Invoke(target, rpc.InputParameters, out rpc.OutputParameters);
                            }
                        }
                        else
                        {
                            bool isBeginSuccessful = false;
 
                            if (manualInvoker != null && isConcurrent && this.bufferedReceiveEnabled)
                            {
                                // This will modify the rpc, it has to be done before rpc.Pause
                                // since IResumeMessageRpc implementation keeps reference of rpc.
                                // This is to ensure consistent rpc whether or not InvokeBegin completed
                                // synchronously or asynchronously.
                                rpc.OperationContext.IncomingMessageProperties.Add(
                                    BufferedReceiveMessageProperty.Name, new BufferedReceiveMessageProperty(ref rpc));
                            }
 
                            IResumeMessageRpc resumeRpc = rpc.Pause();
                            try
                            {
                                if (manualInvoker != null && isConcurrent)
                                {
                                    result = manualInvoker.InvokeBegin(target, rpc.InputParameters, rpc.InvokeNotification, invokeCallback, resumeRpc);
                                }
                                else
                                {
                                    result = this.Invoker.InvokeBegin(target, rpc.InputParameters, invokeCallback, resumeRpc);
                                }
 
                                isBeginSuccessful = true;
                                // if the call above actually went async, then responsibility to call 
                                // ProcessMessage{6,7,Cleanup} has been transferred to InvokeCallback
                            }
                            finally
                            {
                                if (!isBeginSuccessful)
                                {
                                    rpc.UnPause();
                                }
                            }
                        }
                    }
                    finally
                    {
                        try
                        {
                            if (this.parent.SecurityImpersonation != null)
                            {
                                this.parent.SecurityImpersonation.StopImpersonation(ref rpc, impersonationContext, originalPrincipal, isThreadPrincipalSet);
                            }
                        }
#pragma warning suppress 56500 // covered by FxCOP
                        catch
                        {
                            string message = null;
                            try
                            {
                                message = SR.GetString(SR.SFxRevertImpersonationFailed0);
                            }
                            finally
                            {
                                DiagnosticUtility.FailFast(message);
                            }
                        }
 
                        if (this.isSynchronous && DS.OperationInvokerIsEnabled())
                        {
                            DS.InvokeOperationStop(Stopwatch.GetTimestamp());
                        }
                    }
 
                    if (this.isSynchronous)
                    {
                        this.InspectOutputs(ref rpc);
 
                        this.SerializeOutputs(ref rpc);
                    }
                    else
                    {
                        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;
                        }
                    }
                }
#pragma warning suppress 56500 // covered by FxCOP
                catch { throw; } // Make sure user Exception filters are not impersonated.
                finally
                {
                    this.UninitializeCallContext(ref rpc);
                }
            }
        }
 
        void SetClaimsPrincipalToOperationContext(MessageRpc rpc)
        {
            ServiceSecurityContext securityContext = rpc.SecurityContext;
            if (!rpc.HasSecurityContext)
            {
                SecurityMessageProperty securityContextProperty = rpc.Request.Properties.Security;
                if (securityContextProperty != null)
                {
                    securityContext = securityContextProperty.ServiceSecurityContext;
                }
            }
 
            if (securityContext != null)
            {
                object principal;
                if (securityContext.AuthorizationContext.Properties.TryGetValue(AuthorizationPolicy.ClaimsPrincipalKey, out principal))
                {
                    ClaimsPrincipal claimsPrincipal = principal as ClaimsPrincipal;
                    if (claimsPrincipal != null)
                    {
                        //
                        // Always set ClaimsPrincipal to OperationContext.Current if identityModel pipeline is used.
                        //
                        OperationContext.Current.ClaimsPrincipal = claimsPrincipal;
                    }
                    else
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.NoPrincipalSpecifiedInAuthorizationContext)));
                    }
                }
            }
        }
 
        static void InvokeCallback(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
            {
                return;
            }
 
            IResumeMessageRpc resume = result.AsyncState as IResumeMessageRpc;
 
            if (resume == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxInvalidAsyncResultState0));
            }
 
            resume.SignalConditionalResume(result);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical method StartImpersonation.",
            Safe = "Manages the result of impersonation and properly Disposes it.")]
        [DebuggerStepperBoundary]
        [SecuritySafeCritical]
        internal void InvokeEnd(ref MessageRpc rpc)
        {
            if ((rpc.Error == null) && !this.isSynchronous)
            {
                try
                {
                    this.InitializeCallContext(ref rpc);
 
                    if (this.parent.RequireClaimsPrincipalOnOperationContext)
                    {
                        SetClaimsPrincipalToOperationContext(rpc);
                    }
 
                    IDisposable impersonationContext = null;
                    IPrincipal originalPrincipal = null;
                    bool isThreadPrincipalSet = false;
 
                    try
                    {
                        if (this.parent.SecurityImpersonation != null)
                        {
                            this.parent.SecurityImpersonation.StartImpersonation(ref rpc, out impersonationContext, out originalPrincipal, out isThreadPrincipalSet);
                        }
 
                        rpc.ReturnParameter = this.Invoker.InvokeEnd(rpc.Instance, out rpc.OutputParameters, rpc.AsyncResult);
                    }
                    finally
                    {
                        try
                        {
                            if (this.parent.SecurityImpersonation != null)
                            {
                                this.parent.SecurityImpersonation.StopImpersonation(ref rpc, impersonationContext, originalPrincipal, isThreadPrincipalSet);
                            }
                        }
#pragma warning suppress 56500 // covered by FxCOP
                        catch
                        {
                            string message = null;
                            try
                            {
                                message = SR.GetString(SR.SFxRevertImpersonationFailed0);
                            }
                            finally
                            {
                                DiagnosticUtility.FailFast(message);
                            }
                        }
 
                        if (DS.OperationInvokerIsEnabled())
                        {
                            DS.InvokeOperationStop(Stopwatch.GetTimestamp());
                        }
                    }
 
                    this.InspectOutputs(ref rpc);
 
                    this.SerializeOutputs(ref rpc);
                }
#pragma warning suppress 56500 // covered by FxCOP
                catch { throw; } // Make sure user Exception filters are not impersonated.
                finally
                {
                    this.UninitializeCallContext(ref rpc);
                }
            }
        }
 
        void SerializeOutputs(ref MessageRpc rpc)
        {
            if (!this.IsOneWay && this.parent.EnableFaults)
            {
                Message reply;
                if (this.serializeReply)
                {
                    try
                    {
                        if (TD.DispatchFormatterSerializeReplyStartIsEnabled())
                        {
                            TD.DispatchFormatterSerializeReplyStart(rpc.EventTraceActivity);
                        }
 
                        bool outputTiming = DS.MessageFormatterIsEnabled();
                        Stopwatch sw = null;
                        if (outputTiming)
                        {
                            sw = Stopwatch.StartNew();
                        }
 
                        reply = this.Formatter.SerializeReply(rpc.RequestVersion, rpc.OutputParameters, rpc.ReturnParameter);
 
                        if (outputTiming)
                        {
                            DS.DispatchMessageFormatterSerialize(this.Formatter.GetType(), sw.Elapsed);
                        }
                        
                        if (TD.DispatchFormatterSerializeReplyStopIsEnabled())
                        {
                            TD.DispatchFormatterSerializeReplyStop(rpc.EventTraceActivity);
                        }
                    }
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
                        if (ErrorBehavior.ShouldRethrowExceptionAsIs(e))
                        {
                            throw;
                        }
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
                    }
 
                    if (reply == null)
                    {
                        string message = SR.GetString(SR.SFxNullReplyFromFormatter2, this.Formatter.GetType().ToString(), (this.name ?? ""));
                        ErrorBehavior.ThrowAndCatch(new InvalidOperationException(message));
                    }
                }
                else
                {
                    if ((rpc.ReturnParameter == null) && (rpc.OperationContext.RequestContext != null))
                    {
                        string message = SR.GetString(SR.SFxDispatchRuntimeMessageCannotBeNull, this.name);
                        ErrorBehavior.ThrowAndCatch(new InvalidOperationException(message));
                    }
 
                    reply = (Message)rpc.ReturnParameter;
 
                    if ((reply != null) && (!ProxyOperationRuntime.IsValidAction(reply, this.ReplyAction)))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidReplyAction, this.Name, reply.Headers.Action ?? "{NULL}", this.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);
                        }
                    }
                }
 
                // Add the ImpersonateOnSerializingReplyMessageProperty on the reply message iff
                // a. reply message is not null.
                // b. Impersonation is enabled on serializing Reply
 
                if (reply != null && this.parent.IsImpersonationEnabledOnSerializingReply)
                {
                    bool shouldImpersonate = this.parent.SecurityImpersonation != null && this.parent.SecurityImpersonation.IsImpersonationEnabledOnCurrentOperation(ref rpc);
                    if (shouldImpersonate)
                    {
                        reply.Properties.Add(ImpersonateOnSerializingReplyMessageProperty.Name, new ImpersonateOnSerializingReplyMessageProperty(ref rpc));
                        reply = new ImpersonatingMessage(reply);
                    }
                }
 
                if (MessageLogger.LoggingEnabled && null != reply)
                {
                    MessageLogger.LogMessage(ref reply, MessageLoggingSource.ServiceLevelSendReply | MessageLoggingSource.LastChance);
                }
                rpc.Reply = reply;
            }
        }
 
        void ValidateInstanceType(Type type, MethodInfo method)
        {
            if (!method.DeclaringType.IsAssignableFrom(type))
            {
                string message = SR.GetString(SR.SFxMethodNotSupportedByType2,
                                              type.FullName,
                                              method.DeclaringType.FullName);
 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(message));
            }
        }
 
        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));
                }
            }
        }
    }
}