File: System\ServiceModel\Channels\TransportBindingElementImporter.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.Xml;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Security;
    using System.Xml.Schema;
    using System.Collections.ObjectModel;
    using System.Collections.Generic;
    using WsdlNS = System.Web.Services.Description;
 
    // implemented by Indigo Transports
    interface ITransportPolicyImport
    {
        void ImportPolicy(MetadataImporter importer, PolicyConversionContext policyContext);
    }
 
    public class TransportBindingElementImporter : IWsdlImportExtension, IPolicyImportExtension
    {
 
        void IWsdlImportExtension.BeforeImport(WsdlNS.ServiceDescriptionCollection wsdlDocuments, XmlSchemaSet xmlSchemas, ICollection<XmlElement> policy)
        {
            WsdlImporter.SoapInPolicyWorkaroundHelper.InsertAdHocTransportPolicy(wsdlDocuments);
        }
 
        void IWsdlImportExtension.ImportContract(WsdlImporter importer, WsdlContractConversionContext context) { }
 
        void IWsdlImportExtension.ImportEndpoint(WsdlImporter importer, WsdlEndpointConversionContext context)
        {
            if (context == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
            }
 
#pragma warning suppress 56506 // Microsoft, these properties cannot be null in this context
            if (context.Endpoint.Binding == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context.Endpoint.Binding");
            }
 
#pragma warning suppress 56506 // Microsoft, CustomBinding.Elements never be null
            TransportBindingElement transportBindingElement = GetBindingElements(context).Find<TransportBindingElement>();
 
            bool transportHandledExternaly = (transportBindingElement != null) && !StateHelper.IsRegisteredTransportBindingElement(importer, context);
            if (transportHandledExternaly)
                return;
 
#pragma warning suppress 56506 // Microsoft, these properties cannot be null in this context
            WsdlNS.SoapBinding soapBinding = (WsdlNS.SoapBinding)context.WsdlBinding.Extensions.Find(typeof(WsdlNS.SoapBinding));
            if (soapBinding != null && transportBindingElement == null)
            {
                CreateLegacyTransportBindingElement(importer, soapBinding, context);
            }
 
            // Try to import WS-Addressing address from the port
            if (context.WsdlPort != null)
            {
                ImportAddress(context, transportBindingElement);
            }
 
        }
 
        static BindingElementCollection GetBindingElements(WsdlEndpointConversionContext context)
        {
            Binding binding = context.Endpoint.Binding;
            BindingElementCollection elements = binding is CustomBinding ? ((CustomBinding)binding).Elements : binding.CreateBindingElements();
            return elements;
        }
 
        static CustomBinding ConvertToCustomBinding(WsdlEndpointConversionContext context)
        {
            CustomBinding customBinding = context.Endpoint.Binding as CustomBinding;
            if (customBinding == null)
            {
                customBinding = new CustomBinding(context.Endpoint.Binding);
                context.Endpoint.Binding = customBinding;
            }
            return customBinding;
        }
 
        static void ImportAddress(WsdlEndpointConversionContext context, TransportBindingElement transportBindingElement)
        {
            EndpointAddress address = context.Endpoint.Address = WsdlImporter.WSAddressingHelper.ImportAddress(context.WsdlPort);
            if (address != null)
            {
                context.Endpoint.Address = address;
 
                // Replace the http BE with https BE only if the uri scheme is https and the transport binding element is a HttpTransportBindingElement but not HttpsTransportBindingElement
                if (address.Uri.Scheme == Uri.UriSchemeHttps && transportBindingElement is HttpTransportBindingElement && !(transportBindingElement is HttpsTransportBindingElement))
                {
                    BindingElementCollection elements = ConvertToCustomBinding(context).Elements;
                    elements.Remove(transportBindingElement);
                    elements.Add(CreateHttpsFromHttp(transportBindingElement as HttpTransportBindingElement));
                }
            }
        }
 
        static void CreateLegacyTransportBindingElement(WsdlImporter importer, WsdlNS.SoapBinding soapBinding, WsdlEndpointConversionContext context)
        {
            // We create a transportBindingElement based on the SoapBinding's Transport
            TransportBindingElement transportBindingElement = CreateTransportBindingElements(soapBinding.Transport, null);
            if (transportBindingElement != null)
            {
                ConvertToCustomBinding(context).Elements.Add(transportBindingElement);
                StateHelper.RegisterTransportBindingElement(importer, context);
            }
        }
 
        static HttpsTransportBindingElement CreateHttpsFromHttp(HttpTransportBindingElement http)
        {
            if (http == null) return new HttpsTransportBindingElement();
 
            HttpsTransportBindingElement https = HttpsTransportBindingElement.CreateFromHttpBindingElement(http);
 
            return https;
        }
 
        void IPolicyImportExtension.ImportPolicy(MetadataImporter importer, PolicyConversionContext policyContext)
        {
            XmlQualifiedName wsdlBindingQName;
            string transportUri = WsdlImporter.SoapInPolicyWorkaroundHelper.FindAdHocTransportPolicy(policyContext, out wsdlBindingQName);
 
            if (transportUri != null && !policyContext.BindingElements.Contains(typeof(TransportBindingElement)))
            {
                TransportBindingElement transportBindingElement = CreateTransportBindingElements(transportUri, policyContext);
 
                if (transportBindingElement != null)
                {
                    ITransportPolicyImport transportPolicyImport = transportBindingElement as ITransportPolicyImport;
                    if (transportPolicyImport != null)
                        transportPolicyImport.ImportPolicy(importer, policyContext);
 
                    policyContext.BindingElements.Add(transportBindingElement);
                    StateHelper.RegisterTransportBindingElement(importer, wsdlBindingQName);
                }
            }
        }
 
        static TransportBindingElement CreateTransportBindingElements(string transportUri, PolicyConversionContext policyContext)
        {
            TransportBindingElement transportBindingElement = null;
            // Try and Create TransportBindingElement
            switch (transportUri)
            {
                case TransportPolicyConstants.HttpTransportUri:
                    transportBindingElement = GetHttpTransportBindingElement(policyContext);
                    break;
                case TransportPolicyConstants.TcpTransportUri:
                    transportBindingElement = new TcpTransportBindingElement();
                    break;
                case TransportPolicyConstants.NamedPipeTransportUri:
                    transportBindingElement = new NamedPipeTransportBindingElement();
                    break;
                case TransportPolicyConstants.MsmqTransportUri:
                    transportBindingElement = new MsmqTransportBindingElement();
                    break;
                case TransportPolicyConstants.PeerTransportUri:
#pragma warning disable 0618
                    transportBindingElement = new PeerTransportBindingElement();
#pragma warning restore 0618					
                    break;
                case TransportPolicyConstants.WebSocketTransportUri:
                    HttpTransportBindingElement httpTransport = GetHttpTransportBindingElement(policyContext);
                    httpTransport.WebSocketSettings.TransportUsage = WebSocketTransportUsage.Always;
                    httpTransport.WebSocketSettings.SubProtocol = WebSocketTransportSettings.SoapSubProtocol;
                    transportBindingElement = httpTransport;
                    break;
                default:
                    // There may be another registered converter that can handle this transport.
                    break;
            }
 
            return transportBindingElement;
        }
 
        static HttpTransportBindingElement GetHttpTransportBindingElement(PolicyConversionContext policyContext)
        {
            if (policyContext != null)
            {
                WSSecurityPolicy sp = null;
                ICollection<XmlElement> policyCollection = policyContext.GetBindingAssertions();
                if (WSSecurityPolicy.TryGetSecurityPolicyDriver(policyCollection, out sp) && sp.ContainsWsspHttpsTokenAssertion(policyCollection))
                {
                    HttpsTransportBindingElement httpsBinding = new HttpsTransportBindingElement();
                    httpsBinding.MessageSecurityVersion = sp.GetSupportedMessageSecurityVersion(SecurityVersion.WSSecurity11);
                    return httpsBinding;
                }
            }
 
            return new HttpTransportBindingElement();
        }
    }
 
    internal static class StateHelper
    {
        readonly static object StateBagKey = new object();
 
        static Dictionary<XmlQualifiedName, XmlQualifiedName> GetGeneratedTransportBindingElements(MetadataImporter importer)
        {
            object retValue;
            if (!importer.State.TryGetValue(StateHelper.StateBagKey, out retValue))
            {
                retValue = new Dictionary<XmlQualifiedName, XmlQualifiedName>();
                importer.State.Add(StateHelper.StateBagKey, retValue);
            }
            return (Dictionary<XmlQualifiedName, XmlQualifiedName>)retValue;
        }
 
        internal static void RegisterTransportBindingElement(MetadataImporter importer, XmlQualifiedName wsdlBindingQName)
        {
            GetGeneratedTransportBindingElements(importer)[wsdlBindingQName] = wsdlBindingQName;
        }
 
        internal static void RegisterTransportBindingElement(MetadataImporter importer, WsdlEndpointConversionContext context)
        {
            XmlQualifiedName wsdlBindingQName = new XmlQualifiedName(context.WsdlBinding.Name, context.WsdlBinding.ServiceDescription.TargetNamespace);
            GetGeneratedTransportBindingElements(importer)[wsdlBindingQName] = wsdlBindingQName;
        }
 
        internal static bool IsRegisteredTransportBindingElement(WsdlImporter importer, WsdlEndpointConversionContext context)
        {
            XmlQualifiedName key = new XmlQualifiedName(context.WsdlBinding.Name, context.WsdlBinding.ServiceDescription.TargetNamespace);
            return GetGeneratedTransportBindingElements(importer).ContainsKey(key);
        }
    }
 
 
    static class TransportPolicyConstants
    {
        public const string BasicHttpAuthenticationName = "BasicAuthentication";
        public const string CompositeDuplex = "CompositeDuplex";
        public const string CompositeDuplexNamespace = "http://schemas.microsoft.com/net/2006/06/duplex";
        public const string CompositeDuplexPrefix = "cdp";
        public const string DigestHttpAuthenticationName = "DigestAuthentication";
        public const string DotNetFramingNamespace = FramingEncodingString.NamespaceUri + "/policy";
        public const string DotNetFramingPrefix = "msf";
        public const string HttpTransportNamespace = "http://schemas.microsoft.com/ws/06/2004/policy/http";
        public const string HttpTransportPrefix = "http";
        public const string HttpTransportUri = "http://schemas.xmlsoap.org/soap/http";
        public const string MsmqBestEffort = "MsmqBestEffort";
        public const string MsmqSession = "MsmqSession";
        public const string MsmqTransportNamespace = "http://schemas.microsoft.com/ws/06/2004/mspolicy/msmq";
        public const string MsmqTransportPrefix = "msmq";
        public const string MsmqTransportUri = "http://schemas.microsoft.com/soap/msmq";
        public const string MsmqVolatile = "MsmqVolatile";
        public const string MsmqAuthenticated = "Authenticated";
        public const string MsmqWindowsDomain = "WindowsDomain";
        public const string NamedPipeTransportUri = "http://schemas.microsoft.com/soap/named-pipe";
        public const string NegotiateHttpAuthenticationName = "NegotiateAuthentication";
        public const string NtlmHttpAuthenticationName = "NtlmAuthentication";
        public const string PeerTransportUri = "http://schemas.microsoft.com/soap/peer";
        public const string ProtectionLevelName = "ProtectionLevel";
        public const string RequireClientCertificateName = "RequireClientCertificate";
        public const string SslTransportSecurityName = "SslTransportSecurity";
        public const string StreamedName = "Streamed";
        public const string TcpTransportUri = "http://schemas.microsoft.com/soap/tcp";
        public const string WebSocketPolicyPrefix = "mswsp";
        public const string WebSocketPolicyNamespace = "http://schemas.microsoft.com/soap/websocket/policy";
        public const string WebSocketTransportUri = "http://schemas.microsoft.com/soap/websocket";
        public const string WebSocketEnabled = "WebSocketEnabled";
        public const string WindowsTransportSecurityName = "WindowsTransportSecurity";
    }
}