File: FrameworkFork\System.ServiceModel\System\ServiceModel\Channels\ReliableSessionBindingElement.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
namespace System.ServiceModel.Channels
{
    using System.ComponentModel;
    using System.Runtime;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Security;
    using Microsoft.Xml;
 
    public sealed class ReliableSessionBindingElement : BindingElement, IPolicyExportExtension
    {
        private TimeSpan _acknowledgementInterval = ReliableSessionDefaults.AcknowledgementInterval;
        private bool _flowControlEnabled = ReliableSessionDefaults.FlowControlEnabled;
        private TimeSpan _inactivityTimeout = ReliableSessionDefaults.InactivityTimeout;
        private int _maxPendingChannels = ReliableSessionDefaults.MaxPendingChannels;
        private int _maxRetryCount = ReliableSessionDefaults.MaxRetryCount;
        private int _maxTransferWindowSize = ReliableSessionDefaults.MaxTransferWindowSize;
        private bool _ordered = ReliableSessionDefaults.Ordered;
        private ReliableMessagingVersion _reliableMessagingVersion = ReliableMessagingVersion.Default;
 
        private static MessagePartSpecification s_bodyOnly;
 
        public ReliableSessionBindingElement()
        {
        }
 
        internal ReliableSessionBindingElement(ReliableSessionBindingElement elementToBeCloned)
            : base(elementToBeCloned)
        {
            this.AcknowledgementInterval = elementToBeCloned.AcknowledgementInterval;
            this.FlowControlEnabled = elementToBeCloned.FlowControlEnabled;
            this.InactivityTimeout = elementToBeCloned.InactivityTimeout;
            this.MaxPendingChannels = elementToBeCloned.MaxPendingChannels;
            this.MaxRetryCount = elementToBeCloned.MaxRetryCount;
            this.MaxTransferWindowSize = elementToBeCloned.MaxTransferWindowSize;
            this.Ordered = elementToBeCloned.Ordered;
            this.ReliableMessagingVersion = elementToBeCloned.ReliableMessagingVersion;
        }
 
        public ReliableSessionBindingElement(bool ordered)
        {
            _ordered = ordered;
        }
 
