e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
822 lines
32 KiB
C#
822 lines
32 KiB
C#
//-----------------------------------------------------------------------------
|
|
// 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);
|
|
}
|
|
|
|
this.Formatter.DeserializeRequest(rpc.Request, rpc.InputParameters);
|
|
|
|
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;
|
|
|
|
for (int i = 0; i < this.ParameterInspectors.Length; i++)
|
|
{
|
|
IParameterInspector inspector = this.ParameterInspectors[i];
|
|
rpc.Correlation[offset + i] = inspector.BeforeCall(this.Name, rpc.InputParameters);
|
|
if (TD.ParameterInspectorBeforeCallInvokedIsEnabled())
|
|
{
|
|
TD.ParameterInspectorBeforeCallInvoked(rpc.EventTraceActivity, this.ParameterInspectors[i].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;
|
|
|
|
for (int i = this.ParameterInspectors.Length - 1; i >= 0; i--)
|
|
{
|
|
IParameterInspector inspector = this.ParameterInspectors[i];
|
|
inspector.AfterCall(this.Name, rpc.OutputParameters, rpc.ReturnParameter, rpc.Correlation[offset + i]);
|
|
if (TD.ParameterInspectorAfterCallInvokedIsEnabled())
|
|
{
|
|
TD.ParameterInspectorAfterCallInvoked(rpc.EventTraceActivity, this.ParameterInspectors[i].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 (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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
reply = this.Formatter.SerializeReply(rpc.RequestVersion, rpc.OutputParameters, rpc.ReturnParameter);
|
|
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|