File: System\ServiceModel\Channels\ReliableSessionBindingElement.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// 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.
 
using System.ComponentModel;
using System.Runtime;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.Xml;
 
namespace System.ServiceModel.Channels
{
    public sealed class ReliableSessionBindingElement : BindingElement
    {
        private TimeSpan _acknowledgementInterval = ReliableSessionDefaults.AcknowledgementInterval;
        private TimeSpan _inactivityTimeout = ReliableSessionDefaults.InactivityTimeout;
        private int _maxPendingChannels = ReliableSessionDefaults.MaxPendingChannels;
        private int _maxRetryCount = ReliableSessionDefaults.MaxRetryCount;
        private int _maxTransferWindowSize = ReliableSessionDefaults.MaxTransferWindowSize;
        private ReliableMessagingVersion _reliableMessagingVersion = ReliableMessagingVersion.Default;
        private static MessagePartSpecification s_bodyOnly;
 
        public ReliableSessionBindingElement()
        {
        }
 
        internal ReliableSessionBindingElement(ReliableSessionBindingElement elementToBeCloned)
            : base(elementToBeCloned)
        {
            AcknowledgementInterval = elementToBeCloned.AcknowledgementInterval;
            FlowControlEnabled = elementToBeCloned.FlowControlEnabled;
            InactivityTimeout = elementToBeCloned.InactivityTimeout;
            MaxPendingChannels = elementToBeCloned.MaxPendingChannels;
            MaxRetryCount = elementToBeCloned.MaxRetryCount;
            MaxTransferWindowSize = elementToBeCloned.MaxTransferWindowSize;
            Ordered = elementToBeCloned.Ordered;
            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,
                        SRP.TimeSpanMustbeGreaterThanTimeSpanZero));
                }
 
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                        SRP.SFxTimeoutOutOfRangeTooBig));
                }
 
                _acknowledgementInterval = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.FlowControlEnabled)]
        public bool FlowControlEnabled { get; set; } = ReliableSessionDefaults.FlowControlEnabled;
 
        [DefaultValue(typeof(TimeSpan), ReliableSessionDefaults.InactivityTimeoutString)]
        public TimeSpan InactivityTimeout
        {
            get
            {
                return _inactivityTimeout;
            }
            set
            {
                if (value <= TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                        SRP.TimeSpanMustbeGreaterThanTimeSpanZero));
                }
 
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                        SRP.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,
                                                    SRP.Format(SRP.ValueMustBeInRange, 0, 16384)));
                _maxPendingChannels = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.MaxRetryCount)]
        public int MaxRetryCount
        {
            get
            {
                return _maxRetryCount;
            }
            set
            {
                if (value <= 0)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value,
                                                    SRP.ValueMustBePositive));
                _maxRetryCount = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.MaxTransferWindowSize)]
        public int MaxTransferWindowSize
        {
            get
            {
                return _maxTransferWindowSize;
            }
            set
            {
                if (value <= 0 || value > 4096)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value,
                                                    SRP.Format(SRP.ValueMustBeInRange, 0, 4096)));
                _maxTransferWindowSize = value;
            }
        }
 
        [DefaultValue(ReliableSessionDefaults.Ordered)]
        public bool Ordered { get; set; } = ReliableSessionDefaults.Ordered;
 
        [DefaultValue(typeof(ReliableMessagingVersion), ReliableSessionDefaults.ReliableMessagingVersionString)]
        public ReliableMessagingVersion ReliableMessagingVersion
        {
            get
            {
                return _reliableMessagingVersion;
            }
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value));
                }
 
                if (!ReliableMessagingVersion.IsDefined(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(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)
        {
            if (context == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(context));
            }
            if (typeof(T) == typeof(ChannelProtectionRequirements))
            {
                ChannelProtectionRequirements myRequirements = GetProtectionRequirements();
                myRequirements.Add(context.GetInnerProperty<ChannelProtectionRequirements>() ?? new ChannelProtectionRequirements());
                return (T)(object)myRequirements;
            }
            else if (typeof(T) == typeof(IBindingDeliveryCapabilities))
            {
                return (T)(object)new BindingDeliveryCapabilitiesHelper(this, context.GetInnerProperty<IBindingDeliveryCapabilities>());
            }
            else
            {
                return context.GetInnerProperty<T>();
            }
        }
 
        private ChannelProtectionRequirements GetProtectionRequirements()
        {
            // Listing headers that must be signed.
            ChannelProtectionRequirements result = new ChannelProtectionRequirements();
            MessagePartSpecification signedReliabilityMessageParts = WsrmIndex.GetSignedReliabilityMessageParts(
                _reliableMessagingVersion);
            result.IncomingSignatureParts.AddParts(signedReliabilityMessageParts);
            result.OutgoingSignatureParts.AddParts(signedReliabilityMessageParts);
 
            if (_reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessagingFebruary2005)
            {
                // Adding RM protocol message actions so that each RM protocol message's body will be 
                // signed and encrypted.
                // From the Client to the Service
                ScopedMessagePartSpecification signaturePart = result.IncomingSignatureParts;
                ScopedMessagePartSpecification encryptionPart = result.IncomingEncryptionParts;
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.AckRequestedAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.CreateSequenceAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.SequenceAcknowledgementAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.LastMessageAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.TerminateSequenceAction);
 
                // From the Service to the Client
                signaturePart = result.OutgoingSignatureParts;
                encryptionPart = result.OutgoingEncryptionParts;
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.CreateSequenceResponseAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.SequenceAcknowledgementAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.LastMessageAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.TerminateSequenceAction);
            }
            else if (_reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessaging11)
            {
                // Adding RM protocol message actions so that each RM protocol message's body will be 
                // signed and encrypted.
                // From the Client to the Service
                ScopedMessagePartSpecification signaturePart = result.IncomingSignatureParts;
                ScopedMessagePartSpecification encryptionPart = result.IncomingEncryptionParts;
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.AckRequestedAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CloseSequenceAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CloseSequenceResponseAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CreateSequenceAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.FaultAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.SequenceAcknowledgementAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.TerminateSequenceAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.TerminateSequenceResponseAction);
 
                // From the Service to the Client
                signaturePart = result.OutgoingSignatureParts;
                encryptionPart = result.OutgoingEncryptionParts;
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.AckRequestedAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CloseSequenceAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CloseSequenceResponseAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CreateSequenceResponseAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.FaultAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.SequenceAcknowledgementAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.TerminateSequenceAction);
                ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.TerminateSequenceResponseAction);
            }
            else
            {
                throw Fx.AssertAndThrow("Reliable messaging version not supported.");
            }
 
            return result;
        }
 
        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
        {
            if (context == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(context));
 
            VerifyTransportMode(context);
 
            if (typeof(TChannel) == typeof(IOutputSessionChannel))
            {
                if (context.CanBuildInnerChannelFactory<IRequestSessionChannel>())
                {
                    return (IChannelFactory<TChannel>)(object)
                        new ReliableChannelFactory<TChannel, IRequestSessionChannel>(
                        this, context.BuildInnerChannelFactory<IRequestSessionChannel>(), context.Binding);
                }
                else if (context.CanBuildInnerChannelFactory<IRequestChannel>())
                {
                    return (IChannelFactory<TChannel>)(object)
                        new ReliableChannelFactory<TChannel, IRequestChannel>(
                        this, context.BuildInnerChannelFactory<IRequestChannel>(), context.Binding);
                }
                else if (context.CanBuildInnerChannelFactory<IDuplexSessionChannel>())
                {
                    return (IChannelFactory<TChannel>)(object)
                        new ReliableChannelFactory<TChannel, IDuplexSessionChannel>(
                        this, context.BuildInnerChannelFactory<IDuplexSessionChannel>(), context.Binding);
                }
                else if (context.CanBuildInnerChannelFactory<IDuplexChannel>())
                {
                    return (IChannelFactory<TChannel>)(object)
                        new ReliableChannelFactory<TChannel, IDuplexChannel>(
                        this, context.BuildInnerChannelFactory<IDuplexChannel>(), context.Binding);
                }
            }
            else if (typeof(TChannel) == typeof(IDuplexSessionChannel))
            {
                if (context.CanBuildInnerChannelFactory<IDuplexSessionChannel>())
                {
                    return (IChannelFactory<TChannel>)(object)
                        new ReliableChannelFactory<TChannel, IDuplexSessionChannel>(
                        this, context.BuildInnerChannelFactory<IDuplexSessionChannel>(), context.Binding);
                }
                else if (context.CanBuildInnerChannelFactory<IDuplexChannel>())
                {
                    return (IChannelFactory<TChannel>)(object)
                        new ReliableChannelFactory<TChannel, IDuplexChannel>(
                        this, context.BuildInnerChannelFactory<IDuplexChannel>(), context.Binding);
                }
            }
            else if (typeof(TChannel) == typeof(IRequestSessionChannel))
            {
                if (context.CanBuildInnerChannelFactory<IRequestSessionChannel>())
                {
                    return (IChannelFactory<TChannel>)(object)
                        new ReliableChannelFactory<TChannel, IRequestSessionChannel>(
                        this, context.BuildInnerChannelFactory<IRequestSessionChannel>(), context.Binding);
                }
                else if (context.CanBuildInnerChannelFactory<IRequestChannel>())
                {
                    return (IChannelFactory<TChannel>)(object)
                        new ReliableChannelFactory<TChannel, IRequestChannel>(
                        this, context.BuildInnerChannelFactory<IRequestChannel>(), context.Binding);
                }
            }
 
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("TChannel", SRP.Format(SRP.ChannelTypeNotSupported, typeof(TChannel)));
        }
 
        public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
        {
            if (context == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(context));
 
            if (typeof(TChannel) == typeof(IOutputSessionChannel))
            {
                return context.CanBuildInnerChannelFactory<IRequestSessionChannel>()
                    || context.CanBuildInnerChannelFactory<IRequestChannel>()
                    || context.CanBuildInnerChannelFactory<IDuplexSessionChannel>()
                    || context.CanBuildInnerChannelFactory<IDuplexChannel>();
            }
            else if (typeof(TChannel) == typeof(IDuplexSessionChannel))
            {
                return context.CanBuildInnerChannelFactory<IDuplexSessionChannel>()
                    || context.CanBuildInnerChannelFactory<IDuplexChannel>();
            }
            else if (typeof(TChannel) == typeof(IRequestSessionChannel))
            {
                return context.CanBuildInnerChannelFactory<IRequestSessionChannel>()
                    || context.CanBuildInnerChannelFactory<IRequestChannel>();
            }
 
            return false;
        }
 
        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 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(SRP.ManualAddressingNotSupported));
            }
 
            // Verify TransportMode is Buffered.
            TransferMode transportTransferMode;
            Tuple<TransferMode> transportTransferModeHolder = transportElement.GetProperty<Tuple<TransferMode>>(context);
 
            if (transportTransferModeHolder != null)
            {
                transportTransferMode = transportTransferModeHolder.Item1;
            }
            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(SRP.Format(SRP.TransferModeNotSupported,
                    transportTransferMode, GetType().Name)));
            }
        }
 
        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 class BindingDeliveryCapabilitiesHelper : IBindingDeliveryCapabilities
        {
            private readonly ReliableSessionBindingElement _element;
            private readonly 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; }
            }
        }
    }
}