        [DefaultValue(typeof(TimeSpan), ReliableSessionDefaults.AcknowledgementIntervalString)]
        public TimeSpan AcknowledgementInterval
        {
            get
            {
                return _acknowledgementInterval;
            }
            set
            {
                if (value <= TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.TimeSpanMustbeGreaterThanTimeSpanZero));
                }
 
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.SFxTimeoutOutOfRangeTooBig));
                }
 
                _acknowledgementInterval = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.FlowControlEnabled)]
        public bool FlowControlEnabled
        {
            get
            {
                return _flowControlEnabled;
            }
            set
            {
                _flowControlEnabled = value;
            }
        }
 
        [DefaultValue(typeof(TimeSpan), ReliableSessionDefaults.InactivityTimeoutString)]
        public TimeSpan InactivityTimeout
        {
            get
            {
                return _inactivityTimeout;
            }
            set
            {
                if (value <= TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.TimeSpanMustbeGreaterThanTimeSpanZero));
                }
 
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.SFxTimeoutOutOfRangeTooBig));
                }
 
                _inactivityTimeout = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.MaxPendingChannels)]
        public int MaxPendingChannels
        {
            get
            {
                return _maxPendingChannels;
            }
            set
            {
                if (value <= 0 || value > 16384)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                                                    string.Format(SRServiceModel.ValueMustBeInRange, 0, 16384)));
                _maxPendingChannels = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.MaxRetryCount)]
        public int MaxRetryCount
        {
            get
            {
                return _maxRetryCount;
            }
            set
            {
                if (value <= 0)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.ValueMustBePositive));
                _maxRetryCount = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.MaxTransferWindowSize)]
        public int MaxTransferWindowSize
        {
            get
            {
                return _maxTransferWindowSize;
            }
            set
            {
                if (value <= 0 || value > 4096)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                                                    string.Format(SRServiceModel.ValueMustBeInRange, 0, 4096)));
                _maxTransferWindowSize = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.Ordered)]
        public bool Ordered
        {
            get
            {
                return _ordered;
            }
            set
            {
                _ordered = value;
            }
        }
 
        [DefaultValue(typeof(ReliableMessagingVersion), ReliableSessionDefaults.ReliableMessagingVersionString)]
        public ReliableMessagingVersion ReliableMessagingVersion
        {
            get
            {
                return _reliableMessagingVersion;
            }
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                }
 
                if (!ReliableMessagingVersion.IsDefined(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
                }
 
                _reliableMessagingVersion = value;
            }
        }
 
        private static MessagePartSpecification BodyOnly
        {
            get
            {
                if (s_bodyOnly == null)
                {
                    MessagePartSpecification temp = new MessagePartSpecification(true);
                    temp.MakeReadOnly();
                    s_bodyOnly = temp;
                }
 
                return s_bodyOnly;
            }
        }
 
        public override BindingElement Clone()
        {
            return new ReliableSessionBindingElement(this);
        }
 
        public override T GetProperty<T>(BindingContext context)
        {
            throw new NotImplementedException();
        }
 
 
        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
        {
            throw new NotImplementedException();
        }
 
        public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
        {
            throw new NotImplementedException();
        }
 
        internal override bool IsMatch(BindingElement b)
        {
            if (b == null)
                return false;
            ReliableSessionBindingElement session = b as ReliableSessionBindingElement;
            if (session == null)
                return false;
            if (_acknowledgementInterval != session._acknowledgementInterval)
                return false;
            if (_flowControlEnabled != session._flowControlEnabled)
                return false;
            if (_inactivityTimeout != session._inactivityTimeout)
                return false;
            if (_maxPendingChannels != session._maxPendingChannels)
                return false;
            if (_maxRetryCount != session._maxRetryCount)
                return false;
            if (_maxTransferWindowSize != session._maxTransferWindowSize)
                return false;
            if (_ordered != session._ordered)
                return false;
            if (_reliableMessagingVersion != session._reliableMessagingVersion)
                return false;
 
            return true;
        }
 
        private static void ProtectProtocolMessage(
            ScopedMessagePartSpecification signaturePart,
            ScopedMessagePartSpecification encryptionPart,
            string action)
        {
            signaturePart.AddParts(BodyOnly, action);
            encryptionPart.AddParts(MessagePartSpecification.NoParts, action);
            //encryptionPart.AddParts(BodyOnly, action);
        }
 
        private void SetSecuritySettings(BindingContext context)
        {
            SecurityBindingElement element = context.RemainingBindingElements.Find<SecurityBindingElement>();
 
            if (element != null)
            {
                element.LocalServiceSettings.ReconnectTransportOnFailure = true;
            }
        }
 
        private void VerifyTransportMode(BindingContext context)
        {
            TransportBindingElement transportElement = context.RemainingBindingElements.Find<TransportBindingElement>();
 
            // Verify ManualAdderssing is turned off.
            if ((transportElement != null) && (transportElement.ManualAddressing))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new InvalidOperationException(SRServiceModel.ManualAddressingNotSupported));
            }
 
            ConnectionOrientedTransportBindingElement connectionElement = transportElement as ConnectionOrientedTransportBindingElement;
            HttpTransportBindingElement httpElement = transportElement as HttpTransportBindingElement;
 
            // Verify TransportMode is Buffered.
            TransferMode transportTransferMode;
 
            if (connectionElement != null)
            {
                transportTransferMode = connectionElement.TransferMode;
            }
            else if (httpElement != null)
            {
                transportTransferMode = httpElement.TransferMode;
            }
            else
            {
                // Not one of the elements we can check, we have to assume TransferMode.Buffered.
                transportTransferMode = TransferMode.Buffered;
            }
 
            if (transportTransferMode != TransferMode.Buffered)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new InvalidOperationException(string.Format(SRServiceModel.TransferModeNotSupported,
                    transportTransferMode, this.GetType().Name)));
            }
        }
 
        void IPolicyExportExtension.ExportPolicy(MetadataExporter exporter, PolicyConversionContext context)
        {
            if (exporter == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
 
            if (context == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
 
            if (context.BindingElements != null)
            {
                BindingElementCollection bindingElements = context.BindingElements;
                ReliableSessionBindingElement settings = bindingElements.Find<ReliableSessionBindingElement>();
 
                if (settings != null)
                {
                    // ReliableSession assertion
                    XmlElement assertion = settings.CreateReliabilityAssertion(exporter.PolicyVersion, bindingElements);
                    context.GetBindingAssertions().Add(assertion);
                }
            }
        }
 
        private static XmlElement CreatePolicyElement(PolicyVersion policyVersion, XmlDocument doc)
        {
            string policy = MetadataStrings.WSPolicy.Elements.Policy;
            string policyNs = policyVersion.Namespace;
            string policyPrefix = MetadataStrings.WSPolicy.Prefix;
 
            return doc.CreateElement(policyPrefix, policy, policyNs);
        }
 
        private XmlElement CreateReliabilityAssertion(PolicyVersion policyVersion, BindingElementCollection bindingElements)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement child = null;
            string reliableSessionPrefix;
            string reliableSessionNs;
            string assertionPrefix;
            string assertionNs;
 
            if (this.ReliableMessagingVersion == ReliableMessagingVersion.WSReliableMessagingFebruary2005)
            {
                reliableSessionPrefix = ReliableSessionPolicyStrings.ReliableSessionFebruary2005Prefix;
                reliableSessionNs = ReliableSessionPolicyStrings.ReliableSessionFebruary2005Namespace;
                assertionPrefix = reliableSessionPrefix;
                assertionNs = reliableSessionNs;
            }
            else
            {
                reliableSessionPrefix = ReliableSessionPolicyStrings.ReliableSession11Prefix;
                reliableSessionNs = ReliableSessionPolicyStrings.ReliableSession11Namespace;
                assertionPrefix = ReliableSessionPolicyStrings.NET11Prefix;
                assertionNs = ReliableSessionPolicyStrings.NET11Namespace;
            }
 
            // ReliableSession assertion
            XmlElement assertion = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.ReliableSessionName, reliableSessionNs);
 
            if (this.ReliableMessagingVersion == ReliableMessagingVersion.WSReliableMessaging11)
            {
                // Policy
                XmlElement policy = CreatePolicyElement(policyVersion, doc);
 
                // SequenceSTR
                if (IsSecureConversationEnabled(bindingElements))
                {
                    XmlElement sequenceSTR = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.SequenceSTR, reliableSessionNs);
                    policy.AppendChild(sequenceSTR);
                }
 
                // DeliveryAssurance
                XmlElement deliveryAssurance = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.DeliveryAssurance, reliableSessionNs);
 
                // Policy
                XmlElement nestedPolicy = CreatePolicyElement(policyVersion, doc);
 
                // ExactlyOnce
                XmlElement exactlyOnce = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.ExactlyOnce, reliableSessionNs);
                nestedPolicy.AppendChild(exactlyOnce);
 
                if (_ordered)
                {
                    // InOrder
                    XmlElement inOrder = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.InOrder, reliableSessionNs);
                    nestedPolicy.AppendChild(inOrder);
                }
 
                deliveryAssurance.AppendChild(nestedPolicy);
                policy.AppendChild(deliveryAssurance);
                assertion.AppendChild(policy);
            }
 
            // Nested InactivityTimeout assertion
            child = doc.CreateElement(assertionPrefix, ReliableSessionPolicyStrings.InactivityTimeout, assertionNs);
            WriteMillisecondsAttribute(child, this.InactivityTimeout);
            assertion.AppendChild(child);
 
            // Nested AcknowledgementInterval assertion
            child = doc.CreateElement(assertionPrefix, ReliableSessionPolicyStrings.AcknowledgementInterval, assertionNs);
            WriteMillisecondsAttribute(child, this.AcknowledgementInterval);
            assertion.AppendChild(child);
 
            return assertion;
        }
 
        private static bool IsSecureConversationEnabled(BindingElementCollection bindingElements)
        {
            bool foundRM = false;
 
            for (int i = 0; i < bindingElements.Count; i++)
            {
                if (!foundRM)
                {
                    ReliableSessionBindingElement bindingElement = bindingElements[i] as ReliableSessionBindingElement;
                    foundRM = (bindingElement != null);
                }
                else
                {
                    SecurityBindingElement securityBindingElement = bindingElements[i] as SecurityBindingElement;
 
                    if (securityBindingElement != null)
                    {
                        SecurityBindingElement bootstrapSecurity;
 
                        // The difference in bool (requireCancellation) does not affect whether the binding is valid,
                        // but the method does match on the value so we need to pass both true and false.
                        return SecurityBindingElement.IsSecureConversationBinding(securityBindingElement, true, out bootstrapSecurity)
                            || SecurityBindingElement.IsSecureConversationBinding(securityBindingElement, false, out bootstrapSecurity);
                    }
 
                    break;
                }
            }
 
            return false;
        }
 
        private static void WriteMillisecondsAttribute(XmlElement childElement, TimeSpan timeSpan)
        {
            UInt64 milliseconds = Convert.ToUInt64(timeSpan.TotalMilliseconds);
            childElement.SetAttribute(ReliableSessionPolicyStrings.Milliseconds, XmlConvert.ToString(milliseconds));
        }
 
        private class BindingDeliveryCapabilitiesHelper : IBindingDeliveryCapabilities
        {
            private ReliableSessionBindingElement _element;
            private IBindingDeliveryCapabilities _inner;
 
            internal BindingDeliveryCapabilitiesHelper(ReliableSessionBindingElement element, IBindingDeliveryCapabilities inner)
            {
                _element = element;
                _inner = inner;
            }
            bool IBindingDeliveryCapabilities.AssuresOrderedDelivery
            {
                get { return _element.Ordered; }
            }
 
            bool IBindingDeliveryCapabilities.QueuedDelivery
            {
                get { return _inner != null ? _inner.QueuedDelivery : false; }
            }
        }
    }
}