File: FrameworkFork\System.ServiceModel\System\ServiceModel\Channels\HttpTransportBindingElement.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.
 
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Security;
using System.Runtime;
using System.Security.Authentication.ExtendedProtection;
using System.ServiceModel.Description;
using Microsoft.Xml;
 
namespace System.ServiceModel.Channels
{
    public class HttpTransportBindingElement
        : TransportBindingElement, ITransportPolicyImport
    {
        private bool _allowCookies;
        private AuthenticationSchemes _authenticationScheme;
        private bool _decompressionEnabled;
        private HostNameComparisonMode _hostNameComparisonMode;
        private bool _keepAliveEnabled;
        private bool _inheritBaseAddressSettings;
        private int _maxBufferSize;
        private bool _maxBufferSizeInitialized;
        private string _method;
        private string _realm;
        private TimeSpan _requestInitializationTimeout;
        private TransferMode _transferMode;
        private bool _unsafeConnectionNtlmAuthentication;
        private bool _useDefaultWebProxy;
        private WebSocketTransportSettings _webSocketSettings;
#if !FEATURE_NETNATIVE
        private ExtendedProtectionPolicy _extendedProtectionPolicy;
#endif // !FEATURE_NETNATIVE
        private HttpMessageHandlerFactory _httpMessageHandlerFactory;
        private int _maxPendingAccepts;
 
        public HttpTransportBindingElement()
            : base()
        {
            _allowCookies = HttpTransportDefaults.AllowCookies;
            _authenticationScheme = HttpTransportDefaults.AuthenticationScheme;
            _decompressionEnabled = HttpTransportDefaults.DecompressionEnabled;
            _hostNameComparisonMode = HttpTransportDefaults.HostNameComparisonMode;
            _keepAliveEnabled = HttpTransportDefaults.KeepAliveEnabled;
            _maxBufferSize = TransportDefaults.MaxBufferSize;
            _maxPendingAccepts = HttpTransportDefaults.DefaultMaxPendingAccepts;
            _method = string.Empty;
            _realm = HttpTransportDefaults.Realm;
            _requestInitializationTimeout = HttpTransportDefaults.RequestInitializationTimeout;
            _transferMode = HttpTransportDefaults.TransferMode;
            _unsafeConnectionNtlmAuthentication = HttpTransportDefaults.UnsafeConnectionNtlmAuthentication;
            _useDefaultWebProxy = HttpTransportDefaults.UseDefaultWebProxy;
            _webSocketSettings = HttpTransportDefaults.GetDefaultWebSocketTransportSettings();
            _extendedProtectionPolicy = ChannelBindingUtility.DefaultPolicy;
        }
 
        protected HttpTransportBindingElement(HttpTransportBindingElement elementToBeCloned)
            : base(elementToBeCloned)
        {
            _allowCookies = elementToBeCloned._allowCookies;
            _authenticationScheme = elementToBeCloned._authenticationScheme;
            _decompressionEnabled = elementToBeCloned._decompressionEnabled;
            _hostNameComparisonMode = elementToBeCloned._hostNameComparisonMode;
            _inheritBaseAddressSettings = elementToBeCloned.InheritBaseAddressSettings;
            _keepAliveEnabled = elementToBeCloned._keepAliveEnabled;
            _maxBufferSize = elementToBeCloned._maxBufferSize;
            _maxBufferSizeInitialized = elementToBeCloned._maxBufferSizeInitialized;
            _maxPendingAccepts = elementToBeCloned._maxPendingAccepts;
            _method = elementToBeCloned._method;
            _realm = elementToBeCloned._realm;
            _requestInitializationTimeout = elementToBeCloned._requestInitializationTimeout;
            _transferMode = elementToBeCloned._transferMode;
            _unsafeConnectionNtlmAuthentication = elementToBeCloned._unsafeConnectionNtlmAuthentication;
            _useDefaultWebProxy = elementToBeCloned._useDefaultWebProxy;
            _webSocketSettings = elementToBeCloned._webSocketSettings.Clone();
#if !FEATURE_NETNATIVE
            _extendedProtectionPolicy = elementToBeCloned.ExtendedProtectionPolicy;
#endif // !FEATURE_NETNATIVE
            this.MessageHandlerFactory = elementToBeCloned.MessageHandlerFactory;
        }
 
        [DefaultValue(HttpTransportDefaults.AllowCookies)]
        public bool AllowCookies
        {
            get
            {
                return _allowCookies;
            }
            set
            {
                _allowCookies = value;
            }
        }
 
        [DefaultValue(HttpTransportDefaults.AuthenticationScheme)]
        public AuthenticationSchemes AuthenticationScheme
        {
            get
            {
                return _authenticationScheme;
            }
 
            set
            {
                _authenticationScheme = value;
            }
        }
 
        [DefaultValue(HttpTransportDefaults.DecompressionEnabled)]
        public bool DecompressionEnabled
        {
            get
            {
                return _decompressionEnabled;
            }
            set
            {
                _decompressionEnabled = value;
            }
        }
 
        [DefaultValue(HttpTransportDefaults.HostNameComparisonMode)]
        public HostNameComparisonMode HostNameComparisonMode
        {
            get
            {
                return _hostNameComparisonMode;
            }
            set
            {
                HostNameComparisonModeHelper.Validate(value);
                _hostNameComparisonMode = value;
            }
        }
 
        public HttpMessageHandlerFactory MessageHandlerFactory
        {
            get
            {
                return _httpMessageHandlerFactory;
            }
            set
            {
                _httpMessageHandlerFactory = value;
            }
        }
 
#if !FEATURE_NETNATIVE
        public ExtendedProtectionPolicy ExtendedProtectionPolicy
        {
            get
            {
                return _extendedProtectionPolicy;
            }
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                }
 
                if (value.PolicyEnforcement == PolicyEnforcement.Always &&
                    !System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy.OSSupportsExtendedProtection)
                {
                    ExceptionHelper.PlatformNotSupported(SRServiceModel.ExtendedProtectionNotSupported);
                }
 
                _extendedProtectionPolicy = value;
            }
        }
