|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Channels
{
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime;
using System.Runtime.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Description;
using System.ServiceModel.Diagnostics;
using System.ServiceModel.Diagnostics.Application;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Security;
using System.Threading;
// This class is sealed because the constructor could call Abort, which is virtual
sealed class ServiceChannel : CommunicationObject, IChannel, IClientChannel, IDuplexContextChannel, IOutputChannel, IRequestChannel, IServiceChannel
{
int activityCount = 0;
bool allowInitializationUI = true;
bool allowOutputBatching = false;
bool autoClose = true;
CallOnceManager autoDisplayUIManager;
CallOnceManager autoOpenManager;
readonly IChannelBinder binder;
readonly ChannelDispatcher channelDispatcher;
ClientRuntime clientRuntime;
readonly bool closeBinder = true;
bool closeFactory;
bool didInteractiveInitialization;
bool doneReceiving;
EndpointDispatcher endpointDispatcher;
bool explicitlyOpened;
ExtensionCollection<IContextChannel> extensions;
readonly ServiceChannelFactory factory;
readonly bool hasSession;
readonly SessionIdleManager idleManager;
InstanceContext instanceContext;
ServiceThrottle instanceContextServiceThrottle;
bool isPending;
readonly bool isReplyChannel;
EndpointAddress localAddress;
readonly MessageVersion messageVersion;
readonly bool openBinder = false;
TimeSpan operationTimeout;
object proxy;
ServiceThrottle serviceThrottle;
string terminatingOperationName;
InstanceContext wmiInstanceContext;
bool hasChannelStartedAutoClosing;
bool hasIncrementedBusyCount;
bool hasCleanedUpChannelCollections;
EventTraceActivity eventActivity;
EventHandler<UnknownMessageReceivedEventArgs> unknownMessageReceived;
ServiceChannel(IChannelBinder binder, MessageVersion messageVersion, IDefaultCommunicationTimeouts timeouts)
{
if (binder == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binder");
}
this.messageVersion = messageVersion;
this.binder = binder;
this.isReplyChannel = this.binder.Channel is IReplyChannel;
IChannel innerChannel = binder.Channel;
this.hasSession = (innerChannel is ISessionChannel<IDuplexSession>) ||
(innerChannel is ISessionChannel<IInputSession>) ||
(innerChannel is ISessionChannel<IOutputSession>);
this.IncrementActivity();
this.openBinder = (binder.Channel.State == CommunicationState.Created);
this.operationTimeout = timeouts.SendTimeout;
}
internal ServiceChannel(ServiceChannelFactory factory, IChannelBinder binder)
: this(binder, factory.MessageVersion, factory)
{
this.factory = factory;
this.clientRuntime = factory.ClientRuntime;
this.SetupInnerChannelFaultHandler();
DispatchRuntime dispatch = factory.ClientRuntime.DispatchRuntime;
if (dispatch != null)
{
this.autoClose = dispatch.AutomaticInputSessionShutdown;
}
factory.ChannelCreated(this);
}
internal ServiceChannel(IChannelBinder binder,
EndpointDispatcher endpointDispatcher,
ChannelDispatcher channelDispatcher,
SessionIdleManager idleManager)
: this(binder, channelDispatcher.MessageVersion, channelDispatcher.DefaultCommunicationTimeouts)
{
if (endpointDispatcher == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointDispatcher");
}
this.channelDispatcher = channelDispatcher;
this.endpointDispatcher = endpointDispatcher;
this.clientRuntime = endpointDispatcher.DispatchRuntime.CallbackClientRuntime;
this.SetupInnerChannelFaultHandler();
this.autoClose = endpointDispatcher.DispatchRuntime.AutomaticInputSessionShutdown;
this.isPending = true;
IDefaultCommunicationTimeouts timeouts = channelDispatcher.DefaultCommunicationTimeouts;
this.idleManager = idleManager;
if (!binder.HasSession)
this.closeBinder = false;
if (this.idleManager != null)
{
bool didIdleAbort;
this.idleManager.RegisterChannel(this, out didIdleAbort);
if (didIdleAbort)
{
this.Abort();
}
}
}
CallOnceManager AutoOpenManager
{
get
{
if (!this.explicitlyOpened && (this.autoOpenManager == null))
{
this.EnsureAutoOpenManagers();
}
return this.autoOpenManager;
}
}
CallOnceManager AutoDisplayUIManager
{
get
{
if (!this.explicitlyOpened && (this.autoDisplayUIManager == null))
{
this.EnsureAutoOpenManagers();
}
return this.autoDisplayUIManager;
}
}
internal EventTraceActivity EventActivity
{
get
{
if (this.eventActivity == null)
{
//Take the id on the thread so that we know the initiating operation.
this.eventActivity = EventTraceActivity.GetFromThreadOrCreate();
}
return this.eventActivity;
}
}
internal bool CloseFactory
{
get { return this.closeFactory; }
set { this.closeFactory = value; }
}
protected override TimeSpan DefaultCloseTimeout
{
get { return this.CloseTimeout; }
}
protected override TimeSpan DefaultOpenTimeout
{
get { return this.OpenTimeout; }
}
internal DispatchRuntime DispatchRuntime
{
get
{
if (this.endpointDispatcher != null)
{
return this.endpointDispatcher.DispatchRuntime;
}
if (this.clientRuntime != null)
{
return this.clientRuntime.DispatchRuntime;
}
return null;
}
}
internal MessageVersion MessageVersion
{
get { return this.messageVersion; }
}
internal IChannelBinder Binder
{
get { return this.binder; }
}
internal TimeSpan CloseTimeout
{
get
{
if (this.IsClient)
{
return factory.InternalCloseTimeout;
}
else
{
return this.ChannelDispatcher.InternalCloseTimeout;
}
}
}
internal ChannelDispatcher ChannelDispatcher
{
get { return this.channelDispatcher; }
}
internal EndpointDispatcher EndpointDispatcher
{
get { return this.endpointDispatcher; }
set
{
lock (this.ThisLock)
{
this.endpointDispatcher = value;
this.clientRuntime = value.DispatchRuntime.CallbackClientRuntime;
}
}
}
internal ServiceChannelFactory Factory
{
get { return this.factory; }
}
internal IChannel InnerChannel
{
get { return this.binder.Channel; }
}
internal bool IsPending
{
get { return this.isPending; }
set { this.isPending = value; }
}
internal bool HasSession
{
get { return hasSession; }
}
internal bool IsClient
{
get { return this.factory != null; }
}
internal bool IsReplyChannel
{
get { return this.isReplyChannel; }
}
public Uri ListenUri
{
get
{
return this.binder.ListenUri;
}
}
public EndpointAddress LocalAddress
{
get
{
if (this.localAddress == null)
{
if (this.endpointDispatcher != null)
{
this.localAddress = this.endpointDispatcher.EndpointAddress;
}
else
{
this.localAddress = this.binder.LocalAddress;
}
}
return this.localAddress;
}
}
internal TimeSpan OpenTimeout
{
get
{
if (this.IsClient)
{
return factory.InternalOpenTimeout;
}
else
{
return this.ChannelDispatcher.InternalOpenTimeout;
}
}
}
public TimeSpan OperationTimeout
{
get { return this.operationTimeout; }
set
{
if (value < TimeSpan.Zero)
{
string message = SR.GetString(SR.SFxTimeoutOutOfRange0);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, message));
}
if (TimeoutHelper.IsTooLarge(value))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.SFxTimeoutOutOfRangeTooBig)));
}
this.operationTimeout = value;
}
}
internal object Proxy
{
get
{
object proxy = this.proxy;
if (proxy != null)
return proxy;
else
return this;
}
set
{
this.proxy = value;
base.EventSender = value; // need to use "proxy" as open/close event source
}
}
internal ClientRuntime ClientRuntime
{
get { return this.clientRuntime; }
}
public EndpointAddress RemoteAddress
{
get
{
IOutputChannel outputChannel = this.InnerChannel as IOutputChannel;
if (outputChannel != null)
return outputChannel.RemoteAddress;
IRequestChannel requestChannel = this.InnerChannel as IRequestChannel;
if (requestChannel != null)
return requestChannel.RemoteAddress;
return null;
}
}
ProxyOperationRuntime UnhandledProxyOperation
{
get { return this.ClientRuntime.GetRuntime().UnhandledProxyOperation; }
}
public Uri Via
{
get
{
IOutputChannel outputChannel = this.InnerChannel as IOutputChannel;
if (outputChannel != null)
return outputChannel.Via;
IRequestChannel requestChannel = this.InnerChannel as IRequestChannel;
if (requestChannel != null)
return requestChannel.Via;
return null;
}
}
internal InstanceContext InstanceContext
{
get { return this.instanceContext; }
set { this.instanceContext = value; }
}
internal ServiceThrottle InstanceContextServiceThrottle
{
get { return this.instanceContextServiceThrottle; }
set { this.instanceContextServiceThrottle = value; }
}
internal ServiceThrottle ServiceThrottle
{
get { return this.serviceThrottle; }
set
{
this.ThrowIfDisposed();
this.serviceThrottle = value;
}
}
internal InstanceContext WmiInstanceContext
{
get { return this.wmiInstanceContext; }
set { this.wmiInstanceContext = value; }
}
void SetupInnerChannelFaultHandler()
{
// need to call this method after this.binder and this.clientRuntime are set to prevent a potential
// NullReferenceException in this method or in the OnInnerChannelFaulted method;
// because this method accesses this.binder and OnInnerChannelFaulted acesses this.clientRuntime.
this.binder.Channel.Faulted += OnInnerChannelFaulted;
}
void BindDuplexCallbacks()
{
IDuplexChannel duplexChannel = this.InnerChannel as IDuplexChannel;
if ((duplexChannel != null) && (this.factory != null) && (this.instanceContext != null))
{
if (this.binder is DuplexChannelBinder)
((DuplexChannelBinder)this.binder).EnsurePumping();
}
}
internal bool CanCastTo(Type t)
{
if (t.IsAssignableFrom(typeof(IClientChannel)))
return true;
if (t.IsAssignableFrom(typeof(IDuplexContextChannel)))
return this.InnerChannel is IDuplexChannel;
if (t.IsAssignableFrom(typeof(IServiceChannel)))
return true;
return false;
}
internal void CompletedIOOperation()
{
if (this.idleManager != null)
{
this.idleManager.CompletedActivity();
}
}
void EnsureAutoOpenManagers()
{
lock (this.ThisLock)
{
if (!this.explicitlyOpened)
{
if (this.autoOpenManager == null)
{
this.autoOpenManager = new CallOnceManager(this, CallOpenOnce.Instance);
}
if (this.autoDisplayUIManager == null)
{
this.autoDisplayUIManager = new CallOnceManager(this, CallDisplayUIOnce.Instance);
}
}
}
}
void EnsureDisplayUI()
{
CallOnceManager manager = this.AutoDisplayUIManager;
if (manager != null)
{
manager.CallOnce(TimeSpan.MaxValue, null);
}
this.ThrowIfInitializationUINotCalled();
}
IAsyncResult BeginEnsureDisplayUI(AsyncCallback callback, object state)
{
CallOnceManager manager = this.AutoDisplayUIManager;
if (manager != null)
{
return manager.BeginCallOnce(TimeSpan.MaxValue, null, callback, state);
}
else
{
return new CallOnceCompletedAsyncResult(callback, state);
}
}
void EndEnsureDisplayUI(IAsyncResult result)
{
CallOnceManager manager = this.AutoDisplayUIManager;
if (manager != null)
{
manager.EndCallOnce(result);
}
else
{
CallOnceCompletedAsyncResult.End(result);
}
this.ThrowIfInitializationUINotCalled();
}
void EnsureOpened(TimeSpan timeout)
{
CallOnceManager manager = this.AutoOpenManager;
if (manager != null)
{
manager.CallOnce(timeout, this.autoDisplayUIManager);
}
this.ThrowIfOpening();
this.ThrowIfDisposedOrNotOpen();
}
IAsyncResult BeginEnsureOpened(TimeSpan timeout, AsyncCallback callback, object state)
{
CallOnceManager manager = this.AutoOpenManager;
if (manager != null)
{
return manager.BeginCallOnce(timeout, this.autoDisplayUIManager, callback, state);
}
else
{
this.ThrowIfOpening();
this.ThrowIfDisposedOrNotOpen();
return new CallOnceCompletedAsyncResult(callback, state);
}
}
void EndEnsureOpened(IAsyncResult result)
{
CallOnceManager manager = this.AutoOpenManager;
if (manager != null)
{
manager.EndCallOnce(result);
}
else
{
CallOnceCompletedAsyncResult.End(result);
}
}
public T GetProperty<T>() where T : class
{
IChannel innerChannel = this.InnerChannel;
if (innerChannel != null)
return innerChannel.GetProperty<T>();
return null;
}
void PrepareCall(ProxyOperationRuntime operation, bool oneway, ref ProxyRpc rpc)
{
OperationContext context = OperationContext.Current;
// Doing a request reply callback when dispatching in-order deadlocks.
// We never receive the reply until we finish processing the current message.
if (!oneway)
{
DispatchRuntime dispatchBehavior = this.ClientRuntime.DispatchRuntime;
if ((dispatchBehavior != null) && (dispatchBehavior.ConcurrencyMode == ConcurrencyMode.Single))
{
if ((context != null) && (!context.IsUserContext) && (context.InternalServiceChannel == this))
{
if (dispatchBehavior.IsOnServer)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxCallbackRequestReplyInOrder1, typeof(ServiceBehaviorAttribute).Name)));
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxCallbackRequestReplyInOrder1, typeof(CallbackBehaviorAttribute).Name)));
}
}
}
}
if ((this.State == CommunicationState.Created) && !operation.IsInitiating)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxNonInitiatingOperation1, operation.Name)));
}
if (this.terminatingOperationName != null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxTerminatingOperationAlreadyCalled1, this.terminatingOperationName)));
}
if (this.hasChannelStartedAutoClosing)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.SFxClientOutputSessionAutoClosed)));
}
operation.BeforeRequest(ref rpc);
AddMessageProperties(rpc.Request, context);
if (!oneway && !this.ClientRuntime.ManualAddressing && rpc.Request.Version.Addressing != AddressingVersion.None)
{
RequestReplyCorrelator.PrepareRequest(rpc.Request);
MessageHeaders headers = rpc.Request.Headers;
EndpointAddress localAddress = this.LocalAddress;
EndpointAddress replyTo = headers.ReplyTo;
if (replyTo == null)
{
headers.ReplyTo = localAddress ?? EndpointAddress.AnonymousAddress;
}
if (this.IsClient && (localAddress != null) && !localAddress.IsAnonymous)
{
Uri localUri = localAddress.Uri;
if ((replyTo != null) && !replyTo.IsAnonymous && (localUri != replyTo.Uri))
{
string text = SR.GetString(SR.SFxRequestHasInvalidReplyToOnClient, replyTo.Uri, localUri);
Exception error = new InvalidOperationException(text);
throw TraceUtility.ThrowHelperError(error, rpc.Request);
}
EndpointAddress faultTo = headers.FaultTo;
if ((faultTo != null) && !faultTo.IsAnonymous && (localUri != faultTo.Uri))
{
string text = SR.GetString(SR.SFxRequestHasInvalidFaultToOnClient, faultTo.Uri, localUri);
Exception error = new InvalidOperationException(text);
throw TraceUtility.ThrowHelperError(error, rpc.Request);
}
if (this.messageVersion.Addressing == AddressingVersion.WSAddressingAugust2004)
{
EndpointAddress from = headers.From;
if ((from != null) && !from.IsAnonymous && (localUri != from.Uri))
{
string text = SR.GetString(SR.SFxRequestHasInvalidFromOnClient, from.Uri, localUri);
Exception error = new InvalidOperationException(text);
throw TraceUtility.ThrowHelperError(error, rpc.Request);
}
}
}
}
if (TraceUtility.MessageFlowTracingOnly)
{
//always set a new ID if none provided
if (Trace.CorrelationManager.ActivityId == Guid.Empty)
{
rpc.ActivityId = Guid.NewGuid();
FxTrace.Trace.SetAndTraceTransfer(rpc.ActivityId, true);
}
}
if (rpc.Activity != null)
{
TraceUtility.SetActivity(rpc.Request, rpc.Activity);
if (TraceUtility.ShouldPropagateActivity)
{
TraceUtility.AddActivityHeader(rpc.Request);
}
}
else if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity)
{
TraceUtility.AddAmbientActivityToMessage(rpc.Request);
}
operation.Parent.BeforeSendRequest(ref rpc);
//Attach and transfer Activity
if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
{
TraceClientOperationPrepared(ref rpc);
}
TraceUtility.MessageFlowAtMessageSent(rpc.Request, rpc.EventTraceActivity);
if (MessageLogger.LogMessagesAtServiceLevel)
{
MessageLogger.LogMessage(ref rpc.Request, (oneway ? MessageLoggingSource.ServiceLevelSendDatagram : MessageLoggingSource.ServiceLevelSendRequest) | MessageLoggingSource.LastChance);
}
}
private void TraceClientOperationPrepared(ref ProxyRpc rpc)
{
//Retrieve the old id on the RPC and attach the id on the message since we have a message id now.
Guid previousId = rpc.EventTraceActivity != null ? rpc.EventTraceActivity.ActivityId : Guid.Empty;
EventTraceActivity requestActivity = EventTraceActivityHelper.TryExtractActivity(rpc.Request);
if (requestActivity == null)
{
requestActivity = EventTraceActivity.GetFromThreadOrCreate();
EventTraceActivityHelper.TryAttachActivity(rpc.Request, requestActivity);
}
rpc.EventTraceActivity = requestActivity;
if (TD.ClientOperationPreparedIsEnabled())
{
string remoteAddress = string.Empty;
if (this.RemoteAddress != null && this.RemoteAddress.Uri != null)
{
remoteAddress = this.RemoteAddress.Uri.AbsoluteUri;
}
TD.ClientOperationPrepared(rpc.EventTraceActivity,
rpc.Action,
this.clientRuntime.ContractName,
remoteAddress,
previousId);
}
}
internal static IAsyncResult BeginCall(ServiceChannel channel, ProxyOperationRuntime operation, object[] ins, AsyncCallback callback, object asyncState)
{
Fx.Assert(channel != null, "'channel' MUST NOT be NULL.");
Fx.Assert(operation != null, "'operation' MUST NOT be NULL.");
return channel.BeginCall(operation.Action, operation.IsOneWay, operation, ins, channel.operationTimeout, callback, asyncState);
}
internal IAsyncResult BeginCall(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, AsyncCallback callback, object asyncState)
{
return this.BeginCall(action, oneway, operation, ins, this.operationTimeout, callback, asyncState);
}
internal IAsyncResult BeginCall(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, TimeSpan timeout, AsyncCallback callback, object asyncState)
{
this.ThrowIfDisallowedInitializationUI();
this.ThrowIfIdleAborted(operation);
this.ThrowIfIsConnectionOpened(operation);
ServiceModelActivity serviceModelActivity = null;
if (DiagnosticUtility.ShouldUseActivity)
{
serviceModelActivity = ServiceModelActivity.CreateActivity(true);
callback = TraceUtility.WrapExecuteUserCodeAsyncCallback(callback);
}
SendAsyncResult result;
using (Activity boundOperation = ServiceModelActivity.BoundOperation(serviceModelActivity, true))
{
if (DiagnosticUtility.ShouldUseActivity)
{
ServiceModelActivity.Start(serviceModelActivity, SR.GetString(SR.ActivityProcessAction, action), ActivityType.ProcessAction);
}
result = new SendAsyncResult(this, operation, action, ins, oneway, timeout, callback, asyncState);
if (DiagnosticUtility.ShouldUseActivity)
{
result.Rpc.Activity = serviceModelActivity;
}
TraceServiceChannelCallStart(result.Rpc.EventTraceActivity, false);
result.Begin();
}
return result;
}
internal object Call(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, object[] outs)
{
return this.Call(action, oneway, operation, ins, outs, this.operationTimeout);
}
internal object Call(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, object[] outs, TimeSpan timeout)
{
this.ThrowIfDisallowedInitializationUI();
this.ThrowIfIdleAborted(operation);
this.ThrowIfIsConnectionOpened(operation);
ProxyRpc rpc = new ProxyRpc(this, operation, action, ins, timeout);
TraceServiceChannelCallStart(rpc.EventTraceActivity, true);
using (rpc.Activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
{
if (DiagnosticUtility.ShouldUseActivity)
{
ServiceModelActivity.Start(rpc.Activity, SR.GetString(SR.ActivityProcessAction, action), ActivityType.ProcessAction);
}
this.PrepareCall(operation, oneway, ref rpc);
if (!this.explicitlyOpened)
{
this.EnsureDisplayUI();
this.EnsureOpened(rpc.TimeoutHelper.RemainingTime());
}
else
{
this.ThrowIfOpening();
this.ThrowIfDisposedOrNotOpen();
}
try
{
ConcurrencyBehavior.UnlockInstanceBeforeCallout(OperationContext.Current);
if (oneway)
{
this.binder.Send(rpc.Request, rpc.TimeoutHelper.RemainingTime());
}
else
{
rpc.Reply = this.binder.Request(rpc.Request, rpc.TimeoutHelper.RemainingTime());
if (rpc.Reply == null)
{
this.ThrowIfFaulted();
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.GetString(SR.SFxServerDidNotReply)));
}
}
}
finally
{
this.CompletedIOOperation();
CallOnceManager.SignalNextIfNonNull(this.autoOpenManager);
ConcurrencyBehavior.LockInstanceAfterCallout(OperationContext.Current);
}
rpc.OutputParameters = outs;
this.HandleReply(operation, ref rpc);
}
return rpc.ReturnValue;
}
internal object EndCall(string action, object[] outs, IAsyncResult result)
{
SendAsyncResult sendResult = result as SendAsyncResult;
if (sendResult == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxInvalidCallbackIAsyncResult)));
using (ServiceModelActivity rpcActivity = sendResult.Rpc.Activity)
{
using (ServiceModelActivity.BoundOperation(rpcActivity, true))
{
if (sendResult.Rpc.Activity != null && DiagnosticUtility.ShouldUseActivity)
{
sendResult.Rpc.Activity.Resume();
}
if (sendResult.Rpc.Channel != this)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SR.GetString(SR.AsyncEndCalledOnWrongChannel));
if (action != MessageHeaders.WildcardAction && action != sendResult.Rpc.Action)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SR.GetString(SR.AsyncEndCalledWithAnIAsyncResult));
SendAsyncResult.End(sendResult);
sendResult.Rpc.OutputParameters = outs;
this.HandleReply(sendResult.Rpc.Operation, ref sendResult.Rpc);
if (sendResult.Rpc.Activity != null)
{
sendResult.Rpc.Activity = null;
}
return sendResult.Rpc.ReturnValue;
}
}
}
internal void DecrementActivity()
{
int updatedActivityCount = Interlocked.Decrement(ref this.activityCount);
if (!((updatedActivityCount >= 0)))
{
throw Fx.AssertAndThrowFatal("ServiceChannel.DecrementActivity: (updatedActivityCount >= 0)");
}
if (updatedActivityCount == 0 && this.autoClose)
{
try
{
if (this.State == CommunicationState.Opened)
{
if (this.IsClient)
{
ISessionChannel<IDuplexSession> duplexSessionChannel = this.InnerChannel as ISessionChannel<IDuplexSession>;
if (duplexSessionChannel != null)
{
this.hasChannelStartedAutoClosing = true;
duplexSessionChannel.Session.CloseOutputSession(this.CloseTimeout);
}
}
else
{
this.Close(this.CloseTimeout);
}
}
}
catch (CommunicationException e)
{
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
}
catch (TimeoutException e)
{
if (TD.CloseTimeoutIsEnabled())
{
TD.CloseTimeout(e.Message);
}
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
}
catch (ObjectDisposedException e)
{
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
}
catch (InvalidOperationException e)
{
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
}
}
}
internal void FireUnknownMessageReceived(Message message)
{
EventHandler<UnknownMessageReceivedEventArgs> handler = this.unknownMessageReceived;
if (handler != null)
handler(this.proxy, new UnknownMessageReceivedEventArgs(message));
}
TimeoutException GetOpenTimeoutException(TimeSpan timeout)
{
EndpointAddress address = this.RemoteAddress ?? this.LocalAddress;
if (address != null)
{
return new TimeoutException(SR.GetString(SR.TimeoutServiceChannelConcurrentOpen2, address, timeout));
}
else
{
return new TimeoutException(SR.GetString(SR.TimeoutServiceChannelConcurrentOpen1, timeout));
}
}
internal void HandleReceiveComplete(RequestContext context)
{
if (context == null && HasSession)
{
bool first;
lock (this.ThisLock)
{
first = !this.doneReceiving;
this.doneReceiving = true;
}
if (first)
{
DispatchRuntime dispatchBehavior = this.ClientRuntime.DispatchRuntime;
if (dispatchBehavior != null)
dispatchBehavior.GetRuntime().InputSessionDoneReceiving(this);
this.DecrementActivity();
}
}
}
void HandleReply(ProxyOperationRuntime operation, ref ProxyRpc rpc)
{
try
{
//set the ID after response
if (TraceUtility.MessageFlowTracingOnly && rpc.ActivityId != Guid.Empty)
{
System.Runtime.Diagnostics.DiagnosticTraceBase.ActivityId = rpc.ActivityId;
}
if (rpc.Reply != null)
{
TraceUtility.MessageFlowAtMessageReceived(rpc.Reply, null, rpc.EventTraceActivity, false);
if (MessageLogger.LogMessagesAtServiceLevel)
{
MessageLogger.LogMessage(ref rpc.Reply, MessageLoggingSource.ServiceLevelReceiveReply | MessageLoggingSource.LastChance);
}
operation.Parent.AfterReceiveReply(ref rpc);
if ((operation.ReplyAction != MessageHeaders.WildcardAction) && !rpc.Reply.IsFault && rpc.Reply.Headers.Action != null)
{
if (String.CompareOrdinal(operation.ReplyAction, rpc.Reply.Headers.Action) != 0)
{
Exception error = new ProtocolException(SR.GetString(SR.SFxReplyActionMismatch3,
operation.Name,
rpc.Reply.Headers.Action,
operation.ReplyAction));
this.TerminateIfNecessary(ref rpc);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
}
}
if (operation.DeserializeReply && clientRuntime.IsFault(ref rpc.Reply))
{
MessageFault fault = MessageFault.CreateFault(rpc.Reply, this.clientRuntime.MaxFaultSize);
string action = rpc.Reply.Headers.Action;
if (action == rpc.Reply.Version.Addressing.DefaultFaultAction)
{
action = null;
}
ThrowIfFaultUnderstood(rpc.Reply, fault, action, rpc.Reply.Version, rpc.Channel.GetProperty<FaultConverter>());
FaultException fe = rpc.Operation.FaultFormatter.Deserialize(fault, action);
this.TerminateIfNecessary(ref rpc);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(fe);
}
operation.AfterReply(ref rpc);
}
}
finally
{
if (operation.SerializeRequest)
{
rpc.Request.Close();
}
OperationContext operationContext = OperationContext.Current;
bool consumed = ((rpc.Reply != null) && (rpc.Reply.State != MessageState.Created));
if ((operationContext != null) && operationContext.IsUserContext)
{
operationContext.SetClientReply(rpc.Reply, consumed);
}
else if (consumed)
{
rpc.Reply.Close();
}
if (TraceUtility.MessageFlowTracingOnly)
{
if (rpc.ActivityId != Guid.Empty)
{
//reset the ID as it was created internally - ensures each call is uniquely correlatable
System.Runtime.Diagnostics.DiagnosticTraceBase.ActivityId = Guid.Empty;
rpc.ActivityId = Guid.Empty;
}
}
}
this.TerminateIfNecessary(ref rpc);
if (TD.ServiceChannelCallStopIsEnabled())
{
string remoteAddress = string.Empty;
if (this.RemoteAddress != null && this.RemoteAddress.Uri != null)
{
remoteAddress = this.RemoteAddress.Uri.AbsoluteUri;
}
TD.ServiceChannelCallStop(rpc.EventTraceActivity, rpc.Action,
this.clientRuntime.ContractName,
remoteAddress);
}
}
void TerminateIfNecessary(ref ProxyRpc rpc)
{
if (rpc.Operation.IsTerminating)
{
this.terminatingOperationName = rpc.Operation.Name;
TerminatingOperationBehavior.AfterReply(ref rpc);
}
}
void ThrowIfFaultUnderstood(Message reply, MessageFault fault, string action, MessageVersion version, FaultConverter faultConverter)
{
Exception exception;
if (faultConverter != null && faultConverter.TryCreateException(reply, fault, out exception))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(exception);
}
bool checkSender;
bool checkReceiver;
FaultCode code;
if (version.Envelope == EnvelopeVersion.Soap11)
{
checkSender = true;
checkReceiver = true;
code = fault.Code;
}
else
{
checkSender = fault.Code.IsSenderFault;
checkReceiver = fault.Code.IsReceiverFault;
code = fault.Code.SubCode;
}
if (code == null)
{
return;
}
if (code.Namespace == null)
{
return;
}
if (checkSender)
{
if (string.Compare(code.Namespace, FaultCodeConstants.Namespaces.NetDispatch, StringComparison.Ordinal) == 0)
{
if (string.Compare(code.Name, FaultCodeConstants.Codes.SessionTerminated, StringComparison.Ordinal) == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ChannelTerminatedException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text));
}
if (string.Compare(code.Name, FaultCodeConstants.Codes.TransactionAborted, StringComparison.Ordinal) == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ProtocolException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text));
}
}
// throw SecurityAccessDeniedException explicitly
if (string.Compare(code.Namespace, SecurityVersion.Default.HeaderNamespace.Value, StringComparison.Ordinal) == 0)
{
if (string.Compare(code.Name, SecurityVersion.Default.FailedAuthenticationFaultCode.Value, StringComparison.Ordinal) == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SecurityAccessDeniedException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text));
}
}
}
if (checkReceiver)
{
if (string.Compare(code.Namespace, FaultCodeConstants.Namespaces.NetDispatch, StringComparison.Ordinal) == 0)
{
if (string.Compare(code.Name, FaultCodeConstants.Codes.InternalServiceFault, StringComparison.Ordinal) == 0)
{
if (this.HasSession)
{
this.Fault();
}
if (fault.HasDetail)
{
ExceptionDetail detail = fault.GetDetail<ExceptionDetail>();
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new FaultException<ExceptionDetail>(detail, fault.Reason, fault.Code, action));
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new FaultException(fault, action));
}
if (string.Compare(code.Name, FaultCodeConstants.Codes.DeserializationFailed, StringComparison.Ordinal) == 0)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ProtocolException(
fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text));
}
}
}
}
void ThrowIfIdleAborted(ProxyOperationRuntime operation)
{
if (this.idleManager != null && this.idleManager.DidIdleAbort)
{
string text = SR.GetString(SR.SFxServiceChannelIdleAborted, operation.Name);
Exception error = new CommunicationObjectAbortedException(text);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
}
}
void ThrowIfIsConnectionOpened(ProxyOperationRuntime operation)
{
if (operation.IsSessionOpenNotificationEnabled)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SR.GetString(SR.SFxServiceChannelCannotBeCalledBecauseIsSessionOpenNotificationEnabled, operation.Name, "Action", OperationDescription.SessionOpenedAction, "Open")));
}
}
void ThrowIfInitializationUINotCalled()
{
if (!this.didInteractiveInitialization && (this.ClientRuntime.InteractiveChannelInitializers.Count > 0))
{
IInteractiveChannelInitializer example = this.ClientRuntime.InteractiveChannelInitializers[0];
string text = SR.GetString(SR.SFxInitializationUINotCalled, example.GetType().ToString());
Exception error = new InvalidOperationException(text);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
}
}
void ThrowIfDisallowedInitializationUI()
{
if (!this.allowInitializationUI)
{
this.ThrowIfDisallowedInitializationUICore();
}
}
void ThrowIfDisallowedInitializationUICore()
{
if (this.ClientRuntime.InteractiveChannelInitializers.Count > 0)
{
IInteractiveChannelInitializer example = this.ClientRuntime.InteractiveChannelInitializers[0];
string text = SR.GetString(SR.SFxInitializationUIDisallowed, example.GetType().ToString());
Exception error = new InvalidOperationException(text);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
}
}
void ThrowIfOpening()
{
if (this.State == CommunicationState.Opening)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxCannotCallAutoOpenWhenExplicitOpenCalled)));
}
}
internal void IncrementActivity()
{
Interlocked.Increment(ref this.activityCount);
}
void OnInnerChannelFaulted(object sender, EventArgs e)
{
this.Fault();
if (this.HasSession)
{
DispatchRuntime dispatchRuntime = this.ClientRuntime.DispatchRuntime;
if (dispatchRuntime != null)
{
dispatchRuntime.GetRuntime().InputSessionFaulted(this);
}
}
if (this.autoClose && !this.IsClient)
{
this.Abort();
}
}
void AddMessageProperties(Message message, OperationContext context)
{
if (this.allowOutputBatching)
{
message.Properties.AllowOutputBatching = true;
}
if (context != null && context.InternalServiceChannel == this)
{
if (!context.OutgoingMessageVersion.IsMatch(message.Headers.MessageVersion))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SR.GetString(SR.SFxVersionMismatchInOperationContextAndMessage2, context.OutgoingMessageVersion, message.Headers.MessageVersion)
));
}
if (context.HasOutgoingMessageHeaders)
{
message.Headers.CopyHeadersFrom(context.OutgoingMessageHeaders);
}
if (context.HasOutgoingMessageProperties)
{
message.Properties.CopyProperties(context.OutgoingMessageProperties);
}
}
}
#region IChannel Members
public void Send(Message message)
{
this.Send(message, this.OperationTimeout);
}
public void Send(Message message, TimeSpan timeout)
{
ProxyOperationRuntime operation = UnhandledProxyOperation;
this.Call(message.Headers.Action, true, operation, new object[] { message }, EmptyArray<object>.Instance, timeout);
}
public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state)
{
return this.BeginSend(message, this.OperationTimeout, callback, state);
}
public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
{
ProxyOperationRuntime operation = UnhandledProxyOperation;
return this.BeginCall(message.Headers.Action, true, operation, new object[] { message }, timeout, callback, state);
}
public void EndSend(IAsyncResult result)
{
this.EndCall(MessageHeaders.WildcardAction, EmptyArray<object>.Instance, result);
}
public Message Request(Message message)
{
return this.Request(message, this.OperationTimeout);
}
public Message Request(Message message, TimeSpan timeout)
{
ProxyOperationRuntime operation = UnhandledProxyOperation;
return (Message)this.Call(message.Headers.Action, false, operation, new object[] { message }, EmptyArray<object>.Instance, timeout);
}
public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
{
return this.BeginRequest(message, this.OperationTimeout, callback, state);
}
public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
{
ProxyOperationRuntime operation = this.UnhandledProxyOperation;
return this.BeginCall(message.Headers.Action, false, operation, new object[] { message }, timeout, callback, state);
}
public Message EndRequest(IAsyncResult result)
{
return (Message)this.EndCall(MessageHeaders.WildcardAction, EmptyArray<object>.Instance, result);
}
protected override void OnAbort()
{
if (this.idleManager != null)
{
this.idleManager.CancelTimer();
}
this.binder.Abort();
if (this.factory != null)
this.factory.ChannelDisposed(this);
if (this.closeFactory)
{
if (this.factory != null)
this.factory.Abort();
}
CleanupChannelCollections();
ServiceThrottle serviceThrottle = this.serviceThrottle;
if (serviceThrottle != null)
serviceThrottle.DeactivateChannel();
//rollback the attached transaction if one is present
if ((this.instanceContext != null) && this.HasSession)
{
if (instanceContext.HasTransaction)
{
instanceContext.Transaction.CompletePendingTransaction(instanceContext.Transaction.Attached, new Exception()); // error!=null forces Tx rollback
}
}
DecrementBusyCount();
}
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
{
if (this.idleManager != null)
{
this.idleManager.CancelTimer();
}
if (this.factory != null)
{
this.factory.ChannelDisposed(this);
}
if (this.InstanceContext != null && this.InstanceContext.HasTransaction)
{
this.InstanceContext.CompleteAttachedTransaction();
}
if (this.closeBinder)
{
if (this.closeFactory)
{
return new ChainedAsyncResult(timeout, callback, state,
new ChainedBeginHandler(this.InnerChannel.BeginClose), new ChainedEndHandler(this.InnerChannel.EndClose),
new ChainedBeginHandler(this.factory.BeginClose), new ChainedEndHandler(this.factory.EndClose));
}
else
{
return this.InnerChannel.BeginClose(timeout, callback, state);
}
}
else
{
if (this.closeFactory)
return factory.BeginClose(timeout, callback, state);
else
return new CompletedAsyncResult(callback, state);
}
}
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
this.ThrowIfDisallowedInitializationUI();
this.ThrowIfInitializationUINotCalled();
if (this.autoOpenManager == null)
{
this.explicitlyOpened = true;
}
if (this.HasSession && !this.IsClient)
{
IncrementBusyCount();
}
this.TraceChannelOpenStarted();
if (this.openBinder)
{
return this.InnerChannel.BeginOpen(timeout, callback, state);
}
else
{
return new CompletedAsyncResult(callback, state);
}
}
protected override void OnClose(TimeSpan timeout)
{
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
if (this.idleManager != null)
{
this.idleManager.CancelTimer();
}
if (this.factory != null)
{
this.factory.ChannelDisposed(this);
}
if (this.InstanceContext != null && this.InstanceContext.HasTransaction)
{
this.InstanceContext.CompleteAttachedTransaction();
}
if (this.closeBinder)
this.InnerChannel.Close(timeoutHelper.RemainingTime());
if (this.closeFactory)
this.factory.Close(timeoutHelper.RemainingTime());
CleanupChannelCollections();
ServiceThrottle serviceThrottle = this.serviceThrottle;
if (serviceThrottle != null)
{
serviceThrottle.DeactivateChannel();
}
DecrementBusyCount();
}
protected override void OnEndClose(IAsyncResult result)
{
if (this.closeBinder)
{
if (this.closeFactory)
ChainedAsyncResult.End(result);
else
this.InnerChannel.EndClose(result);
}
else
{
if (this.closeFactory)
factory.EndClose(result);
else
CompletedAsyncResult.End(result);
}
CleanupChannelCollections();
ServiceThrottle serviceThrottle = this.serviceThrottle;
if (serviceThrottle != null)
{
serviceThrottle.DeactivateChannel();
}
DecrementBusyCount();
}
protected override void OnEndOpen(IAsyncResult result)
{
if (this.openBinder)
InnerChannel.EndOpen(result);
else
CompletedAsyncResult.End(result);
this.BindDuplexCallbacks();
this.CompletedIOOperation();
this.TraceChannelOpenCompleted();
}
protected override void OnOpen(TimeSpan timeout)
{
this.ThrowIfDisallowedInitializationUI();
this.ThrowIfInitializationUINotCalled();
if (this.autoOpenManager == null)
{
this.explicitlyOpened = true;
}
if (this.HasSession && !this.IsClient)
{
IncrementBusyCount();
}
this.TraceChannelOpenStarted();
if (this.openBinder)
{
this.InnerChannel.Open(timeout);
}
this.BindDuplexCallbacks();
this.CompletedIOOperation();
this.TraceChannelOpenCompleted();
}
void CleanupChannelCollections()
{
if (!this.hasCleanedUpChannelCollections)
{
lock (this.ThisLock)
{
if (!this.hasCleanedUpChannelCollections)
{
if (this.InstanceContext != null && this.InstanceContext.State != CommunicationState.Closed && this.InstanceContext.State != CommunicationState.Faulted)
{
//Only attempt to remove the channel if the state is not closed or faulted.
try
{
this.InstanceContext.OutgoingChannels.Remove((IChannel)this.proxy);
}
catch (CommunicationException) { } // Race condition if InstanceContext was faulted since the state check. Ignoring Exception
catch (ObjectDisposedException) { } // Race condition if InstanceContext was closed since the state check. Ignoring Exception
}
if (this.WmiInstanceContext != null)
{
this.WmiInstanceContext.WmiChannels.Remove((IChannel)this.proxy);
}
this.hasCleanedUpChannelCollections = true;
}
}
}
}
void IncrementBusyCount()
{
lock (this.ThisLock)
{
if (this.State == CommunicationState.Opening)
{
AspNetEnvironment.Current.IncrementBusyCount();
if (AspNetEnvironment.Current.TraceIncrementBusyCountIsEnabled())
{
AspNetEnvironment.Current.TraceIncrementBusyCount(this.GetType().FullName);
}
this.hasIncrementedBusyCount = true;
}
}
}
void DecrementBusyCount()
{
lock (this.ThisLock)
{
if (this.hasIncrementedBusyCount)
{
AspNetEnvironment.Current.DecrementBusyCount();
if (AspNetEnvironment.Current.TraceDecrementBusyCountIsEnabled())
{
AspNetEnvironment.Current.TraceDecrementBusyCount(this.GetType().FullName);
}
this.hasIncrementedBusyCount = false;
}
}
}
#endregion
#region IClientChannel Members
bool IDuplexContextChannel.AutomaticInputSessionShutdown
{
get { return this.autoClose; }
set { this.autoClose = value; }
}
bool IClientChannel.AllowInitializationUI
{
get { return this.allowInitializationUI; }
set
{
this.ThrowIfDisposedOrImmutable();
this.allowInitializationUI = value;
}
}
bool IContextChannel.AllowOutputBatching
{
get { return this.allowOutputBatching; }
set { this.allowOutputBatching = value; }
}
bool IClientChannel.DidInteractiveInitialization
{
get { return this.didInteractiveInitialization; }
}
IAsyncResult IDuplexContextChannel.BeginCloseOutputSession(TimeSpan timeout, AsyncCallback callback, object state)
{
return GetDuplexSessionOrThrow().BeginCloseOutputSession(timeout, callback, state);
}
void IDuplexContextChannel.EndCloseOutputSession(IAsyncResult result)
{
GetDuplexSessionOrThrow().EndCloseOutputSession(result);
}
void IDuplexContextChannel.CloseOutputSession(TimeSpan timeout)
{
GetDuplexSessionOrThrow().CloseOutputSession(timeout);
}
IDuplexSession GetDuplexSessionOrThrow()
{
if (this.InnerChannel == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.channelIsNotAvailable0)));
}
ISessionChannel<IDuplexSession> duplexSessionChannel = this.InnerChannel as ISessionChannel<IDuplexSession>;
if (duplexSessionChannel == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.channelDoesNotHaveADuplexSession0)));
}
return duplexSessionChannel.Session;
}
IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions
{
get
{
lock (this.ThisLock)
{
if (this.extensions == null)
this.extensions = new ExtensionCollection<IContextChannel>((IContextChannel)this.Proxy, this.ThisLock);
return this.extensions;
}
}
}
InstanceContext IDuplexContextChannel.CallbackInstance
{
get { return this.instanceContext; }
set
{
lock (this.ThisLock)
{
if (this.instanceContext != null)
{
this.instanceContext.OutgoingChannels.Remove((IChannel)this.proxy);
}
this.instanceContext = value;
if (this.instanceContext != null)
{
this.instanceContext.OutgoingChannels.Add((IChannel)this.proxy);
}
}
}
}
IInputSession IContextChannel.InputSession
{
get
{
if (this.InnerChannel != null)
{
ISessionChannel<IInputSession> inputSession = this.InnerChannel as ISessionChannel<IInputSession>;
if (inputSession != null)
return inputSession.Session;
ISessionChannel<IDuplexSession> duplexSession = this.InnerChannel as ISessionChannel<IDuplexSession>;
if (duplexSession != null)
return duplexSession.Session;
}
return null;
}
}
IOutputSession IContextChannel.OutputSession
{
get
{
if (this.InnerChannel != null)
{
ISessionChannel<IOutputSession> outputSession = this.InnerChannel as ISessionChannel<IOutputSession>;
if (outputSession != null)
return outputSession.Session;
ISessionChannel<IDuplexSession> duplexSession = this.InnerChannel as ISessionChannel<IDuplexSession>;
if (duplexSession != null)
return duplexSession.Session;
}
return null;
}
}
string IContextChannel.SessionId
{
get
{
if (this.InnerChannel != null)
{
ISessionChannel<IInputSession> inputSession = this.InnerChannel as ISessionChannel<IInputSession>;
if (inputSession != null)
return inputSession.Session.Id;
ISessionChannel<IOutputSession> outputSession = this.InnerChannel as ISessionChannel<IOutputSession>;
if (outputSession != null)
return outputSession.Session.Id;
ISessionChannel<IDuplexSession> duplexSession = this.InnerChannel as ISessionChannel<IDuplexSession>;
if (duplexSession != null)
return duplexSession.Session.Id;
}
return null;
}
}
event EventHandler<UnknownMessageReceivedEventArgs> IClientChannel.UnknownMessageReceived
{
add
{
lock (this.ThisLock)
{
this.unknownMessageReceived += value;
}
}
remove
{
lock (this.ThisLock)
{
this.unknownMessageReceived -= value;
}
}
}
public void DisplayInitializationUI()
{
this.ThrowIfDisallowedInitializationUI();
if (this.autoDisplayUIManager == null)
{
this.explicitlyOpened = true;
}
this.ClientRuntime.GetRuntime().DisplayInitializationUI(this);
this.didInteractiveInitialization = true;
}
public IAsyncResult BeginDisplayInitializationUI(AsyncCallback callback, object state)
{
this.ThrowIfDisallowedInitializationUI();
if (this.autoDisplayUIManager == null)
{
this.explicitlyOpened = true;
}
return this.ClientRuntime.GetRuntime().BeginDisplayInitializationUI(this, callback, state);
}
public void EndDisplayInitializationUI(IAsyncResult result)
{
this.ClientRuntime.GetRuntime().EndDisplayInitializationUI(result);
this.didInteractiveInitialization = true;
}
void IDisposable.Dispose()
{
this.Close();
}
#endregion
void TraceChannelOpenStarted()
{
if (TD.ClientChannelOpenStartIsEnabled() && this.endpointDispatcher == null)
{
TD.ClientChannelOpenStart(this.EventActivity);
}
else if (TD.ServiceChannelOpenStartIsEnabled())
{
TD.ServiceChannelOpenStart(this.EventActivity);
}
if (DiagnosticUtility.ShouldTraceInformation)
{
Dictionary<string, string> values = new Dictionary<string, string>(4);
bool traceNeeded = false;
DispatchRuntime behavior = this.DispatchRuntime;
if (behavior != null)
{
if (behavior.Type != null)
{
values["ServiceType"] = behavior.Type.AssemblyQualifiedName;
}
values["ContractNamespace"] = this.clientRuntime.ContractNamespace;
values["ContractName"] = this.clientRuntime.ContractName;
traceNeeded = true;
}
if ((this.endpointDispatcher != null) && (this.endpointDispatcher.ListenUri != null))
{
values["Uri"] = this.endpointDispatcher.ListenUri.ToString();
traceNeeded = true;
}
if (traceNeeded)
{
TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.ServiceChannelLifetime,
SR.GetString(SR.TraceCodeServiceChannelLifetime),
new DictionaryTraceRecord(values), this, null);
}
}
}
void TraceChannelOpenCompleted()
{
if (this.endpointDispatcher == null && TD.ClientChannelOpenStopIsEnabled())
{
TD.ClientChannelOpenStop(this.EventActivity);
}
else if (TD.ServiceChannelOpenStopIsEnabled())
{
TD.ServiceChannelOpenStop(this.EventActivity);
}
}
static void TraceServiceChannelCallStart(EventTraceActivity eventTraceActivity, bool isSynchronous)
{
if (TD.ServiceChannelCallStartIsEnabled())
{
if (isSynchronous)
{
TD.ServiceChannelCallStart(eventTraceActivity);
}
else
{
TD.ServiceChannelBeginCallStart(eventTraceActivity);
}
}
}
// Invariants for signalling the CallOnce manager.
//
// 1) If a Call, BeginCall, or EndCall on the channel throws,
// the manager will SignalNext itself.
// 2) If a Waiter times out, it will SignalNext its manager
// once it is both timed out and signalled.
// 3) Once Call or EndCall returns successfully, it guarantees
// that SignalNext will be called once the // next stage
// has sufficiently completed.
class SendAsyncResult : TraceAsyncResult
{
readonly bool isOneWay;
readonly ProxyOperationRuntime operation;
internal ProxyRpc Rpc;
OperationContext operationContext;
static AsyncCallback ensureInteractiveInitCallback = Fx.ThunkCallback(EnsureInteractiveInitCallback);
static AsyncCallback ensureOpenCallback = Fx.ThunkCallback(EnsureOpenCallback);
static AsyncCallback sendCallback = Fx.ThunkCallback(SendCallback);
internal SendAsyncResult(ServiceChannel channel, ProxyOperationRuntime operation,
string action, object[] inputParameters, bool isOneWay, TimeSpan timeout,
AsyncCallback userCallback, object userState)
: base(userCallback, userState)
{
this.Rpc = new ProxyRpc(channel, operation, action, inputParameters, timeout);
this.isOneWay = isOneWay;
this.operation = operation;
this.operationContext = OperationContext.Current;
}
internal void Begin()
{
this.Rpc.Channel.PrepareCall(this.operation, this.isOneWay, ref this.Rpc);
if (this.Rpc.Channel.explicitlyOpened)
{
this.Rpc.Channel.ThrowIfOpening();
this.Rpc.Channel.ThrowIfDisposedOrNotOpen();
this.StartSend(true);
}
else
{
this.StartEnsureInteractiveInit();
}
}
void StartEnsureInteractiveInit()
{
IAsyncResult result = this.Rpc.Channel.BeginEnsureDisplayUI(ensureInteractiveInitCallback, this);
if (result.CompletedSynchronously)
{
this.FinishEnsureInteractiveInit(result, true);
}
}
static void EnsureInteractiveInitCallback(IAsyncResult result)
{
if (!result.CompletedSynchronously)
{
((SendAsyncResult)result.AsyncState).FinishEnsureInteractiveInit(result, false);
}
}
void FinishEnsureInteractiveInit(IAsyncResult result, bool completedSynchronously)
{
Exception exception = null;
try
{
this.Rpc.Channel.EndEnsureDisplayUI(result);
}
catch (Exception e)
{
if (Fx.IsFatal(e) || completedSynchronously)
{
throw;
}
exception = e;
}
if (exception != null)
{
this.CallComplete(completedSynchronously, exception);
}
else
{
this.StartEnsureOpen(completedSynchronously);
}
}
void StartEnsureOpen(bool completedSynchronously)
{
TimeSpan timeout = this.Rpc.TimeoutHelper.RemainingTime();
IAsyncResult result = null;
Exception exception = null;
try
{
result = this.Rpc.Channel.BeginEnsureOpened(timeout, ensureOpenCallback, this);
}
catch (Exception e)
{
if (Fx.IsFatal(e) || completedSynchronously)
{
throw;
}
exception = e;
}
if (exception != null)
{
this.CallComplete(completedSynchronously, exception);
}
else if (result.CompletedSynchronously)
{
this.FinishEnsureOpen(result, completedSynchronously);
}
}
static void EnsureOpenCallback(IAsyncResult result)
{
if (!result.CompletedSynchronously)
{
((SendAsyncResult)result.AsyncState).FinishEnsureOpen(result, false);
}
}
void FinishEnsureOpen(IAsyncResult result, bool completedSynchronously)
{
Exception exception = null;
using (ServiceModelActivity.BoundOperation(this.Rpc.Activity))
{
try
{
this.Rpc.Channel.EndEnsureOpened(result);
}
catch (Exception e)
{
if (Fx.IsFatal(e) || completedSynchronously)
{
throw;
}
exception = e;
}
if (exception != null)
{
this.CallComplete(completedSynchronously, exception);
}
else
{
this.StartSend(completedSynchronously);
}
}
}
void StartSend(bool completedSynchronously)
{
TimeSpan timeout = this.Rpc.TimeoutHelper.RemainingTime();
IAsyncResult result = null;
Exception exception = null;
try
{
ConcurrencyBehavior.UnlockInstanceBeforeCallout(this.operationContext);
if (this.isOneWay)
{
result = this.Rpc.Channel.binder.BeginSend(this.Rpc.Request, timeout, sendCallback, this);
}
else
{
result = this.Rpc.Channel.binder.BeginRequest(this.Rpc.Request, timeout, sendCallback, this);
}
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
if (completedSynchronously)
{
ConcurrencyBehavior.LockInstanceAfterCallout(this.operationContext);
throw;
}
exception = e;
}
finally
{
CallOnceManager.SignalNextIfNonNull(this.Rpc.Channel.autoOpenManager);
}
if (exception != null)
{
this.CallComplete(completedSynchronously, exception);
}
else if (result.CompletedSynchronously)
{
this.FinishSend(result, completedSynchronously);
}
}
static void SendCallback(IAsyncResult result)
{
if (!result.CompletedSynchronously)
{
((SendAsyncResult)result.AsyncState).FinishSend(result, false);
}
}
void FinishSend(IAsyncResult result, bool completedSynchronously)
{
Exception exception = null;
try
{
if (this.isOneWay)
{
this.Rpc.Channel.binder.EndSend(result);
}
else
{
this.Rpc.Reply = this.Rpc.Channel.binder.EndRequest(result);
if (this.Rpc.Reply == null)
{
this.Rpc.Channel.ThrowIfFaulted();
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.GetString(SR.SFxServerDidNotReply)));
}
}
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
if (completedSynchronously)
{
ConcurrencyBehavior.LockInstanceAfterCallout(this.operationContext);
throw;
}
exception = e;
}
this.CallComplete(completedSynchronously, exception);
}
void CallComplete(bool completedSynchronously, Exception exception)
{
this.Rpc.Channel.CompletedIOOperation();
this.Complete(completedSynchronously, exception);
}
public static void End(SendAsyncResult result)
{
try
{
AsyncResult.End<SendAsyncResult>(result);
}
finally
{
ConcurrencyBehavior.LockInstanceAfterCallout(result.operationContext);
}
}
}
interface ICallOnce
{
void Call(ServiceChannel channel, TimeSpan timeout);
IAsyncResult BeginCall(ServiceChannel channel, TimeSpan timeout, AsyncCallback callback, object state);
void EndCall(ServiceChannel channel, IAsyncResult result);
}
class CallDisplayUIOnce : ICallOnce
{
static CallDisplayUIOnce instance;
internal static CallDisplayUIOnce Instance
{
get
{
if (CallDisplayUIOnce.instance == null)
{
CallDisplayUIOnce.instance = new CallDisplayUIOnce();
}
return CallDisplayUIOnce.instance;
}
}
[Conditional("DEBUG")]
void ValidateTimeoutIsMaxValue(TimeSpan timeout)
{
if (timeout != TimeSpan.MaxValue)
{
Fx.Assert("non-MaxValue timeout for displaying interactive initialization UI");
}
}
void ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
{
this.ValidateTimeoutIsMaxValue(timeout);
channel.DisplayInitializationUI();
}
IAsyncResult ICallOnce.BeginCall(ServiceChannel channel, TimeSpan timeout, AsyncCallback callback, object state)
{
this.ValidateTimeoutIsMaxValue(timeout);
return channel.BeginDisplayInitializationUI(callback, state);
}
void ICallOnce.EndCall(ServiceChannel channel, IAsyncResult result)
{
channel.EndDisplayInitializationUI(result);
}
}
class CallOpenOnce : ICallOnce
{
static CallOpenOnce instance;
internal static CallOpenOnce Instance
{
get
{
if (CallOpenOnce.instance == null)
{
CallOpenOnce.instance = new CallOpenOnce();
}
return CallOpenOnce.instance;
}
}
void ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
{
channel.Open(timeout);
}
IAsyncResult ICallOnce.BeginCall(ServiceChannel channel, TimeSpan timeout, AsyncCallback callback, object state)
{
return channel.BeginOpen(timeout, callback, state);
}
void ICallOnce.EndCall(ServiceChannel channel, IAsyncResult result)
{
channel.EndOpen(result);
}
}
class CallOnceManager
{
readonly ICallOnce callOnce;
readonly ServiceChannel channel;
bool isFirst = true;
Queue<IWaiter> queue;
static Action<object> signalWaiter = new Action<object>(CallOnceManager.SignalWaiter);
internal CallOnceManager(ServiceChannel channel, ICallOnce callOnce)
{
this.callOnce = callOnce;
this.channel = channel;
this.queue = new Queue<IWaiter>();
}
object ThisLock
{
get { return this; }
}
internal void CallOnce(TimeSpan timeout, CallOnceManager cascade)
{
SyncWaiter waiter = null;
bool first = false;
if (this.queue != null)
{
lock (this.ThisLock)
{
if (this.queue != null)
{
if (this.isFirst)
{
first = true;
this.isFirst = false;
}
else
{
waiter = new SyncWaiter(this);
this.queue.Enqueue(waiter);
}
}
}
}
CallOnceManager.SignalNextIfNonNull(cascade);
if (first)
{
bool throwing = true;
try
{
this.callOnce.Call(this.channel, timeout);
throwing = false;
}
finally
{
if (throwing)
{
this.SignalNext();
}
}
}
else if (waiter != null)
{
waiter.Wait(timeout);
}
}
internal IAsyncResult BeginCallOnce(TimeSpan timeout, CallOnceManager cascade,
AsyncCallback callback, object state)
{
AsyncWaiter waiter = null;
bool first = false;
if (this.queue != null)
{
lock (this.ThisLock)
{
if (this.queue != null)
{
if (this.isFirst)
{
first = true;
this.isFirst = false;
}
else
{
waiter = new AsyncWaiter(this, timeout, callback, state);
this.queue.Enqueue(waiter);
}
}
}
}
CallOnceManager.SignalNextIfNonNull(cascade);
if (first)
{
bool throwing = true;
try
{
IAsyncResult result = this.callOnce.BeginCall(this.channel, timeout, callback, state);
throwing = false;
return result;
}
finally
{
if (throwing)
{
this.SignalNext();
}
}
}
else if (waiter != null)
{
return waiter;
}
else
{
return new CallOnceCompletedAsyncResult(callback, state);
}
}
internal void EndCallOnce(IAsyncResult result)
{
if (result is CallOnceCompletedAsyncResult)
{
CallOnceCompletedAsyncResult.End(result);
}
else if (result is AsyncWaiter)
{
AsyncWaiter.End(result);
}
else
{
bool throwing = true;
try
{
this.callOnce.EndCall(this.channel, result);
throwing = false;
}
finally
{
if (throwing)
{
this.SignalNext();
}
}
}
}
static internal void SignalNextIfNonNull(CallOnceManager manager)
{
if (manager != null)
{
manager.SignalNext();
}
}
internal void SignalNext()
{
if (this.queue == null)
{
return;
}
IWaiter waiter = null;
lock (this.ThisLock)
{
if (this.queue != null)
{
if (this.queue.Count > 0)
{
waiter = this.queue.Dequeue();
}
else
{
this.queue = null;
}
}
}
if (waiter != null)
{
ActionItem.Schedule(CallOnceManager.signalWaiter, waiter);
}
}
static void SignalWaiter(object state)
{
((IWaiter)state).Signal();
}
interface IWaiter
{
void Signal();
}
class SyncWaiter : IWaiter
{
ManualResetEvent wait = new ManualResetEvent(false);
CallOnceManager manager;
bool isTimedOut = false;
bool isSignaled = false;
int waitCount = 0;
internal SyncWaiter(CallOnceManager manager)
{
this.manager = manager;
}
bool ShouldSignalNext
{
get { return this.isTimedOut && this.isSignaled; }
}
void IWaiter.Signal()
{
wait.Set();
this.CloseWaitHandle();
bool signalNext;
lock (this.manager.ThisLock)
{
this.isSignaled = true;
signalNext = this.ShouldSignalNext;
}
if (signalNext)
{
this.manager.SignalNext();
}
}
internal bool Wait(TimeSpan timeout)
{
try
{
if (!TimeoutHelper.WaitOne(this.wait, timeout))
{
bool signalNext;
lock (this.manager.ThisLock)
{
this.isTimedOut = true;
signalNext = this.ShouldSignalNext;
}
if (signalNext)
{
this.manager.SignalNext();
}
}
}
finally
{
this.CloseWaitHandle();
}
return !this.isTimedOut;
}
void CloseWaitHandle()
{
if (Interlocked.Increment(ref this.waitCount) == 2)
{
this.wait.Close();
}
}
}
class AsyncWaiter : AsyncResult, IWaiter
{
static Action<object> timerCallback = new Action<object>(AsyncWaiter.TimerCallback);
CallOnceManager manager;
TimeSpan timeout;
IOThreadTimer timer;
internal AsyncWaiter(CallOnceManager manager, TimeSpan timeout,
AsyncCallback callback, object state)
: base(callback, state)
{
this.manager = manager;
this.timeout = timeout;
if (timeout != TimeSpan.MaxValue)
{
this.timer = new IOThreadTimer(timerCallback, this, false);
this.timer.Set(timeout);
}
}
internal static void End(IAsyncResult result)
{
AsyncResult.End<AsyncWaiter>(result);
}
void IWaiter.Signal()
{
if ((this.timer == null) || this.timer.Cancel())
{
this.Complete(false);
this.manager.channel.Closed -= this.OnClosed;
}
else
{
this.manager.SignalNext();
}
}
void OnClosed(object sender, EventArgs e)
{
if ((this.timer == null) || this.timer.Cancel())
{
this.Complete(false, this.manager.channel.CreateClosedException());
}
}
static void TimerCallback(object state)
{
AsyncWaiter _this = (AsyncWaiter)state;
_this.Complete(false, _this.manager.channel.GetOpenTimeoutException(_this.timeout));
}
}
}
class CallOnceCompletedAsyncResult : AsyncResult
{
internal CallOnceCompletedAsyncResult(AsyncCallback callback, object state)
: base(callback, state)
{
this.Complete(true);
}
static internal void End(IAsyncResult result)
{
AsyncResult.End<CallOnceCompletedAsyncResult>(result);
}
}
internal class SessionIdleManager
{
readonly IChannelBinder binder;
ServiceChannel channel;
readonly long idleTicks;
long lastActivity;
readonly IOThreadTimer timer;
static Action<object> timerCallback;
bool didIdleAbort;
bool isTimerCancelled;
object thisLock;
SessionIdleManager(IChannelBinder binder, TimeSpan idle)
{
this.binder = binder;
this.timer = new IOThreadTimer(GetTimerCallback(), this, false);
this.idleTicks = Ticks.FromTimeSpan(idle);
this.timer.SetAt(Ticks.Now + this.idleTicks);
this.thisLock = new Object();
}
internal static SessionIdleManager CreateIfNeeded(IChannelBinder binder, TimeSpan idle)
{
if (binder.HasSession && (idle != TimeSpan.MaxValue))
{
return new SessionIdleManager(binder, idle);
}
else
{
return null;
}
}
internal bool DidIdleAbort
{
get
{
lock (thisLock)
{
return this.didIdleAbort;
}
}
}
internal void CancelTimer()
{
lock (thisLock)
{
this.isTimerCancelled = true;
this.timer.Cancel();
}
}
internal void CompletedActivity()
{
Interlocked.Exchange(ref this.lastActivity, Ticks.Now);
}
internal void RegisterChannel(ServiceChannel channel, out bool didIdleAbort)
{
lock (thisLock)
{
this.channel = channel;
didIdleAbort = this.didIdleAbort;
}
}
static Action<object> GetTimerCallback()
{
if (SessionIdleManager.timerCallback == null)
{
SessionIdleManager.timerCallback = SessionIdleManager.TimerCallback;
}
return SessionIdleManager.timerCallback;
}
static void TimerCallback(object state)
{
((SessionIdleManager)state).TimerCallback();
}
void TimerCallback()
{
// This reads lastActivity atomically without changing its value.
// (it only sets if it is zero, and then it sets it to zero).
long last = Interlocked.CompareExchange(ref this.lastActivity, 0, 0);
long abortTime = last + this.idleTicks;
lock (thisLock)
{
if (Ticks.Now > abortTime)
{
if (TD.SessionIdleTimeoutIsEnabled())
{
string listenUri = string.Empty;
if (this.binder.ListenUri != null)
{
listenUri = this.binder.ListenUri.AbsoluteUri;
}
TD.SessionIdleTimeout(listenUri);
}
this.didIdleAbort = true;
if (this.channel != null)
{
this.channel.Abort();
}
else
{
this.binder.Abort();
}
}
else
{
if (!this.isTimerCancelled && binder.Channel.State != CommunicationState.Faulted && binder.Channel.State != CommunicationState.Closed)
{
this.timer.SetAt(abortTime);
}
}
}
}
}
}
}
|