File: System\ServiceModel\Security\SecuritySessionClientSettings.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//----------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Security
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IdentityModel.Claims;
    using System.IdentityModel.Selectors;
    using System.IdentityModel.Tokens;
    using System.Runtime;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Security.Tokens;
    using System.Net;
    using System.Threading;
    using System.Xml;
    using System.Globalization;
    using System.ServiceModel.Diagnostics.Application;
 
    // Please use 'sdv //depot/devdiv/private/indigo_xws/ndp/indigo/src/ServiceModel/System/ServiceModel/Security/SecuritySessionChannelFactory.cs' 
    // to see version history before the file was renamed
    // This class is named Settings since the only public APIs are for
    // settings; however, this class also manages all functionality
    // for session channels through internal APIs
 
    static class SecuritySessionClientSettings
    {
        internal const string defaultKeyRenewalIntervalString = "10:00:00";
        internal const string defaultKeyRolloverIntervalString = "00:05:00";
 
        internal static readonly TimeSpan defaultKeyRenewalInterval = TimeSpan.Parse(defaultKeyRenewalIntervalString, CultureInfo.InvariantCulture);
        internal static readonly TimeSpan defaultKeyRolloverInterval = TimeSpan.Parse(defaultKeyRolloverIntervalString, CultureInfo.InvariantCulture);
        internal const bool defaultTolerateTransportFailures = true;
    }
 
    sealed class SecuritySessionClientSettings<TChannel> : IChannelSecureConversationSessionSettings, ISecurityCommunicationObject
    {
        SecurityProtocolFactory sessionProtocolFactory;
        TimeSpan keyRenewalInterval;
        TimeSpan keyRolloverInterval;
        bool tolerateTransportFailures;
        SecurityChannelFactory<TChannel> securityChannelFactory;
        IChannelFactory innerChannelFactory;
        ChannelBuilder channelBuilder;
        WrapperSecurityCommunicationObject communicationObject;
        SecurityStandardsManager standardsManager;
        SecurityTokenParameters issuedTokenParameters;
        int issuedTokenRenewalThreshold;
        bool canRenewSession = true;
        object thisLock = new object();
 
        public SecuritySessionClientSettings()
        {
            this.keyRenewalInterval = SecuritySessionClientSettings.defaultKeyRenewalInterval;
            this.keyRolloverInterval = SecuritySessionClientSettings.defaultKeyRolloverInterval;
            this.tolerateTransportFailures = SecuritySessionClientSettings.defaultTolerateTransportFailures;
            this.communicationObject = new WrapperSecurityCommunicationObject(this);
        }
 
        IChannelFactory InnerChannelFactory
        {
            get
            {
                return this.innerChannelFactory;
            }
        }
 
        internal ChannelBuilder ChannelBuilder
        {
            get
            {
                return this.channelBuilder;
            }
            set
            {
                this.channelBuilder = value;
            }
        }
 
        SecurityChannelFactory<TChannel> SecurityChannelFactory
        {
            get
            {
                return this.securityChannelFactory;
            }
        }
 
        public SecurityProtocolFactory SessionProtocolFactory
        {
            get
            {
                return this.sessionProtocolFactory;
            }
            set
            {
                this.communicationObject.ThrowIfDisposedOrImmutable();
                this.sessionProtocolFactory = value;
            }
        }
 
        public TimeSpan KeyRenewalInterval
        {
            get
            {
                return this.keyRenewalInterval;
            }
            set
            {
                if (value <= TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero)));
                }
                this.communicationObject.ThrowIfDisposedOrImmutable();
                this.keyRenewalInterval = value;
            }
        }
 
        public TimeSpan KeyRolloverInterval
        {
            get
            {
                return this.keyRolloverInterval;
            }
            set
            {
                if (value <= TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero)));
                }
                this.communicationObject.ThrowIfDisposedOrImmutable();
                this.keyRolloverInterval = value;
            }
        }
 
        public bool TolerateTransportFailures
        {
            get
            {
                return this.tolerateTransportFailures;
            }
            set
            {
                this.communicationObject.ThrowIfDisposedOrImmutable();
                this.tolerateTransportFailures = value;
            }
        }
 
        public bool CanRenewSession
        {
            get
            {
                return this.canRenewSession;
            }
            set
            {
                this.canRenewSession = value;
            }
        }
 
        public SecurityTokenParameters IssuedSecurityTokenParameters
        {
            get
            {
                return this.issuedTokenParameters;
            }
            set
            {
                this.communicationObject.ThrowIfDisposedOrImmutable();
                this.issuedTokenParameters = value;
            }
        }
 
        public SecurityStandardsManager SecurityStandardsManager
        {
            get
            {
                return this.standardsManager;
            }
            set
            {
                this.communicationObject.ThrowIfDisposedOrImmutable();
                this.standardsManager = value;
            }
        }
 
        // ISecurityCommunicationObject members
        public TimeSpan DefaultOpenTimeout
        {
            get { return ServiceDefaults.OpenTimeout; }
        }
 
        public TimeSpan DefaultCloseTimeout
        {
            get { return ServiceDefaults.CloseTimeout; }
        }
 
        internal IChannelFactory CreateInnerChannelFactory()
        {
            if (this.ChannelBuilder.CanBuildChannelFactory<IDuplexSessionChannel>())
            {
                return this.ChannelBuilder.BuildChannelFactory<IDuplexSessionChannel>();
            }
            else if (this.ChannelBuilder.CanBuildChannelFactory<IDuplexChannel>())
            {
                return this.ChannelBuilder.BuildChannelFactory<IDuplexChannel>();
            }
            else if (this.ChannelBuilder.CanBuildChannelFactory<IRequestChannel>())
            {
                return this.ChannelBuilder.BuildChannelFactory<IRequestChannel>();
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
            }
        }
 
        public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return this.communicationObject.BeginClose(timeout, callback, state);
        }
 
        public void EndClose(IAsyncResult result)
        {
            this.communicationObject.EndClose(result);
        }
 
        IAsyncResult ISecurityCommunicationObject.OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new OperationWithTimeoutAsyncResult(new OperationWithTimeoutCallback(this.OnClose), timeout, callback, state);
        }
 
        IAsyncResult ISecurityCommunicationObject.OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new OperationWithTimeoutAsyncResult(new OperationWithTimeoutCallback(this.OnOpen), timeout, callback, state);
        }
 
        public void OnClosed()
        {
        }
 
        public void OnClosing()
        {
        }
 
        void ISecurityCommunicationObject.OnEndClose(IAsyncResult result)
        {
            OperationWithTimeoutAsyncResult.End(result);
        }
 
        void ISecurityCommunicationObject.OnEndOpen(IAsyncResult result)
        {
            OperationWithTimeoutAsyncResult.End(result);
        }
 
        public void OnFaulted()
        {
        }
 
        public void OnOpened()
        {
        }
 
        public void OnOpening()
        {
        }
 
        public void OnClose(TimeSpan timeout)
        {
            if (this.sessionProtocolFactory != null)
            {
                this.sessionProtocolFactory.Close(false, timeout);
            }
        }
 
        public void OnAbort()
        {
            if (this.sessionProtocolFactory != null)
            {
                this.sessionProtocolFactory.Close(true, TimeSpan.Zero);
            }
        }
 
        public void OnOpen(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            if (this.sessionProtocolFactory == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecuritySessionProtocolFactoryShouldBeSetBeforeThisOperation)));
            }
            if (this.standardsManager == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityStandardsManagerNotSet, this.GetType().ToString())));
            }
            if (this.issuedTokenParameters == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.IssuedSecurityTokenParametersNotSet, this.GetType())));
            }
            if (this.keyRenewalInterval < this.keyRolloverInterval)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.KeyRolloverGreaterThanKeyRenewal)));
            }
            this.issuedTokenRenewalThreshold = this.sessionProtocolFactory.SecurityBindingElement.LocalClientSettings.CookieRenewalThresholdPercentage;
            this.ConfigureSessionProtocolFactory();
            this.sessionProtocolFactory.Open(true, timeoutHelper.RemainingTime());
        }
 
        internal void Close(TimeSpan timeout)
        {
            this.communicationObject.Close(timeout);
        }
 
        internal void Abort()
        {
            this.communicationObject.Abort();
        }
 
        internal void Open(SecurityChannelFactory<TChannel> securityChannelFactory,
            IChannelFactory innerChannelFactory, ChannelBuilder channelBuilder, TimeSpan timeout)
        {
            this.securityChannelFactory = securityChannelFactory;
            this.innerChannelFactory = innerChannelFactory;
            this.channelBuilder = channelBuilder;
            this.communicationObject.Open(timeout);
        }
 
        internal TChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via)
        {
            return OnCreateChannel(remoteAddress, via, null);
        }
 
        internal TChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via, MessageFilter filter)
        {
            this.communicationObject.ThrowIfClosed();
            if (filter != null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
            }
 
            if (typeof(TChannel) == typeof(IRequestSessionChannel))
            {
                return (TChannel)((object)(new SecurityRequestSessionChannel(this, remoteAddress, via)));
            }
            else if (typeof(TChannel) == typeof(IDuplexSessionChannel))
            {
                // typeof(TChannel) == typeof(IDuplexSessionChannel)
                return (TChannel)((object)(new ClientSecurityDuplexSessionChannel(this, remoteAddress, via)));
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.ChannelTypeNotSupported, typeof(TChannel)), "TChannel"));
            }
        }
 
        void ConfigureSessionProtocolFactory()
        {
            if (this.sessionProtocolFactory is SessionSymmetricMessageSecurityProtocolFactory)
            {
                AddressingVersion addressing = MessageVersion.Default.Addressing;
                if (this.channelBuilder != null)
                {
                    MessageEncodingBindingElement encoding = this.channelBuilder.Binding.Elements.Find<MessageEncodingBindingElement>();
                    if (encoding != null)
                    {
                        addressing = encoding.MessageVersion.Addressing;
                    }
                }
 
                if (addressing != AddressingVersion.WSAddressing10 && addressing != AddressingVersion.WSAddressingAugust2004)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, addressing)));
                }
 
                SessionSymmetricMessageSecurityProtocolFactory symmetric = (SessionSymmetricMessageSecurityProtocolFactory)this.sessionProtocolFactory;
                if (!symmetric.ApplyIntegrity || !symmetric.RequireIntegrity)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecuritySessionRequiresMessageIntegrity)));
                MessagePartSpecification bodyPart = new MessagePartSpecification(true);
                symmetric.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, this.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction);
                symmetric.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, this.SecurityStandardsManager.SecureConversationDriver.CloseAction);
                symmetric.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, addressing.FaultAction);
                symmetric.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, addressing.DefaultFaultAction);
                symmetric.ProtectionRequirements.OutgoingSignatureParts.AddParts(bodyPart, DotNetSecurityStrings.SecuritySessionFaultAction);
                symmetric.ProtectionRequirements.IncomingSignatureParts.AddParts(bodyPart, this.SecurityStandardsManager.SecureConversationDriver.CloseAction);
                symmetric.ProtectionRequirements.IncomingSignatureParts.AddParts(bodyPart, this.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction);
                if (symmetric.ApplyConfidentiality)
                {
                    symmetric.ProtectionRequirements.IncomingEncryptionParts.AddParts(MessagePartSpecification.NoParts, this.SecurityStandardsManager.SecureConversationDriver.CloseAction);
                    symmetric.ProtectionRequirements.IncomingEncryptionParts.AddParts(MessagePartSpecification.NoParts, this.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction);
                }
                if (symmetric.RequireConfidentiality)
                {
                    symmetric.ProtectionRequirements.OutgoingEncryptionParts.AddParts(MessagePartSpecification.NoParts, this.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction);
                    symmetric.ProtectionRequirements.OutgoingEncryptionParts.AddParts(MessagePartSpecification.NoParts, this.SecurityStandardsManager.SecureConversationDriver.CloseAction);
                    symmetric.ProtectionRequirements.OutgoingEncryptionParts.AddParts(bodyPart, addressing.FaultAction);
                    symmetric.ProtectionRequirements.OutgoingEncryptionParts.AddParts(bodyPart, addressing.DefaultFaultAction);
                    symmetric.ProtectionRequirements.OutgoingEncryptionParts.AddParts(bodyPart, DotNetSecurityStrings.SecuritySessionFaultAction);
                }
            }
            else if (this.sessionProtocolFactory is SessionSymmetricTransportSecurityProtocolFactory)
            {
                SessionSymmetricTransportSecurityProtocolFactory transport = (SessionSymmetricTransportSecurityProtocolFactory)this.sessionProtocolFactory;
                transport.AddTimestamp = true;
                transport.SecurityTokenParameters.RequireDerivedKeys = false;
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
            }
        }
 
        abstract class ClientSecuritySessionChannel : ChannelBase
        {
            EndpointAddress to;
            Uri via;
            IClientReliableChannelBinder channelBinder;
            ChannelParameterCollection channelParameters;
            SecurityToken currentSessionToken;
            SecurityToken previousSessionToken;
            DateTime keyRenewalTime;
            DateTime keyRolloverTime;
            SecurityProtocol securityProtocol;
            SecuritySessionClientSettings<TChannel> settings;
            SecurityTokenProvider sessionTokenProvider;
            bool isKeyRenewalOngoing = false;
            InterruptibleWaitObject keyRenewalCompletedEvent;
            bool sentClose;
            bool receivedClose;
            volatile bool isOutputClosed;
            volatile bool isInputClosed;
            InterruptibleWaitObject inputSessionClosedHandle = new InterruptibleWaitObject(false);
            bool sendCloseHandshake = false;
            MessageVersion messageVersion;
            bool isCompositeDuplexConnection;
            Message closeResponse;
            InterruptibleWaitObject outputSessionCloseHandle = new InterruptibleWaitObject(true);
            WebHeaderCollection webHeaderCollection;
 
            protected ClientSecuritySessionChannel(SecuritySessionClientSettings<TChannel> settings, EndpointAddress to, Uri via)
                : base(settings.SecurityChannelFactory)
            {
                this.settings = settings;
                this.to = to;
                this.via = via;
                this.keyRenewalCompletedEvent = new InterruptibleWaitObject(false);
                this.messageVersion = settings.SecurityChannelFactory.MessageVersion;
                this.channelParameters = new ChannelParameterCollection(this);
                this.InitializeChannelBinder();
                this.webHeaderCollection = new WebHeaderCollection();
            }
 
            protected SecuritySessionClientSettings<TChannel> Settings
            {
                get
                {
                    return this.settings;
                }
            }
 
            protected IClientReliableChannelBinder ChannelBinder
            {
                get
                {
                    return this.channelBinder;
                }
            }
 
            public EndpointAddress RemoteAddress
            {
                get
                {
                    return this.to;
                }
            }
 
            public Uri Via
            {
                get
                {
                    return this.via;
                }
            }
 
            protected bool SendCloseHandshake
            {
                get
                {
                    return this.sendCloseHandshake;
                }
            }
 
            protected EndpointAddress InternalLocalAddress
            {
                get
                {
                    if (this.channelBinder != null)
                        return this.channelBinder.LocalAddress;
                    return null;
                }
            }
 
            protected virtual bool CanDoSecurityCorrelation
            {
                get
                {
                    return false;
                }
            }
 
            public MessageVersion MessageVersion
            {
                get { return this.messageVersion; }
            }
 
            protected bool IsInputClosed
            {
                get { return isInputClosed; }
            }
 
            protected bool IsOutputClosed
            {
                get { return isOutputClosed; }
            }
 
            protected abstract bool ExpectClose
            {
                get;
            }
 
            protected abstract string SessionId
            {
                get;
            }
 
            public override T GetProperty<T>()
            {
                if (typeof(T) == typeof(ChannelParameterCollection))
                {
                    return this.channelParameters as T;
                }
 
                if (typeof(T) == typeof(FaultConverter) && (this.channelBinder != null))
                {
                    return new SecurityChannelFaultConverter(this.channelBinder.Channel) as T;
                }
                else if (typeof(T) == typeof(WebHeaderCollection))
                {
                    return (T)(object)this.webHeaderCollection;
                }
 
 
                T result = base.GetProperty<T>();
                if ((result == null) && (channelBinder != null) && (channelBinder.Channel != null))
                {
                    result = channelBinder.Channel.GetProperty<T>();
                }
 
                return result;
            }
 
            protected abstract void InitializeSession(SecurityToken sessionToken);
 
            void InitializeSecurityState(SecurityToken sessionToken)
            {
                InitializeSession(sessionToken);
                this.currentSessionToken = sessionToken;
                this.previousSessionToken = null;
                List<SecurityToken> incomingSessionTokens = new List<SecurityToken>(1);
                incomingSessionTokens.Add(sessionToken);
                ((IInitiatorSecuritySessionProtocol)this.securityProtocol).SetIdentityCheckAuthenticator(new GenericXmlSecurityTokenAuthenticator());
                ((IInitiatorSecuritySessionProtocol)this.securityProtocol).SetIncomingSessionTokens(incomingSessionTokens);
                ((IInitiatorSecuritySessionProtocol)this.securityProtocol).SetOutgoingSessionToken(sessionToken);
                if (this.CanDoSecurityCorrelation)
                {
                    ((IInitiatorSecuritySessionProtocol)this.securityProtocol).ReturnCorrelationState = true;
                }
                this.keyRenewalTime = GetKeyRenewalTime(sessionToken);
            }
 
            void SetupSessionTokenProvider()
            {
                InitiatorServiceModelSecurityTokenRequirement requirement = new InitiatorServiceModelSecurityTokenRequirement();
                this.Settings.IssuedSecurityTokenParameters.InitializeSecurityTokenRequirement(requirement);
                requirement.KeyUsage = SecurityKeyUsage.Signature;
                requirement.SupportSecurityContextCancellation = true;
                requirement.SecurityAlgorithmSuite = this.Settings.SessionProtocolFactory.OutgoingAlgorithmSuite;
                requirement.SecurityBindingElement = this.Settings.SessionProtocolFactory.SecurityBindingElement;
                requirement.TargetAddress = this.to;
                requirement.Via = this.Via;
                requirement.MessageSecurityVersion = this.Settings.SessionProtocolFactory.MessageSecurityVersion.SecurityTokenVersion;
                requirement.Properties[ServiceModelSecurityTokenRequirement.PrivacyNoticeUriProperty] = this.Settings.SessionProtocolFactory.PrivacyNoticeUri;
                requirement.WebHeaders = this.webHeaderCollection;
 
                if (this.channelParameters != null)
                {
                    requirement.Properties[ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty] = this.channelParameters;
                }
                requirement.Properties[ServiceModelSecurityTokenRequirement.PrivacyNoticeVersionProperty] = this.Settings.SessionProtocolFactory.PrivacyNoticeVersion;
                if (this.channelBinder.LocalAddress != null)
                {
                    requirement.DuplexClientLocalAddress = this.channelBinder.LocalAddress;
                }
                this.sessionTokenProvider = this.Settings.SessionProtocolFactory.SecurityTokenManager.CreateSecurityTokenProvider(requirement);
            }
 
            void OpenCore(SecurityToken sessionToken, TimeSpan timeout)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                this.securityProtocol = this.Settings.SessionProtocolFactory.CreateSecurityProtocol(this.to, this.Via, null, true, timeoutHelper.RemainingTime());
                if (!(this.securityProtocol is IInitiatorSecuritySessionProtocol))
                {
                    Fx.Assert("Security protocol must be IInitiatorSecuritySessionProtocol.");
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ProtocolMisMatch, "IInitiatorSecuritySessionProtocol", this.GetType().ToString())));
                }
                this.securityProtocol.Open(timeoutHelper.RemainingTime());
                this.channelBinder.Open(timeoutHelper.RemainingTime());
                this.InitializeSecurityState(sessionToken);
            }
 
            protected override void OnFaulted()
            {
                this.AbortCore();
                this.inputSessionClosedHandle.Fault(this);
                this.keyRenewalCompletedEvent.Fault(this);
                this.outputSessionCloseHandle.Fault(this);
                base.OnFaulted();
            }
 
            protected override void OnOpen(TimeSpan timeout)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                SetupSessionTokenProvider();
                SecurityUtils.OpenTokenProviderIfRequired(this.sessionTokenProvider, timeoutHelper.RemainingTime());
                using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ?
                    ServiceModelActivity.CreateBoundedActivity() : null)
                {
                    if (DiagnosticUtility.ShouldUseActivity)
                    {
                        ServiceModelActivity.Start(activity, SR.GetString(SR.ActivitySecuritySetup), ActivityType.SecuritySetup);
                    }
                    SecurityToken sessionToken = this.sessionTokenProvider.GetToken(timeoutHelper.RemainingTime());
                    // Token was issued, do send cancel on close;
                    this.sendCloseHandshake = true;
                    this.OpenCore(sessionToken, timeoutHelper.RemainingTime());
                }
            }
 
            protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
            {
                ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ?
                    ServiceModelActivity.CreateAsyncActivity() : null;
                using (ServiceModelActivity.BoundOperation(activity, true))
                {
                    if (DiagnosticUtility.ShouldUseActivity)
                    {
                        ServiceModelActivity.Start(activity, SR.GetString(SR.ActivitySecuritySetup), ActivityType.SecuritySetup);
                    }
                    return new OpenAsyncResult(this, timeout, callback, state);
                }
            }
 
            protected override void OnEndOpen(IAsyncResult result)
            {
                OpenAsyncResult.End(result);
            }
 
            void InitializeChannelBinder()
            {
                ChannelBuilder channelBuilder = this.Settings.ChannelBuilder;
                TolerateFaultsMode faultMode = this.Settings.TolerateTransportFailures ? TolerateFaultsMode.Always : TolerateFaultsMode.Never;
                if (channelBuilder.CanBuildChannelFactory<IDuplexSessionChannel>())
                {
                    this.channelBinder = ClientReliableChannelBinder<IDuplexSessionChannel>.CreateBinder(this.RemoteAddress, this.Via, (IChannelFactory<IDuplexSessionChannel>)(object)this.Settings.InnerChannelFactory,
                        MaskingMode.None, faultMode, this.channelParameters, this.DefaultCloseTimeout, this.DefaultSendTimeout);
                }
                else if (channelBuilder.CanBuildChannelFactory<IDuplexChannel>())
                {
                    this.channelBinder = ClientReliableChannelBinder<IDuplexChannel>.CreateBinder(this.RemoteAddress, this.Via, (IChannelFactory<IDuplexChannel>)(object)this.Settings.InnerChannelFactory,
                        MaskingMode.None, faultMode, this.channelParameters, this.DefaultCloseTimeout, this.DefaultSendTimeout);
                    this.isCompositeDuplexConnection = true;
                }
                else if (channelBuilder.CanBuildChannelFactory<IRequestChannel>())
                {
                    this.channelBinder = ClientReliableChannelBinder<IRequestChannel>.CreateBinder(this.RemoteAddress, this.Via, (IChannelFactory<IRequestChannel>)(object)this.Settings.InnerChannelFactory,
                        MaskingMode.None, faultMode, this.channelParameters, this.DefaultCloseTimeout, this.DefaultSendTimeout);
                }
                else if (channelBuilder.CanBuildChannelFactory<IRequestSessionChannel>())
                {
                    this.channelBinder = ClientReliableChannelBinder<IRequestSessionChannel>.CreateBinder(this.RemoteAddress, this.Via, (IChannelFactory<IRequestSessionChannel>)(object)this.Settings.InnerChannelFactory,
                        MaskingMode.None, faultMode, this.channelParameters, this.DefaultCloseTimeout, this.DefaultSendTimeout);
                }
                this.channelBinder.Faulted += this.OnInnerFaulted;
            }
 
            void OnInnerFaulted(IReliableChannelBinder sender, Exception exception)
            {
                this.Fault(exception);
            }
 
            protected virtual bool OnCloseResponseReceived()
            {
                bool setInputSessionClosedHandle = false;
                bool isCloseResponseExpected = false;
                lock (ThisLock)
                {
                    isCloseResponseExpected = this.sentClose;
                    if (isCloseResponseExpected && !this.isInputClosed)
                    {
                        this.isInputClosed = true;
                        setInputSessionClosedHandle = true;
                    }
                }
                if (!isCloseResponseExpected)
                {
                    this.Fault(new ProtocolException(SR.GetString(SR.UnexpectedSecuritySessionCloseResponse)));
                    return false;
                }
                if (setInputSessionClosedHandle)
                {
                    this.inputSessionClosedHandle.Set();
                }
                return true;
            }
 
            protected virtual bool OnCloseReceived()
            {
                if (!ExpectClose)
                {
                    this.Fault(new ProtocolException(SR.GetString(SR.UnexpectedSecuritySessionClose)));
                    return false;
                }
                bool setInputSessionClosedHandle = false;
                lock (ThisLock)
                {
                    if (!this.isInputClosed)
                    {
                        this.isInputClosed = true;
                        this.receivedClose = true;
                        setInputSessionClosedHandle = true;
                    }
                }
                if (setInputSessionClosedHandle)
                {
                    this.inputSessionClosedHandle.Set();
                }
                return true;
            }
 
            Message PrepareCloseMessage()
            {
                SecurityToken tokenToClose;
                lock (ThisLock)
                {
                    tokenToClose = this.currentSessionToken;
                }
                RequestSecurityToken rst = new RequestSecurityToken(this.Settings.SecurityStandardsManager);
                rst.RequestType = this.Settings.SecurityStandardsManager.TrustDriver.RequestTypeClose;
                rst.CloseTarget = this.Settings.IssuedSecurityTokenParameters.CreateKeyIdentifierClause(tokenToClose, SecurityTokenReferenceStyle.External);
                rst.MakeReadOnly();
                Message closeMessage = Message.CreateMessage(this.MessageVersion, ActionHeader.Create(this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseAction, this.MessageVersion.Addressing), rst);
 
                RequestReplyCorrelator.PrepareRequest(closeMessage);
                if (this.webHeaderCollection != null && this.webHeaderCollection.Count > 0)
                {
                    object prop = null;
                    HttpRequestMessageProperty rmp = null;
                    if (closeMessage.Properties.TryGetValue(HttpRequestMessageProperty.Name, out prop))
                    {
                        rmp = prop as HttpRequestMessageProperty;
                    }
                    else
                    {
                        rmp = new HttpRequestMessageProperty();
                        closeMessage.Properties.Add(HttpRequestMessageProperty.Name, rmp);
                    }
 
                    if (rmp != null && rmp.Headers != null)
                    {
                        rmp.Headers.Add(this.webHeaderCollection);
                    }
                }
 
 
                if (this.InternalLocalAddress != null)
                {
                    closeMessage.Headers.ReplyTo = this.InternalLocalAddress;
                }
                else
                {
                    if (closeMessage.Version.Addressing == AddressingVersion.WSAddressing10)
                    {
                        closeMessage.Headers.ReplyTo = null;
                    }
                    else if (closeMessage.Version.Addressing == AddressingVersion.WSAddressingAugust2004)
                    {
                        closeMessage.Headers.ReplyTo = EndpointAddress.AnonymousAddress;
                    }
                    else
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, closeMessage.Version.Addressing)));
                    }
                }
                if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity)
                {
                    TraceUtility.AddAmbientActivityToMessage(closeMessage);
                }
                return closeMessage;
            }
 
            protected SecurityProtocolCorrelationState SendCloseMessage(TimeSpan timeout)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                SecurityProtocolCorrelationState closeCorrelationState;
                Message closeMessage = PrepareCloseMessage();
                try
                {
                    closeCorrelationState = this.securityProtocol.SecureOutgoingMessage(ref closeMessage, timeoutHelper.RemainingTime(), null);
                    this.ChannelBinder.Send(closeMessage, timeoutHelper.RemainingTime());
                }
                finally
                {
                    closeMessage.Close();
                }
 
                SecurityTraceRecordHelper.TraceCloseMessageSent(this.currentSessionToken, this.RemoteAddress);
                return closeCorrelationState;
            }
 
            protected void SendCloseResponseMessage(TimeSpan timeout)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
 
                Message message = null;
                try
                {
                    message = this.closeResponse;
                    this.securityProtocol.SecureOutgoingMessage(ref message, timeoutHelper.RemainingTime(), null);
                    this.ChannelBinder.Send(message, timeoutHelper.RemainingTime());
                    SecurityTraceRecordHelper.TraceCloseResponseMessageSent(this.currentSessionToken, this.RemoteAddress);
                }
                finally
                {
                    message.Close();
                }
            }
 
            IAsyncResult BeginSendCloseMessage(TimeSpan timeout, AsyncCallback callback, object state)
            {
                using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ?
                    ServiceModelActivity.CreateBoundedActivity() : null)
                {
                    if (DiagnosticUtility.ShouldUseActivity)
                    {
                        ServiceModelActivity.Start(activity, SR.GetString(SR.ActivitySecurityClose), ActivityType.SecuritySetup);
                    }
                    Message closeMessage = PrepareCloseMessage();
                    return new SecureSendAsyncResult(closeMessage, this, timeout, callback, state, true);
                }
            }
 
            SecurityProtocolCorrelationState EndSendCloseMessage(IAsyncResult result)
            {
                SecurityProtocolCorrelationState correlationState = SecureSendAsyncResult.End(result);
                SecurityTraceRecordHelper.TraceCloseMessageSent(this.currentSessionToken, this.RemoteAddress);
                return correlationState;
            }
 
            IAsyncResult BeginSendCloseResponseMessage(TimeSpan timeout, AsyncCallback callback, object state)
            {
                return new SecureSendAsyncResult(this.closeResponse, this, timeout, callback, state, true);
            }
 
            void EndSendCloseResponseMessage(IAsyncResult result)
            {
                SecureSendAsyncResult.End(result);
                SecurityTraceRecordHelper.TraceCloseResponseMessageSent(this.currentSessionToken, this.RemoteAddress);
            }
 
            MessageFault GetProtocolFault(ref Message message, out bool isKeyRenewalFault, out bool isSessionAbortedFault)
            {
                isKeyRenewalFault = false;
                isSessionAbortedFault = false;
                MessageFault result = null;
                using (MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue))
                {
                    message = buffer.CreateMessage();
                    Message copy = buffer.CreateMessage();
                    MessageFault fault = MessageFault.CreateFault(copy, TransportDefaults.MaxSecurityFaultSize);
                    if (fault.Code.IsSenderFault)
                    {
                        FaultCode subCode = fault.Code.SubCode;
                        if (subCode != null)
                        {
                            SecurityStandardsManager standardsManager = this.securityProtocol.SecurityProtocolFactory.StandardsManager;
                            SecureConversationDriver scDriver = standardsManager.SecureConversationDriver;
                            if (subCode.Namespace == scDriver.Namespace.Value && subCode.Name == scDriver.RenewNeededFaultCode.Value)
                            {
                                result = fault;
                                isKeyRenewalFault = true;
                            }
                            else if (subCode.Namespace == DotNetSecurityStrings.Namespace && subCode.Name == DotNetSecurityStrings.SecuritySessionAbortedFault)
                            {
                                result = fault;
                                isSessionAbortedFault = true;
                            }
                        }
                    }
                }
                return result;
            }
 
 
            void ProcessKeyRenewalFault()
            {
                SecurityTraceRecordHelper.TraceSessionKeyRenewalFault(this.currentSessionToken, this.RemoteAddress);
                lock (ThisLock)
                {
                    this.keyRenewalTime = DateTime.UtcNow;
                }
            }
 
            void ProcessSessionAbortedFault(MessageFault sessionAbortedFault)
            {
                SecurityTraceRecordHelper.TraceRemoteSessionAbortedFault(this.currentSessionToken, this.RemoteAddress);
                this.Fault(new FaultException(sessionAbortedFault));
            }
 
            void ProcessCloseResponse(Message response)
            {
                // the close message may have been received by the channel after the channel factory has been closed
                if (response.Headers.Action != this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction.Value)
                {
                    throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.InvalidCloseResponseAction, response.Headers.Action)), response);
                }
                RequestSecurityTokenResponse rstr = null;
                XmlDictionaryReader bodyReader = response.GetReaderAtBodyContents();
                using (bodyReader)
                {
                    if (this.Settings.SecurityStandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrustFeb2005)
                        rstr = this.Settings.SecurityStandardsManager.TrustDriver.CreateRequestSecurityTokenResponse(bodyReader);
                    else if (this.Settings.SecurityStandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrust13)
                    {
                        RequestSecurityTokenResponseCollection rstrc = this.Settings.SecurityStandardsManager.TrustDriver.CreateRequestSecurityTokenResponseCollection(bodyReader);
                        foreach (RequestSecurityTokenResponse rstrItem in rstrc.RstrCollection)
                        {
                            if (rstr != null)
                            {
                                // More than one RSTR is found. So throw an exception.
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.MoreThanOneRSTRInRSTRC)));
                            }
                            rstr = rstrItem;
                        }
                    }
                    else
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
                    }
 
                    response.ReadFromBodyContentsToEnd(bodyReader);
                }
                if (!rstr.IsRequestedTokenClosed)
                {
                    throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.SessionTokenWasNotClosed)), response);
                }
            }
 
            void PrepareReply(Message request, Message reply)
            {
                if (request.Headers.ReplyTo != null)
                {
                    request.Headers.ReplyTo.ApplyTo(reply);
                }
                else if (request.Headers.From != null)
                {
                    request.Headers.From.ApplyTo(reply);
                }
                if (request.Headers.MessageId != null)
                {
                    reply.Headers.RelatesTo = request.Headers.MessageId;
                }
                TraceUtility.CopyActivity(request, reply);
                if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity)
                {
                    TraceUtility.AddActivityHeader(reply);
                }
            }
 
            bool DoesSkiClauseMatchSigningToken(SecurityContextKeyIdentifierClause skiClause, Message request)
            {
                if (this.SessionId == null)
                {
                    return false;
                }
                return (skiClause.ContextId.ToString() == this.SessionId);
            }
 
            void ProcessCloseMessage(Message message)
            {
                RequestSecurityToken rst;
                XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
                using (bodyReader)
                {
                    rst = this.Settings.SecurityStandardsManager.TrustDriver.CreateRequestSecurityToken(bodyReader);
                    message.ReadFromBodyContentsToEnd(bodyReader);
                }
                if (rst.RequestType != null && rst.RequestType != this.Settings.SecurityStandardsManager.TrustDriver.RequestTypeClose)
                {
                    throw TraceUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.InvalidRstRequestType, rst.RequestType)), message);
                }
                if (rst.CloseTarget == null)
                {
                    throw TraceUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.NoCloseTargetSpecified)), message);
                }
                SecurityContextKeyIdentifierClause sctSkiClause = rst.CloseTarget as SecurityContextKeyIdentifierClause;
                if (sctSkiClause == null || !DoesSkiClauseMatchSigningToken(sctSkiClause, message))
                {
                    throw TraceUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.BadCloseTarget, rst.CloseTarget)), message);
                }
                // prepare the close response
                RequestSecurityTokenResponse rstr = new RequestSecurityTokenResponse(this.Settings.SecurityStandardsManager);
                rstr.Context = rst.Context;
                rstr.IsRequestedTokenClosed = true;
                rstr.MakeReadOnly();
                Message response = null;
                if (this.Settings.SecurityStandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrustFeb2005)
                    response = Message.CreateMessage(message.Version, ActionHeader.Create(this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction, message.Version.Addressing), rstr);
                else if (this.Settings.SecurityStandardsManager.MessageSecurityVersion.TrustVersion == TrustVersion.WSTrust13)
                {
                    List<RequestSecurityTokenResponse> rstrList = new List<RequestSecurityTokenResponse>();
                    rstrList.Add(rstr);
                    RequestSecurityTokenResponseCollection rstrCollection = new RequestSecurityTokenResponseCollection(rstrList, this.Settings.SecurityStandardsManager);
                    response = Message.CreateMessage(message.Version, ActionHeader.Create(this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction, message.Version.Addressing), rstrCollection);
                }
                else
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
                }
                PrepareReply(message, response);
                this.closeResponse = response;
            }
 
            bool ShouldWrapException(Exception e)
            {
                return ((e is FormatException) || (e is XmlException));
            }
 
            protected Message ProcessIncomingMessage(Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState, out MessageFault protocolFault)
            {
                protocolFault = null;
                lock (ThisLock)
                {
                    DoKeyRolloverIfNeeded();
                }
 
                try
                {
                    VerifyIncomingMessage(ref message, timeout, correlationState);
 
                    string action = message.Headers.Action;
 
                    if (action == this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseResponseAction.Value)
                    {
                        SecurityTraceRecordHelper.TraceCloseResponseReceived(this.currentSessionToken, this.RemoteAddress);
                        this.ProcessCloseResponse(message);
                        this.OnCloseResponseReceived();
                    }
                    else if (action == this.Settings.SecurityStandardsManager.SecureConversationDriver.CloseAction.Value)
                    {
                        SecurityTraceRecordHelper.TraceCloseMessageReceived(this.currentSessionToken, this.RemoteAddress);
                        this.ProcessCloseMessage(message);
                        this.OnCloseReceived();
                    }
                    else if (action == DotNetSecurityStrings.SecuritySessionFaultAction)
                    {
                        bool isKeyRenewalFault;
                        bool isSessionAbortedFault;
                        protocolFault = GetProtocolFault(ref message, out isKeyRenewalFault, out isSessionAbortedFault);
                        if (isKeyRenewalFault)
                        {
                            ProcessKeyRenewalFault();
                        }
                        else if (isSessionAbortedFault)
                        {
                            ProcessSessionAbortedFault(protocolFault);
                        }
                        else
                        {
                            return message;
                        }
                    }
                    else
                    {
                        return message;
                    }
                }
#pragma warning suppress 56500 // covered by FxCOP
                catch (Exception e)
                {
                    if ((e is CommunicationException) || (e is TimeoutException) || (Fx.IsFatal(e)) || !ShouldWrapException(e))
                    {
                        throw;
                    }
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.MessageSecurityVerificationFailed), e));
                }
 
                message.Close();
                return null;
            }
 
            protected Message ProcessRequestContext(RequestContext requestContext, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
            {
                if (requestContext == null)
                {
                    return null;
                }
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                Message message = requestContext.RequestMessage;
                Message unverifiedMessage = message;
                try
                {
                    Exception faultException = null;
                    try
                    {
                        MessageFault dummyProtocolFault;
                        return ProcessIncomingMessage(message, timeoutHelper.RemainingTime(), correlationState, out dummyProtocolFault);
                    }
                    catch (MessageSecurityException e)
                    {
                        // if the message is an unsecured security fault from the other party over the same connection then fault the session
                        if (!isCompositeDuplexConnection)
                        {
                            if (unverifiedMessage.IsFault)
                            {
                                MessageFault fault = MessageFault.CreateFault(unverifiedMessage, TransportDefaults.MaxSecurityFaultSize);
                                if (SecurityUtils.IsSecurityFault(fault, this.settings.sessionProtocolFactory.StandardsManager))
                                {
                                    faultException = SecurityUtils.CreateSecurityFaultException(fault);
                                }
                            }
                            else
                            {
                                faultException = e;
                            }
                        }
                    }
                    if (faultException != null)
                    {
                        this.Fault(faultException);
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(faultException);
                    }
                    return null;
                }
                finally
                {
                    requestContext.Close(timeoutHelper.RemainingTime());
                }
            }
 
            /// <summary>
            /// This method removes the previous session key when the key rollover time is past.
            /// It must be called within a lock
            /// </summary>
            void DoKeyRolloverIfNeeded()
            {
                if (DateTime.UtcNow >= this.keyRolloverTime && this.previousSessionToken != null)
                {
                    SecurityTraceRecordHelper.TracePreviousSessionKeyDiscarded(this.previousSessionToken, this.currentSessionToken, this.RemoteAddress);
                    // forget the previous session token 
                    this.previousSessionToken = null;
                    List<SecurityToken> incomingTokens = new List<SecurityToken>(1);
                    incomingTokens.Add(this.currentSessionToken);
                    ((IInitiatorSecuritySessionProtocol)this.securityProtocol).SetIncomingSessionTokens(incomingTokens);
                }
            }
 
            DateTime GetKeyRenewalTime(SecurityToken token)
            {
                TimeSpan tokenValidityInterval = TimeSpan.FromTicks((long)(((token.ValidTo.Ticks - token.ValidFrom.Ticks) * this.settings.issuedTokenRenewalThreshold) / 100));
                DateTime keyRenewalTime1 = TimeoutHelper.Add(token.ValidFrom, tokenValidityInterval);
                DateTime keyRenewalTime2 = TimeoutHelper.Add(token.ValidFrom, this.settings.keyRenewalInterval);
                if (keyRenewalTime1 < keyRenewalTime2)
                {
                    return keyRenewalTime1;
                }
                else
                {
                    return keyRenewalTime2;
                }
            }
 
            /// <summary>
            /// This method returns true if key renewal is needed.
            /// It must be called within a lock
            /// </summary>
            bool IsKeyRenewalNeeded()
            {
                return DateTime.UtcNow >= this.keyRenewalTime;
            }
 
            /// <summary>
            /// When the new session token is obtained, mark the current token as previous and remove it
            /// after KeyRolloverTime. Mark the new current as pending and update the next key renewal time
            /// </summary>
            void UpdateSessionTokens(SecurityToken newToken)
            {
                lock (ThisLock)
                {
                    this.previousSessionToken = this.currentSessionToken;
                    this.keyRolloverTime = TimeoutHelper.Add(DateTime.UtcNow, this.Settings.KeyRolloverInterval);
                    this.currentSessionToken = newToken;
                    this.keyRenewalTime = GetKeyRenewalTime(newToken);
                    List<SecurityToken> incomingTokens = new List<SecurityToken>(2);
                    incomingTokens.Add(this.previousSessionToken);
                    incomingTokens.Add(this.currentSessionToken);
                    ((IInitiatorSecuritySessionProtocol)this.securityProtocol).SetIncomingSessionTokens(incomingTokens);
                    ((IInitiatorSecuritySessionProtocol)this.securityProtocol).SetOutgoingSessionToken(this.currentSessionToken);
                    SecurityTraceRecordHelper.TraceSessionKeyRenewed(this.currentSessionToken, this.previousSessionToken, this.RemoteAddress);
                }
            }
 
            void RenewKey(TimeSpan timeout)
            {
                if (!this.settings.CanRenewSession)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SessionKeyExpiredException(SR.GetString(SR.SessionKeyRenewalNotSupported)));
                }
 
                bool startKeyRenewal;
                lock (ThisLock)
                {
                    if (!this.isKeyRenewalOngoing)
                    {
                        this.isKeyRenewalOngoing = true;
                        this.keyRenewalCompletedEvent.Reset();
                        startKeyRenewal = true;
                    }
                    else
                    {
                        startKeyRenewal = false;
                    }
                }
                if (startKeyRenewal == true)
                {
                    try
                    {
                        using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ?
                            ServiceModelActivity.CreateBoundedActivity() : null)
                        {
                            if (DiagnosticUtility.ShouldUseActivity)
                            {
                                ServiceModelActivity.Start(activity, SR.GetString(SR.ActivitySecurityRenew), ActivityType.SecuritySetup);
                            }
                            SecurityToken renewedToken = this.sessionTokenProvider.RenewToken(timeout, this.currentSessionToken);
                            UpdateSessionTokens(renewedToken);
                        }
                    }
                    finally
                    {
                        lock (ThisLock)
                        {
                            this.isKeyRenewalOngoing = false;
                            this.keyRenewalCompletedEvent.Set();
                        }
                    }
                }
                else
                {
                    this.keyRenewalCompletedEvent.Wait(timeout);
                    lock (ThisLock)
                    {
                        if (IsKeyRenewalNeeded())
                        {
                            // the key renewal attempt failed. Throw an exception to the user
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SessionKeyExpiredException(SR.GetString(SR.UnableToRenewSessionKey)));
                        }
                    }
                }
            }
 
            bool CheckIfKeyRenewalNeeded()
            {
                bool doKeyRenewal = false;
                lock (ThisLock)
                {
                    doKeyRenewal = IsKeyRenewalNeeded();
                    DoKeyRolloverIfNeeded();
                }
                return doKeyRenewal;
            }
 
            protected IAsyncResult BeginSecureOutgoingMessage(Message message, TimeSpan timeout, AsyncCallback callback, object state)
            {
                bool doKeyRenewal = CheckIfKeyRenewalNeeded();
                if (!doKeyRenewal)
                {
                    SecurityProtocolCorrelationState correlationState = this.securityProtocol.SecureOutgoingMessage(ref message, timeout, null);
                    return new CompletedAsyncResult<Message, SecurityProtocolCorrelationState>(message, correlationState, callback, state);
                }
                else
                {
                    return new KeyRenewalAsyncResult(message, this, timeout, callback, state);
                }
            }
 
            protected Message EndSecureOutgoingMessage(IAsyncResult result, out SecurityProtocolCorrelationState correlationState)
            {
                if (result is CompletedAsyncResult<Message, SecurityProtocolCorrelationState>)
                {
                    return CompletedAsyncResult<Message, SecurityProtocolCorrelationState>.End(result, out correlationState);
                }
                else
                {
                    TimeSpan remainingTime;
                    Message message = KeyRenewalAsyncResult.End(result, out remainingTime);
                    correlationState = this.securityProtocol.SecureOutgoingMessage(ref message, remainingTime, null);
                    return message;
                }
            }
 
            protected SecurityProtocolCorrelationState SecureOutgoingMessage(ref Message message, TimeSpan timeout)
            {
                bool doKeyRenewal = CheckIfKeyRenewalNeeded();
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                if (doKeyRenewal)
                {
                    RenewKey(timeoutHelper.RemainingTime());
                }
                return this.securityProtocol.SecureOutgoingMessage(ref message, timeoutHelper.RemainingTime(), null);
            }
 
            protected void VerifyIncomingMessage(ref Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
            {
                this.securityProtocol.VerifyIncomingMessage(ref message, timeout, correlationState);
            }
 
            protected virtual void AbortCore()
            {
                if (this.channelBinder != null)
                {
                    this.channelBinder.Abort();
                }
                if (this.sessionTokenProvider != null)
                {
                    SecurityUtils.AbortTokenProviderIfRequired(this.sessionTokenProvider);
                }
            }
 
            protected virtual void CloseCore(TimeSpan timeout)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
 
                try
                {
                    if (this.channelBinder != null)
                    {
                        this.channelBinder.Close(timeoutHelper.RemainingTime());
                    }
                    if (this.sessionTokenProvider != null)
                    {
                        SecurityUtils.CloseTokenProviderIfRequired(this.sessionTokenProvider, timeoutHelper.RemainingTime());
                    }
                    this.keyRenewalCompletedEvent.Abort(this);
                    this.inputSessionClosedHandle.Abort(this);
                }
                catch (CommunicationObjectAbortedException)
                {
                    if (this.State != CommunicationState.Closed)
                    {
                        throw;
                    }
                }
            }
 
            protected virtual IAsyncResult BeginCloseCore(TimeSpan timeout, AsyncCallback callback, object state)
            {
                return new CloseCoreAsyncResult(this, timeout, callback, state);
            }
 
            protected virtual void EndCloseCore(IAsyncResult result)
            {
                CloseCoreAsyncResult.End(result);
            }
 
            protected IAsyncResult BeginReceiveInternal(TimeSpan timeout, SecurityProtocolCorrelationState correlationState, AsyncCallback callback, object state)
            {
                return new ReceiveAsyncResult(this, timeout, correlationState, callback, state);
            }
 
            protected Message EndReceiveInternal(IAsyncResult result)
            {
                return ReceiveAsyncResult.End(result);
            }
 
            protected Message ReceiveInternal(TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                while (!this.isInputClosed)
                {
                    RequestContext requestContext;
 
                    if (this.ChannelBinder.TryReceive(timeoutHelper.RemainingTime(), out requestContext))
                    {
                        if (requestContext == null)
                        {
                            return null;
                        }
 
                        Message message = ProcessRequestContext(requestContext, timeoutHelper.RemainingTime(), correlationState);
 
                        if (message != null)
                        {
                            return message;
                        }
                    }
 
                    if (timeoutHelper.RemainingTime() == TimeSpan.Zero)
                    {
                        // we timed out
                        break;
                    }
                }
 
                return null;
            }
 
            protected bool CloseSession(TimeSpan timeout, out bool wasAborted)
            {
                using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ?
                    ServiceModelActivity.CreateBoundedActivity() : null)
                {
                    if (DiagnosticUtility.ShouldUseActivity)
                    {
                        ServiceModelActivity.Start(activity, SR.GetString(SR.ActivitySecurityClose), ActivityType.SecuritySetup);
                    }
 
                    TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                    wasAborted = false;
                    try
                    {
                        this.CloseOutputSession(timeoutHelper.RemainingTime());
                        return this.inputSessionClosedHandle.Wait(timeoutHelper.RemainingTime(), false);
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (this.State != CommunicationState.Closed)
                        {
                            throw;
                        }
                        wasAborted = true;
                    }
                    return false;
                }
            }
 
            protected IAsyncResult BeginCloseSession(TimeSpan timeout, AsyncCallback callback, object state)
            {
                using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ?
                    ServiceModelActivity.CreateAsyncActivity() : null)
                {
                    if (DiagnosticUtility.ShouldUseActivity)
                    {
                        ServiceModelActivity.Start(activity, SR.GetString(SR.ActivitySecurityClose), ActivityType.SecuritySetup);
                    }
                    return new CloseSessionAsyncResult(timeout, this, callback, state);
                }
            }
 
            protected bool EndCloseSession(IAsyncResult result, out bool wasAborted)
            {
                return CloseSessionAsyncResult.End(result, out wasAborted);
            }
 
            void DetermineCloseMessageToSend(out bool sendClose, out bool sendCloseResponse)
            {
                sendClose = false;
                sendCloseResponse = false;
                lock (ThisLock)
                {
                    if (!this.isOutputClosed)
                    {
                        this.isOutputClosed = true;
                        if (this.receivedClose)
                        {
                            sendCloseResponse = true;
                        }
                        else
                        {
                            sendClose = true;
                            this.sentClose = true;
                        }
                        this.outputSessionCloseHandle.Reset();
                    }
                }
            }
 
            protected virtual SecurityProtocolCorrelationState CloseOutputSession(TimeSpan timeout)
            {
                ThrowIfFaulted();
                if (!this.SendCloseHandshake)
                {
                    return null;
                }
                bool sendClose;
                bool sendCloseResponse;
                DetermineCloseMessageToSend(out sendClose, out sendCloseResponse);
                if (sendClose || sendCloseResponse)
                {
                    try
                    {
                        if (sendClose)
                        {
                            return this.SendCloseMessage(timeout);
                        }
                        else
                        {
                            this.SendCloseResponseMessage(timeout);
                            return null;
                        }
                    }
                    finally
                    {
                        this.outputSessionCloseHandle.Set();
                    }
                }
                else
                {
                    return null;
                }
            }
 
            protected virtual IAsyncResult BeginCloseOutputSession(TimeSpan timeout, AsyncCallback callback, object state)
            {
                ThrowIfFaulted();
                if (!this.SendCloseHandshake)
                {
                    return new CompletedAsyncResult(callback, state);
                }
                bool sendClose;
                bool sendCloseResponse;
                DetermineCloseMessageToSend(out sendClose, out sendCloseResponse);
                if (sendClose || sendCloseResponse)
                {
                    bool setOutputSessionCloseHandle = true;
                    try
                    {
                        IAsyncResult result;
                        if (sendClose)
                        {
                            result = this.BeginSendCloseMessage(timeout, callback, state);
                        }
                        else
                        {
                            result = this.BeginSendCloseResponseMessage(timeout, callback, state);
                        }
                        setOutputSessionCloseHandle = false;
                        return result;
                    }
                    finally
                    {
                        if (setOutputSessionCloseHandle)
                        {
                            this.outputSessionCloseHandle.Set();
                        }
                    }
                }
                else
                {
                    return new CompletedAsyncResult(callback, state);
                }
            }
 
            protected virtual SecurityProtocolCorrelationState EndCloseOutputSession(IAsyncResult result)
            {
                if (result is CompletedAsyncResult)
                {
                    CompletedAsyncResult.End(result);
                    return null;
                }
                bool sentCloseLocal;
                lock (ThisLock)
                {
                    sentCloseLocal = this.sentClose;
                }
                try
                {
                    if (sentCloseLocal)
                    {
                        return this.EndSendCloseMessage(result);
                    }
                    else
                    {
                        this.EndSendCloseResponseMessage(result);
                        return null;
                    }
                }
                finally
                {
                    this.outputSessionCloseHandle.Set();
                }
            }
 
            protected void CheckOutputOpen()
            {
                ThrowIfClosedOrNotOpen();
                lock (ThisLock)
                {
                    if (isOutputClosed)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new CommunicationException(SR.GetString(SR.OutputNotExpected)));
                    }
                }
            }
 
            protected override void OnAbort()
            {
                this.AbortCore();
                this.inputSessionClosedHandle.Abort(this);
                this.keyRenewalCompletedEvent.Abort(this);
                this.outputSessionCloseHandle.Abort(this);
            }
 
            protected override void OnClose(TimeSpan timeout)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                if (this.SendCloseHandshake)
                {
                    bool wasAborted;
                    bool wasSessionClosed = this.CloseSession(timeout, out wasAborted);
                    if (wasAborted)
                    {
                        return;
                    }
                    if (!wasSessionClosed)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ClientSecurityCloseTimeout, timeout)));
                    }
                    // wait for any concurrent output session close to finish
                    try
                    {
                        if (!this.outputSessionCloseHandle.Wait(timeoutHelper.RemainingTime(), false))
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ClientSecurityOutputSessionCloseTimeout, timeoutHelper.OriginalTimeout)));
                        }
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (this.State == CommunicationState.Closed)
                        {
                            return;
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
 
                this.CloseCore(timeoutHelper.RemainingTime());
            }
 
            protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
            {
                return new CloseAsyncResult(this, timeout, callback, state);
            }
 
            protected override void OnEndClose(IAsyncResult result)
            {
                CloseAsyncResult.End(result);
            }
 
            class CloseCoreAsyncResult : TraceAsyncResult
            {
                static AsyncCallback closeChannelBinderCallback = Fx.ThunkCallback(new AsyncCallback(ChannelBinderCloseCallback));
                static AsyncCallback closeTokenProviderCallback = Fx.ThunkCallback(new AsyncCallback(CloseTokenProviderCallback));
 
                TimeoutHelper timeoutHelper;
                ClientSecuritySessionChannel channel;
 
                public CloseCoreAsyncResult(ClientSecuritySessionChannel channel, TimeSpan timeout, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.channel = channel;
                    this.timeoutHelper = new TimeoutHelper(timeout);
                    bool completeSelf = false;
                    if (channel.channelBinder != null)
                    {
                        try
                        {
                            IAsyncResult result = this.channel.channelBinder.BeginClose(timeoutHelper.RemainingTime(), closeChannelBinderCallback, this);
                            if (!result.CompletedSynchronously)
                            {
                                return;
                            }
                            this.channel.channelBinder.EndClose(result);
                        }
                        catch (CommunicationObjectAbortedException)
                        {
                            if (this.channel.State != CommunicationState.Closed)
                            {
                                throw;
                            }
                            completeSelf = true;
                        }
                    }
                    if (!completeSelf)
                    {
                        completeSelf = this.OnChannelBinderClosed();
                    }
                    if (completeSelf)
                    {
                        Complete(true);
                    }
                }
 
                static void ChannelBinderCloseCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseCoreAsyncResult self = (CloseCoreAsyncResult)(result.AsyncState);
                    Exception completionException = null;
                    bool completeSelf = false;
                    try
                    {
                        try
                        {
                            self.channel.channelBinder.EndClose(result);
                        }
                        catch (CommunicationObjectAbortedException)
                        {
                            if (self.channel.State != CommunicationState.Closed)
                            {
                                throw;
                            }
                            completeSelf = true;
                        }
                        if (!completeSelf)
                        {
                            completeSelf = self.OnChannelBinderClosed();
                        }
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        self.Complete(false, completionException);
                    }
                }
 
                bool OnChannelBinderClosed()
                {
                    if (channel.sessionTokenProvider != null)
                    {
                        try
                        {
                            IAsyncResult result = SecurityUtils.BeginCloseTokenProviderIfRequired(this.channel.sessionTokenProvider, timeoutHelper.RemainingTime(), closeTokenProviderCallback, this);
                            if (!result.CompletedSynchronously)
                            {
                                return false;
                            }
                            SecurityUtils.EndCloseTokenProviderIfRequired(result);
                        }
                        catch (CommunicationObjectAbortedException)
                        {
                            if (channel.State != CommunicationState.Closed)
                            {
                                throw;
                            }
                            return true;
                        }
                    }
                    return this.OnTokenProviderClosed();
                }
 
                static void CloseTokenProviderCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseCoreAsyncResult self = (CloseCoreAsyncResult)(result.AsyncState);
                    Exception completionException = null;
                    bool completeSelf = false;
                    try
                    {
                        try
                        {
                            SecurityUtils.EndCloseTokenProviderIfRequired(result);
                        }
                        catch (CommunicationObjectAbortedException)
                        {
                            if (self.channel.State != CommunicationState.Closed)
                            {
                                throw;
                            }
                            completeSelf = true;
                        }
                        if (!completeSelf)
                        {
                            completeSelf = self.OnTokenProviderClosed();
                        }
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        self.Complete(false, completionException);
                    }
                }
 
                bool OnTokenProviderClosed()
                {
                    this.channel.keyRenewalCompletedEvent.Abort(this.channel);
                    this.channel.inputSessionClosedHandle.Abort(this.channel);
                    return true;
                }
 
                public static void End(IAsyncResult result)
                {
                    AsyncResult.End<CloseCoreAsyncResult>(result);
                }
            }
 
            class ReceiveAsyncResult : TraceAsyncResult
            {
                static AsyncCallback onReceive = Fx.ThunkCallback(new AsyncCallback(OnReceive));
                ClientSecuritySessionChannel channel;
                Message message;
                SecurityProtocolCorrelationState correlationState;
                TimeoutHelper timeoutHelper;
 
                public ReceiveAsyncResult(ClientSecuritySessionChannel channel, TimeSpan timeout, SecurityProtocolCorrelationState correlationState, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.channel = channel;
                    this.correlationState = correlationState;
                    this.timeoutHelper = new TimeoutHelper(timeout);
 
                    IAsyncResult result = channel.ChannelBinder.BeginTryReceive(timeoutHelper.RemainingTime(), onReceive, this);
 
                    if (!result.CompletedSynchronously)
                        return;
 
                    bool completedSynchronously = CompleteReceive(result);
                    if (completedSynchronously)
                    {
                        Complete(true);
                    }
                }
 
                bool CompleteReceive(IAsyncResult result)
                {
                    while (!channel.isInputClosed)
                    {
                        RequestContext requestContext;
                        if (channel.ChannelBinder.EndTryReceive(result, out requestContext))
                        {
                            if (requestContext == null)
                                break;
 
                            message = channel.ProcessRequestContext(requestContext, timeoutHelper.RemainingTime(), this.correlationState);
 
                            if (message != null || channel.isInputClosed)
                                break;
                        }
 
                        TimeSpan timeout = timeoutHelper.RemainingTime();
                        if (timeout == TimeSpan.Zero)
                        {
                            // we timed out
                            break;
                        }
 
                        result = channel.ChannelBinder.BeginTryReceive(timeoutHelper.RemainingTime(), onReceive, this);
 
                        if (!result.CompletedSynchronously)
                            return false;
                    }
 
                    return true;
                }
 
                public static Message End(IAsyncResult result)
                {
                    ReceiveAsyncResult receiveResult = AsyncResult.End<ReceiveAsyncResult>(result);
                    return receiveResult.message;
                }
 
                static void OnReceive(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                        return;
                    ReceiveAsyncResult self = (ReceiveAsyncResult)result.AsyncState;
                    bool completeSelf = false;
                    Exception completionException = null;
                    try
                    {
                        completeSelf = self.CompleteReceive(result);
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        self.Complete(false, completionException);
                    }
                }
            }
 
            class OpenAsyncResult : TraceAsyncResult
            {
                static readonly AsyncCallback getTokenCallback = Fx.ThunkCallback(new AsyncCallback(GetTokenCallback));
                static readonly AsyncCallback openTokenProviderCallback = Fx.ThunkCallback(new AsyncCallback(OpenTokenProviderCallback));
                ClientSecuritySessionChannel sessionChannel;
                TimeoutHelper timeoutHelper;
 
                public OpenAsyncResult(ClientSecuritySessionChannel sessionChannel, TimeSpan timeout, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.timeoutHelper = new TimeoutHelper(timeout);
                    this.sessionChannel = sessionChannel;
                    this.sessionChannel.SetupSessionTokenProvider();
                    IAsyncResult result = SecurityUtils.BeginOpenTokenProviderIfRequired(this.sessionChannel.sessionTokenProvider, timeoutHelper.RemainingTime(), openTokenProviderCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return;
                    }
                    SecurityUtils.EndOpenTokenProviderIfRequired(result);
                    bool completeSelf = this.OnTokenProviderOpened();
                    if (completeSelf)
                    {
                        Complete(true);
                    }
                }
 
                static void OpenTokenProviderCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    OpenAsyncResult thisAsyncResult = (OpenAsyncResult)result.AsyncState;
                    bool completeSelf = false;
                    Exception completionException = null;
                    try
                    {
                        SecurityUtils.EndOpenTokenProviderIfRequired(result);
                        completeSelf = thisAsyncResult.OnTokenProviderOpened();
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        thisAsyncResult.Complete(false, completionException);
                    }
                }
 
                bool OnTokenProviderOpened()
                {
                    IAsyncResult result = this.sessionChannel.sessionTokenProvider.BeginGetToken(timeoutHelper.RemainingTime(), getTokenCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return false;
                    }
 
                    SecurityToken sessionToken = this.sessionChannel.sessionTokenProvider.EndGetToken(result);
                    return this.OnTokenObtained(sessionToken);
                }
 
 
                bool OnTokenObtained(SecurityToken sessionToken)
                {
                    // Token was issued, do send cancel on close;
                    this.sessionChannel.sendCloseHandshake = true;
                    this.sessionChannel.OpenCore(sessionToken, timeoutHelper.RemainingTime());
                    return true;
                }
 
                static void GetTokenCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    OpenAsyncResult thisAsyncResult = (OpenAsyncResult)result.AsyncState;
                    try
                    {
                        using (ServiceModelActivity.BoundOperation(thisAsyncResult.CallbackActivity))
                        {
                            bool completeSelf = false;
                            Exception completionException = null;
                            try
                            {
                                SecurityToken sessionToken = thisAsyncResult.sessionChannel.sessionTokenProvider.EndGetToken(result);
                                completeSelf = thisAsyncResult.OnTokenObtained(sessionToken);
                            }
#pragma warning suppress 56500 // covered by FxCOP
                            catch (Exception e)
                            {
                                if (Fx.IsFatal(e))
                                {
                                    throw;
                                }
 
                                completeSelf = true;
                                completionException = e;
                            }
                            if (completeSelf)
                            {
                                thisAsyncResult.Complete(false, completionException);
                            }
                        }
                    }
                    finally
                    {
                        if (thisAsyncResult.CallbackActivity != null)
                        {
                            thisAsyncResult.CallbackActivity.Dispose();
                        }
                    }
                }
 
                public static void End(IAsyncResult result)
                {
                    AsyncResult.End<OpenAsyncResult>(result);
                    ServiceModelActivity.Stop(((OpenAsyncResult)result).CallbackActivity);
                }
            }
 
            class CloseSessionAsyncResult : TraceAsyncResult
            {
                static readonly AsyncCallback closeOutputSessionCallback = Fx.ThunkCallback(new AsyncCallback(CloseOutputSessionCallback));
                static readonly AsyncCallback shutdownWaitCallback = Fx.ThunkCallback(new AsyncCallback(ShutdownWaitCallback));
 
                ClientSecuritySessionChannel sessionChannel;
                bool closeCompleted;
                bool wasAborted;
                TimeoutHelper timeoutHelper;
 
                public CloseSessionAsyncResult(TimeSpan timeout, ClientSecuritySessionChannel sessionChannel, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.timeoutHelper = new TimeoutHelper(timeout);
                    this.sessionChannel = sessionChannel;
                    bool completeSelf = false;
                    try
                    {
                        IAsyncResult result = this.sessionChannel.BeginCloseOutputSession(timeoutHelper.RemainingTime(), closeOutputSessionCallback, this);
                        if (!result.CompletedSynchronously)
                        {
                            return;
                        }
                        this.sessionChannel.EndCloseOutputSession(result);
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (this.sessionChannel.State != CommunicationState.Closed)
                        {
                            throw;
                        }
                        completeSelf = true;
                        wasAborted = true;
                    }
                    if (!wasAborted)
                    {
                        completeSelf = this.OnOutputSessionClosed();
                    }
                    if (completeSelf)
                    {
                        Complete(true);
                    }
                }
 
                static void CloseOutputSessionCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseSessionAsyncResult thisResult = (CloseSessionAsyncResult)result.AsyncState;
                    bool completeSelf = false;
                    Exception completionException = null;
                    try
                    {
                        try
                        {
                            thisResult.sessionChannel.EndCloseOutputSession(result);
                        }
                        catch (CommunicationObjectAbortedException)
                        {
                            if (thisResult.sessionChannel.State != CommunicationState.Closed)
                            {
                                throw;
                            }
                            thisResult.wasAborted = true;
                            completeSelf = true;
                        }
                        if (!thisResult.wasAborted)
                        {
                            completeSelf = thisResult.OnOutputSessionClosed();
                        }
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        thisResult.Complete(false, completionException);
                    }
                }
 
                bool OnOutputSessionClosed()
                {
                    try
                    {
                        IAsyncResult result = this.sessionChannel.inputSessionClosedHandle.BeginWait(this.timeoutHelper.RemainingTime(), true, shutdownWaitCallback, this);
                        if (!result.CompletedSynchronously)
                        {
                            return false;
                        }
                        this.sessionChannel.inputSessionClosedHandle.EndWait(result);
                        this.closeCompleted = true;
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (this.sessionChannel.State != CommunicationState.Closed)
                        {
                            throw;
                        }
                        // if the channel was aborted by a parallel thread, allow the Close to
                        // complete cleanly
                        this.wasAborted = true;
                    }
                    catch (TimeoutException)
                    {
                        this.closeCompleted = false;
                    }
                    return true;
                }
 
                static void ShutdownWaitCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseSessionAsyncResult thisResult = (CloseSessionAsyncResult)result.AsyncState;
                    Exception completionException = null;
                    try
                    {
                        thisResult.sessionChannel.inputSessionClosedHandle.EndWait(result);
                        thisResult.closeCompleted = true;
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (thisResult.sessionChannel.State != CommunicationState.Closed)
                        {
                            throw;
                        }
                        thisResult.wasAborted = true;
                    }
                    catch (TimeoutException)
                    {
                        thisResult.closeCompleted = false;
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
                        completionException = e;
                    }
                    thisResult.Complete(false, completionException);
                }
 
                public static bool End(IAsyncResult result, out bool wasAborted)
                {
                    CloseSessionAsyncResult thisResult = AsyncResult.End<CloseSessionAsyncResult>(result);
                    wasAborted = thisResult.wasAborted;
                    ServiceModelActivity.Stop(thisResult.CallbackActivity);
                    return thisResult.closeCompleted;
                }
            }
 
            class CloseAsyncResult : TraceAsyncResult
            {
                static readonly AsyncCallback closeSessionCallback = Fx.ThunkCallback(new AsyncCallback(CloseSessionCallback));
                static readonly AsyncCallback outputSessionClosedCallback = Fx.ThunkCallback(new AsyncCallback(OutputSessionClosedCallback));
                static readonly AsyncCallback closeCoreCallback = Fx.ThunkCallback(new AsyncCallback(CloseCoreCallback));
 
                ClientSecuritySessionChannel sessionChannel;
                TimeoutHelper timeoutHelper;
 
                public CloseAsyncResult(ClientSecuritySessionChannel sessionChannel, TimeSpan timeout, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    sessionChannel.ThrowIfFaulted();
                    this.timeoutHelper = new TimeoutHelper(timeout);
                    this.sessionChannel = sessionChannel;
 
                    if (!sessionChannel.SendCloseHandshake)
                    {
                        if (this.CloseCore())
                        {
                            Complete(true);
                        }
                        return;
                    }
 
                    bool wasClosed;
                    bool wasAborted = false;
                    IAsyncResult result = this.sessionChannel.BeginCloseSession(this.timeoutHelper.RemainingTime(), closeSessionCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return;
                    }
                    wasClosed = this.sessionChannel.EndCloseSession(result, out wasAborted);
                    if (wasAborted)
                    {
                        Complete(true);
                        return;
                    }
                    if (!wasClosed)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ClientSecurityCloseTimeout, timeout)));
                    }
                    bool completeSelf = this.OnWaitForOutputSessionClose(out wasAborted);
                    if (wasAborted || completeSelf)
                    {
                        Complete(true);
                    }
                }
 
                static void CloseSessionCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseAsyncResult thisResult = (CloseAsyncResult)result.AsyncState;
                    bool completeSelf = false;
                    Exception completionException = null;
                    try
                    {
                        bool wasAborted;
                        bool wasClosed = thisResult.sessionChannel.EndCloseSession(result, out wasAborted);
                        if (wasAborted)
                        {
                            completeSelf = true;
                        }
                        else
                        {
                            if (!wasClosed)
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ClientSecurityCloseTimeout, thisResult.timeoutHelper.OriginalTimeout)));
                            }
                            completeSelf = thisResult.OnWaitForOutputSessionClose(out wasAborted);
                            if (wasAborted)
                            {
                                completeSelf = true;
                            }
                        }
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        thisResult.Complete(false, completionException);
                    }
                }
 
                bool OnWaitForOutputSessionClose(out bool wasAborted)
                {
                    wasAborted = false;
                    bool wasOutputSessionClosed = false;
                    // wait for pending output sessions to finish
                    try
                    {
                        IAsyncResult result = this.sessionChannel.outputSessionCloseHandle.BeginWait(timeoutHelper.RemainingTime(), true, outputSessionClosedCallback, this);
                        if (!result.CompletedSynchronously)
                        {
                            return false;
                        }
                        this.sessionChannel.outputSessionCloseHandle.EndWait(result);
                        wasOutputSessionClosed = true;
                    }
                    catch (TimeoutException)
                    {
                        wasOutputSessionClosed = false;
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (this.sessionChannel.State == CommunicationState.Closed)
                        {
                            wasAborted = true;
                        }
                        else
                        {
                            throw;
                        }
                    }
                    if (wasAborted)
                    {
                        return true;
                    }
                    if (!wasOutputSessionClosed)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ClientSecurityOutputSessionCloseTimeout, timeoutHelper.OriginalTimeout)));
                    }
                    else
                    {
                        return this.CloseCore();
                    }
                }
 
                static void OutputSessionClosedCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseAsyncResult self = (CloseAsyncResult)(result.AsyncState);
                    Exception completionException = null;
                    bool completeSelf = false;
                    try
                    {
                        bool wasOutputSessionClosed = false;
                        bool wasAborted = false;
                        try
                        {
                            self.sessionChannel.outputSessionCloseHandle.EndWait(result);
                            wasOutputSessionClosed = true;
                        }
                        catch (TimeoutException)
                        {
                            wasOutputSessionClosed = false;
                        }
                        catch (CommunicationObjectFaultedException)
                        {
                            if (self.sessionChannel.State == CommunicationState.Closed)
                            {
                                wasAborted = true;
                            }
                            else
                            {
                                throw;
                            }
                        }
                        if (!wasAborted)
                        {
                            if (!wasOutputSessionClosed)
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new TimeoutException(SR.GetString(SR.ClientSecurityOutputSessionCloseTimeout, self.timeoutHelper.OriginalTimeout)));
                            }
                            completeSelf = self.CloseCore();
                        }
                        else
                        {
                            completeSelf = true;
                        }
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
                        completionException = e;
                        completeSelf = true;
                    }
                    if (completeSelf)
                    {
                        self.Complete(false, completionException);
                    }
                }
 
                bool CloseCore()
                {
                    IAsyncResult result = this.sessionChannel.BeginCloseCore(timeoutHelper.RemainingTime(), closeCoreCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return false;
                    }
                    this.sessionChannel.EndCloseCore(result);
                    return true;
                }
 
                static void CloseCoreCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseAsyncResult self = (CloseAsyncResult)(result.AsyncState);
                    Exception completionException = null;
                    try
                    {
                        self.sessionChannel.EndCloseCore(result);
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
                        completionException = e;
                    }
                    self.Complete(false, completionException);
                }
 
                public static void End(IAsyncResult result)
                {
                    AsyncResult.End<CloseAsyncResult>(result);
                }
            }
 
            class KeyRenewalAsyncResult : TraceAsyncResult
            {
                static readonly Action<object> renewKeyCallback = new Action<object>(RenewKeyCallback);
                Message message;
                ClientSecuritySessionChannel sessionChannel;
                TimeoutHelper timeoutHelper;
 
                public KeyRenewalAsyncResult(Message message, ClientSecuritySessionChannel sessionChannel, TimeSpan timeout, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    timeoutHelper = new TimeoutHelper(timeout);
                    this.message = message;
                    this.sessionChannel = sessionChannel;
                    // its ok to start up a separate thread for this since this is a very rare event.
                    ActionItem.Schedule(renewKeyCallback, this);
                }
 
                static void RenewKeyCallback(object state)
                {
                    KeyRenewalAsyncResult thisResult = (KeyRenewalAsyncResult)state;
                    Exception completionException = null;
                    try
                    {
                        using (thisResult.CallbackActivity == null ? null : ServiceModelActivity.BoundOperation(thisResult.CallbackActivity))
                        {
                            thisResult.sessionChannel.RenewKey(thisResult.timeoutHelper.RemainingTime());
                        }
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completionException = e;
                    }
                    thisResult.Complete(false, completionException);
                }
 
                public static Message End(IAsyncResult result, out TimeSpan remainingTime)
                {
                    KeyRenewalAsyncResult thisResult = AsyncResult.End<KeyRenewalAsyncResult>(result);
                    remainingTime = thisResult.timeoutHelper.RemainingTime();
                    return thisResult.message;
                }
            }
 
            internal abstract class SecureSendAsyncResultBase : TraceAsyncResult
            {
                static readonly AsyncCallback secureOutgoingMessageCallback = Fx.ThunkCallback(new AsyncCallback(SecureOutgoingMessageCallback));
                Message message;
                SecurityProtocolCorrelationState correlationState;
                ClientSecuritySessionChannel sessionChannel;
                bool didSecureOutgoingMessageCompleteSynchronously = false;
                TimeoutHelper timeoutHelper;
 
                protected SecureSendAsyncResultBase(Message message, ClientSecuritySessionChannel sessionChannel, TimeSpan timeout, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.message = message;
                    this.sessionChannel = sessionChannel;
                    this.timeoutHelper = new TimeoutHelper(timeout);
                    IAsyncResult result = this.sessionChannel.BeginSecureOutgoingMessage(message, timeoutHelper.RemainingTime(), secureOutgoingMessageCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return;
                    }
                    this.message = this.sessionChannel.EndSecureOutgoingMessage(result, out this.correlationState);
                    this.didSecureOutgoingMessageCompleteSynchronously = true;
                }
 
                protected bool DidSecureOutgoingMessageCompleteSynchronously
                {
                    get
                    {
                        return this.didSecureOutgoingMessageCompleteSynchronously;
                    }
                }
 
                protected TimeoutHelper TimeoutHelper
                {
                    get
                    {
                        return this.timeoutHelper;
                    }
                }
 
                protected IClientReliableChannelBinder ChannelBinder
                {
                    get
                    {
                        return this.sessionChannel.ChannelBinder;
                    }
                }
 
                protected Message Message
                {
                    get
                    {
                        return this.message;
                    }
                }
 
                protected SecurityProtocolCorrelationState SecurityCorrelationState
                {
                    get
                    {
                        return this.correlationState;
                    }
                }
 
                protected abstract bool OnMessageSecured();
 
                static void SecureOutgoingMessageCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    SecureSendAsyncResultBase thisResult = (SecureSendAsyncResultBase)result.AsyncState;
                    bool completeSelf = false;
                    Exception completionException = null;
                    try
                    {
                        thisResult.message = thisResult.sessionChannel.EndSecureOutgoingMessage(result, out thisResult.correlationState);
                        completeSelf = thisResult.OnMessageSecured();
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        thisResult.Complete(false, completionException);
                    }
                }
            }
 
            internal sealed class SecureSendAsyncResult : SecureSendAsyncResultBase
            {
                static readonly AsyncCallback sendCallback = Fx.ThunkCallback(new AsyncCallback(SendCallback));
 
                bool autoCloseMessage;
 
                public SecureSendAsyncResult(Message message, ClientSecuritySessionChannel sessionChannel, TimeSpan timeout, AsyncCallback callback, object state, bool autoCloseMessage)
                    : base(message, sessionChannel, timeout, callback, state)
                {
                    this.autoCloseMessage = autoCloseMessage;
 
                    if (!this.DidSecureOutgoingMessageCompleteSynchronously)
                    {
                        return;
                    }
                    bool completeSelf = this.OnMessageSecured();
                    if (completeSelf)
                    {
                        Complete(true);
                    }
                }
 
                protected override bool OnMessageSecured()
                {
                    bool closeMessage = true;
                    try
                    {
                        IAsyncResult result = this.ChannelBinder.BeginSend(this.Message, this.TimeoutHelper.RemainingTime(), sendCallback, this);
                        if (!result.CompletedSynchronously)
                        {
                            closeMessage = false;
                            return false;
                        }
                        this.ChannelBinder.EndSend(result);
                        return true;
                    }
                    finally
                    {
                        if (closeMessage && this.autoCloseMessage && this.Message != null)
                        {
                            this.Message.Close();
                        }
                    }
                }
 
                static void SendCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    SecureSendAsyncResult thisResult = (SecureSendAsyncResult)result.AsyncState;
                    Exception completionException = null;
                    try
                    {
                        thisResult.ChannelBinder.EndSend(result);
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completionException = e;
                    }
                    finally
                    {
                        if (thisResult.autoCloseMessage && thisResult.Message != null)
                        {
                            thisResult.Message.Close();
                        }
                        if (thisResult.CallbackActivity != null && DiagnosticUtility.ShouldUseActivity)
                        {
                            thisResult.CallbackActivity.Stop();
                        }
                    }
 
                    thisResult.Complete(false, completionException);
                }
 
                public static SecurityProtocolCorrelationState End(IAsyncResult result)
                {
                    SecureSendAsyncResult thisResult = AsyncResult.End<SecureSendAsyncResult>(result);
                    return thisResult.SecurityCorrelationState;
                }
            }
 
            protected class SoapSecurityOutputSession : ISecureConversationSession, IOutputSession
            {
                ClientSecuritySessionChannel channel;
                EndpointIdentity remoteIdentity;
                UniqueId sessionId;
                SecurityKeyIdentifierClause sessionTokenIdentifier;
                SecurityStandardsManager standardsManager;
 
                public SoapSecurityOutputSession(ClientSecuritySessionChannel channel)
                {
                    this.channel = channel;
                }
 
                internal void Initialize(SecurityToken sessionToken, SecuritySessionClientSettings<TChannel> settings)
                {
                    if (sessionToken == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("sessionToken");
                    }
                    if (settings == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("settings");
                    }
                    Claim identityClaim = SecurityUtils.GetPrimaryIdentityClaim(((GenericXmlSecurityToken)sessionToken).AuthorizationPolicies);
                    if (identityClaim != null)
                    {
                        this.remoteIdentity = EndpointIdentity.CreateIdentity(identityClaim);
                    }
                    this.standardsManager = settings.SessionProtocolFactory.StandardsManager;
                    this.sessionId = GetSessionId(sessionToken, this.standardsManager);
                    this.sessionTokenIdentifier = settings.IssuedSecurityTokenParameters.CreateKeyIdentifierClause(sessionToken,
                        SecurityTokenReferenceStyle.External);
                }
 
                UniqueId GetSessionId(SecurityToken sessionToken, SecurityStandardsManager standardsManager)
                {
                    GenericXmlSecurityToken gxt = sessionToken as GenericXmlSecurityToken;
                    if (gxt == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SessionTokenIsNotGenericXmlToken, sessionToken, typeof(GenericXmlSecurityToken))));
                    }
                    return standardsManager.SecureConversationDriver.GetSecurityContextTokenId(XmlDictionaryReader.CreateDictionaryReader(new XmlNodeReader(gxt.TokenXml)));
                }
 
                public string Id
                {
                    get
                    {
                        if (this.sessionId == null)
                        {
                            // PreSharp Bug: Property get methods should not throw exceptions.
#pragma warning suppress 56503
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ChannelMustBeOpenedToGetSessionId)));
                        }
                        return this.sessionId.ToString();
                    }
                }
 
                public EndpointIdentity RemoteIdentity
                {
                    get
                    {
                        return this.remoteIdentity;
                    }
                }
 
                public void WriteSessionTokenIdentifier(XmlDictionaryWriter writer)
                {
                    this.channel.ThrowIfDisposedOrNotOpen();
                    this.standardsManager.SecurityTokenSerializer.WriteKeyIdentifierClause(writer, this.sessionTokenIdentifier);
                }
 
                public bool TryReadSessionTokenIdentifier(XmlReader reader)
                {
                    this.channel.ThrowIfDisposedOrNotOpen();
                    if (!this.standardsManager.SecurityTokenSerializer.CanReadKeyIdentifierClause(reader))
                    {
                        return false;
                    }
                    SecurityContextKeyIdentifierClause incomingTokenIdentifier =
                        this.standardsManager.SecurityTokenSerializer.ReadKeyIdentifierClause(reader) as SecurityContextKeyIdentifierClause;
                    return incomingTokenIdentifier != null && incomingTokenIdentifier.Matches(sessionId, null);
                }
            }
        }
 
        abstract class ClientSecuritySimplexSessionChannel : ClientSecuritySessionChannel
        {
            SoapSecurityOutputSession outputSession;
 
            protected ClientSecuritySimplexSessionChannel(SecuritySessionClientSettings<TChannel> settings, EndpointAddress to, Uri via)
                : base(settings, to, via)
            {
                this.outputSession = new SoapSecurityOutputSession(this);
            }
 
            public IOutputSession Session
            {
                get
                {
                    return this.outputSession;
                }
            }
 
            protected override bool ExpectClose
            {
                get { return false; }
            }
 
            protected override string SessionId
            {
                get { return this.Session.Id; }
            }
 
            protected override void InitializeSession(SecurityToken sessionToken)
            {
                this.outputSession.Initialize(sessionToken, this.Settings);
            }
        }
 
        sealed class SecurityRequestSessionChannel : ClientSecuritySimplexSessionChannel, IRequestSessionChannel
        {
            public SecurityRequestSessionChannel(SecuritySessionClientSettings<TChannel> settings, EndpointAddress to, Uri via)
                : base(settings, to, via)
            {
            }
 
            protected override bool CanDoSecurityCorrelation
            {
                get
                {
                    return true;
                }
            }
 
            protected override SecurityProtocolCorrelationState CloseOutputSession(TimeSpan timeout)
            {
                ThrowIfFaulted();
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                SecurityProtocolCorrelationState correlationState = base.CloseOutputSession(timeoutHelper.RemainingTime());
 
                Message message = ReceiveInternal(timeoutHelper.RemainingTime(), correlationState);
 
                if (message != null)
                {
                    using (message)
                    {
                        ProtocolException error = ProtocolException.ReceiveShutdownReturnedNonNull(message);
                        throw TraceUtility.ThrowHelperWarning(error, message);
                    }
                }
                return null;
            }
 
            protected override IAsyncResult BeginCloseOutputSession(TimeSpan timeout, AsyncCallback callback, object state)
            {
                ThrowIfFaulted();
                return new CloseOutputSessionAsyncResult(this, timeout, callback, state);
            }
 
            protected override SecurityProtocolCorrelationState EndCloseOutputSession(IAsyncResult result)
            {
                CloseOutputSessionAsyncResult.End(result);
                return null;
            }
 
            IAsyncResult BeginBaseCloseOutputSession(TimeSpan timeout, AsyncCallback callback, object state)
            {
                return base.BeginCloseOutputSession(timeout, callback, state);
            }
 
            SecurityProtocolCorrelationState EndBaseCloseOutputSession(IAsyncResult result)
            {
                return base.EndCloseOutputSession(result);
            }
 
            public Message Request(Message message)
            {
                return this.Request(message, this.DefaultSendTimeout);
            }
 
            Message ProcessReply(Message reply, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
            {
                if (reply == null)
                {
                    return null;
                }
                Message unverifiedReply = reply;
                Message processedReply = null;
                MessageFault protocolFault = null;
                Exception faultException = null;
                try
                {
                    processedReply = this.ProcessIncomingMessage(reply, timeout, correlationState, out protocolFault);
                }
                catch (MessageSecurityException)
                {
                    if (unverifiedReply.IsFault)
                    {
                        MessageFault fault = MessageFault.CreateFault(unverifiedReply, TransportDefaults.MaxSecurityFaultSize);
                        if (SecurityUtils.IsSecurityFault(fault, this.Settings.standardsManager))
                        {
                            faultException = SecurityUtils.CreateSecurityFaultException(fault);
                        }
                    }
                    if (faultException == null)
                    {
                        throw;
                    }
                }
                if (faultException != null)
                {
                    Fault(faultException);
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(faultException);
                }
                if (processedReply == null && protocolFault != null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.SecuritySessionFaultReplyWasSent), new FaultException(protocolFault)));
                }
                return processedReply;
            }
 
 
            public Message Request(Message message, TimeSpan timeout)
            {
                ThrowIfFaulted();
                CheckOutputOpen();
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                SecurityProtocolCorrelationState correlationState = this.SecureOutgoingMessage(ref message, timeoutHelper.RemainingTime());
                Message reply = this.ChannelBinder.Request(message, timeoutHelper.RemainingTime());
                return ProcessReply(reply, timeoutHelper.RemainingTime(), correlationState);
            }
 
            public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
            {
                return this.BeginRequest(message, this.DefaultSendTimeout, callback, state);
            }
 
            public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
            {
                ThrowIfFaulted();
                CheckOutputOpen();
                return new SecureRequestAsyncResult(message, this, timeout, callback, state);
            }
 
            public Message EndRequest(IAsyncResult result)
            {
                SecurityProtocolCorrelationState requestCorrelationState;
                TimeSpan remainingTime;
                Message reply = SecureRequestAsyncResult.EndAsReply(result, out requestCorrelationState, out remainingTime);
                return ProcessReply(reply, remainingTime, requestCorrelationState);
            }
 
            sealed class SecureRequestAsyncResult : SecureSendAsyncResultBase
            {
                static readonly AsyncCallback requestCallback = Fx.ThunkCallback(new AsyncCallback(RequestCallback));
                Message reply;
 
                public SecureRequestAsyncResult(Message request, ClientSecuritySessionChannel sessionChannel, TimeSpan timeout, AsyncCallback callback, object state)
                    : base(request, sessionChannel, timeout, callback, state)
                {
                    if (!this.DidSecureOutgoingMessageCompleteSynchronously)
                    {
                        return;
                    }
                    bool completeSelf = OnMessageSecured();
                    if (completeSelf)
                    {
                        Complete(true);
                    }
                }
 
                protected override bool OnMessageSecured()
                {
                    IAsyncResult result = this.ChannelBinder.BeginRequest(this.Message, this.TimeoutHelper.RemainingTime(), requestCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return false;
                    }
                    this.reply = this.ChannelBinder.EndRequest(result);
                    return true;
                }
 
                static void RequestCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    SecureRequestAsyncResult thisAsyncResult = (SecureRequestAsyncResult)result.AsyncState;
                    Exception completionException = null;
                    try
                    {
                        thisAsyncResult.reply = thisAsyncResult.ChannelBinder.EndRequest(result);
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completionException = e;
                    }
                    thisAsyncResult.Complete(false, completionException);
                }
 
                public static Message EndAsReply(IAsyncResult result, out SecurityProtocolCorrelationState correlationState, out TimeSpan remainingTime)
                {
                    SecureRequestAsyncResult thisResult = AsyncResult.End<SecureRequestAsyncResult>(result);
                    correlationState = thisResult.SecurityCorrelationState;
                    remainingTime = thisResult.TimeoutHelper.RemainingTime();
                    return thisResult.reply;
                }
            }
 
            class CloseOutputSessionAsyncResult : TraceAsyncResult
            {
                static readonly AsyncCallback baseCloseOutputSessionCallback = Fx.ThunkCallback(new AsyncCallback(BaseCloseOutputSessionCallback));
                static readonly AsyncCallback receiveInternalCallback = Fx.ThunkCallback(new AsyncCallback(ReceiveInternalCallback));
                SecurityRequestSessionChannel requestChannel;
                SecurityProtocolCorrelationState correlationState;
                TimeoutHelper timeoutHelper;
 
                public CloseOutputSessionAsyncResult(SecurityRequestSessionChannel requestChannel, TimeSpan timeout, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.timeoutHelper = new TimeoutHelper(timeout);
                    this.requestChannel = requestChannel;
                    IAsyncResult result = this.requestChannel.BeginBaseCloseOutputSession(timeoutHelper.RemainingTime(), baseCloseOutputSessionCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return;
                    }
                    this.correlationState = this.requestChannel.EndBaseCloseOutputSession(result);
                    bool completeSelf = this.OnBaseOutputSessionClosed();
                    if (completeSelf)
                    {
                        Complete(true);
                    }
                }
 
                static void BaseCloseOutputSessionCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseOutputSessionAsyncResult thisAsyncResult = (CloseOutputSessionAsyncResult)result.AsyncState;
                    bool completeSelf = false;
                    Exception completionException = null;
                    try
                    {
                        thisAsyncResult.correlationState = thisAsyncResult.requestChannel.EndBaseCloseOutputSession(result);
                        completeSelf = thisAsyncResult.OnBaseOutputSessionClosed();
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        thisAsyncResult.Complete(false, completionException);
                    }
                }
 
                bool OnBaseOutputSessionClosed()
                {
                    IAsyncResult result = this.requestChannel.BeginReceiveInternal(this.timeoutHelper.RemainingTime(), this.correlationState, receiveInternalCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return false;
                    }
                    Message message = this.requestChannel.EndReceiveInternal(result);
                    return this.OnMessageReceived(message);
                }
 
                static void ReceiveInternalCallback(IAsyncResult result)
                {
                    if (result.CompletedSynchronously)
                    {
                        return;
                    }
                    CloseOutputSessionAsyncResult thisAsyncResult = (CloseOutputSessionAsyncResult)result.AsyncState;
                    bool completeSelf = false;
                    Exception completionException = null;
                    try
                    {
                        Message message = thisAsyncResult.requestChannel.EndReceiveInternal(result);
                        completeSelf = thisAsyncResult.OnMessageReceived(message);
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e))
                        {
                            throw;
                        }
 
                        completeSelf = true;
                        completionException = e;
                    }
                    if (completeSelf)
                    {
                        thisAsyncResult.Complete(false, completionException);
                    }
                }
 
                bool OnMessageReceived(Message message)
                {
                    if (message != null)
                    {
                        using (message)
                        {
                            ProtocolException error = ProtocolException.ReceiveShutdownReturnedNonNull(message);
                            throw TraceUtility.ThrowHelperWarning(error, message);
                        }
                    }
                    return true;
                }
 
                public static void End(IAsyncResult result)
                {
                    AsyncResult.End<CloseOutputSessionAsyncResult>(result);
                }
            }
        }
 
        class ClientSecurityDuplexSessionChannel : ClientSecuritySessionChannel, IDuplexSessionChannel
        {
            static AsyncCallback onReceive = Fx.ThunkCallback(new AsyncCallback(OnReceive));
            SoapSecurityClientDuplexSession session;
            InputQueue<Message> queue;
            Action startReceiving;
            Action<object> completeLater;
 
            public ClientSecurityDuplexSessionChannel(SecuritySessionClientSettings<TChannel> settings, EndpointAddress to, Uri via)
                : base(settings, to, via)
            {
                this.session = new SoapSecurityClientDuplexSession(this);
                this.queue = TraceUtility.CreateInputQueue<Message>();
                this.startReceiving = new Action(StartReceiving);
                this.completeLater = new Action<object>(CompleteLater);
            }
 
            public EndpointAddress LocalAddress
            {
                get
                {
                    return base.InternalLocalAddress;
                }
            }
 
            public IDuplexSession Session
            {
                get
                {
                    return this.session;
                }
            }
 
            protected override bool ExpectClose
            {
                get { return true; }
            }
 
            protected override string SessionId
            {
                get { return this.session.Id; }
            }
 
            public Message Receive()
            {
                return this.Receive(this.DefaultReceiveTimeout);
            }
 
            public Message Receive(TimeSpan timeout)
            {
                return InputChannel.HelpReceive(this, timeout);
            }
 
            public IAsyncResult BeginReceive(AsyncCallback callback, object state)
            {
                return this.BeginReceive(this.DefaultReceiveTimeout, callback, state);
            }
 
            public IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, object state)
            {
                return InputChannel.HelpBeginReceive(this, timeout, callback, state);
            }
 
            public Message EndReceive(IAsyncResult result)
            {
                return InputChannel.HelpEndReceive(result);
            }
 
            public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state)
            {
                ThrowIfFaulted();
                return queue.BeginDequeue(timeout, callback, state);
            }
 
            public bool EndTryReceive(IAsyncResult result, out Message message)
            {
                bool wasDequeued = queue.EndDequeue(result, out message);
                if (message == null)
                {
                    // the channel could have faulted, shutting down the input queue
                    ThrowIfFaulted();
                }
                return wasDequeued;
            }
 
            protected override void OnOpened()
            {
                base.OnOpened();
                StartReceiving();
            }
 
            public bool TryReceive(TimeSpan timeout, out Message message)
            {
                ThrowIfFaulted();
                bool wasDequeued = queue.Dequeue(timeout, out message);
                if (message == null)
                {
                    // the channel could have faulted, shutting down the input queue
                    ThrowIfFaulted();
                }
                return wasDequeued;
            }
 
            public void Send(Message message)
            {
                this.Send(message, this.DefaultSendTimeout);
            }
 
            public void Send(Message message, TimeSpan timeout)
            {
                ThrowIfFaulted();
                CheckOutputOpen();
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                this.SecureOutgoingMessage(ref message, timeoutHelper.RemainingTime());
                this.ChannelBinder.Send(message, timeoutHelper.RemainingTime());
            }
 
            public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state)
            {
                return this.BeginSend(message, this.DefaultSendTimeout, callback, state);
            }
 
            public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
            {
                ThrowIfFaulted();
                CheckOutputOpen();
                return new SecureSendAsyncResult(message, this, timeout, callback, state, false);
            }
 
            public void EndSend(IAsyncResult result)
            {
                SecureSendAsyncResult.End(result);
            }
 
            protected override void InitializeSession(SecurityToken sessionToken)
            {
                this.session.Initialize(sessionToken, this.Settings);
            }
 
            void StartReceiving()
            {
                IAsyncResult result = this.IssueReceive();
                if (result != null && result.CompletedSynchronously)
                {
                    ActionItem.Schedule(completeLater, result);
                }
            }
 
            IAsyncResult IssueReceive()
            {
                while (true)
                {
                    // no need to receive anymore if in the closed state
                    if (this.State == CommunicationState.Closed || this.State == CommunicationState.Faulted || this.IsInputClosed)
                    {
                        return null;
                    }
                    try
                    {
                        return this.BeginReceiveInternal(TimeSpan.MaxValue, null, onReceive, this);
 
                    }
                    catch (CommunicationException e)
                    {
                        // BeginReceive failed. ignore the exception and start another receive
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                    }
                    catch (TimeoutException e)
                    {
                        // BeginReceive failed. ignore the exception and start another receive
                        if (TD.ReceiveTimeoutIsEnabled())
                        {
                            TD.ReceiveTimeout(e.Message);
                        }
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                    }
                }
            }
 
            void CompleteLater(object obj)
            {
                CompleteReceive((IAsyncResult)obj);
            }
 
            static void OnReceive(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                    return;
 
                ((ClientSecurityDuplexSessionChannel)result.AsyncState).CompleteReceive(result);
            }
 
            void CompleteReceive(IAsyncResult result)
            {
                Message message = null;
                bool issueAnotherReceive = false;
                try
                {
                    message = this.EndReceiveInternal(result);
                    issueAnotherReceive = true;
                }
                catch (MessageSecurityException)
                {
                    // a messagesecurityexception will only be thrown if the channel received a fault
                    // from the other side, in which case the channel would have faulted
                    issueAnotherReceive = false;
                }
                catch (CommunicationException e)
                {
                    issueAnotherReceive = true;
                    // EndReceive failed. ignore the exception and start another receive
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                catch (TimeoutException e)
                {
                    issueAnotherReceive = true;
                    // EndReceive failed. ignore the exception and start another receive
                    if (TD.ReceiveTimeoutIsEnabled())
                    {
                        TD.ReceiveTimeout(e.Message);
                    }
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                IAsyncResult nextReceiveResult = null;
                if (issueAnotherReceive)
                {
                    nextReceiveResult = this.IssueReceive();
                    if (nextReceiveResult != null && nextReceiveResult.CompletedSynchronously)
                    {
                        ActionItem.Schedule(completeLater, nextReceiveResult);
                    }
                }
                if (message != null)
                {
                    // since we may be dispatching to user code ---- non-fatal exceptions
                    try
                    {
                        this.queue.EnqueueAndDispatch(message);
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e)) throw;
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
                    }
                }
            }
 
            protected override void AbortCore()
            {
                try
                {
                    this.queue.Dispose();
                }
                catch (CommunicationException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                catch (TimeoutException e)
                {
                    if (TD.CloseTimeoutIsEnabled())
                    {
                        TD.CloseTimeout(e.Message);
                    }
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                base.AbortCore();
            }
 
            public bool WaitForMessage(TimeSpan timeout)
            {
                return this.queue.WaitForItem(timeout);
            }
 
            public IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback, object state)
            {
                return this.queue.BeginWaitForItem(timeout, callback, state);
            }
 
            public bool EndWaitForMessage(IAsyncResult result)
            {
                return this.queue.EndWaitForItem(result);
            }
 
            protected override void OnFaulted()
            {
                queue.Shutdown(() => this.GetPendingException());
                base.OnFaulted();
            }
 
            protected override bool OnCloseResponseReceived()
            {
                if (base.OnCloseResponseReceived())
                {
                    this.queue.Shutdown();
                    return true;
                }
                else
                {
                    return false;
                }
            }
 
            protected override bool OnCloseReceived()
            {
                if (base.OnCloseReceived())
                {
                    this.queue.Shutdown();
                    return true;
                }
                else
                {
                    return false;
                }
            }
 
            class SoapSecurityClientDuplexSession : SoapSecurityOutputSession, IDuplexSession
            {
                ClientSecurityDuplexSessionChannel channel;
                bool initialized = false;
 
                public SoapSecurityClientDuplexSession(ClientSecurityDuplexSessionChannel channel)
                    : base(channel)
                {
                    this.channel = channel;
                }
 
                internal new void Initialize(SecurityToken sessionToken, SecuritySessionClientSettings<TChannel> settings)
                {
                    base.Initialize(sessionToken, settings);
                    this.initialized = true;
                }
 
                void CheckInitialized()
                {
                    if (!this.initialized)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ChannelNotOpen)));
                    }
                }
 
                public void CloseOutputSession()
                {
                    this.CloseOutputSession(this.channel.DefaultCloseTimeout);
                }
 
                public void CloseOutputSession(TimeSpan timeout)
                {
                    CheckInitialized();
                    this.channel.ThrowIfFaulted();
                    this.channel.ThrowIfNotOpened();
                    Exception pendingException = null;
                    try
                    {
                        this.channel.CloseOutputSession(timeout);
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (this.channel.State != CommunicationState.Closed)
                        {
                            throw;
                        }
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e)) throw;
                        pendingException = e;
                    }
                    if (pendingException != null)
                    {
                        this.channel.Fault(pendingException);
                        throw pendingException;
                    }
                }
 
                public IAsyncResult BeginCloseOutputSession(AsyncCallback callback, object state)
                {
                    return this.BeginCloseOutputSession(this.channel.DefaultCloseTimeout, callback, state);
                }
 
                public IAsyncResult BeginCloseOutputSession(TimeSpan timeout, AsyncCallback callback, object state)
                {
                    CheckInitialized();
                    this.channel.ThrowIfFaulted();
                    this.channel.ThrowIfNotOpened();
                    Exception pendingException = null;
                    try
                    {
                        return this.channel.BeginCloseOutputSession(timeout, callback, state);
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (this.channel.State != CommunicationState.Closed)
                        {
                            throw;
                        }
                        // another thread must have aborted the channel. Allow the close to complete
                        // gracefully
                        return new CompletedAsyncResult(callback, state);
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e)) throw;
                        pendingException = e;
                    }
                    if (pendingException != null)
                    {
                        this.channel.Fault(pendingException);
                        if (pendingException is CommunicationException)
                        {
                            throw pendingException;
                        }
                        else
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(pendingException);
                        }
                    }
                    // we should never reach here
                    Fx.Assert("Unexpected control flow");
                    return null;
                }
 
                public void EndCloseOutputSession(IAsyncResult result)
                {
                    if (result is CompletedAsyncResult)
                    {
                        CompletedAsyncResult.End(result);
                        return;
                    }
                    Exception pendingException = null;
                    try
                    {
                        this.channel.EndCloseOutputSession(result);
                    }
                    catch (CommunicationObjectAbortedException)
                    {
                        if (this.channel.State != CommunicationState.Closed)
                        {
                            throw;
                        }
                        // another thread must have aborted the channel. Allow the close to complete
                        // gracefully
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e)) throw;
                        pendingException = e;
                    }
                    if (pendingException != null)
                    {
                        this.channel.Fault(pendingException);
                        if (pendingException is CommunicationException)
                        {
                            throw pendingException;
                        }
                        else
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(pendingException);
                        }
                    }
                }
            }
        }
    }
}