//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Dispatcher { using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Runtime; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Diagnostics; using System.Threading; using System.Transactions; using System.ServiceModel.Diagnostics.Application; using System.Runtime.Diagnostics; using System.Security; class ImmutableDispatchRuntime { readonly AuthenticationBehavior authenticationBehavior; readonly AuthorizationBehavior authorizationBehavior; readonly int correlationCount; readonly ConcurrencyBehavior concurrency; readonly IDemuxer demuxer; readonly ErrorBehavior error; readonly bool enableFaults; readonly bool ignoreTransactionFlow; readonly bool impersonateOnSerializingReply; readonly IInputSessionShutdown[] inputSessionShutdownHandlers; readonly InstanceBehavior instance; readonly bool isOnServer; readonly bool manualAddressing; readonly IDispatchMessageInspector[] messageInspectors; readonly int parameterInspectorCorrelationOffset; readonly IRequestReplyCorrelator requestReplyCorrelator; readonly SecurityImpersonationBehavior securityImpersonation; readonly TerminatingOperationBehavior terminate; readonly ThreadBehavior thread; readonly TransactionBehavior transaction; readonly bool validateMustUnderstand; readonly bool receiveContextEnabledChannel; readonly bool sendAsynchronously; readonly bool requireClaimsPrincipalOnOperationContext; readonly MessageRpcProcessor processMessage1; readonly MessageRpcProcessor processMessage11; readonly MessageRpcProcessor processMessage2; readonly MessageRpcProcessor processMessage3; readonly MessageRpcProcessor processMessage31; readonly MessageRpcProcessor processMessage4; readonly MessageRpcProcessor processMessage41; readonly MessageRpcProcessor processMessage5; readonly MessageRpcProcessor processMessage6; readonly MessageRpcProcessor processMessage7; readonly MessageRpcProcessor processMessage8; readonly MessageRpcProcessor processMessage9; readonly MessageRpcProcessor processMessageCleanup; readonly MessageRpcProcessor processMessageCleanupError; static AsyncCallback onFinalizeCorrelationCompleted = Fx.ThunkCallback(new AsyncCallback(OnFinalizeCorrelationCompletedCallback)); static AsyncCallback onReplyCompleted = Fx.ThunkCallback(new AsyncCallback(OnReplyCompletedCallback)); bool didTraceProcessMessage1 = false; bool didTraceProcessMessage2 = false; bool didTraceProcessMessage3 = false; bool didTraceProcessMessage31 = false; bool didTraceProcessMessage4 = false; bool didTraceProcessMessage41 = false; internal ImmutableDispatchRuntime(DispatchRuntime dispatch) { this.authenticationBehavior = AuthenticationBehavior.TryCreate(dispatch); this.authorizationBehavior = AuthorizationBehavior.TryCreate(dispatch); this.concurrency = new ConcurrencyBehavior(dispatch); this.error = new ErrorBehavior(dispatch.ChannelDispatcher); this.enableFaults = dispatch.EnableFaults; this.inputSessionShutdownHandlers = EmptyArray.ToArray(dispatch.InputSessionShutdownHandlers); this.instance = new InstanceBehavior(dispatch, this); this.isOnServer = dispatch.IsOnServer; this.manualAddressing = dispatch.ManualAddressing; this.messageInspectors = EmptyArray.ToArray(dispatch.MessageInspectors); this.requestReplyCorrelator = new RequestReplyCorrelator(); this.securityImpersonation = SecurityImpersonationBehavior.CreateIfNecessary(dispatch); this.requireClaimsPrincipalOnOperationContext = dispatch.RequireClaimsPrincipalOnOperationContext; this.impersonateOnSerializingReply = dispatch.ImpersonateOnSerializingReply; this.terminate = TerminatingOperationBehavior.CreateIfNecessary(dispatch); this.thread = new ThreadBehavior(dispatch); this.validateMustUnderstand = dispatch.ValidateMustUnderstand; this.ignoreTransactionFlow = dispatch.IgnoreTransactionMessageProperty; this.transaction = TransactionBehavior.CreateIfNeeded(dispatch); this.receiveContextEnabledChannel = dispatch.ChannelDispatcher.ReceiveContextEnabled; this.sendAsynchronously = dispatch.ChannelDispatcher.SendAsynchronously; this.parameterInspectorCorrelationOffset = (dispatch.MessageInspectors.Count + dispatch.MaxCallContextInitializers); this.correlationCount = this.parameterInspectorCorrelationOffset + dispatch.MaxParameterInspectors; DispatchOperationRuntime unhandled = new DispatchOperationRuntime(dispatch.UnhandledDispatchOperation, this); if (dispatch.OperationSelector == null) { ActionDemuxer demuxer = new ActionDemuxer(); for (int i = 0; i < dispatch.Operations.Count; i++) { DispatchOperation operation = dispatch.Operations[i]; DispatchOperationRuntime operationRuntime = new DispatchOperationRuntime(operation, this); demuxer.Add(operation.Action, operationRuntime); } demuxer.SetUnhandled(unhandled); this.demuxer = demuxer; } else { CustomDemuxer demuxer = new CustomDemuxer(dispatch.OperationSelector); for (int i = 0; i < dispatch.Operations.Count; i++) { DispatchOperation operation = dispatch.Operations[i]; DispatchOperationRuntime operationRuntime = new DispatchOperationRuntime(operation, this); demuxer.Add(operation.Name, operationRuntime); } demuxer.SetUnhandled(unhandled); this.demuxer = demuxer; } this.processMessage1 = new MessageRpcProcessor(this.ProcessMessage1); this.processMessage11 = new MessageRpcProcessor(this.ProcessMessage11); this.processMessage2 = new MessageRpcProcessor(this.ProcessMessage2); this.processMessage3 = new MessageRpcProcessor(this.ProcessMessage3); this.processMessage31 = new MessageRpcProcessor(this.ProcessMessage31); this.processMessage4 = new MessageRpcProcessor(this.ProcessMessage4); this.processMessage41 = new MessageRpcProcessor(this.ProcessMessage41); this.processMessage5 = new MessageRpcProcessor(this.ProcessMessage5); this.processMessage6 = new MessageRpcProcessor(this.ProcessMessage6); this.processMessage7 = new MessageRpcProcessor(this.ProcessMessage7); this.processMessage8 = new MessageRpcProcessor(this.ProcessMessage8); this.processMessage9 = new MessageRpcProcessor(this.ProcessMessage9); this.processMessageCleanup = new MessageRpcProcessor(this.ProcessMessageCleanup); this.processMessageCleanupError = new MessageRpcProcessor(this.ProcessMessageCleanupError); } internal int CallContextCorrelationOffset { get { return this.messageInspectors.Length; } } internal int CorrelationCount { get { return this.correlationCount; } } internal bool EnableFaults { get { return this.enableFaults; } } internal InstanceBehavior InstanceBehavior { get { return this.instance; } } internal bool IsImpersonationEnabledOnSerializingReply { get { return this.impersonateOnSerializingReply; } } internal bool RequireClaimsPrincipalOnOperationContext { get { return this.requireClaimsPrincipalOnOperationContext; } } internal bool ManualAddressing { get { return this.manualAddressing; } } internal int MessageInspectorCorrelationOffset { get { return 0; } } internal int ParameterInspectorCorrelationOffset { get { return this.parameterInspectorCorrelationOffset; } } internal IRequestReplyCorrelator RequestReplyCorrelator { get { return this.requestReplyCorrelator; } } internal SecurityImpersonationBehavior SecurityImpersonation { get { return this.securityImpersonation; } } internal bool ValidateMustUnderstand { get { return validateMustUnderstand; } } internal ErrorBehavior ErrorBehavior { get { return this.error; } } bool AcquireDynamicInstanceContext(ref MessageRpc rpc) { if (rpc.InstanceContext.QuotaThrottle != null) { return AcquireDynamicInstanceContextCore(ref rpc); } else { return true; } } bool AcquireDynamicInstanceContextCore(ref MessageRpc rpc) { bool success = rpc.InstanceContext.QuotaThrottle.Acquire(rpc.Pause()); if (success) { rpc.UnPause(); } return success; } internal void AfterReceiveRequest(ref MessageRpc rpc) { if (this.messageInspectors.Length > 0) { AfterReceiveRequestCore(ref rpc); } } internal void AfterReceiveRequestCore(ref MessageRpc rpc) { int offset = this.MessageInspectorCorrelationOffset; try { for (int i = 0; i < this.messageInspectors.Length; i++) { rpc.Correlation[offset + i] = this.messageInspectors[i].AfterReceiveRequest(ref rpc.Request, (IClientChannel)rpc.Channel.Proxy, rpc.InstanceContext); if (TD.MessageInspectorAfterReceiveInvokedIsEnabled()) { TD.MessageInspectorAfterReceiveInvoked(rpc.EventTraceActivity, this.messageInspectors[i].GetType().FullName); } } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (ErrorBehavior.ShouldRethrowExceptionAsIs(e)) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); } } void BeforeSendReply(ref MessageRpc rpc, ref Exception exception, ref bool thereIsAnUnhandledException) { if (this.messageInspectors.Length > 0) { BeforeSendReplyCore(ref rpc, ref exception, ref thereIsAnUnhandledException); } } internal void BeforeSendReplyCore(ref MessageRpc rpc, ref Exception exception, ref bool thereIsAnUnhandledException) { int offset = this.MessageInspectorCorrelationOffset; for (int i = 0; i < this.messageInspectors.Length; i++) { try { Message originalReply = rpc.Reply; Message reply = originalReply; this.messageInspectors[i].BeforeSendReply(ref reply, rpc.Correlation[offset + i]); if (TD.MessageInspectorBeforeSendInvokedIsEnabled()) { TD.MessageInspectorBeforeSendInvoked(rpc.EventTraceActivity, this.messageInspectors[i].GetType().FullName); } if ((reply == null) && (originalReply != null)) { string message = SR.GetString(SR.SFxNullReplyFromExtension2, this.messageInspectors[i].GetType().ToString(), (rpc.Operation.Name ?? "")); ErrorBehavior.ThrowAndCatch(new InvalidOperationException(message)); } rpc.Reply = reply; } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (!ErrorBehavior.ShouldRethrowExceptionAsIs(e)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); } if (exception == null) { exception = e; } thereIsAnUnhandledException = (!this.error.HandleError(e)) || thereIsAnUnhandledException; } } } void FinalizeCorrelation(ref MessageRpc rpc) { Message reply = rpc.Reply; if (reply != null && rpc.Error == null) { if (rpc.transaction != null && rpc.transaction.Current != null && rpc.transaction.Current.TransactionInformation.Status != TransactionStatus.Active) { return; } CorrelationCallbackMessageProperty callback; if (CorrelationCallbackMessageProperty.TryGet(reply, out callback)) { if (callback.IsFullyDefined) { try { rpc.RequestContextThrewOnReply = true; rpc.CorrelationCallback = callback; rpc.Reply = rpc.CorrelationCallback.FinalizeCorrelation(reply, rpc.ReplyTimeoutHelper.RemainingTime()); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (!this.error.HandleError(e)) { rpc.CorrelationCallback = null; rpc.CanSendReply = false; } } } else { rpc.CorrelationCallback = new RpcCorrelationCallbackMessageProperty(callback, this, ref rpc); reply.Properties[CorrelationCallbackMessageProperty.Name] = rpc.CorrelationCallback; } } } } void BeginFinalizeCorrelation(ref MessageRpc rpc) { Message reply = rpc.Reply; if (reply != null && rpc.Error == null) { if (rpc.transaction != null && rpc.transaction.Current != null && rpc.transaction.Current.TransactionInformation.Status != TransactionStatus.Active) { return; } CorrelationCallbackMessageProperty callback; if (CorrelationCallbackMessageProperty.TryGet(reply, out callback)) { if (callback.IsFullyDefined) { bool success = false; try { rpc.RequestContextThrewOnReply = true; rpc.CorrelationCallback = callback; IResumeMessageRpc resume = rpc.Pause(); rpc.AsyncResult = rpc.CorrelationCallback.BeginFinalizeCorrelation(reply, rpc.ReplyTimeoutHelper.RemainingTime(), onFinalizeCorrelationCompleted, resume); success = true; if (rpc.AsyncResult.CompletedSynchronously) { rpc.UnPause(); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (!this.error.HandleError(e)) { rpc.CorrelationCallback = null; rpc.CanSendReply = false; } } finally { if (!success) { rpc.UnPause(); } } } else { rpc.CorrelationCallback = new RpcCorrelationCallbackMessageProperty(callback, this, ref rpc); reply.Properties[CorrelationCallbackMessageProperty.Name] = rpc.CorrelationCallback; } } } } void Reply(ref MessageRpc rpc) { rpc.RequestContextThrewOnReply = true; rpc.SuccessfullySendReply = false; try { rpc.RequestContext.Reply(rpc.Reply, rpc.ReplyTimeoutHelper.RemainingTime()); rpc.RequestContextThrewOnReply = false; rpc.SuccessfullySendReply = true; if (TD.DispatchMessageStopIsEnabled()) { TD.DispatchMessageStop(rpc.EventTraceActivity); } } catch (CommunicationException e) { this.error.HandleError(e); } catch (TimeoutException e) { this.error.HandleError(e); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (DiagnosticUtility.ShouldTraceError) { TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.ServiceOperationExceptionOnReply, SR.GetString(SR.TraceCodeServiceOperationExceptionOnReply), this, e); } if (!this.error.HandleError(e)) { rpc.RequestContextThrewOnReply = true; rpc.CanSendReply = false; } } } void BeginReply(ref MessageRpc rpc) { bool success = false; try { IResumeMessageRpc resume = rpc.Pause(); rpc.AsyncResult = rpc.RequestContext.BeginReply(rpc.Reply, rpc.ReplyTimeoutHelper.RemainingTime(), onReplyCompleted, resume); success = true; if (rpc.AsyncResult.CompletedSynchronously) { rpc.UnPause(); } } catch (CommunicationException e) { this.error.HandleError(e); } catch (TimeoutException e) { this.error.HandleError(e); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (DiagnosticUtility.ShouldTraceError) { TraceUtility.TraceEvent(System.Diagnostics.TraceEventType.Error, TraceCode.ServiceOperationExceptionOnReply, SR.GetString(SR.TraceCodeServiceOperationExceptionOnReply), this, e); } if (!this.error.HandleError(e)) { rpc.RequestContextThrewOnReply = true; rpc.CanSendReply = false; } } finally { if (!success) { rpc.UnPause(); } } } internal bool Dispatch(ref MessageRpc rpc, bool isOperationContextSet) { rpc.ErrorProcessor = this.processMessage8; rpc.NextProcessor = this.processMessage1; return rpc.Process(isOperationContextSet); } void EndFinalizeCorrelation(ref MessageRpc rpc) { try { rpc.Reply = rpc.CorrelationCallback.EndFinalizeCorrelation(rpc.AsyncResult); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (!this.error.HandleError(e)) { rpc.CanSendReply = false; } } } bool EndReply(ref MessageRpc rpc) { bool success = false; try { rpc.RequestContext.EndReply(rpc.AsyncResult); rpc.RequestContextThrewOnReply = false; success = true; if (TD.DispatchMessageStopIsEnabled()) { TD.DispatchMessageStop(rpc.EventTraceActivity); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } return success; } internal void InputSessionDoneReceiving(ServiceChannel channel) { if (this.inputSessionShutdownHandlers.Length > 0) { this.InputSessionDoneReceivingCore(channel); } } void InputSessionDoneReceivingCore(ServiceChannel channel) { IDuplexContextChannel proxy = channel.Proxy as IDuplexContextChannel; if (proxy != null) { IInputSessionShutdown[] handlers = this.inputSessionShutdownHandlers; try { for (int i = 0; i < handlers.Length; i++) { handlers[i].DoneReceiving(proxy); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (!this.error.HandleError(e)) { proxy.Abort(); } } } } internal bool IsConcurrent(ref MessageRpc rpc) { return this.concurrency.IsConcurrent(ref rpc); } internal void InputSessionFaulted(ServiceChannel channel) { if (this.inputSessionShutdownHandlers.Length > 0) { this.InputSessionFaultedCore(channel); } } void InputSessionFaultedCore(ServiceChannel channel) { IDuplexContextChannel proxy = channel.Proxy as IDuplexContextChannel; if (proxy != null) { IInputSessionShutdown[] handlers = this.inputSessionShutdownHandlers; try { for (int i = 0; i < handlers.Length; i++) { handlers[i].ChannelFaulted(proxy); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (!this.error.HandleError(e)) { proxy.Abort(); } } } } static internal void GotDynamicInstanceContext(object state) { bool alreadyResumedNoLock; ((IResumeMessageRpc)state).Resume(out alreadyResumedNoLock); if (alreadyResumedNoLock) { Fx.Assert("GotDynamicInstanceContext more than once for same call."); } } void AddMessageProperties(Message message, OperationContext context, ServiceChannel replyChannel) { if (context.InternalServiceChannel == replyChannel) { if (context.HasOutgoingMessageHeaders) { message.Headers.CopyHeadersFrom(context.OutgoingMessageHeaders); } if (context.HasOutgoingMessageProperties) { message.Properties.MergeProperties(context.OutgoingMessageProperties); } } } static void OnFinalizeCorrelationCompletedCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } IResumeMessageRpc resume = result.AsyncState as IResumeMessageRpc; if (resume == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxInvalidAsyncResultState0)); } resume.Resume(result); } static void OnReplyCompletedCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } IResumeMessageRpc resume = result.AsyncState as IResumeMessageRpc; if (resume == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxInvalidAsyncResultState0)); } resume.Resume(result); } void PrepareReply(ref MessageRpc rpc) { RequestContext context = rpc.OperationContext.RequestContext; Exception exception = null; bool thereIsAnUnhandledException = false; if (!rpc.Operation.IsOneWay) { if (DiagnosticUtility.ShouldTraceWarning) { // If a service both returns null and sets RequestContext null, that // means they handled it (either by calling Close or Reply manually). // These traces catch accidents, where you accidentally return null, // or you accidentally close the context so we can't return your message. if ((rpc.Reply == null) && (context != null)) { TraceUtility.TraceEvent(System.Diagnostics.TraceEventType.Warning, TraceCode.ServiceOperationMissingReply, SR.GetString(SR.TraceCodeServiceOperationMissingReply, rpc.Operation.Name ?? String.Empty), null, null); } else if ((context == null) && (rpc.Reply != null)) { TraceUtility.TraceEvent(System.Diagnostics.TraceEventType.Warning, TraceCode.ServiceOperationMissingReplyContext, SR.GetString(SR.TraceCodeServiceOperationMissingReplyContext, rpc.Operation.Name ?? String.Empty), null, null); } } if ((context != null) && (rpc.Reply != null)) { try { rpc.CanSendReply = PrepareAndAddressReply(ref rpc); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } thereIsAnUnhandledException = (!this.error.HandleError(e)) || thereIsAnUnhandledException; exception = e; } } } this.BeforeSendReply(ref rpc, ref exception, ref thereIsAnUnhandledException); if (rpc.Operation.IsOneWay) { rpc.CanSendReply = false; } if (!rpc.Operation.IsOneWay && (context != null) && (rpc.Reply != null)) { if (exception != null) { // We don't call ProvideFault again, since we have already passed the // point where SFx addresses the reply, and it is reasonable for // ProvideFault to expect that SFx will address the reply. Instead // we always just do 'internal server error' processing. rpc.Error = exception; this.error.ProvideOnlyFaultOfLastResort(ref rpc); try { rpc.CanSendReply = PrepareAndAddressReply(ref rpc); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } } } else if ((exception != null) && thereIsAnUnhandledException) { rpc.Abort(); } } bool PrepareAndAddressReply(ref MessageRpc rpc) { bool canSendReply = true; if (!this.manualAddressing) { if (!object.ReferenceEquals(rpc.RequestID, null)) { System.ServiceModel.Channels.RequestReplyCorrelator.PrepareReply(rpc.Reply, rpc.RequestID); } if (!rpc.Channel.HasSession) { canSendReply = System.ServiceModel.Channels.RequestReplyCorrelator.AddressReply(rpc.Reply, rpc.ReplyToInfo); } } AddMessageProperties(rpc.Reply, rpc.OperationContext, rpc.Channel); if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled && rpc.EventTraceActivity != null) { rpc.Reply.Properties[EventTraceActivity.Name] = rpc.EventTraceActivity; } return canSendReply; } internal DispatchOperationRuntime GetOperation(ref Message message) { return this.demuxer.GetOperation(ref message); } internal void ProcessMessage1(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage11; if (this.receiveContextEnabledChannel) { ReceiveContextRPCFacet.CreateIfRequired(this, ref rpc); } if (!rpc.IsPaused) { this.ProcessMessage11(ref rpc); } else if (this.isOnServer && DiagnosticUtility.ShouldTraceInformation && !this.didTraceProcessMessage1) { this.didTraceProcessMessage1 = true; TraceUtility.TraceEvent( TraceEventType.Information, TraceCode.MessageProcessingPaused, SR.GetString(SR.TraceCodeProcessMessage31Paused, rpc.Channel.DispatchRuntime.EndpointDispatcher.ContractName, rpc.Channel.DispatchRuntime.EndpointDispatcher.EndpointAddress)); } } internal void ProcessMessage11(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage2; if (rpc.Operation.IsOneWay) { rpc.RequestContext.Reply(null); rpc.OperationContext.RequestContext = null; } else { if (!rpc.Channel.IsReplyChannel && ((object)rpc.RequestID == null) && (rpc.Operation.Action != MessageHeaders.WildcardAction)) { CommunicationException error = new CommunicationException(SR.GetString(SR.SFxOneWayMessageToTwoWayMethod0)); throw TraceUtility.ThrowHelperError(error, rpc.Request); } if (!this.manualAddressing) { EndpointAddress replyTo = rpc.ReplyToInfo.ReplyTo; if (replyTo != null && replyTo.IsNone && rpc.Channel.IsReplyChannel) { CommunicationException error = new CommunicationException(SR.GetString(SR.SFxRequestReplyNone)); throw TraceUtility.ThrowHelperError(error, rpc.Request); } if (this.isOnServer) { EndpointAddress remoteAddress = rpc.Channel.RemoteAddress; if ((remoteAddress != null) && !remoteAddress.IsAnonymous) { MessageHeaders headers = rpc.Request.Headers; Uri remoteUri = remoteAddress.Uri; if ((replyTo != null) && !replyTo.IsAnonymous && (remoteUri != replyTo.Uri)) { string text = SR.GetString(SR.SFxRequestHasInvalidReplyToOnServer, replyTo.Uri, remoteUri); Exception error = new InvalidOperationException(text); throw TraceUtility.ThrowHelperError(error, rpc.Request); } EndpointAddress faultTo = headers.FaultTo; if ((faultTo != null) && !faultTo.IsAnonymous && (remoteUri != faultTo.Uri)) { string text = SR.GetString(SR.SFxRequestHasInvalidFaultToOnServer, faultTo.Uri, remoteUri); Exception error = new InvalidOperationException(text); throw TraceUtility.ThrowHelperError(error, rpc.Request); } if (rpc.RequestVersion.Addressing == AddressingVersion.WSAddressingAugust2004) { EndpointAddress from = headers.From; if ((from != null) && !from.IsAnonymous && (remoteUri != from.Uri)) { string text = SR.GetString(SR.SFxRequestHasInvalidFromOnServer, from.Uri, remoteUri); Exception error = new InvalidOperationException(text); throw TraceUtility.ThrowHelperError(error, rpc.Request); } } } } } } if (this.concurrency.IsConcurrent(ref rpc)) { rpc.Channel.IncrementActivity(); rpc.SuccessfullyIncrementedActivity = true; } if (this.authenticationBehavior != null) { this.authenticationBehavior.Authenticate(ref rpc); } if (this.authorizationBehavior != null) { this.authorizationBehavior.Authorize(ref rpc); } this.instance.EnsureInstanceContext(ref rpc); this.TransferChannelFromPendingList(ref rpc); this.AcquireDynamicInstanceContext(ref rpc); if (!rpc.IsPaused) { this.ProcessMessage2(ref rpc); } } void ProcessMessage2(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage3; this.AfterReceiveRequest(ref rpc); if (!this.ignoreTransactionFlow) { // Transactions need to have the context in the message rpc.TransactionMessageProperty = TransactionMessageProperty.TryGet(rpc.Request); } this.concurrency.LockInstance(ref rpc); if (!rpc.IsPaused) { this.ProcessMessage3(ref rpc); } else if (this.isOnServer && DiagnosticUtility.ShouldTraceInformation && !this.didTraceProcessMessage2) { this.didTraceProcessMessage2 = true; TraceUtility.TraceEvent( TraceEventType.Information, TraceCode.MessageProcessingPaused, SR.GetString(SR.TraceCodeProcessMessage2Paused, rpc.Channel.DispatchRuntime.EndpointDispatcher.ContractName, rpc.Channel.DispatchRuntime.EndpointDispatcher.EndpointAddress)); } } void ProcessMessage3(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage31; rpc.SuccessfullyLockedInstance = true; // This also needs to happen after LockInstance, in case // we are using an AutoComplete=false transaction. if (this.transaction != null) { this.transaction.ResolveTransaction(ref rpc); if (rpc.Operation.TransactionRequired) { this.transaction.SetCurrent(ref rpc); } } if (!rpc.IsPaused) { this.ProcessMessage31(ref rpc); } else if (this.isOnServer && DiagnosticUtility.ShouldTraceInformation && !this.didTraceProcessMessage3) { this.didTraceProcessMessage3 = true; TraceUtility.TraceEvent( TraceEventType.Information, TraceCode.MessageProcessingPaused, SR.GetString(SR.TraceCodeProcessMessage3Paused, rpc.Channel.DispatchRuntime.EndpointDispatcher.ContractName, rpc.Channel.DispatchRuntime.EndpointDispatcher.EndpointAddress)); } } void ProcessMessage31(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage4; if (this.transaction != null) { if (rpc.Operation.TransactionRequired) { ReceiveContextRPCFacet receiveContext = rpc.ReceiveContext; if (receiveContext != null) { rpc.ReceiveContext = null; receiveContext.Complete(this, ref rpc, TimeSpan.MaxValue, rpc.Transaction.Current); } } } if (!rpc.IsPaused) { this.ProcessMessage4(ref rpc); } else if (this.isOnServer && DiagnosticUtility.ShouldTraceInformation && !this.didTraceProcessMessage31) { this.didTraceProcessMessage31 = true; TraceUtility.TraceEvent( TraceEventType.Information, TraceCode.MessageProcessingPaused, SR.GetString(SR.TraceCodeProcessMessage31Paused, rpc.Channel.DispatchRuntime.EndpointDispatcher.ContractName, rpc.Channel.DispatchRuntime.EndpointDispatcher.EndpointAddress)); } } void ProcessMessage4(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage41; try { this.thread.BindThread(ref rpc); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(e.Message, e); } if (!rpc.IsPaused) { this.ProcessMessage41(ref rpc); } else if (this.isOnServer && DiagnosticUtility.ShouldTraceInformation && !this.didTraceProcessMessage4) { this.didTraceProcessMessage4 = true; TraceUtility.TraceEvent( TraceEventType.Information, TraceCode.MessageProcessingPaused, SR.GetString(SR.TraceCodeProcessMessage4Paused, rpc.Channel.DispatchRuntime.EndpointDispatcher.ContractName, rpc.Channel.DispatchRuntime.EndpointDispatcher.EndpointAddress)); } } void ProcessMessage41(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage5; // This needs to happen after LockInstance--LockInstance guarantees // in-order delivery, so we can't receive the next message until we // have acquired the lock. // // This also needs to happen after BindThread, since EricZ believes // that running on UI thread should guarantee in-order delivery if // the SynchronizationContext is single threaded. // Note: for IManualConcurrencyOperationInvoker, the invoke assumes full control over pumping. if (this.concurrency.IsConcurrent(ref rpc) && !(rpc.Operation.Invoker is IManualConcurrencyOperationInvoker)) { rpc.EnsureReceive(); } this.instance.EnsureServiceInstance(ref rpc); if (!rpc.IsPaused) { this.ProcessMessage5(ref rpc); } else if (this.isOnServer && DiagnosticUtility.ShouldTraceInformation && !this.didTraceProcessMessage41) { this.didTraceProcessMessage41 = true; TraceUtility.TraceEvent( TraceEventType.Information, TraceCode.MessageProcessingPaused, SR.GetString(SR.TraceCodeProcessMessage4Paused, rpc.Channel.DispatchRuntime.EndpointDispatcher.ContractName, rpc.Channel.DispatchRuntime.EndpointDispatcher.EndpointAddress)); } } void ProcessMessage5(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage6; try { bool success = false; try { if (!rpc.Operation.IsSynchronous) { // If async call completes in [....], it tells us through the gate below rpc.PrepareInvokeContinueGate(); } if (this.transaction != null) { this.transaction.InitializeCallContext(ref rpc); } SetActivityIdOnThread(ref rpc); rpc.Operation.InvokeBegin(ref rpc); success = true; } finally { try { try { if (this.transaction != null) { this.transaction.ClearCallContext(ref rpc); } } finally { if (!rpc.Operation.IsSynchronous && rpc.IsPaused) { // Check if the callback produced the async result and set it back on the RPC on this stack // and proceed only if the gate was signaled by the callback and completed synchronously if (rpc.UnlockInvokeContinueGate(out rpc.AsyncResult)) { rpc.UnPause(); } } } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (success && (rpc.Operation.IsSynchronous || !rpc.IsPaused)) { throw; } this.error.HandleError(e); } } } catch { // This catch clause forces ClearCallContext to run prior to stackwalks exiting this frame. throw; } // Proceed if rpc is unpaused and invoke begin was successful. if (!rpc.IsPaused) { this.ProcessMessage6(ref rpc); } } void ProcessMessage6(ref MessageRpc rpc) { rpc.NextProcessor = (rpc.Operation.IsSynchronous) ? this.processMessage8 : this.processMessage7; try { this.thread.BindEndThread(ref rpc); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(e.Message, e); } if (!rpc.IsPaused) { if (rpc.Operation.IsSynchronous) { this.ProcessMessage8(ref rpc); } else { this.ProcessMessage7(ref rpc); } } } void ProcessMessage7(ref MessageRpc rpc) { rpc.NextProcessor = null; try { bool success = false; try { if (this.transaction != null) { this.transaction.InitializeCallContext(ref rpc); } rpc.Operation.InvokeEnd(ref rpc); success = true; } finally { try { if (this.transaction != null) { this.transaction.ClearCallContext(ref rpc); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } if (success) { // Throw the transaction.ClearContextException only if // there isn't an exception on the thread already. throw; } this.error.HandleError(e); } } } catch { // Make sure user Exception filters are not run with bad call context throw; } // this never pauses this.ProcessMessage8(ref rpc); } void ProcessMessage8(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessage9; try { this.error.ProvideMessageFault(ref rpc); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } this.PrepareReply(ref rpc); if (rpc.CanSendReply) { rpc.ReplyTimeoutHelper = new TimeoutHelper(rpc.Channel.OperationTimeout); if (this.sendAsynchronously) { this.BeginFinalizeCorrelation(ref rpc); } else { this.FinalizeCorrelation(ref rpc); } } if (!rpc.IsPaused) { this.ProcessMessage9(ref rpc); } } void ProcessMessage9(ref MessageRpc rpc) { rpc.NextProcessor = this.processMessageCleanup; if (rpc.FinalizeCorrelationImplicitly && this.sendAsynchronously) { this.EndFinalizeCorrelation(ref rpc); } if (rpc.CorrelationCallback == null || rpc.FinalizeCorrelationImplicitly) { this.ResolveTransactionOutcome(ref rpc); } if (rpc.CanSendReply) { if (rpc.Reply != null) { TraceUtility.MessageFlowAtMessageSent(rpc.Reply, rpc.EventTraceActivity); } if (this.sendAsynchronously) { this.BeginReply(ref rpc); } else { this.Reply(ref rpc); } } if (!rpc.IsPaused) { this.ProcessMessageCleanup(ref rpc); } } // Logic for knowing when to close stuff: // // ASSUMPTIONS: // Closing a stream over a message also closes the message. // Closing a message over a stream does not close the stream. // (OperationStreamProvider.ReleaseStream is no-op) // // This is a table of what should be disposed in what cases. // The rows represent the type of parameter to the method and // whether we are disposing parameters or not. The columns // are for the inputs vs. the outputs. The cells contain the // values that need to be Disposed. M^P means that exactly // one of the message and parameter needs to be disposed, // since they refer to the same object. // // Request Reply // Message | M or P | M or P // Dispose Stream | P | M and P // Params | M and P | M and P // | | // Message | none | none // NoDispose Stream | none | M // Params | M | M // // By choosing to dispose the parameter in both of the "M or P" // cases, the logic needed to generate this table is: // // CloseRequestMessage = IsParams // CloseRequestParams = rpc.Operation.DisposeParameters // CloseReplyMessage = rpc.Operation.SerializeReply // CloseReplyParams = rpc.Operation.DisposeParameters // // IsParams can be calculated based on whether the request // message was consumed after deserializing but before calling // the user. This is stored as rpc.DidDeserializeRequestBody. // void ProcessMessageCleanup(ref MessageRpc rpc) { Fx.Assert( !object.ReferenceEquals(rpc.ErrorProcessor, this.processMessageCleanupError), "ProcessMessageCleanup run twice on the same MessageRpc!"); rpc.ErrorProcessor = this.processMessageCleanupError; bool replyWasSent = false; if (rpc.CanSendReply) { if (this.sendAsynchronously) { replyWasSent = this.EndReply(ref rpc); } else { replyWasSent = rpc.SuccessfullySendReply; } } try { try { if (rpc.DidDeserializeRequestBody) { rpc.Request.Close(); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } if (rpc.HostingProperty != null) { try { rpc.HostingProperty.Close(); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(e.Message, e); } } // for wf, wf owns the lifetime of the request message. So in that case, we should not dispose the inputs IManualConcurrencyOperationInvoker manualInvoker = rpc.Operation.Invoker as IManualConcurrencyOperationInvoker; rpc.DisposeParameters(manualInvoker != null && manualInvoker.OwnsFormatter); //Dispose all input/output/return parameters if (rpc.FaultInfo.IsConsideredUnhandled) { if (!replyWasSent) { rpc.AbortRequestContext(); rpc.AbortChannel(); } else { rpc.CloseRequestContext(); rpc.CloseChannel(); } rpc.AbortInstanceContext(); } else { if (rpc.RequestContextThrewOnReply) { rpc.AbortRequestContext(); } else { rpc.CloseRequestContext(); } } if ((rpc.Reply != null) && (rpc.Reply != rpc.ReturnParameter)) { try { rpc.Reply.Close(); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } } if ((rpc.FaultInfo.Fault != null) && (rpc.FaultInfo.Fault.State != MessageState.Closed)) { // maybe ProvideFault gave a Message, but then BeforeSendReply replaced it // in that case, we need to close the one from ProvideFault try { rpc.FaultInfo.Fault.Close(); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } } try { rpc.OperationContext.FireOperationCompleted(); } #pragma warning suppress 56500 // covered by FxCOP catch (Exception e) { if (Fx.IsFatal(e)) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); } this.instance.AfterReply(ref rpc, this.error); if (rpc.SuccessfullyLockedInstance) { try { this.concurrency.UnlockInstance(ref rpc); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } Fx.Assert("Exceptions should be caught by callee"); rpc.InstanceContext.FaultInternal(); this.error.HandleError(e); } } if (this.terminate != null) { try { this.terminate.AfterReply(ref rpc); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } } if (rpc.SuccessfullyIncrementedActivity) { try { rpc.Channel.DecrementActivity(); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } } } finally { if (rpc.MessageRpcOwnsInstanceContextThrottle && rpc.channelHandler.InstanceContextServiceThrottle != null) { rpc.channelHandler.InstanceContextServiceThrottle.DeactivateInstanceContext(); } if (rpc.Activity != null && DiagnosticUtility.ShouldUseActivity) { rpc.Activity.Stop(); } } this.error.HandleError(ref rpc); } void ProcessMessageCleanupError(ref MessageRpc rpc) { this.error.HandleError(ref rpc); } void ResolveTransactionOutcome(ref MessageRpc rpc) { if (this.transaction != null) { try { bool hadError = (rpc.Error != null); try { this.transaction.ResolveOutcome(ref rpc); } catch (FaultException e) { if (rpc.Error == null) { rpc.Error = e; } } finally { if (!hadError && rpc.Error != null) { this.error.ProvideMessageFault(ref rpc); this.PrepareAndAddressReply(ref rpc); } } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } this.error.HandleError(e); } } } [Fx.Tag.SecurityNote(Critical = "Calls security critical method to set the ActivityId on the thread", Safe = "Set the ActivityId only when MessageRpc is available")] [SecuritySafeCritical] void SetActivityIdOnThread(ref MessageRpc rpc) { if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled && rpc.EventTraceActivity != null) { // Propogate the ActivityId to the service operation EventTraceActivityHelper.SetOnThread(rpc.EventTraceActivity); } } void TransferChannelFromPendingList(ref MessageRpc rpc) { if (rpc.Channel.IsPending) { rpc.Channel.IsPending = false; ChannelDispatcher channelDispatcher = rpc.Channel.ChannelDispatcher; IInstanceContextProvider provider = this.instance.InstanceContextProvider; if (!InstanceContextProviderBase.IsProviderSessionful(provider) && !InstanceContextProviderBase.IsProviderSingleton(provider)) { IChannel proxy = rpc.Channel.Proxy as IChannel; if (!rpc.InstanceContext.IncomingChannels.Contains(proxy)) { channelDispatcher.Channels.Add(proxy); } } channelDispatcher.PendingChannels.Remove(rpc.Channel.Binder.Channel); } } interface IDemuxer { DispatchOperationRuntime GetOperation(ref Message request); } class ActionDemuxer : IDemuxer { HybridDictionary map; DispatchOperationRuntime unhandled; internal ActionDemuxer() { this.map = new HybridDictionary(); } internal void Add(string action, DispatchOperationRuntime operation) { if (map.Contains(action)) { DispatchOperationRuntime existingOperation = (DispatchOperationRuntime)map[action]; throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxActionDemuxerDuplicate, existingOperation.Name, operation.Name, action))); } this.map.Add(action, operation); } internal void SetUnhandled(DispatchOperationRuntime operation) { this.unhandled = operation; } public DispatchOperationRuntime GetOperation(ref Message request) { string action = request.Headers.Action; if (action == null) { action = MessageHeaders.WildcardAction; } DispatchOperationRuntime operation = (DispatchOperationRuntime)this.map[action]; if (operation != null) { return operation; } return this.unhandled; } } class CustomDemuxer : IDemuxer { Dictionary map; IDispatchOperationSelector selector; DispatchOperationRuntime unhandled; internal CustomDemuxer(IDispatchOperationSelector selector) { this.selector = selector; this.map = new Dictionary(); } internal void Add(string name, DispatchOperationRuntime operation) { this.map.Add(name, operation); } internal void SetUnhandled(DispatchOperationRuntime operation) { this.unhandled = operation; } public DispatchOperationRuntime GetOperation(ref Message request) { string operationName = this.selector.SelectOperation(ref request); DispatchOperationRuntime operation = null; if (this.map.TryGetValue(operationName, out operation)) { return operation; } else { return this.unhandled; } } } class RpcCorrelationCallbackMessageProperty : CorrelationCallbackMessageProperty { CorrelationCallbackMessageProperty innerCallback; MessageRpc rpc; ImmutableDispatchRuntime runtime; TransactionScope scope; // This constructor should be used when creating the RPCCorrelationMessageproperty the first time // Here we copy the data & the needed data from the original callback public RpcCorrelationCallbackMessageProperty(CorrelationCallbackMessageProperty innerCallback, ImmutableDispatchRuntime runtime, ref MessageRpc rpc) : base(innerCallback) { this.innerCallback = innerCallback; this.runtime = runtime; this.rpc = rpc; } // This constructor should be used when we are making a copy from the already initialized RPCCorrelationCallbackMessageProperty public RpcCorrelationCallbackMessageProperty(RpcCorrelationCallbackMessageProperty rpcCallbackMessageProperty) : base(rpcCallbackMessageProperty) { this.innerCallback = rpcCallbackMessageProperty.innerCallback; this.runtime = rpcCallbackMessageProperty.runtime; this.rpc = rpcCallbackMessageProperty.rpc; } public override IMessageProperty CreateCopy() { return new RpcCorrelationCallbackMessageProperty(this); } protected override IAsyncResult OnBeginFinalizeCorrelation(Message message, TimeSpan timeout, AsyncCallback callback, object state) { bool success = false; this.Enter(); try { IAsyncResult result = this.innerCallback.BeginFinalizeCorrelation(message, timeout, callback, state); success = true; return result; } finally { this.Leave(success); } } protected override Message OnEndFinalizeCorrelation(IAsyncResult result) { bool success = false; this.Enter(); try { Message message = this.innerCallback.EndFinalizeCorrelation(result); success = true; return message; } finally { this.Leave(success); this.CompleteTransaction(); } } protected override Message OnFinalizeCorrelation(Message message, TimeSpan timeout) { bool success = false; this.Enter(); try { Message newMessage = this.innerCallback.FinalizeCorrelation(message, timeout); success = true; return newMessage; } finally { this.Leave(success); this.CompleteTransaction(); } } void CompleteTransaction() { this.runtime.ResolveTransactionOutcome(ref this.rpc); } void Enter() { if (this.rpc.transaction != null && this.rpc.transaction.Current != null) { this.scope = new TransactionScope(this.rpc.transaction.Current); } } void Leave(bool complete) { if (this.scope != null) { if (complete) { scope.Complete(); } scope.Dispose(); this.scope = null; } } } } }