#endif // !FEATURE_NETNATIVE
 
        // MB#26970: used by MEX to ensure that we don't conflict on base-address scoped settings
        internal bool InheritBaseAddressSettings
        {
            get
            {
                return _inheritBaseAddressSettings;
            }
            set
            {
                _inheritBaseAddressSettings = value;
            }
        }
 
        [DefaultValue(HttpTransportDefaults.KeepAliveEnabled)]
        public bool KeepAliveEnabled
        {
            get
            {
                return _keepAliveEnabled;
            }
            set
            {
                _keepAliveEnabled = value;
            }
        }
 
        // client
        // server
        [DefaultValue(TransportDefaults.MaxBufferSize)]
        public int MaxBufferSize
        {
            get
            {
                if (_maxBufferSizeInitialized || TransferMode != TransferMode.Buffered)
                    return _maxBufferSize;
 
                long maxReceivedMessageSize = MaxReceivedMessageSize;
                if (maxReceivedMessageSize > int.MaxValue)
                    return int.MaxValue;
                else
                    return (int)maxReceivedMessageSize;
            }
            set
            {
                if (value <= 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                        SRServiceModel.ValueMustBePositive));
                }
 
                _maxBufferSizeInitialized = true;
                _maxBufferSize = value;
            }
        }
 
        // server
        [DefaultValue(HttpTransportDefaults.DefaultMaxPendingAccepts)]
        public int MaxPendingAccepts
        {
            get
            {
                return _maxPendingAccepts;
            }
            set
            {
                if (value < 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                        SRServiceModel.ValueMustBeNonNegative));
                }
 
                if (value > HttpTransportDefaults.MaxPendingAcceptsUpperLimit)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                        string.Format(SRServiceModel.HttpMaxPendingAcceptsTooLargeError, HttpTransportDefaults.MaxPendingAcceptsUpperLimit)));
                }
 
                _maxPendingAccepts = value;
            }
        }
 
        // string.Empty == wildcard
        internal string Method
        {
            get
            {
                return _method;
            }
 
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                }
 
                _method = value;
            }
        }
 
        [DefaultValue(HttpTransportDefaults.Realm)]
        public string Realm
        {
            get
            {
                return _realm;
            }
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                }
 
                _realm = value;
            }
        }
 
        [DefaultValue(typeof(TimeSpan), HttpTransportDefaults.RequestInitializationTimeoutString)]
        public TimeSpan RequestInitializationTimeout
        {
            get
            {
                return _requestInitializationTimeout;
            }
            set
            {
                if (value < TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.SFxTimeoutOutOfRange0));
                }
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SRServiceModel.SFxTimeoutOutOfRangeTooBig));
                }
 
                _requestInitializationTimeout = value;
            }
        }
 
        public override string Scheme { get { return "http"; } }
 
        // client
        // server
        [DefaultValue(HttpTransportDefaults.TransferMode)]
        public TransferMode TransferMode
        {
            get
            {
                return _transferMode;
            }
            set
            {
                TransferModeHelper.Validate(value);
                _transferMode = value;
            }
        }
 
        public WebSocketTransportSettings WebSocketSettings
        {
            get
            {
                return _webSocketSettings;
            }
            set
            {
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                }
                _webSocketSettings = value;
            }
        }
 
        internal virtual bool GetSupportsClientAuthenticationImpl(AuthenticationSchemes effectiveAuthenticationSchemes)
        {
            return effectiveAuthenticationSchemes != AuthenticationSchemes.None &&
                effectiveAuthenticationSchemes.IsNotSet(AuthenticationSchemes.Anonymous);
        }
 
        internal virtual bool GetSupportsClientWindowsIdentityImpl(AuthenticationSchemes effectiveAuthenticationSchemes)
        {
            return effectiveAuthenticationSchemes != AuthenticationSchemes.None &&
                effectiveAuthenticationSchemes.IsNotSet(AuthenticationSchemes.Anonymous);
        }
 
        [DefaultValue(HttpTransportDefaults.UnsafeConnectionNtlmAuthentication)]
        public bool UnsafeConnectionNtlmAuthentication
        {
            get
            {
                return _unsafeConnectionNtlmAuthentication;
            }
 
            set
            {
                _unsafeConnectionNtlmAuthentication = value;
            }
        }
 
        public bool UseDefaultWebProxy
        {
            get
            {
                return _useDefaultWebProxy;
            }
        }
 
        internal string GetWsdlTransportUri(bool useWebSocketTransport)
        {
            if (useWebSocketTransport)
            {
                return TransportPolicyConstants.WebSocketTransportUri;
            }
 
            return TransportPolicyConstants.HttpTransportUri;
        }
 
        public override BindingElement Clone()
        {
            return new HttpTransportBindingElement(this);
        }
 
        public override T GetProperty<T>(BindingContext context)
        {
            if (context == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
            }
            if (typeof(T) == typeof(ISecurityCapabilities))
            {
                AuthenticationSchemes effectiveAuthenticationSchemes = this.AuthenticationScheme;
                // Desktop: HttpTransportBindingElement.GetEffectiveAuthenticationSchemes(this.AuthenticationScheme, context.BindingParameters);
 
                return (T)(object)new SecurityCapabilities(this.GetSupportsClientAuthenticationImpl(effectiveAuthenticationSchemes),
                    effectiveAuthenticationSchemes == AuthenticationSchemes.Negotiate,
                    this.GetSupportsClientWindowsIdentityImpl(effectiveAuthenticationSchemes),
                    ProtectionLevel.None,
                    ProtectionLevel.None);
            }
            else if (typeof(T) == typeof(IBindingDeliveryCapabilities))
            {
                return (T)(object)new BindingDeliveryCapabilitiesHelper();
            }
            else if (typeof(T) == typeof(TransferMode))
            {
                return (T)(object)this.TransferMode;
            }
#if !FEATURE_NETNATIVE
            else if (typeof(T) == typeof(ExtendedProtectionPolicy))
            {
                return (T)(object)this.ExtendedProtectionPolicy;
            }
#endif //!FEATURE_NETNATIVE
            else if (typeof(T) == typeof(ITransportCompressionSupport))
            {
                return (T)(object)new TransportCompressionSupportHelper();
            }
            else
            {
                Contract.Assert(context.BindingParameters != null);
                if (context.BindingParameters.Find<MessageEncodingBindingElement>() == null)
                {
                    context.BindingParameters.Add(new TextMessageEncodingBindingElement());
                }
                return base.GetProperty<T>(context);
            }
        }
 
        public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
        {
            if (typeof(TChannel) == typeof(IRequestChannel))
            {
                return this.WebSocketSettings.TransportUsage != WebSocketTransportUsage.Always;
            }
            else if (typeof(TChannel) == typeof(IDuplexSessionChannel))
            {
                return this.WebSocketSettings.TransportUsage != WebSocketTransportUsage.Never;
            }
            return false;
        }
 
        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
        {
            if (context == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
            }
 
            if (this.MessageHandlerFactory != null)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(SRServiceModel.HttpPipelineNotSupportedOnClientSide, "MessageHandlerFactory")));
            }
 
            if (!this.CanBuildChannelFactory<TChannel>(context))
            {
                Contract.Assert(context.Binding != null);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("TChannel", string.Format(SRServiceModel.CouldnTCreateChannelForChannelType2, context.Binding.Name, typeof(TChannel)));
            }
 
            if (_authenticationScheme == AuthenticationSchemes.None)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", string.Format(SRServiceModel.HttpAuthSchemeCannotBeNone,
                    _authenticationScheme));
            }
            else if (!_authenticationScheme.IsSingleton())
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", string.Format(SRServiceModel.HttpRequiresSingleAuthScheme,
                    _authenticationScheme));
            }
 
            return (IChannelFactory<TChannel>)(object)new HttpChannelFactory<TChannel>(this, context);
        }
 
        internal virtual void OnImportPolicy(MetadataImporter importer, PolicyConversionContext policyContext)
        {
        }
 
        void ITransportPolicyImport.ImportPolicy(MetadataImporter importer, PolicyConversionContext policyContext)
        {
            ICollection<XmlElement> bindingAssertions = policyContext.GetBindingAssertions();
            List<XmlElement> httpAuthAssertions = new List<XmlElement>();
 
            bool foundAssertion = false;
            foreach (XmlElement assertion in bindingAssertions)
            {
                if (assertion.NamespaceURI != TransportPolicyConstants.HttpTransportNamespace)
                {
                    continue;
                }
 
                switch (assertion.LocalName)
                {
                    case TransportPolicyConstants.BasicHttpAuthenticationName:
                        this.AuthenticationScheme = AuthenticationSchemes.Basic;
                        break;
                    case TransportPolicyConstants.DigestHttpAuthenticationName:
                        this.AuthenticationScheme = AuthenticationSchemes.Digest;
                        break;
                    case TransportPolicyConstants.NegotiateHttpAuthenticationName:
                        this.AuthenticationScheme = AuthenticationSchemes.Negotiate;
                        break;
                    case TransportPolicyConstants.NtlmHttpAuthenticationName:
                        this.AuthenticationScheme = AuthenticationSchemes.Ntlm;
                        break;
                    default:
                        continue;
                }
 
                if (foundAssertion)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
                        string.Format(SRServiceModel.HttpTransportCannotHaveMultipleAuthenticationSchemes, policyContext.Contract.Namespace, policyContext.Contract.Name)));
                }
 
                foundAssertion = true;
                httpAuthAssertions.Add(assertion);
            }
            httpAuthAssertions.ForEach(delegate (XmlElement element) { bindingAssertions.Remove(element); });
 
            // This code is being used when we are generating the client configration. Before that, we should already set TransportUsage as Always if the server
            // is using WebSocket.
            if (this.WebSocketSettings.TransportUsage == WebSocketTransportUsage.Always)
            {
                foreach (XmlElement assertion in bindingAssertions)
                {
                    if (assertion.NamespaceURI != TransportPolicyConstants.WebSocketPolicyNamespace)
                    {
                        continue;
                    }
 
                    string transferMode = assertion.LocalName;
                    TransferMode result;
                    if (!Enum.TryParse<TransferMode>(transferMode, true, out result) || !TransferModeHelper.IsDefined(result) || result == TransferMode.Buffered)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(string.Format(
                                SRServiceModel.WebSocketTransportPolicyAssertionInvalid,
                                policyContext.Contract.Namespace,
                                policyContext.Contract.Name,
                                transferMode,
                                TransferMode.Streamed,
                                TransferMode.StreamedRequest,
                                TransferMode.StreamedResponse)));
                    }
 
                    this.TransferMode = result;
                    bindingAssertions.Remove(assertion);
                    break;
                }
            }
 
            OnImportPolicy(importer, policyContext);
        }
 
        internal override bool IsMatch(BindingElement b)
        {
            if (!base.IsMatch(b))
                return false;
            HttpTransportBindingElement http = b as HttpTransportBindingElement;
            if (http == null)
                return false;
            if (_allowCookies != http._allowCookies)
                return false;
            if (_authenticationScheme != http._authenticationScheme)
                return false;
            if (_decompressionEnabled != http._decompressionEnabled)
                return false;
            if (_hostNameComparisonMode != http._hostNameComparisonMode)
                return false;
            if (_inheritBaseAddressSettings != http._inheritBaseAddressSettings)
                return false;
            if (_keepAliveEnabled != http._keepAliveEnabled)
                return false;
            if (_maxBufferSize != http._maxBufferSize)
                return false;
            if (_method != http._method)
                return false;
            if (_realm != http._realm)
                return false;
            if (_transferMode != http._transferMode)
                return false;
            if (_unsafeConnectionNtlmAuthentication != http._unsafeConnectionNtlmAuthentication)
                return false;
            if (_useDefaultWebProxy != http._useDefaultWebProxy)
                return false;
            if (!this.WebSocketSettings.Equals(http.WebSocketSettings))
                return false;
 
            return true;
        }
 
        private MessageEncodingBindingElement FindMessageEncodingBindingElement(BindingElementCollection bindingElements, out bool createdNew)
        {
            createdNew = false;
            MessageEncodingBindingElement encodingBindingElement = bindingElements.Find<MessageEncodingBindingElement>();
            if (encodingBindingElement == null)
            {
                createdNew = true;
                encodingBindingElement = new TextMessageEncodingBindingElement();
            }
            return encodingBindingElement;
        }
 
        private class BindingDeliveryCapabilitiesHelper : IBindingDeliveryCapabilities
        {
            internal BindingDeliveryCapabilitiesHelper()
            {
            }
            bool IBindingDeliveryCapabilities.AssuresOrderedDelivery
            {
                get { return false; }
            }
 
            bool IBindingDeliveryCapabilities.QueuedDelivery
            {
                get { return false; }
            }
        }
 
        private class TransportCompressionSupportHelper : ITransportCompressionSupport
        {
            public bool IsCompressionFormatSupported(CompressionFormat compressionFormat)
            {
                return true;
            }
        }
    }
}