File: System\ServiceModel\Channels\ConnectionOrientedTransportChannelListener.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Channels
{
    using System.Collections.ObjectModel;
    using System.Runtime;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Diagnostics;
    using System.Text;
 
    abstract class ConnectionOrientedTransportChannelListener
        : TransportChannelListener, 
          IConnectionOrientedTransportFactorySettings, 
          IConnectionOrientedListenerSettings
    {
        int connectionBufferSize;
        bool exposeConnectionProperty;
        TimeSpan channelInitializationTimeout;
        int maxBufferSize;
        int maxPendingConnections;
        TimeSpan maxOutputDelay;
        int maxPendingAccepts;
        TimeSpan idleTimeout;
        int maxPooledConnections;
        TransferMode transferMode;
        ISecurityCapabilities securityCapabilities;
        StreamUpgradeProvider upgrade;
        bool ownUpgrade;
        EndpointIdentity identity;
 
        protected ConnectionOrientedTransportChannelListener(ConnectionOrientedTransportBindingElement bindingElement,
            BindingContext context)
            : base(bindingElement, context, bindingElement.HostNameComparisonMode)
        {
            if (bindingElement.TransferMode == TransferMode.Buffered)
            {
                if (bindingElement.MaxReceivedMessageSize > int.MaxValue)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize",
                        SR.GetString(SR.MaxReceivedMessageSizeMustBeInIntegerRange)));
                }
 
                if (bindingElement.MaxBufferSize != bindingElement.MaxReceivedMessageSize)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
                        SR.GetString(SR.MaxBufferSizeMustMatchMaxReceivedMessageSize));
                }
            }
            else
            {
                if (bindingElement.MaxBufferSize > bindingElement.MaxReceivedMessageSize)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
                        SR.GetString(SR.MaxBufferSizeMustNotExceedMaxReceivedMessageSize));
                }
            }
 
 
            this.connectionBufferSize = bindingElement.ConnectionBufferSize;
            this.exposeConnectionProperty = bindingElement.ExposeConnectionProperty;
            this.InheritBaseAddressSettings = bindingElement.InheritBaseAddressSettings;
            this.channelInitializationTimeout = bindingElement.ChannelInitializationTimeout;
            this.maxBufferSize = bindingElement.MaxBufferSize;
            this.maxPendingConnections = bindingElement.MaxPendingConnections;
            this.maxOutputDelay = bindingElement.MaxOutputDelay;
            this.maxPendingAccepts = bindingElement.MaxPendingAccepts;
            this.transferMode = bindingElement.TransferMode;
 
            Collection<StreamUpgradeBindingElement> upgradeBindingElements =
                context.BindingParameters.FindAll<StreamUpgradeBindingElement>();
 
            if (upgradeBindingElements.Count > 1)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MultipleStreamUpgradeProvidersInParameters)));
            }
            else if ((upgradeBindingElements.Count == 1) && this.SupportsUpgrade(upgradeBindingElements[0]))
            {
                this.upgrade = upgradeBindingElements[0].BuildServerStreamUpgradeProvider(context);
                this.ownUpgrade = true;
                context.BindingParameters.Remove<StreamUpgradeBindingElement>();
                this.securityCapabilities = upgradeBindingElements[0].GetProperty<ISecurityCapabilities>(context);
            }
        }
 
        public int ConnectionBufferSize
        {
            get
            {
                return this.connectionBufferSize;
            }
        }
 
        public TimeSpan IdleTimeout
        {
            get { return this.idleTimeout; }
        }
 
        public int MaxPooledConnections
        {
            get { return this.maxPooledConnections; }
        }
 
        internal void SetIdleTimeout(TimeSpan idleTimeout)
        {
            this.idleTimeout = idleTimeout;
        }
 
        internal void InitializeMaxPooledConnections(int maxOutboundConnectionsPerEndpoint)
        {
            if (maxOutboundConnectionsPerEndpoint == ConnectionOrientedTransportDefaults.MaxOutboundConnectionsPerEndpoint)
            {
                this.maxPooledConnections = ConnectionOrientedTransportDefaults.GetMaxConnections();
            }
            else
            {
                this.maxPooledConnections = maxOutboundConnectionsPerEndpoint;
            }
        }
 
        internal bool ExposeConnectionProperty
        {
            get { return this.exposeConnectionProperty; }
        }
 
        public HostNameComparisonMode HostNameComparisonMode
        {
            get
            {
                return this.HostNameComparisonModeInternal;
            }
        }
 
        public override T GetProperty<T>()
        {
            if (typeof(T) == typeof(EndpointIdentity))
            {
                return (T)(object)(this.identity);
            }
            else if (typeof(T) == typeof(ISecurityCapabilities))
            {
                return (T)(object)this.securityCapabilities;
            }
            else
            {
                T result = base.GetProperty<T>();
 
                if (result == null && this.upgrade != null)
                {
                    result = this.upgrade.GetProperty<T>();
                }
 
                return result;
            }
        }
 
        public TimeSpan ChannelInitializationTimeout
        {
            get
            {
                return this.channelInitializationTimeout;
            }
        }
 
        public int MaxBufferSize
        {
            get
            {
                return maxBufferSize;
            }
        }
 
        public int MaxPendingConnections
        {
            get
            {
                return this.maxPendingConnections;
            }
        }
 
        public TimeSpan MaxOutputDelay
        {
            get
            {
                return maxOutputDelay;
            }
        }
 
        public int MaxPendingAccepts
        {
            get
            {
                return this.maxPendingAccepts;
            }
        }
 
        public StreamUpgradeProvider Upgrade
        {
            get
            {
                return this.upgrade;
            }
        }
 
        public TransferMode TransferMode
        {
            get
            {
                return transferMode;
            }
        }
 
        int IConnectionOrientedTransportFactorySettings.MaxBufferSize
        {
            get { return MaxBufferSize; }
        }
 
        TransferMode IConnectionOrientedTransportFactorySettings.TransferMode
        {
            get { return TransferMode; }
        }
 
        StreamUpgradeProvider IConnectionOrientedTransportFactorySettings.Upgrade
        {
            get { return Upgrade; }
        }
 
        ServiceSecurityAuditBehavior IConnectionOrientedTransportFactorySettings.AuditBehavior
        {
            get { return base.AuditBehavior; }
        }
 
        internal override int GetMaxBufferSize()
        {
            return MaxBufferSize;
        }
 
        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            StreamUpgradeProvider localUpgrade = this.Upgrade;
            if (localUpgrade != null)
            {
                return new ChainedOpenAsyncResult(timeout, callback, state, base.OnBeginOpen, base.OnEndOpen, localUpgrade);
            }
            else
            {
                return base.OnBeginOpen(timeout, callback, state);
            }
        }
 
        protected override void OnEndOpen(IAsyncResult result)
        {
            if (result is ChainedOpenAsyncResult)
            {
                ChainedOpenAsyncResult.End(result);
            }
            else
            {
                base.OnEndOpen(result);
            }
        }
 
        protected override void OnOpen(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            base.OnOpen(timeout);
            StreamUpgradeProvider localUpgrade = this.Upgrade;
            if (localUpgrade != null)
            {
                localUpgrade.Open(timeoutHelper.RemainingTime());
            }
        }
 
        protected override void OnOpened()
        {
            base.OnOpened();
            StreamSecurityUpgradeProvider security = this.Upgrade as StreamSecurityUpgradeProvider;
            if (security != null)
            {
                this.identity = security.Identity;
            }
        }
 
        protected override void OnAbort()
        {
            StreamUpgradeProvider localUpgrade = GetUpgrade();
            if (localUpgrade != null)
            {
                localUpgrade.Abort();
            }
            base.OnAbort();
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            StreamUpgradeProvider localUpgrade = GetUpgrade();
            if (localUpgrade != null)
            {
                return new ChainedCloseAsyncResult(timeout, callback, state, base.OnBeginClose, base.OnEndClose, localUpgrade);
            }
            else
            {
                return new ChainedCloseAsyncResult(timeout, callback, state, base.OnBeginClose, base.OnEndClose);
            }
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            ChainedCloseAsyncResult.End(result);
        }
 
        protected override void OnClose(TimeSpan timeout)
        {
            StreamUpgradeProvider localUpgrade = GetUpgrade();
            if (localUpgrade != null)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                localUpgrade.Close(timeoutHelper.RemainingTime());
                base.OnClose(timeoutHelper.RemainingTime());
            }
            else
            {
                base.OnClose(timeout);
            }
        }
 
        StreamUpgradeProvider GetUpgrade()
        {
            StreamUpgradeProvider result = null;
 
            lock (ThisLock)
            {
                if (this.ownUpgrade)
                {
                    result = this.upgrade;
                    this.ownUpgrade = false;
                }
            }
 
            return result;
        }
 
        protected override void ValidateUri(Uri uri)
        {
            base.ValidateUri(uri);
            int maxViaSize = ConnectionOrientedTransportDefaults.MaxViaSize;
            int encodedSize = Encoding.UTF8.GetByteCount(uri.AbsoluteUri);
            if (encodedSize > maxViaSize)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new QuotaExceededException(SR.GetString(SR.UriLengthExceedsMaxSupportedSize, uri, encodedSize, maxViaSize)));
            }
        }
 
        protected virtual bool SupportsUpgrade(StreamUpgradeBindingElement upgradeBindingElement)
        {
            return true;
        }
 
        // transfers around the StreamUpgradeProvider from an ownership perspective
        protected class ConnectionOrientedTransportReplyChannelAcceptor : TransportReplyChannelAcceptor
        {
            StreamUpgradeProvider upgrade;
 
            public ConnectionOrientedTransportReplyChannelAcceptor(ConnectionOrientedTransportChannelListener listener)
                : base(listener)
            {
                this.upgrade = listener.GetUpgrade();
            }
 
            protected override ReplyChannel OnCreateChannel()
            {
                return new ConnectionOrientedTransportReplyChannel(this.ChannelManager, null);
            }
 
            protected override void OnAbort()
            {
                base.OnAbort();
                if (this.upgrade != null && !TransferUpgrade())
                {
                    this.upgrade.Abort();
                }
            }
 
            IAsyncResult DummyBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
            {
                return new CompletedAsyncResult(callback, state);
            }
 
            void DummyEndClose(IAsyncResult result)
            {
                CompletedAsyncResult.End(result);
            }
 
            protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
            {
                ChainedBeginHandler begin1 = DummyBeginClose;
                ChainedEndHandler end1 = DummyEndClose;
                if (this.upgrade != null && !TransferUpgrade())
                {
                    begin1 = this.upgrade.BeginClose;
                    end1 = this.upgrade.EndClose;
                }
 
                return new ChainedAsyncResult(timeout, callback, state, base.OnBeginClose, base.OnEndClose, begin1, end1);
            }
 
            protected override void OnEndClose(IAsyncResult result)
            {
                ChainedAsyncResult.End(result);
            }
 
            protected override void OnClose(TimeSpan timeout)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                base.OnClose(timeoutHelper.RemainingTime());
                if (this.upgrade != null && !TransferUpgrade())
                {
                    this.upgrade.Close(timeoutHelper.RemainingTime());
                }
            }
 
            // used to decouple our channel and listener lifetimes
            bool TransferUpgrade()
            {
                ConnectionOrientedTransportReplyChannel singletonChannel = (ConnectionOrientedTransportReplyChannel)base.GetCurrentChannel();
                if (singletonChannel == null)
                {
                    return false;
                }
                else
                {
                    return singletonChannel.TransferUpgrade(this.upgrade);
                }
            }
 
            // tracks StreamUpgradeProvider so that the channel can outlive the Listener
            class ConnectionOrientedTransportReplyChannel : TransportReplyChannel
            {
                StreamUpgradeProvider upgrade;
 
                public ConnectionOrientedTransportReplyChannel(ChannelManagerBase channelManager, EndpointAddress localAddress)
                    : base(channelManager, localAddress)
                {
                }
 
                public bool TransferUpgrade(StreamUpgradeProvider upgrade)
                {
                    lock (ThisLock)
                    {
                        if (this.State != CommunicationState.Opened)
                        {
                            return false;
                        }
 
                        this.upgrade = upgrade;
                        return true;
                    }
                }
 
                protected override void OnAbort()
                {
                    if (this.upgrade != null)
                    {
                        this.upgrade.Abort();
                    }
                    base.OnAbort();
                }
 
                protected override void OnClose(TimeSpan timeout)
                {
                    TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                    if (this.upgrade != null)
                    {
                        this.upgrade.Close(timeoutHelper.RemainingTime());
                    }
                    base.OnClose(timeoutHelper.RemainingTime());
                }
 
                IAsyncResult DummyBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
                {
                    return new CompletedAsyncResult(callback, state);
                }
 
                void DummyEndClose(IAsyncResult result)
                {
                    CompletedAsyncResult.End(result);
                }
 
                protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
                {
                    ChainedBeginHandler begin1 = DummyBeginClose;
                    ChainedEndHandler end1 = DummyEndClose;
                    if (this.upgrade != null)
                    {
                        begin1 = this.upgrade.BeginClose;
                        end1 = this.upgrade.EndClose;
                    }
 
                    return new ChainedAsyncResult(timeout, callback, state, begin1, end1,
                            base.OnBeginClose, base.OnEndClose);
                }
 
                protected override void OnEndClose(IAsyncResult result)
                {
                    ChainedAsyncResult.End(result);
                }
            }
        }
    }
}